rubocop-rails 2.13.0 → 2.13.1

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: f12ec262c68bd72f514b80f2140b51278a947f21726ef6307e6267eaec7df4ae
4
- data.tar.gz: 2a09b4db338465d1904a19e8d4af871ba485e75244613299fabe473854125e4c
3
+ metadata.gz: 624c6bfb8dc9c5db0f6dd6bec835bd71e5e5a7aadc298342c15faae187226079
4
+ data.tar.gz: 4354d39a8367f0e4cb46b5a890cdd0836bfff6d838b5ae320b3df3a480f529af
5
5
  SHA512:
6
- metadata.gz: fbf73dc2e978bc210e2dc190b8042720f24e6e374962b2873e6c29ec989ed2a7c1b05646c22a38d57f28e753104ac40a0e592730e63703d99dbb80fc6b498651
7
- data.tar.gz: b70e78355a3fa5b09f3a2a1603c71b16547c3f2f80c0ce7c2dbbc3c38cae0a0ca9464a50895eb54ac8ccd558c9259bf28a1e4ebebb9e3d4f05102832f178fc11
6
+ metadata.gz: 19863e8b5b71b4403d66b46e73ed41ceb1953d2a68996fbcc800ab4c06fcf4dfebadf3907f62d2c575b002223a95fa67fce2412c01fa42b6234848c2791b37e9
7
+ data.tar.gz: 1e3d14e3240f7fdb9182529865ee8257cf19368bac19c33abfba44b0a3b63c54e64457f73725e6c629a5846ea402a96e650f7d6ce0e1943386c857d6a8642d7b
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-21 Bozhidar Batsov
1
+ Copyright (c) 2012-22 Bozhidar Batsov
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -23,22 +23,22 @@ module RuboCop
23
23
 
24
24
  def_node_matcher :on_bad_each_with_object, <<~PATTERN
25
25
  (block
26
- ({send csend} _ :each_with_object (hash))
26
+ (call _ :each_with_object (hash))
27
27
  (args (arg $_el) (arg _memo))
28
- ({send csend} (lvar _memo) :[]= $!`_memo (lvar _el)))
28
+ (call (lvar _memo) :[]= $!`_memo (lvar _el)))
29
29
  PATTERN
30
30
 
31
31
  def_node_matcher :on_bad_to_h, <<~PATTERN
32
32
  (block
33
- ({send csend} _ :to_h)
33
+ (call _ :to_h)
34
34
  (args (arg $_el))
35
35
  (array $_ (lvar _el)))
36
36
  PATTERN
37
37
 
38
38
  def_node_matcher :on_bad_map_to_h, <<~PATTERN
39
- ({send csend}
39
+ (call
40
40
  (block
41
- ({send csend} _ {:map :collect})
41
+ (call _ {:map :collect})
42
42
  (args (arg $_el))
43
43
  (array $_ (lvar _el)))
44
44
  :to_h)
@@ -49,7 +49,7 @@ module RuboCop
49
49
  (const _ :Hash)
50
50
  :[]
51
51
  (block
52
- ({send csend} _ {:map :collect})
52
+ (call _ {:map :collect})
53
53
  (args (arg $_el))
54
54
  (array $_ (lvar _el))))
55
55
  PATTERN
@@ -26,22 +26,22 @@ module RuboCop
26
26
 
27
27
  def_node_matcher :on_bad_each_with_object, <<~PATTERN
28
28
  (block
29
- ({send csend} _ :each_with_object (hash))
29
+ (call _ :each_with_object (hash))
30
30
  (args (arg $_el) (arg _memo))
31
- ({send csend} (lvar _memo) :[]= (lvar _el) $!`_memo))
31
+ (call (lvar _memo) :[]= (lvar _el) $!`_memo))
32
32
  PATTERN
33
33
 
34
34
  def_node_matcher :on_bad_to_h, <<~PATTERN
35
35
  (block
36
- ({send csend} _ :to_h)
36
+ (call _ :to_h)
37
37
  (args (arg $_el))
38
38
  (array (lvar _el) $_))
39
39
  PATTERN
40
40
 
41
41
  def_node_matcher :on_bad_map_to_h, <<~PATTERN
42
- ({send csend}
42
+ (call
43
43
  (block
44
- ({send csend} _ {:map :collect})
44
+ (call _ {:map :collect})
45
45
  (args (arg $_el))
46
46
  (array (lvar _el) $_))
47
47
  :to_h)
@@ -52,7 +52,7 @@ module RuboCop
52
52
  (const _ :Hash)
53
53
  :[]
54
54
  (block
55
- ({send csend} _ {:map :collect})
55
+ (call _ {:map :collect})
56
56
  (args (arg $_el))
57
57
  (array (lvar _el) $_)))
58
58
  PATTERN
@@ -23,6 +23,17 @@ module RuboCop
23
23
  # # good
