rubocop-rails 2.10.1 → 2.11.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: 79de98cdd4ff48a6ee51c47f2a0bdb76e2393aac1b7974b9181e66723922c148
4
- data.tar.gz: 85c6c566a98c399bcc73b555187e37d8270975cfde4c5bc067726ee61d6c3704
3
+ metadata.gz: bfb9c60dfc813cddff8b72b3f19b337ec3a6570f6210398841ed3852d9dcfad1
4
+ data.tar.gz: 4d026033a03b8f08ccf51e9503cd83c0266132e677478732b64b4fdbe664b02a
5
5
  SHA512:
6
- metadata.gz: 7cf4cddba4e79e79579415bb93aed3bc75e8571a862f32727edc56ffbe5580ba4f5781cf7205c84dbdf1cae9979c7c4b500c227f82ba1a5d476af279a8a9f575
7
- data.tar.gz: c3591c01fe64b61de0ea39d136a57697b90deb71c90c7738de7c4eb8102f0b6a03548c2e2add574d343ce025d087150d57f7b51860d7471f85943fda125fc7e4
6
+ metadata.gz: 1fbc8870d37ee91a53e15f1d067517f8430cd9a0bb383bf27c80c4f84db32ded964c3f345caa6d026aaba187f3e7c78d6a838824433b16d3e9cac9ed7ecd337c
7
+ data.tar.gz: 0cf8c155c3b987a10f581d4983240696fa444d7157e54e4bb2d2ac616721e80a4dacd264e32d15a0165e66d7d1511d97b9c8a07d4e94e1e41accfc71b7803228
data/config/default.yml CHANGED
@@ -83,6 +83,16 @@ Rails/ActiveSupportAliases:
83
83
  Enabled: true
84
84
  VersionAdded: '0.48'
85
85
 
86
+ Rails/AddColumnIndex:
87
+ Description: >-
88
+ Rails migrations don't make use of a given `index` key, but also
89
+ doesn't given an error when it's used, so it makes it seem like an
90
+ index might be used.
91
+ Enabled: pending
92
+ VersionAdded: '2.11'
93
+ Include:
94
+ - db/migrate/*.rb
95
+
86
96
  Rails/AfterCommitOverride:
87
97
  Description: >-
88
98
  This cop enforces that there is only one call to `after_commit`
@@ -193,7 +203,7 @@ Rails/Date:
193
203
  such as Date.today, Date.current etc.
194
204
  Enabled: true
195
205
  VersionAdded: '0.30'
196
- VersionChanged: '0.33'
206
+ VersionChanged: '2.11'
197
207
  # The value `strict` disallows usage of `Date.today`, `Date.current`,
198
208
  # `Date#to_time` etc.
199
209
  # The value `flexible` allows usage of `Date.current`, `Date.yesterday`, etc
@@ -203,6 +213,7 @@ Rails/Date:
203
213
  SupportedStyles:
204
214
  - strict
205
215
  - flexible
216
+ AllowToTime: true
206
217
 
207
218
  Rails/DefaultScope:
208
219
  Description: 'Avoid use of `default_scope`.'
@@ -240,6 +251,12 @@ Rails/DynamicFindBy:
240
251
  AllowedReceivers:
241
252
  - Gem::Specification
242
253
 
254
+ Rails/EagerEvaluationLogMessage:
255
+ Description: 'Checks that blocks are used for interpolated strings passed to `Rails.logger.debug`.'
256
+ Reference: 'https://guides.rubyonrails.org/debugging_rails_applications.html#impact-of-logs-on-performance'
257
+ Enabled: pending
258
+ VersionAdded: '2.11'
259
+
243
260
  Rails/EnumHash:
244
261
  Description: 'Prefer hash syntax over array syntax when defining enums.'
245
262
  StyleGuide: 'https://rails.rubystyle.guide#enums'
@@ -262,8 +279,10 @@ Rails/EnvironmentComparison:
262
279
 
263
280
  Rails/EnvironmentVariableAccess:
264
281
  Description: 'Do not access `ENV` directly after initialization.'
265
- Enabled: pending
282
+ # TODO: Set to `pending` status in RuboCop Rails 2 series when migration doc will be written.
283
+ Enabled: false
266
284
  VersionAdded: '2.10'
285
+ VersionChanged: '2.11'
267
286
  Include:
268
287
  - app/**/*.rb
269
288
  - lib/**/*.rb
@@ -286,6 +305,11 @@ Rails/Exit:
286
305
  Exclude:
287
306
  - lib/**/*.rake
288
307
 
308
+ Rails/ExpandedDateRange:
309
+ Description: 'Checks for expanded date range.'
310
+ Enabled: pending
311
+ VersionAdded: '2.11'
312
+
289
313
  Rails/FilePath:
290
314
  Description: 'Use `Rails.root.join` for file path joining.'
291
315
  Enabled: true
@@ -301,6 +325,8 @@ Rails/FindBy:
301
325
  StyleGuide: 'https://rails.rubystyle.guide#find_by'
302
326
  Enabled: true
303
327
  VersionAdded: '0.30'
328
+ VersionChanged: '2.11'
329
+ IgnoreWhereFirst: true
304
330
  Include:
