rubocop-rails 2.10.1 → 2.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/default.yml +44 -2
- data/lib/rubocop/cop/rails/add_column_index.rb +64 -0
- data/lib/rubocop/cop/rails/date.rb +17 -6
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +2 -1
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +78 -0
- data/lib/rubocop/cop/rails/expanded_date_range.rb +86 -0
- data/lib/rubocop/cop/rails/find_by.rb +19 -11
- data/lib/rubocop/cop/rails/http_status.rb +12 -3
- data/lib/rubocop/cop/rails/i18n_locale_assignment.rb +37 -0
- data/lib/rubocop/cop/rails/relative_date_constant.rb +18 -20
- data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +1 -1
- data/lib/rubocop/cop/rails/time_zone.rb +11 -19
- data/lib/rubocop/cop/rails/time_zone_assignment.rb +3 -3
- data/lib/rubocop/cop/rails/unused_ignored_columns.rb +69 -0
- data/lib/rubocop/cop/rails_cops.rb +5 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +1 -1
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b189297521a834dca247787947efd0f3a7c5fed19b351b37d6c729b0622ff52d
|
4
|
+
data.tar.gz: e68accc2d7c9c5726b63967f665414d09f9d3597117de042ee9dbc024cbc6350
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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: '
|
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
|
-
|
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
|
21
|
-
#
|
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?(
|
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.
|
7
|
-
#
|
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
|
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 |
|
53
|
-
status =
|
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
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
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
|
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
|
97
|
-
|
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
|
101
|
-
(:or_asgn (casgn _ _) (send _
|
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
|
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
|
@@ -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
|
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:
|
34
|
-
# # `
|
28
|
+
# @example EnforcedStyle: strict
|
29
|
+
# # `strict` means that `Time` should be used with `zone`.
|
35
30
|
#
|
36
31
|
# # bad
|
37
|
-
# Time.
|
38
|
-
# Time.parse('2015-03-02T19:05:37')
|
32
|
+
# Time.at(timestamp).in_time_zone
|
39
33
|
#
|
40
|
-
#
|
41
|
-
#
|
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
|
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
|
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
|
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'
|
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.
|
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-
|
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.
|
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.
|
206
|
+
rubygems_version: 3.2.18
|
202
207
|
signing_key:
|
203
208
|
specification_version: 4
|
204
209
|
summary: Automatic Rails code style checking tool.
|