24
24
  # x = self[:attr]
25
25
  # self[:attr] = val
26
+ #
27
+ # When called from within a method with the same name as the attribute,
28
+ # `read_attribute` and `write_attribute` must be used to prevent an
29
+ # infinite loop:
30
+ #
31
+ # @example
32
+ #
33
+ # # good
34
+ # def foo
35
+ # bar || read_attribute(:foo)
36
+ # end
26
37
  class ReadWriteAttribute < Base
27
38
  extend AutoCorrector
28
39
 
@@ -38,6 +49,7 @@ module RuboCop
38
49
 
39
50
  def on_send(node)
40
51
  return unless read_write_attribute?(node)
52
+ return if within_shadowing_method?(node)
41
53
 
42
54
  add_offense(node.loc.selector, message: message(node)) do |corrector|
43
55
  case node.method_name
@@ -53,6 +65,15 @@ module RuboCop
53
65
 
54
66
  private
55
67
 
68
+ def within_shadowing_method?(node)
69
+ node.each_ancestor(:def).any? do |enclosing_method|
70
+ shadowing_method_name = node.first_argument.value.to_s
71
+ shadowing_method_name << '=' if node.method?(:write_attribute)
72
+
73
+ enclosing_method.method_name.to_s == shadowing_method_name
74
+ end
75
+ end
76
+
56
77
  def message(node)
57
78
  if node.method?(:read_attribute)
58
79
  format(MSG, prefer: 'self[:attr]', current: 'read_attribute(:attr)')
@@ -32,7 +32,7 @@ module RuboCop
32
32
  extend AutoCorrector
33
33
  extend TargetRailsVersion
34
34
 
35
- MSG = 'Remove explicit presence validation for `%<association>s`.'
35
+ MSG = 'Remove explicit presence validation for %<association>s.'
36
36
  RESTRICT_ON_SEND = %i[validates].freeze
37
37
 
38
38
  minimum_target_rails_version 5.0
@@ -43,28 +43,39 @@ module RuboCop
43
43
  # @example source that matches - by association
44
44
  # validates :user, presence: true
45
45
  #
46
+ # @example source that matches - by association
47
+ # validates :name, :user, presence: true
48
+ #
46
49
  # @example source that matches - with presence options
47
50
  # validates :user, presence: { message: 'duplicate' }
48
51
  #
49
52
  # @example source that matches - by a foreign key
50
53
  # validates :user_id, presence: true
54
+ #
55
+ # @example source that DOES NOT match - strict validation
56
+ # validates :user_id, presence: true, strict: true
57
+ #
58
+ # @example source that DOES NOT match - custom strict validation
59
+ # validates :user_id, presence: true, strict: MissingUserError
51
60
  def_node_matcher :presence_validation?, <<~PATTERN