305
331
  - app/models/**/*.rb
306
332
 
@@ -362,11 +388,20 @@ Rails/HttpStatus:
362
388
  Description: 'Enforces use of symbolic or numeric value to define HTTP status.'
363
389
  Enabled: true
364
390
  VersionAdded: '0.54'
391
+ VersionChanged: '2.11'
365
392
  EnforcedStyle: symbolic
366
393
  SupportedStyles:
367
394
  - numeric
368
395
  - symbolic
369
396
 
397
+ Rails/I18nLocaleAssignment:
398
+ Description: 'Prefer the usage of `I18n.with_locale` instead of manually updating `I18n.locale` value.'
399
+ Enabled: 'pending'
400
+ VersionAdded: '2.11'
401
+ Include:
402
+ - spec/**/*.rb
403
+ - test/**/*.rb
404
+
370
405
  Rails/IgnoredSkipActionFilterOption:
371
406
  Description: 'Checks that `if` and `only` (or `except`) are not used together as options of `skip_*` action filter.'
372
407
  Reference: 'https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-_normalize_callback_options'
@@ -784,6 +819,13 @@ Rails/UnknownEnv:
784
819
  - test
785
820
  - production
786
821
 
822
+ Rails/UnusedIgnoredColumns:
823
+ Description: 'Remove a column that does not exist from `ignored_columns`.'
824
+ Enabled: pending
825
+ VersionAdded: '2.11'
826
+ Include:
827
+ - app/models/**/*.rb
828
+
787
829
  Rails/Validation:
788
830
  Description: 'Use validates :attribute, hash of validations.'
789
831
  Enabled: true
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks for migrations using `add_column` that have an `index`
7
+ # key. `add_column` does not accept `index`, but also does not raise an
8
+ # error for extra keys, so it is possible to mistakenly add the key without
9
+ # realizing it will not actually add an index.
10
+ #
11
+ # @example
12
+ # # bad (will not add an index)
13
+ # add_column :table, :column, :integer, index: true
14
+ #
15
+ # # good
16
+ # add_column :table, :column, :integer
17
+ # add_index :table, :column
18
+ #
19
+ class AddColumnIndex < Base
20
+ extend AutoCorrector
21
+ include RangeHelp
22
+
23
+ MSG = '`add_column` does not accept an `index` key, use `add_index` instead.'
24
+ RESTRICT_ON_SEND = %i[add_column].freeze
25
+
26
+ # @!method add_column_with_index(node)
27
+ def_node_matcher :add_column_with_index, <<~PATTERN
28
+ (
29
+ send nil? :add_column $_table $_column
30
+ <(hash <$(pair {(sym :index) (str "index")} $_) ...>) ...>
31
+ )
32
+ PATTERN
33
+
34
+ def on_send(node)
35
+ table, column, pair, value = add_column_with_index(node)
36
+ return unless pair
37
+
38
+ add_offense(pair) do |corrector|
39
+ corrector.remove(index_range(pair))
40
+
41
+ add_index = "add_index #{table.source}, #{column.source}"
42
+ add_index_opts = ''
43
+
44
+ if value.hash_type?
45
+ hash = value.loc.expression.adjust(begin_pos: 1, end_pos: -1).source.strip
46
+ add_index_opts = ", #{hash}"
47
+ end
48
+
49
+ corrector.insert_after(node, "\n#{add_index}#{add_index_opts}")
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def index_range(pair_node)
56
+ range_with_surrounding_comma(
57
+ range_with_surrounding_space(range: pair_node.loc.expression, side: :left),
58
+ :left
59
+ )
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -12,20 +12,21 @@ module RuboCop
12
12
  # The cop also reports warnings when you are using `to_time` method,
13
13
  # because it doesn't know about Rails time zone either.
14
14
  #
15
- # Two styles are supported for this cop. When EnforcedStyle is 'strict'
15
+ # Two styles are supported for this cop. When `EnforcedStyle` is 'strict'
16
16
  # then the Date methods `today`, `current`, `yesterday`, and `tomorrow`
17
17
  # are prohibited and the usage of both `to_time`
18
18
  # and 'to_time_in_current_zone' are reported as warning.
19
19
  #
20
- # When EnforcedStyle is 'flexible' then only `Date.today` is prohibited
21
- # and only `to_time` is reported as warning.
20
+ # When `EnforcedStyle` is `flexible` then only `Date.today` is prohibited.
21
+ #
22
+ # And you can set a warning for `to_time` with `AllowToTime: false`.
23
+ # `AllowToTime` is `true` by default to prevent false positive on `DateTime` object.
22
24
  #
23
25
  # @example EnforcedStyle: strict
24
26
  # # bad
25
27
  # Date.current
26
28
  # Date.yesterday
27
29
  # Date.today
28
- # date.to_time
29
30
  #
30
31
  # # good
31
32
  # Time.zone.today
@@ -34,7 +35,6 @@ module RuboCop
34
35
  # @example EnforcedStyle: flexible (default)
35
36
  # # bad
36
37
  # Date.today
37
- # date.to_time
38
38
  #
39
39
  # # good
40
40
  # Time.zone.today
