-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathordered_arguments.rb
114 lines (100 loc) · 3.36 KB
/
ordered_arguments.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# frozen_string_literal: true
module RuboCop
module Cop
module GraphQL
# Arguments should be alphabetically sorted within groups.
#
# @example
# # good
#
# class UpdateProfile < BaseMutation
# argument :email, String, required: false
# argument :name, String, required: false
# end
#
# # good
#
# class UpdateProfile < BaseMutation
# argument :uuid, ID, required: true
#
# argument :email, String, required: false
# argument :name, String, required: false
# end
#
# # good
#
# class UserType < BaseType
# field :posts, PostType do
# argument :created_after, ISO8601DateTime, required: false
# argument :created_before, ISO8601DateTime, required: false
# end
# end
#
# # bad
#
# class UpdateProfile < BaseMutation
# argument :uuid, ID, required: true
# argument :name, String, required: false
# argument :email, String, required: false
# end
#
# # bad
#
# class UserType < BaseType
# field :posts, PostType do
# argument :created_before, ISO8601DateTime, required: false
# argument :created_after, ISO8601DateTime, required: false
# end
# end
#
class OrderedArguments < Base
extend AutoCorrector
include RuboCop::GraphQL::SwapRange
include RuboCop::GraphQL::CompareOrder
MSG = "Arguments should be sorted in an alphabetical order within their section. " \
"Field `%<current>s` should appear before `%<previous>s`."
def on_class(node)
# Do a single pass over descendants to get argument declarations
# with and without a block.
argument_declarations = argument_declaration(node).map do |declaration|
if argument_declaration_with_block?(declaration)
declaration.parent
else
declaration
end
end
argument_declarations.each_cons(2) do |previous, current|
next unless consecutive_lines(previous, current)
next if correct_order?(argument_name(previous), argument_name(current))
register_offense(previous, current)
end
end
private
def argument_declaration_with_block?(node)
node.parent&.block_type? && node.parent.send_node == node
end
def register_offense(previous, current)
message = format(
self.class::MSG,
previous: argument_name(previous),
current: argument_name(current)
)
add_offense(current, message: message) do |corrector|
swap_range(corrector, current, previous)
end
end
def argument_name(node)
argument = node.block_type? ? node.children.first.first_argument : node.first_argument
argument.value.to_s
end
def consecutive_lines(previous, current)
previous.source_range.last_line == current.source_range.first_line - 1
end
# @!method argument_declaration(node)
def_node_search :argument_declaration, <<~PATTERN
(send nil? :argument (:sym _) ...)
PATTERN
end
end
end
end