52
- $(
61
+ (
53
62
  send nil? :validates
54
- (sym $_)
55
- ...
56
- $(hash <$(pair (sym :presence) {true hash}) ...>)
63
+ (sym $_)+
64
+ $[
65
+ (hash <$(pair (sym :presence) {true hash}) ...>) # presence: true
66
+ !(hash <$(pair (sym :strict) {true const}) ...>) # strict: true
67
+ ]
57
68
  )
58
69
  PATTERN
59
70
 
60
- # @!method optional_option?(node)
61
- # Match a `belongs_to` association with an optional option in a hash
71
+ # @!method optional?(node)
72
+ # Match a `belongs_to` association with an optional option in a hash
62
73
  def_node_matcher :optional?, <<~PATTERN
63
74
  (send nil? :belongs_to _ ... #optional_option?)
64
75
  PATTERN
65
76
 
66
77
  # @!method optional_option?(node)
67
- # Match an optional option in a hash
78
+ # Match an optional option in a hash
68
79
  def_node_matcher :optional_option?, <<~PATTERN
69
80
  {
70
81
  (hash <(pair (sym :optional) true) ...>) # optional: true
@@ -122,7 +133,7 @@ module RuboCop
122
133
  )
123
134
  PATTERN
124
135
 
125
- # @!method belongs_to_without_fk?(node, fk)
136
+ # @!method belongs_to_without_fk?(node, key)
126
137
  # Match a matching `belongs_to` association, without an explicit `foreign_key` option
127
138
  #
128
139
  # @param node [RuboCop::AST::Node]
@@ -150,21 +161,43 @@ module RuboCop
150
161
  PATTERN
151
162
 
152
163
  def on_send(node)
153
- validation, key, options, presence = presence_validation?(node)
154
- return unless validation
164
+ presence_validation?(node) do |all_keys, options, presence|
165
+ keys = non_optional_belongs_to(node.parent, all_keys)
166
+ return if keys.none?
155
167
 
156
- belongs_to = belongs_to_for(node.parent, key)
157
- return unless belongs_to
158
- return if optional?(belongs_to)
168
+ add_offense_and_correct(node, all_keys, keys, options, presence)
169
+ end
170
+ end
159
171
 
160
- message = format(MSG, association: key.to_s)
172
+ private
161
173
 
162
- add_offense(presence, message: message) do |corrector|
163
- remove_presence_validation(corrector, node, options, presence)
174
+ def add_offense_and_correct(node, all_keys, keys, options, presence)
175
+ add_offense(presence, message: message_for(keys)) do |corrector|
176
+ if options.children.one? # `presence: true` is the only option
177
+ if keys == all_keys
178
+ remove_validation(corrector, node)
179
+ else
180
+ remove_keys_from_validation(corrector, node, keys)
181
+ end
182
+ elsif keys == all_keys
183
+ remove_presence_option(corrector, presence)
184
+ else
185
+ extract_validation_for_keys(corrector, node, keys, options)
186
+ end
164
187
  end
165
188
  end
166
189
 
167
- private
190
+ def message_for(keys)
191
+ display_keys = keys.map { |key| "`#{key}`" }.join('/')
192
+ format(MSG, association: display_keys)
193
+ end
194
+
195
+ def non_optional_belongs_to(node, keys)
196
+ keys.select do |key|
197
+ belongs_to = belongs_to_for(node, key)
198
+ belongs_to && !optional?(belongs_to)
199
+ end
200
+ end
168
201
 
169
202
  def belongs_to_for(model_class_node, key)
170
203
  if key.to_s.end_with?('_id')
@@ -175,17 +208,48 @@ module RuboCop
175
208
  end
176
209
  end
177
210
 
178
- def remove_presence_validation(corrector, node, options, presence)
179
- if options.children.one?
180
- corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
181
- else
182
- range = range_with_surrounding_comma(
183
- range_with_surrounding_space(range: presence.source_range, side: :left),
184
- :left
211
+ def remove_validation(corrector, node)
212
+ corrector.remove(validation_range(node))
213
+ end
214
+
215
+ def remove_keys_from_validation(corrector, node, keys)
216
+ keys.each do |key|
217
+ key_node = node.arguments.find { |arg| arg.value == key }
218
+ key_range = range_with_surrounding_space(
219
+ range: range_with_surrounding_comma(key_node.source_range, :right),
220
+ side: :right
185
221
  )
186
- corrector.remove(range)
222
+ corrector.remove(key_range)
187
223
  end
188
224
  end
225
+
226
+ def remove_presence_option(corrector, presence)
227
+ range = range_with_surrounding_comma(
228
+ range_with_surrounding_space(range: presence.source_range, side: :left),
229
+ :left
230
+ )
231
+ corrector.remove(range)
232
+ end
233
+
234
+ def extract_validation_for_keys(corrector, node, keys, options)
235
+ indentation = ' ' * node.source_range.column
236
+ options_without_presence = options.children.reject { |pair| pair.key.value == :presence }
237
+ source = [
238
+ indentation,
239
+ 'validates ',
240
+ keys.map(&:inspect).join(', '),
241
+ ', ',
242
+ options_without_presence.map(&:source).join(', '),
243
+ "\n"
244
+ ].join
245
+
246
+ remove_keys_from_validation(corrector, node, keys)
247
+ corrector.insert_after(validation_range(node), source)
248
+ end
249
+
250
+ def validation_range(node)
251
+ range_by_whole_lines(node.source_range, include_final_newline: true)
252
+ end
189
253
  end
190
254
  end
191
255
  end
@@ -179,7 +179,7 @@ module RuboCop
179
179
  MSG = '%<action>s is not reversible.'
180
180
 
181
181
  def_node_matcher :irreversible_schema_statement_call, <<~PATTERN
182
- (send nil? ${:change_column :execute :remove_belongs_to :remove_reference} ...)
182
+ (send nil? ${:change_column :execute} ...)
183
183
  PATTERN
184
184
 
185
185
  def_node_matcher :drop_table_call, <<~PATTERN
@@ -61,6 +61,8 @@ module RuboCop
61
61
 
62
62
  def table(node)
63
63
  klass = class_node(node)
64
+ return unless klass
65
+
64
66
  schema.table_by(name: table_name(klass))
65
67
  end
66
68
  end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Rails
5
5
  # This module holds the RuboCop Rails version information.
6
6
  module Version
7
- STRING = '2.13.0'
7
+ STRING = '2.13.1'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.13.0
4
+ version: 2.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-12-25 00:00:00.000000000 Z
13
+ date: 2022-01-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -212,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
212
212
  - !ruby/object:Gem::Version
213
213
  version: '0'
214
214
  requirements: []
215
- rubygems_version: 3.2.32
215
+ rubygems_version: 3.3.3
216
216
  signing_key:
217
217
  specification_version: 4
218
218
  summary: Automatic Rails code style checking tool.