@@ -43,6 +43,13 @@ module RuboCop
43
43
  # Date.yesterday
44
44
  # date.in_time_zone
45
45
  #
46
+ # @example AllowToTime: true (default)
47
+ # # good
48
+ # date.to_time
49
+ #
50
+ # @example AllowToTime: false
51
+ # # bad
52
+ # date.to_time
46
53
  class Date < Base
47
54
  include ConfigurableEnforcedStyle
48
55
 
@@ -73,7 +80,7 @@ module RuboCop
73
80
 
74
81
  def on_send(node)
75
82
  return unless node.receiver && bad_methods.include?(node.method_name)
76
-
83
+ return if allow_to_time? && node.method?(:to_time)
77
84
  return if safe_chain?(node) || safe_to_time?(node)
78
85
 
79
86
  check_deprecated_methods(node)
@@ -139,6 +146,10 @@ module RuboCop
139
146
  end
140
147
  end
141
148
 
149
+ def allow_to_time?
150
+ cop_config.fetch('AllowToTime', true)
151
+ end
152
+
142
153
  def good_days
143
154
  style == :strict ? [] : %i[current yesterday tomorrow]
144
155
  end
@@ -37,6 +37,7 @@ module RuboCop
37
37
 
38
38
  MSG = 'Use `%<static_name>s` instead of dynamic `%<method>s`.'
39
39
  METHOD_PATTERN = /^find_by_(.+?)(!)?$/.freeze
40
+ IGNORED_ARGUMENT_TYPES = %i[hash splat].freeze
40
41
 
41
42
  def on_send(node)
42
43
  return if node.receiver.nil? && !inherit_active_record_base?(node) || allowed_invocation?(node)
@@ -44,7 +45,7 @@ module RuboCop
44
45
  method_name = node.method_name
45
46
  static_name = static_method_name(method_name)
46
47
  return unless static_name
47
- return if node.arguments.any?(&:splat_type?)
48
+ return if node.arguments.any? { |argument| IGNORED_ARGUMENT_TYPES.include?(argument.type) }
48
49
 
49
50
  message = format(MSG, static_name: static_name, method: method_name)
50
51
  add_offense(node, message: message) do |corrector|
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks that blocks are used for interpolated strings passed to
7
+ # `Rails.logger.debug`.
8
+ #
9
+ # By default, Rails production environments use the `:info` log level.
10
+ # At the `:info` log level, `Rails.logger.debug` statements do not result
11
+ # in log output. However, Ruby must eagerly evaluate interpolated string
12
+ # arguments passed as method arguments. Passing a block to
13
+ # `Rails.logger.debug` prevents costly evaluation of interpolated strings
14
+ # when no output would be produced anyway.
15
+ #
16
+ # @example
17
+ # #bad
18
+ # Rails.logger.debug "The time is #{Time.zone.now}."
19
+ #
20
+ # #good
21
+ # Rails.logger.debug { "The time is #{Time.zone.now}." }
22
+ #
23
+ class EagerEvaluationLogMessage < Base
24
+ extend AutoCorrector
25
+
26
+ MSG = 'Pass a block to `Rails.logger.debug`.'
27
+ RESTRICT_ON_SEND = %i[debug].freeze
28
+
29
+ def_node_matcher :interpolated_string_passed_to_debug, <<~PATTERN
30
+ (send
31
+ (send
32
+ (const {cbase nil?} :Rails)
33
+ :logger
34
+ )
35
+ :debug
36
+ $(dstr ...)
37
+ )
38
+ PATTERN
39
+
40
+ def on_send(node)
41
+ return if node.parent&.block_type?
42
+
43
+ interpolated_string_passed_to_debug(node) do |arguments|
44
+ message = format(MSG)
45
+
46
+ range = replacement_range(node)
47
+ replacement = replacement_source(node, arguments)
48
+
49
+ add_offense(range, message: message) do |corrector|
50
+ corrector.replace(range, replacement)
51
+ end
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def replacement_range(node)
58
+ stop = node.loc.expression.end
59
+ start = node.loc.selector.end
60
+
61
+ if node.parenthesized_call?
62
+ stop.with(begin_pos: start.begin_pos)
63
+ else
64
+ stop.with(begin_pos: start.begin_pos + 1)
65
+ end
66
+ end
67
+
68
+ def replacement_source(node, arguments)
69
+ if node.parenthesized_call?
70
+ " { #{arguments.source} }"
71
+ else
72
+ "{ #{arguments.source} }"
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -20,8 +20,7 @@ module RuboCop
20
20
 
21
21
  MSG = 'Favor `%<bang>sRails.env.%<env>s?` over `%<source>s`.'
22
22
 
23
- SYM_MSG = 'Do not compare `Rails.env` with a symbol, it will always ' \
24
- 'evaluate to `false`.'
23
+ SYM_MSG = 'Do not compare `Rails.env` with a symbol, it will always evaluate to `false`.'
25
24
 
26
25
  RESTRICT_ON_SEND = %i[== !=].freeze
