sevencop 0.31.1 → 0.32.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc9eabfebd03bc54220aefb905dcc084374a282323a47907cbd05b7bd03df7ae
4
- data.tar.gz: b27b429ddbdfa4324f2bb929fbd84622265133a78a5045cab24ac20171c62ae3
3
+ metadata.gz: b73ab476e9b75a7c542283153fb21a6e34bc065305f174ad7fc360d8d4fe83ab
4
+ data.tar.gz: 36704b1877d0042042c91112699c201566a60bfc84e9bea4eb00824f910d6516
5
5
  SHA512:
6
- metadata.gz: e9667ad3506390f48001dc07e16faa48c07eab28afb91f06d5406043481a0c7637fb789e2718b0f7c3414a6666b8f7cda70c950eb4b40c11217629748fc85880
7
- data.tar.gz: 799f7f8cd175b53edf5804b507e954834e030c5973887b8b148ea803b0ace967e60c181bbf4f212386607eff838371cfccb8d1d255b5e2414b0a6fc9cfccf746
6
+ metadata.gz: b6d0f76043cf888387a230e1de66233a0ba68be0b647883bd8dac259f7cd7c97b504a3ce24577a14fd15fc590f146e17ff605223e2616e6c13992ec4e9a855d0
7
+ data.tar.gz: ac8f462e97a095a80c612318d9eb69373c0b3c9ce11a3fc49c8143ba92d40adcdfc2279b6ad0edb0eaf07a6c12ac500a8090b12969feee0529ec91c981a1a72f
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sevencop (0.31.1)
4
+ sevencop (0.32.0)
5
5
  activesupport
6
6
  rubocop
7
7
 
data/README.md CHANGED
@@ -40,6 +40,7 @@ Note that all cops are `Enabled: false` by default.
40
40
  - [Sevencop/RailsBelongsToOptional](lib/rubocop/cop/sevencop/rails_belongs_to_optional.rb)
41
41
  - [Sevencop/RailsDateAndTimeCalculation](lib/rubocop/cop/sevencop/rails_date_and_time_calculation.rb)
42
42
  - [Sevencop/RailsOrderField](lib/rubocop/cop/sevencop/rails_order_field.rb)
43
+ - [Sevencop/RailsOrderFieldInOrderOf](lib/rubocop/cop/sevencop/rails_order_field_in_order_of.rb)
43
44
  - [Sevencop/RailsSpecificActionName](lib/rubocop/cop/sevencop/rails_specific_action_name.rb)
44
45
  - [Sevencop/RailsUniquenessValidatorExplicitCaseSensitivity](lib/rubocop/cop/sevencop/rails_uniqueness_validator_explicit_case_sensitivity.rb)
45
46
  - [Sevencop/RailsWhereNot](lib/rubocop/cop/sevencop/rails_where_not.rb)
data/config/default.yml CHANGED
@@ -7,13 +7,13 @@ Sevencop/AutoloadOrdered:
7
7
 
8
8
  Sevencop/ConstantBase:
9
9
  Description: |
10
- Remove unnecessary `::` prefix from constant.
10
+ Avoid unnecessary `::` prefix on constant.
11
11
  Enabled: false
12
12
  VersionAdded: '0.31'
13
13
 
14
14
  Sevencop/FactoryBotAssociationOption:
15
15
  Description: |
16
- Remove redundant options from FactoryBot associations.
16
+ Avoid redundant options on FactoryBot associations.
17
17
  Enabled: false
18
18
  VersionAdded: '0.21'
19
19
 
@@ -95,6 +95,12 @@ Sevencop/RailsOrderField:
95
95
  Safe: false
96
96
  VersionAdded: '0.4'
97
97
 
98
+ Sevencop/RailsOrderFieldInOrderOf:
99
+ Description: |
100
+ Prefer `in_order_of` to MySQL `FIELD` function.
101
+ Enabled: false
102
+ VersionAdded: '0.32'
103
+
98
104
  Sevencop/RailsSpecificActionName:
99
105
  Description: |
100
106
  Use only specific action names.
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Sevencop
6
- # Remove unnecessary `::` prefix from constant.
6
+ # Avoid unnecessary `::` prefix on constant.
7
7
  #
8
8
  # @example
9
9
  # # bad
@@ -23,18 +23,18 @@ module RuboCop
23
23
  # end
24
24
  #
25
25
  # # good
26
- # class Klass
26
+ # class A
27
27
  # ::Const
28
28
  # end
29
29
  #
30
30
  # # good
31
- # class module
31
+ # class A
32
32
  # ::Const
