rubocop-rails 2.10.1 → 2.11.0

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: 79de98cdd4ff48a6ee51c47f2a0bdb76e2393aac1b7974b9181e66723922c148
4
- data.tar.gz: 85c6c566a98c399bcc73b555187e37d8270975cfde4c5bc067726ee61d6c3704
3
+ metadata.gz: b189297521a834dca247787947efd0f3a7c5fed19b351b37d6c729b0622ff52d
4
+ data.tar.gz: e68accc2d7c9c5726b63967f665414d09f9d3597117de042ee9dbc024cbc6350
5
5
  SHA512:
6
- metadata.gz: 7cf4cddba4e79e79579415bb93aed3bc75e8571a862f32727edc56ffbe5580ba4f5781cf7205c84dbdf1cae9979c7c4b500c227f82ba1a5d476af279a8a9f575
7
- data.tar.gz: c3591c01fe64b61de0ea39d136a57697b90deb71c90c7738de7c4eb8102f0b6a03548c2e2add574d343ce025d087150d57f7b51860d7471f85943fda125fc7e4
6
+ metadata.gz: 2474d4d0321548d02443df1ec10e20252c5496fa6df4dd4bc5d8c0544c9ff884f23da90273075d808bfa1ef22659ee8d8b81cff4f5dbf8a8134c054adf298187
7
+ data.tar.gz: f7cb276a49bdaffffbc020ac96951f3e5074c4f993f77710b22a48fc7efb357d447853768f6006c7e173640e483601cd6bab61b6ffb3df5a70dffb3a07b49541
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
@@ -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,8 @@ 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 if ignore_where_first? && node.method?(:first)
29
36
 
30
37
  range = range_between(node.receiver.loc.selector.begin_pos, node.loc.selector.end_pos)
31
38
 
@@ -38,9 +45,6 @@ module RuboCop
38
45
  private
39
46
 
40
47
  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
48
  return if node.method?(:first)
45
49
 
46
50
  where_loc = node.receiver.loc.selector
@@ -49,6 +53,10 @@ module RuboCop
49
53
  corrector.replace(where_loc, 'find_by')
50
54
  corrector.replace(first_loc, '')
51
55
  end
56
+
57
+ def ignore_where_first?
58
+ cop_config.fetch('IgnoreWhereFirst', true)
59
+ end
52
60
  end
53
61
  end
54
62
  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
@@ -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.0'
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.0
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-21 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.