27
26
 
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks for expanded date range. It only compatible `..` range is targeted.
7
+ # Incompatible `...` range is ignored.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # date.beginning_of_day..date.end_of_day
12
+ # date.beginning_of_week..date.end_of_week
13
+ # date.beginning_of_month..date.end_of_month
14
+ # date.beginning_of_quarter..date.end_of_quarter
15
+ # date.beginning_of_year..date.end_of_year
16
+ #
17
+ # # good
18
+ # date.all_day
19
+ # date.all_week
20
+ # date.all_month
21
+ # date.all_quarter
22
+ # date.all_year
23
+ #
24
+ class ExpandedDateRange < Base
25
+ extend AutoCorrector
26
+ extend TargetRailsVersion
27
+
28
+ MSG = 'Use `%<preferred_method>s` instead.'
29
+
30
+ minimum_target_rails_version 5.1
31
+
32
+ def_node_matcher :expanded_date_range, <<~PATTERN
33
+ (irange
34
+ (send
35
+ $_ {:beginning_of_day :beginning_of_week :beginning_of_month :beginning_of_quarter :beginning_of_year})
36
+ (send
37
+ $_ {:end_of_day :end_of_week :end_of_month :end_of_quarter :end_of_year}))
38
+ PATTERN
39
+
40
+ PREFERRED_METHODS = {
41
+ beginning_of_day: 'all_day',
42
+ beginning_of_week: 'all_week',
43
+ beginning_of_month: 'all_month',
44
+ beginning_of_quarter: 'all_quarter',
45
+ beginning_of_year: 'all_year'
46
+ }.freeze
47
+
48
+ MAPPED_DATE_RANGE_METHODS = {
49
+ beginning_of_day: :end_of_day,
50
+ beginning_of_week: :end_of_week,
51
+ beginning_of_month: :end_of_month,
52
+ beginning_of_quarter: :end_of_quarter,
53
+ beginning_of_year: :end_of_year
54
+ }.freeze
55
+
56
+ def on_irange(node)
57
+ return unless expanded_date_range(node)
58
+
59
+ begin_node = node.begin
60
+ end_node = node.end
61
+ return unless same_receiver?(begin_node, end_node)
62
+
63
+ beginning_method = begin_node.method_name
64
+ end_method = end_node.method_name
65
+ return unless use_mapped_methods?(beginning_method, end_method)
66
+
67
+ preferred_method = "#{begin_node.receiver.source}.#{PREFERRED_METHODS[beginning_method]}"
68
+
69
+ add_offense(node, message: format(MSG, preferred_method: preferred_method)) do |corrector|
70
+ corrector.replace(node, preferred_method)
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def same_receiver?(begin_node, end_node)
77
+ begin_node.receiver.source == end_node.receiver.source
78
+ end
79
+
80
+ def use_mapped_methods?(beginning_method, end_method)
81
+ MAPPED_DATE_RANGE_METHODS[beginning_method] == end_method
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -3,16 +3,27 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop is used to identify usages of `where.first` and
7
- # change them to use `find_by` instead.
6
+ # This cop is used to identify usages of `where.take` and change them to use `find_by` instead.
7
+ #
8
+ # And `where(...).first` can return different results from `find_by`.
9
+ # (They order records differently, so the "first" record can be different.)
10
+ #
11
+ # If you also want to detect `where.first`, you can set `IgnoreWhereFirst` to false.
8
12
  #
9
13
  # @example
10
14
  # # bad
11
- # User.where(name: 'Bruce').first
12
15
  # User.where(name: 'Bruce').take
13
16
  #
14
17
  # # good
15
18
  # User.find_by(name: 'Bruce')
19
+ #
20
+ # @example IgnoreWhereFirst: true (default)
21
+ # # good
22
+ # User.where(name: 'Bruce').first
23
+ #
24
+ # @example IgnoreWhereFirst: false
25
+ # # bad
26
+ # User.where(name: 'Bruce').first
16
27
  class FindBy < Base
17
28
  include RangeHelp
18
29
  extend AutoCorrector
@@ -20,12 +31,9 @@ module RuboCop
20
31
  MSG = 'Use `find_by` instead of `where.%<method>s`.'
21
32
  RESTRICT_ON_SEND = %i[first take].freeze
22
33
 
23
- def_node_matcher :where_first?, <<~PATTERN
24
- (send ({send csend} _ :where ...) {:first :take})
25
- PATTERN
26
-
27
34
  def on_send(node)
28
- return unless where_first?(node)
35
+ return unless where_method?(node.receiver)
36
+ return if ignore_where_first? && node.method?(:first)
29
37
 
30
38
  range = range_between(node.receiver.loc.selector.begin_pos, node.loc.selector.end_pos)
31
39
 
@@ -37,10 +45,13 @@ module RuboCop
37
45
 
38
46
  private
39
47
 
48
+ def where_method?(receiver)
49
+ return false unless receiver
50
+
51
+ receiver.respond_to?(:method?) && receiver.method?(:where)
52
+ end
53
+
40
54
  def autocorrect(corrector, node)
41
- # Don't autocorrect where(...).first, because it can return different
42
- # results from find_by. (They order records differently, so the
43
- # 'first' record can be different.)
44
55
  return if node.method?(:first)
45
56
 
46
57
  where_loc = node.receiver.loc.selector
@@ -49,6 +60,10 @@ module RuboCop
49
60
  corrector.replace(where_loc, 'find_by')
