rubocop-rails 2.13.0 → 2.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.