33
33
  # end
34
34
  class ConstantBase < Base
35
35
  extend AutoCorrector
36
36
 
37
- MSG = 'Remove unnecessary `::` prefix from constant.'
37
+ MSG = 'Avoid unnecessary `::` prefix on constant.'
38
38
 
39
39
  # @param node [RuboCop::AST::CbaseNode]
40
40
  # @return [void]
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Sevencop
6
- # Remove redundant options from FactoryBot associations.
6
+ # Avoid redundant options on FactoryBot associations.
7
7
  #
8
8
  # @example
9
9
  # # bad
@@ -16,7 +16,7 @@ module RuboCop
16
16
 
17
17
  include RangeHelp
18
18
 
19
- MSG = 'Remove redundant options from FactoryBot associations.'
19
+ MSG = 'Avoid redundant options on FactoryBot associations.'
20
20
 
21
21
  RESTRICT_ON_SEND = %i[association].freeze
22
22
 
@@ -0,0 +1,316 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Sevencop
6
+ # Prefer `in_order_of` to MySQL `FIELD` function.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # order('FIELD(id, 1, 2, 3)')
11
+ #
12
+ # # good
13
+ # in_order_of(:id, [1, 2, 3])
14
+ #
15
+ # # bad
16
+ # order(Arel.sql('FIELD(id, 1, 2, 3)'))
17
+ #
18
+ # # good
19
+ # in_order_of(:id, [1, 2, 3])
20
+ #
21
+ # # bad
22
+ # order('FIELD(id, 1, 2, 3) DESC')
23
+ #
24
+ # # good
25
+ # in_order_of(:id, [1, 2, 3]).reverse_order
26
+ #
27
+ # # bad
28
+ # order("FIELD(id, #{ids.join(', ')})")
29
+ #
30
+ # # good
31
+ # in_order_of(:id, ids)
32
+ class RailsOrderFieldInOrderOf < Base
33
+ extend AutoCorrector
34
+
35
+ REGEXP_FIELD_DSTR_HEAD = /
36
+ \A
37
+ \s*
38
+ field\(
39
+ \s*
40
+ (?<column_name>\w+)
41
+ ,\s*
42
+ \z
43
+ /ix.freeze
44
+
45
+ REGEXP_FIELD_DSTR_TAIL = /
46
+ \A
47
+ \s*
48
+ \)
49
+ \s*
50
+ (?:
51
+ \s+
52
+ (?<order>asc|desc)
53
+ )?
54
+ \z
55
+ /ix.freeze
56
+
57
+ REGEXP_FIELD_STR = /
58
+ \A
59
+ \s*
60
+ field\(
61
+ \s*
62
+ (?<column_name>\w+)
63
+ ,\s*
64
+ (?<values>.+)
65
+ \s*
66
+ \)
67
+ (?:
68
+ \s+
69
+ (?<order>asc|desc)
70
+ )?
71
+ \s*
72
+ \z
73
+ /ix.freeze
74
+
75
+ MSG = 'Prefer `in_order_of` to MySQL `FIELD` function.'
76
+
77
+ RESTRICT_ON_SEND = %i[
78
+ order
79
+ reorder
80
+ ].freeze
81
+
82
+ # @param node [RuboCop::AST::SendNode]
83
+ # @return [void]
84
+ def on_send(node)
85
+ return unless bad?(node)
86
+
87
+ add_offense(node) do |corrector|
88
+ autocorrect(corrector, node)
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ # @!method match_arel_sql_field?(node)
95
+ # @param node [RuboCop::AST::Node, nil]
96
+ # @return [Boolean]
97
+ def_node_matcher :match_arel_sql_field?, <<~PATTERN
98
+ (send
99
+ (const {nil? cbase} :Arel)
100
+ :sql
101
+ #match_field?
102
+ )
103
+ PATTERN
104
+
105
+ # @!method match_field?(node)
106
+ # @param node [RuboCop::AST::Node, nil]
107
+ # @return [Boolean]
108
+ def_node_matcher :match_field?, <<~PATTERN
109
+ {
110
+ #match_field_dstr?
111
+ #match_field_str?
112
+ }
113
+ PATTERN
114
+
115
+ # @!method match_field_dstr_body?(node)
116
+ # @param node [RuboCop::AST::Node, nil]
117
+ # @return [Boolean]
118
+ def_node_matcher :match_field_dstr_body?, <<~PATTERN
119
+ (begin
120
+ (send
121
+ _
122
+ :join
123
+ (str #match_field_dstr_body_separator?)
124
+ )
125
+ )
126
+ PATTERN
127
+
128
+ # @!method match_order_with_field?(node)
129
+ # @param node [RuboCop::AST::Node, nil]
130
+ # @return [Boolean]
131
+ def_node_matcher :match_order_with_field?, <<~PATTERN
132
+ (send
133
+ _
134
+ _
135
+ {
136
+ #match_arel_sql_field?
137
+ #match_field?
138
+ }
139
+ ...
140
+ )
141
+ PATTERN
142
+ alias bad? match_order_with_field?
143
+
144
+ # @param corrector [RuboCop::Cop::Corrector]
145
+ # @param node [RuboCop::AST::SendNode]
146
+ def autocorrect(
147
+ corrector,
148
+ node
149
+ )
150
+ corrector.replace(
151
+ node.location.expression.with(
152
+ begin_pos: node.location.selector.begin_pos
153
+ ),
154
+ format_in_order_of(node)
155
+ )
156
+ end
157
+
158
+ # @param node [RuboCop::AST::SendNode]
159
+ # @return [String, nil]
160
+ def extract_column_name(node)
161
+ if node.each_descendant(:dstr).any?
162
+ extract_column_name_from_dstr(node)
163
+ else
164
+ extract_column_name_from_str(node)
165
+ end
166
+ end
167
+
168
+ # @param node [RuboCop::AST::SendNode]
169
+ # @return [String]
170
+ def extract_column_name_from_dstr(node)
171
+ node.each_descendant(:str).first.value[REGEXP_FIELD_DSTR_HEAD, :column_name]
172
+ end
173
+
174
+ # @param node [RuboCop::AST::SendNode]
175
+ # @return [String]
176
+ def extract_column_name_from_str(node)
177
+ node.each_descendant(:str).first.value[REGEXP_FIELD_STR, :column_name]
178
+ end
179
+
180
+ # @param node [RuboCop::AST::SendNode]
181
+ # @return [String, nil]
182
+ def extract_order_from_dstr(node)
183
+ node.each_descendant(:str).to_a.last.value[REGEXP_FIELD_DSTR_TAIL, :order]
184
+ end
185
+
186
+ # @param node [RuboCop::AST::SendNode]
187
+ # @return [String, nil]
188
+ def extract_order_from_str(node)
189
+ node.each_descendant(:str).first.value[REGEXP_FIELD_STR, :order]
190
+ end
191
+
192
+ # @param node [RuboCop::AST::SendNode]
193
+ # @return [String, nil]
194
+ def extract_rest_order(node)
195
+ rest_order_arguments = node.arguments[1..]
196
+ return if rest_order_arguments.empty?
197
+
198
+ format(
199
+ '.order(%<rest_order_arguments>s)',
200
+ rest_order_arguments: rest_order_arguments.map(&:source).join(', ')
201
+ )
202
+ end
203
+
204
+ # @param node [RuboCop::AST::SendNode]
205
+ # @return [String, nil]
206
+ def extract_reverse_order_from_dstr(node)
207
+ '.reverse_order' if match_desc_on_dstr?(node)
208
+ end
209
+
210
+ # @param node [RuboCop::AST::SendNode]
211
+ # @return [String, nil]
212
+ def extract_reverse_order_from_str(node)
213
+ '.reverse_order' if match_desc_on_str?(node)
214
+ end
215
+
216
+ # @param node [RuboCop::AST::SendNode]
217
+ # @return [String]
218
+ def extract_values_from_dstr(node)
219
+ node.each_descendant.find do |descendant|
220
+ match_field_dstr_body?(descendant)
221
+ end.children.first.receiver.source
222
+ end
223
+
224
+ # @param node [RuboCop::AST::SendNode]
225
+ # @return [String]
226
+ def extract_values_from_str(node)
227
+ format(
228
+ '[%<values>s]',
229
+ values: node.each_descendant(:str).first.value[REGEXP_FIELD_STR, :values].split(',').map(&:strip).join(', ')
230
+ )
231
+ end
232
+
233
+ # @param node [RuboCop::AST::SendNode]
234
+ # @return [String]
235
+ def format_in_order_of(node)
236
+ if node.each_descendant(:dstr).any?
237
+ format_in_order_of_on_dstr(node)
238
+ else
239
+ format_in_order_of_on_str(node)
240
+ end
241
+ end
242
+
243
+ # @param node [RuboCop::AST::SendNode]
244
+ # @return [String]
245
+ def format_in_order_of_on_dstr(node)
246
+ format(
247
+ 'in_order_of(:%<column_name>s, %<values>s)%<reverse_order>s%<rest_order>s',
248
+ column_name: extract_column_name_from_dstr(node),
249
+ rest_order: extract_rest_order(node),
250
+ reverse_order: extract_reverse_order_from_dstr(node),
251
+ values: extract_values_from_dstr(node)
252
+ )
253
+ end
254
+
255
+ # @param node [RuboCop::AST::SendNode]
256
+ # @return [String]
257
+ def format_in_order_of_on_str(node)
258
+ format(
259
+ 'in_order_of(:%<column_name>s, %<values>s)%<reverse_order>s%<rest_order>s',
260
+ column_name: extract_column_name_from_str(node),
261
+ rest_order: extract_rest_order(node),
262
+ reverse_order: extract_reverse_order_from_str(node),
263
+ values: extract_values_from_str(node)
264
+ )
265
+ end
266
+
267
+ # @param node [RuboCop::AST::SendNode]
268
+ # @return [Boolean]
269
+ def match_desc_on_dstr?(node)
270
+ extract_order_from_dstr(node).to_s.casecmp('desc').zero?
271
+ end
272
+
273
+ # @param node [RuboCop::AST::SendNode]
274
+ # @return [Boolean]
275
+ def match_desc_on_str?(node)
276
+ extract_order_from_str(node).to_s.casecmp('desc').zero?
277
+ end
278
+
279
+ # @param node [RuboCop::AST::Node]
280
+ # @return [Boolean]
281
+ def match_field_dstr?(node)
282
+ node.dstr_type? &&
283
+ node.children.size == 3 &&
284
+ match_field_dstr_head?(node.children[0]) &&
285
+ match_field_dstr_body?(node.children[1]) &&
286
+ match_field_dstr_tail?(node.children[2])
287
+ end
288
+
289
+ # @param separator [String]
290
+ # @return [Boolean]
291
+ def match_field_dstr_body_separator?(separator)
292
+ separator.strip == ','
293
+ end
294
+
295
+ # @param node [RuboCop::AST::Node]
296
+ # @return [Boolean]
297
+ def match_field_dstr_head?(node)
298
+ node&.str_type? &&
299
+ node.value.match?(REGEXP_FIELD_DSTR_HEAD)
300
+ end
301
+
302
+ def match_field_dstr_tail?(node)
303
+ node&.str_type? &&
304
+ node.value.match?(REGEXP_FIELD_DSTR_TAIL)
305
+ end
306
+
307
+ # @param node [RuboCop::AST::Node]
308
+ # @return [Boolean]
309
+ def match_field_str?(node)
310
+ node.str_type? &&
311
+ node.value.match?(REGEXP_FIELD_STR)
312
+ end
313
+ end
314
+ end
315
+ end
316
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sevencop
4
- VERSION = '0.31.1'
4
+ VERSION = '0.32.0'
5
5
  end
data/lib/sevencop.rb CHANGED
@@ -16,6 +16,7 @@ require_relative 'rubocop/cop/sevencop/method_definition_ordered'
16
16
  require_relative 'rubocop/cop/sevencop/rails_belongs_to_optional'
17
17
  require_relative 'rubocop/cop/sevencop/rails_date_and_time_calculation'
18
18
  require_relative 'rubocop/cop/sevencop/rails_order_field'
19
+ require_relative 'rubocop/cop/sevencop/rails_order_field_in_order_of'
19
20
  require_relative 'rubocop/cop/sevencop/rails_specific_action_name'
20
21
  require_relative 'rubocop/cop/sevencop/rails_uniqueness_validator_explicit_case_sensitivity'
21
22
  require_relative 'rubocop/cop/sevencop/rails_where_not'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sevencop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.1
4
+ version: 0.32.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Nakamura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-11-15 00:00:00.000000000 Z
11
+ date: 2022-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -68,6 +68,7 @@ files:
68
68
  - lib/rubocop/cop/sevencop/rails_belongs_to_optional.rb
69
69
  - lib/rubocop/cop/sevencop/rails_date_and_time_calculation.rb
70
70
  - lib/rubocop/cop/sevencop/rails_order_field.rb
71
+ - lib/rubocop/cop/sevencop/rails_order_field_in_order_of.rb
71
72
  - lib/rubocop/cop/sevencop/rails_specific_action_name.rb
72
73
  - lib/rubocop/cop/sevencop/rails_uniqueness_validator_explicit_case_sensitivity.rb
73
74
  - lib/rubocop/cop/sevencop/rails_where_not.rb