50
61
  corrector.replace(first_loc, '')
51
62
  end
63
+
64
+ def ignore_where_first?
65
+ cop_config.fetch('IgnoreWhereFirst', true)
66
+ end
52
67
  end
53
68
  end
54
69
  end
@@ -11,12 +11,14 @@ module RuboCop
11
11
  # render json: { foo: 'bar' }, status: 200
12
12
  # render plain: 'foo/bar', status: 304
13
13
  # redirect_to root_url, status: 301
14
+ # head 200
14
15
  #
15
16
  # # good
16
17
  # render :foo, status: :ok
17
18
  # render json: { foo: 'bar' }, status: :ok
18
19
  # render plain: 'foo/bar', status: :not_modified
19
20
  # redirect_to root_url, status: :moved_permanently
21
+ # head :ok
20
22
  #
21
23
  # @example EnforcedStyle: numeric
22
24
  # # bad
@@ -24,23 +26,26 @@ module RuboCop
24
26
  # render json: { foo: 'bar' }, status: :not_found
25
27
  # render plain: 'foo/bar', status: :not_modified
26
28
  # redirect_to root_url, status: :moved_permanently
29
+ # head :ok
27
30
  #
28
31
  # # good
29
32
  # render :foo, status: 200
30
33
  # render json: { foo: 'bar' }, status: 404
31
34
  # render plain: 'foo/bar', status: 304
32
35
  # redirect_to root_url, status: 301
36
+ # head 200
33
37
  #
34
38
  class HttpStatus < Base
35
39
  include ConfigurableEnforcedStyle
36
40
  extend AutoCorrector
37
41
 
38
- RESTRICT_ON_SEND = %i[render redirect_to].freeze
42
+ RESTRICT_ON_SEND = %i[render redirect_to head].freeze
39
43
 
40
44
  def_node_matcher :http_status, <<~PATTERN
41
45
  {
42
46
  (send nil? {:render :redirect_to} _ $hash)
43
47
  (send nil? {:render :redirect_to} $hash)
48
+ (send nil? :head ${int sym} ...)
44
49
  }
45
50
  PATTERN
46
51
 
@@ -49,8 +54,12 @@ module RuboCop
49
54
  PATTERN
50
55
 
51
56
  def on_send(node)
52
- http_status(node) do |hash_node|
53
- status = status_code(hash_node)
57
+ http_status(node) do |hash_node_or_status_code|
58
+ status = if hash_node_or_status_code.hash_type?
59
+ status_code(hash_node_or_status_code)
60
+ else
61
+ hash_node_or_status_code
62
+ end
54
63
  return unless status
55
64
 
56
65
  checker = checker_class.new(status)
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks for the use of `I18n.locale=` method.
7
+ #
8
+ # The `locale` attribute persists for the rest of the Ruby runtime, potentially causing
9
+ # unexpected behavior at a later time.
10
+ # Using `I18n.with_locale` ensures the code passed in the block is the only place `I18n.locale` is affected.
11
+ # It eliminates the possibility of a `locale` sticking around longer than intended.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # I18n.locale = :fr
16
+ #
17
+ # # good
18
+ # I18n.with_locale(:fr) do
19
+ # end
20
+ #
21
+ class I18nLocaleAssignment < Base
22
+ MSG = 'Use `I18n.with_locale` with block instead of `I18n.locale=`.'
23
+ RESTRICT_ON_SEND = %i[locale=].freeze
24
+
25
+ def_node_matcher :i18n_locale_assignment?, <<~PATTERN
26
+ (send (const {nil? cbase} :I18n) :locale= ...)
27
+ PATTERN
28
+
29
+ def on_send(node)
30
+ return unless i18n_locale_assignment?(node)
31
+
32
+ add_offense(node)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -130,8 +130,7 @@ module RuboCop
130
130
  # @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses
131
131
  class InverseOf < Base
132
132
  SPECIFY_MSG = 'Specify an `:inverse_of` option.'
133
- NIL_MSG = 'You specified `inverse_of: nil`, you probably meant to ' \
134
- 'use `inverse_of: false`.'
133
+ NIL_MSG = 'You specified `inverse_of: nil`, you probably meant to use `inverse_of: false`.'
135
134
  RESTRICT_ON_SEND = %i[has_many has_one belongs_to].freeze
136
135
 
137
136
  def_node_matcher :association_recv_arguments, <<~PATTERN
@@ -40,7 +40,7 @@ module RuboCop
40
40
 
41
41
  def reflection_class_value?(class_value)
42
42
  if class_value.send_type?
43
- !class_value.method?(:to_s) || class_value.receiver.const_type?
43
+ !class_value.method?(:to_s) || class_value.receiver&.const_type?
44
44
  else
45
45
  !ALLOWED_REFLECTION_CLASS_TYPES.include?(class_value.type)
46
46
  end
@@ -33,16 +33,12 @@ module RuboCop
33
33
 
34
34
  MSG = 'Do not assign `%<method_name>s` to constants as it ' \
35
35
  'will be evaluated only once.'
