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 +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.
|