36
- RELATIVE_DATE_METHODS = %i[since from_now after ago until before yesterday tomorrow].freeze
36
+ RELATIVE_DATE_METHODS = %i[since from_now after ago until before yesterday tomorrow].to_set.freeze
37
37
 
38
38
  def on_casgn(node)
39
- return if node.children[2]&.block_type?
40
-
41
- node.each_descendant(:send) do |send_node|
42
- relative_date?(send_node) do |method_name|
43
- add_offense(node, message: message(method_name)) do |corrector|
44
- autocorrect(corrector, node)
45
- end
39
+ nested_relative_date(node) do |method_name|
40
+ add_offense(node, message: message(method_name)) do |corrector|
41
+ autocorrect(corrector, node)
46
42
  end
47
43
  end
48
44
  end
@@ -55,7 +51,7 @@ module RuboCop
55
51
  lhs.children.zip(rhs.children).each do |(name, value)|
56
52
  next unless name.casgn_type?
57
53
 
58
- relative_date?(value) do |method_name|
54
+ nested_relative_date(value) do |method_name|
59
55
  add_offense(offense_range(name, value), message: message(method_name)) do |corrector|
60
56
  autocorrect(corrector, node)
61
57
  end
@@ -64,7 +60,7 @@ module RuboCop
64
60
  end
65
61
 
66
62
  def on_or_asgn(node)
67
- relative_date_or_assignment?(node) do |method_name|
63
+ relative_date_or_assignment(node) do |method_name|
68
64
  add_offense(node, message: format(MSG, method_name: method_name))
69
65
  end
70
66
  end
@@ -93,20 +89,22 @@ module RuboCop
93
89
  range_between(name.loc.expression.begin_pos, value.loc.expression.end_pos)
94
90
  end
95
91
 
96
- def relative_date_method?(method_name)
97
- RELATIVE_DATE_METHODS.include?(method_name)
92
+ def nested_relative_date(node, &callback)
93
+ return if node.block_type?
94
+
95
+ node.each_child_node do |child|
96
+ nested_relative_date(child, &callback)
97
+ end
98
+
99
+ relative_date(node, &callback)
98
100
  end
99
101
 
100
- def_node_matcher :relative_date_or_assignment?, <<~PATTERN
101
- (:or_asgn (casgn _ _) (send _ $#relative_date_method?))
102
+ def_node_matcher :relative_date_or_assignment, <<~PATTERN
103
+ (:or_asgn (casgn _ _) (send _ $RELATIVE_DATE_METHODS))
102
104
  PATTERN
103
105
 
104
- def_node_matcher :relative_date?, <<~PATTERN
105
- {
106
- ({erange irange} _ (send _ $#relative_date_method?))
107
- ({erange irange} (send _ $#relative_date_method?) _)
108
- (send _ $#relative_date_method?)
109
- }
106
+ def_node_matcher :relative_date, <<~PATTERN
107
+ (send _ $RELATIVE_DATE_METHODS)
110
108
  PATTERN
111
109
  end
112
110
  end
@@ -50,7 +50,7 @@ module RuboCop
50
50
  (class
51
51
  (const nil? _)
52
52
  (send
53
- (const (const nil? :ActiveRecord) :Migration)
53
+ (const (const {nil? cbase} :ActiveRecord) :Migration)
54
54
  :[]
55
55
  (float _))
56
56
  _)
@@ -8,41 +8,33 @@ module RuboCop
8
8
  # Built on top of Ruby on Rails style guide (https://rails.rubystyle.guide#time)
9
9
  # and the article http://danilenko.org/2012/7/6/rails_timezones/
10
10
  #
11
- # Two styles are supported for this cop. When EnforcedStyle is 'strict'
12
- # then only use of Time.zone is allowed.
11
+ # Two styles are supported for this cop. When `EnforcedStyle` is 'strict'
12
+ # then only use of `Time.zone` is allowed.
13
13
  #
14
14
  # When EnforcedStyle is 'flexible' then it's also allowed
15
- # to use Time.in_time_zone.
16
- #
17
- # @example EnforcedStyle: strict
18
- # # `strict` means that `Time` should be used with `zone`.
15
+ # to use `Time#in_time_zone`.
19
16
  #
17
+ # @example
20
18
  # # bad
21
19
  # Time.now
22
20
  # Time.parse('2015-03-02T19:05:37')
23
21
  #
24
- # # bad
25
- # Time.current
26
- # Time.at(timestamp).in_time_zone
27
- #
28
22
  # # good
23
+ # Time.current
29
24
  # Time.zone.now
30
25
  # Time.zone.parse('2015-03-02T19:05:37')
31
26
  # Time.zone.parse('2015-03-02T19:05:37Z') # Respect ISO 8601 format with timezone specifier.
32
27
  #
33
- # @example EnforcedStyle: flexible (default)
34
- # # `flexible` allows usage of `in_time_zone` instead of `zone`.
28
+ # @example EnforcedStyle: strict
29
+ # # `strict` means that `Time` should be used with `zone`.
35
30
  #
36
31
  # # bad
37
- # Time.now
38
- # Time.parse('2015-03-02T19:05:37')
32
+ # Time.at(timestamp).in_time_zone
39
33
  #
40
- # # good
41
- # Time.zone.now
42
- # Time.zone.parse('2015-03-02T19:05:37')
34
+ # @example EnforcedStyle: flexible (default)
35
+ # # `flexible` allows usage of `in_time_zone` instead of `zone`.
43
36
  #
44
37
  # # good
45
- # Time.current
46
38
  # Time.at(timestamp).in_time_zone
47
39
  class TimeZone < Base
48
40
  include ConfigurableEnforcedStyle
@@ -59,7 +51,7 @@ module RuboCop
59
51
 
60
52
  GOOD_METHODS = %i[zone zone_default find_zone find_zone!].freeze
61
53
 
62
- DANGEROUS_METHODS = %i[now local new parse at current].freeze
54
+ DANGEROUS_METHODS = %i[now local new parse at].freeze
63
55
 
64
56
  ACCEPTED_METHODS = %i[in_time_zone utc getlocal xmlschema iso8601
65
57
  jisx0301 rfc3339 httpdate to_i to_f].freeze
@@ -6,8 +6,8 @@ module RuboCop
6
6
  # This cop checks for the use of `Time.zone=` method.
7
7
  #
8
8
  # The `zone` attribute persists for the rest of the Ruby runtime, potentially causing
9
- # unexpected behaviour at a later time.
10
- # Using `Time.use_zone` ensures the code passed in block is the only place Time.zone is affected.
9
+ # unexpected behavior at a later time.
10
+ # Using `Time.use_zone` ensures the code passed in the block is the only place Time.zone is affected.
11
11
  # It eliminates the possibility of a `zone` sticking around longer than intended.
12
12
  #
13
13
  # @example
@@ -19,7 +19,7 @@ module RuboCop
19
19
  # end
20
20
  #
21
21
  class TimeZoneAssignment < Base
22
- MSG = 'Use `Time.use_zone` with blocks instead of `Time.zone=`.'
22
+ MSG = 'Use `Time.use_zone` with block instead of `Time.zone=`.'
23
23
  RESTRICT_ON_SEND = %i[zone=].freeze
24
24
 
25
25
  def_node_matcher :time_zone_assignement?, <<~PATTERN
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop suggests you remove a column that does not exist in the schema from `ignored_columns`.
7
+ # `ignored_columns` is necessary to drop a column from RDBMS, but you don't need it after the migration
8
+ # to drop the column. You avoid forgetting to remove `ignored_columns` by this cop.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # class User < ApplicationRecord
13
+ # self.ignored_columns = [:already_removed_column]
14
+ # end
15
+ #
16
+ # # good
17
+ # class User < ApplicationRecord
18
+ # self.ignored_columns = [:still_existing_column]
19
+ # end
20
+ #
21
+ class UnusedIgnoredColumns < Base
22
+ include ActiveRecordHelper
23
+
24
+ MSG = 'Remove `%<column_name>s` from `ignored_columns` because the column does not exist.'
25
+ RESTRICT_ON_SEND = %i[ignored_columns=].freeze
26
+
27
+ def_node_matcher :ignored_columns, <<~PATTERN
28
+ (send self :ignored_columns= $array)
29
+ PATTERN
30
+
31
+ def_node_matcher :column_name, <<~PATTERN
32
+ ({str sym} $_)
33
+ PATTERN
34
+
35
+ def on_send(node)
36
+ return unless (columns = ignored_columns(node))
37
+ return unless schema
38
+
39
+ table = table(node)
40
+ return unless table
41
+
42
+ columns.children.each do |column_node|
43
+ check_column_existence(column_node, table)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def check_column_existence(column_node, table)
50
+ column_name = column_name(column_node)
51
+ return unless column_name
52
+ return if table.with_column?(name: column_name.to_s)
53
+
54
+ message = format(MSG, column_name: column_name)
55
+ add_offense(column_node, message: message)
56
+ end
57
+
58
+ def class_node(node)
59
+ node.each_ancestor.find(&:class_type?)
60
+ end
61
+
62
+ def table(node)
63
+ klass = class_node(node)
64
+ schema.table_by(name: table_name(klass))
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -10,6 +10,7 @@ require_relative 'rails/active_record_aliases'
10
10
  require_relative 'rails/active_record_callbacks_order'
11
11
  require_relative 'rails/active_record_override'
12
12
  require_relative 'rails/active_support_aliases'
13
+ require_relative 'rails/add_column_index'
13
14
  require_relative 'rails/after_commit_override'
14
15
  require_relative 'rails/application_controller'
15
16
  require_relative 'rails/application_job'
@@ -28,11 +29,13 @@ require_relative 'rails/default_scope'
28
29
  require_relative 'rails/delegate'
29
30
  require_relative 'rails/delegate_allow_blank'
30
31
  require_relative 'rails/dynamic_find_by'
32
+ require_relative 'rails/eager_evaluation_log_message'
31
33
  require_relative 'rails/enum_hash'
32
34
  require_relative 'rails/enum_uniqueness'
33
35
  require_relative 'rails/environment_comparison'
34
36
  require_relative 'rails/environment_variable_access'
35
37
  require_relative 'rails/exit'
38
+ require_relative 'rails/expanded_date_range'
36
39
  require_relative 'rails/file_path'
37
40
  require_relative 'rails/find_by'
38
41
  require_relative 'rails/find_by_id'
@@ -42,6 +45,7 @@ require_relative 'rails/has_many_or_has_one_dependent'
42
45
  require_relative 'rails/helper_instance_variable'
43
46
  require_relative 'rails/http_positional_arguments'
44
47
  require_relative 'rails/http_status'
48
+ require_relative 'rails/i18n_locale_assignment'
45
49
  require_relative 'rails/ignored_skip_action_filter_option'
46
50
  require_relative 'rails/index_by'
47
51
  require_relative 'rails/index_with'
@@ -89,6 +93,7 @@ require_relative 'rails/time_zone_assignment'
89
93
  require_relative 'rails/uniq_before_pluck'
90
94
  require_relative 'rails/unique_validation_without_index'
91
95
  require_relative 'rails/unknown_env'
96
+ require_relative 'rails/unused_ignored_columns'
92
97
  require_relative 'rails/validation'
93
98
  require_relative 'rails/where_equals'
94
99
  require_relative 'rails/where_exists'
@@ -114,7 +114,7 @@ module RuboCop
114
114
  attr_reader :name, :type, :not_null
115
115
 
116
116
  def initialize(node)
117
- @name = node.first_argument.value
117
+ @name = node.first_argument.str_content
118
118
  @type = node.method_name
119
119
  @not_null = nil
120
120
 
@@ -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.10.1'
7
+ STRING = '2.11.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.10.1
4
+ version: 2.11.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-05-05 00:00:00.000000000 Z
13
+ date: 2021-06-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -85,6 +85,7 @@ files:
85
85
  - lib/rubocop/cop/rails/active_record_callbacks_order.rb
86
86
  - lib/rubocop/cop/rails/active_record_override.rb
87
87
  - lib/rubocop/cop/rails/active_support_aliases.rb
88
+ - lib/rubocop/cop/rails/add_column_index.rb
88
89
  - lib/rubocop/cop/rails/after_commit_override.rb
89
90
  - lib/rubocop/cop/rails/application_controller.rb
90
91
  - lib/rubocop/cop/rails/application_job.rb
@@ -103,11 +104,13 @@ files:
103
104
  - lib/rubocop/cop/rails/delegate.rb
104
105
  - lib/rubocop/cop/rails/delegate_allow_blank.rb
105
106
  - lib/rubocop/cop/rails/dynamic_find_by.rb
107
+ - lib/rubocop/cop/rails/eager_evaluation_log_message.rb
106
108
  - lib/rubocop/cop/rails/enum_hash.rb
107
109
  - lib/rubocop/cop/rails/enum_uniqueness.rb
108
110
  - lib/rubocop/cop/rails/environment_comparison.rb
109
111
  - lib/rubocop/cop/rails/environment_variable_access.rb
110
112
  - lib/rubocop/cop/rails/exit.rb
113
+ - lib/rubocop/cop/rails/expanded_date_range.rb
111
114
  - lib/rubocop/cop/rails/file_path.rb
112
115
  - lib/rubocop/cop/rails/find_by.rb
113
116
  - lib/rubocop/cop/rails/find_by_id.rb
@@ -117,6 +120,7 @@ files:
117
120
  - lib/rubocop/cop/rails/helper_instance_variable.rb
118
121
  - lib/rubocop/cop/rails/http_positional_arguments.rb
119
122
  - lib/rubocop/cop/rails/http_status.rb
123
+ - lib/rubocop/cop/rails/i18n_locale_assignment.rb
120
124
  - lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb
121
125
  - lib/rubocop/cop/rails/index_by.rb
122
126
  - lib/rubocop/cop/rails/index_with.rb
@@ -164,6 +168,7 @@ files:
164
168
  - lib/rubocop/cop/rails/uniq_before_pluck.rb
165
169
  - lib/rubocop/cop/rails/unique_validation_without_index.rb
166
170
  - lib/rubocop/cop/rails/unknown_env.rb
171
+ - lib/rubocop/cop/rails/unused_ignored_columns.rb
167
172
  - lib/rubocop/cop/rails/validation.rb
168
173
  - lib/rubocop/cop/rails/where_equals.rb
169
174
  - lib/rubocop/cop/rails/where_exists.rb
@@ -181,7 +186,7 @@ metadata:
181
186
  homepage_uri: https://docs.rubocop.org/rubocop-rails/
182
187
  changelog_uri: https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md
183
188
  source_code_uri: https://github.com/rubocop/rubocop-rails/
184
- documentation_uri: https://docs.rubocop.org/rubocop-rails/2.10/
189
+ documentation_uri: https://docs.rubocop.org/rubocop-rails/2.11/
185
190
  bug_tracker_uri: https://github.com/rubocop/rubocop-rails/issues
186
191
  post_install_message:
187
192
  rdoc_options: []
@@ -198,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
203
  - !ruby/object:Gem::Version
199
204
  version: '0'
200
205
  requirements: []
201
- rubygems_version: 3.2.16
206
+ rubygems_version: 3.2.18
202
207
  signing_key:
203
208
  specification_version: 4
204
209
  summary: Automatic Rails code style checking tool.