rubocop-rails 2.7.1 → 2.8.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 +45 -3
- data/lib/rubocop/cop/mixin/index_method.rb +17 -0
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +2 -4
- data/lib/rubocop/cop/rails/after_commit_override.rb +84 -0
- data/lib/rubocop/cop/rails/content_tag.rb +2 -2
- data/lib/rubocop/cop/rails/file_path.rb +1 -1
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +1 -5
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +2 -0
- data/lib/rubocop/cop/rails/index_by.rb +8 -0
- data/lib/rubocop/cop/rails/index_with.rb +8 -0
- data/lib/rubocop/cop/rails/inquiry.rb +5 -1
- data/lib/rubocop/cop/rails/order_by_id.rb +53 -0
- data/lib/rubocop/cop/rails/pluck_in_where.rb +35 -1
- data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
- data/lib/rubocop/cop/rails/relative_date_constant.rb +5 -2
- data/lib/rubocop/cop/rails/reversible_migration.rb +79 -0
- data/lib/rubocop/cop/rails/save_bang.rb +2 -2
- data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +83 -0
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +2 -0
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +15 -7
- data/lib/rubocop/cop/rails/where_exists.rb +68 -5
- data/lib/rubocop/cop/rails/where_not.rb +106 -0
- data/lib/rubocop/cop/rails_cops.rb +4 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +2 -4
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e89f00783e4dce6642e43ba4a4bf8ee29d078131f71404230db8fd2a1f87248
|
4
|
+
data.tar.gz: bfbca38eb400e89e260b572babc327afec01b25bad89178179e8276ef7ce651c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 241d43809d5cbfa4684fa83c1b92c9069447bae05ca0235d137e6934a2a9ad35ff3e00fba46928e793045ee1cf35dc3a520cca9f177e8c3280530282eb52ac06
|
7
|
+
data.tar.gz: 7019b400331651ad86a94e9562f93e31c32025ae5ed9c8c5da0f62c65974b21052438908a535da9314a6b903ea3ff6815dc7a0a6350ce3db9d6ab1d0ac18d015
|
data/config/default.yml
CHANGED
@@ -62,6 +62,14 @@ Rails/ActiveSupportAliases:
|
|
62
62
|
Enabled: true
|
63
63
|
VersionAdded: '0.48'
|
64
64
|
|
65
|
+
Rails/AfterCommitOverride:
|
66
|
+
Description: >-
|
67
|
+
This cop enforces that there is only one call to `after_commit`
|
68
|
+
(and its aliases - `after_create_commit`, `after_update_commit`,
|
69
|
+
and `after_destroy_commit`) with the same callback name per model.
|
70
|
+
Enabled: 'pending'
|
71
|
+
VersionAdded: '2.8'
|
72
|
+
|
65
73
|
Rails/ApplicationController:
|
66
74
|
Description: 'Check that controllers subclass ApplicationController.'
|
67
75
|
Enabled: true
|
@@ -313,14 +321,16 @@ Rails/IgnoredSkipActionFilterOption:
|
|
313
321
|
- app/controllers/**/*.rb
|
314
322
|
|
315
323
|
Rails/IndexBy:
|
316
|
-
Description: 'Prefer `index_by` over `each_with_object` or `map`.'
|
324
|
+
Description: 'Prefer `index_by` over `each_with_object`, `to_h`, or `map`.'
|
317
325
|
Enabled: true
|
318
326
|
VersionAdded: '2.5'
|
327
|
+
VersionChanged: '2.8'
|
319
328
|
|
320
329
|
Rails/IndexWith:
|
321
|
-
Description: 'Prefer `index_with` over `each_with_object` or `map`.'
|
330
|
+
Description: 'Prefer `index_with` over `each_with_object`, `to_h`, or `map`.'
|
322
331
|
Enabled: true
|
323
332
|
VersionAdded: '2.5'
|
333
|
+
VersionChanged: '2.8'
|
324
334
|
|
325
335
|
Rails/Inquiry:
|
326
336
|
Description: "Prefer Ruby's comparison operators over Active Support's `Array#inquiry` and `String#inquiry`."
|
@@ -357,6 +367,7 @@ Rails/MailerName:
|
|
357
367
|
Description: 'Mailer should end with `Mailer` suffix.'
|
358
368
|
StyleGuide: 'https://rails.rubystyle.guide/#mailer-name'
|
359
369
|
Enabled: 'pending'
|
370
|
+
SafeAutoCorrect: false
|
360
371
|
VersionAdded: '2.7'
|
361
372
|
Include:
|
362
373
|
- app/mailers/**/*.rb
|
@@ -385,6 +396,14 @@ Rails/NotNullColumn:
|
|
385
396
|
Include:
|
386
397
|
- db/migrate/*.rb
|
387
398
|
|
399
|
+
Rails/OrderById:
|
400
|
+
Description: >-
|
401
|
+
Do not use the `id` column for ordering.
|
402
|
+
Use a timestamp column to order chronologically.
|
403
|
+
StyleGuide: 'https://rails.rubystyle.guide/#order-by-id'
|
404
|
+
Enabled: false
|
405
|
+
VersionAdded: '2.8'
|
406
|
+
|
388
407
|
Rails/Output:
|
389
408
|
Description: 'Checks for calls to puts, print, etc.'
|
390
409
|
Enabled: true
|
@@ -426,6 +445,11 @@ Rails/PluckInWhere:
|
|
426
445
|
Enabled: 'pending'
|
427
446
|
Safe: false
|
428
447
|
VersionAdded: '2.7'
|
448
|
+
VersionChanged: '2.8'
|
449
|
+
EnforcedStyle: conservative
|
450
|
+
SupportedStyles:
|
451
|
+
- conservative
|
452
|
+
- aggressive
|
429
453
|
|
430
454
|
Rails/PluralizationGrammar:
|
431
455
|
Description: 'Checks for incorrect grammar when using methods like `3.day.ago`.'
|
@@ -624,6 +648,12 @@ Rails/SkipsModelValidations:
|
|
624
648
|
- upsert_all
|
625
649
|
AllowedMethods: []
|
626
650
|
|
651
|
+
Rails/SquishedSQLHeredocs:
|
652
|
+
Description: 'Checks SQL heredocs to use `.squish`.'
|
653
|
+
StyleGuide: 'https://rails.rubystyle.guide/#squished-heredocs'
|
654
|
+
Enabled: 'pending'
|
655
|
+
VersionAdded: '2.8'
|
656
|
+
|
627
657
|
Rails/TimeZone:
|
628
658
|
Description: 'Checks the correct usage of time zone aware methods.'
|
629
659
|
StyleGuide: 'https://rails.rubystyle.guide#time'
|
@@ -643,11 +673,12 @@ Rails/UniqBeforePluck:
|
|
643
673
|
Description: 'Prefer the use of uniq or distinct before pluck.'
|
644
674
|
Enabled: true
|
645
675
|
VersionAdded: '0.40'
|
646
|
-
VersionChanged: '2.
|
676
|
+
VersionChanged: '2.8'
|
647
677
|
EnforcedStyle: conservative
|
648
678
|
SupportedStyles:
|
649
679
|
- conservative
|
650
680
|
- aggressive
|
681
|
+
SafeAutoCorrect: false
|
651
682
|
AutoCorrect: false
|
652
683
|
|
653
684
|
Rails/UniqueValidationWithoutIndex:
|
@@ -677,7 +708,18 @@ Rails/Validation:
|
|
677
708
|
Rails/WhereExists:
|
678
709
|
Description: 'Prefer `exists?(...)` over `where(...).exists?`.'
|
679
710
|
Enabled: 'pending'
|
711
|
+
EnforcedStyle: exists
|
712
|
+
SupportedStyles:
|
713
|
+
- exists
|
714
|
+
- where
|
680
715
|
VersionAdded: '2.7'
|
716
|
+
VersionChanged: '2.8'
|
717
|
+
|
718
|
+
Rails/WhereNot:
|
719
|
+
Description: 'Use `where.not(...)` instead of manually constructing negated SQL in `where`.'
|
720
|
+
StyleGuide: 'https://rails.rubystyle.guide/#where-not'
|
721
|
+
Enabled: 'pending'
|
722
|
+
VersionAdded: '2.8'
|
681
723
|
|
682
724
|
# Accept `redirect_to(...) and return` and similar cases.
|
683
725
|
Style/AndOr:
|
@@ -8,6 +8,12 @@ module RuboCop
|
|
8
8
|
on_bad_each_with_object(node) do |*match|
|
9
9
|
handle_possible_offense(node, match, 'each_with_object')
|
10
10
|
end
|
11
|
+
|
12
|
+
return if target_ruby_version < 2.6
|
13
|
+
|
14
|
+
on_bad_to_h(node) do |*match|
|
15
|
+
handle_possible_offense(node, match, 'to_h { ... }')
|
16
|
+
end
|
11
17
|
end
|
12
18
|
|
13
19
|
def on_send(node)
|
@@ -40,6 +46,11 @@ module RuboCop
|
|
40
46
|
raise NotImplementedError
|
41
47
|
end
|
42
48
|
|
49
|
+
# @abstract Implemented with `def_node_matcher`
|
50
|
+
def on_bad_to_h(_node)
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
|
43
54
|
# @abstract Implemented with `def_node_matcher`
|
44
55
|
def on_bad_map_to_h(_node)
|
45
56
|
raise NotImplementedError
|
@@ -73,6 +84,8 @@ module RuboCop
|
|
73
84
|
def prepare_correction(node)
|
74
85
|
if (match = on_bad_each_with_object(node))
|
75
86
|
Autocorrection.from_each_with_object(node, match)
|
87
|
+
elsif (match = on_bad_to_h(node))
|
88
|
+
Autocorrection.from_to_h(node, match)
|
76
89
|
elsif (match = on_bad_map_to_h(node))
|
77
90
|
Autocorrection.from_map_to_h(node, match)
|
78
91
|
elsif (match = on_bad_hash_brackets_map(node))
|
@@ -111,6 +124,10 @@ module RuboCop
|
|
111
124
|
new(match, node, 0, 0)
|
112
125
|
end
|
113
126
|
|
127
|
+
def self.from_to_h(node, match)
|
128
|
+
new(match, node, 0, 0)
|
129
|
+
end
|
130
|
+
|
114
131
|
def self.from_map_to_h(node, match)
|
115
132
|
strip_trailing_chars = 0
|
116
133
|
|
@@ -44,9 +44,7 @@ module RuboCop
|
|
44
44
|
after_touch
|
45
45
|
].freeze
|
46
46
|
|
47
|
-
CALLBACKS_ORDER_MAP =
|
48
|
-
CALLBACKS_IN_ORDER.map.with_index { |name, index| [name, index] }
|
49
|
-
].freeze
|
47
|
+
CALLBACKS_ORDER_MAP = CALLBACKS_IN_ORDER.each_with_index.to_h.freeze
|
50
48
|
|
51
49
|
def on_class(class_node)
|
52
50
|
previous_index = -1
|
@@ -68,7 +66,7 @@ module RuboCop
|
|
68
66
|
|
69
67
|
# Autocorrect by swapping between two nodes autocorrecting them
|
70
68
|
def autocorrect(node)
|
71
|
-
previous = left_siblings_of(node).find do |sibling|
|
69
|
+
previous = left_siblings_of(node).reverse_each.find do |sibling|
|
72
70
|
callback?(sibling)
|
73
71
|
end
|
74
72
|
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop enforces that there is only one call to `after_commit`
|
7
|
+
# (and its aliases - `after_create_commit`, `after_update_commit`,
|
8
|
+
# and `after_destroy_commit`) with the same callback name per model.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # bad
|
12
|
+
# # This won't be triggered.
|
13
|
+
# after_create_commit :log_action
|
14
|
+
#
|
15
|
+
# # This will override the callback added by
|
16
|
+
# # after_create_commit.
|
17
|
+
# after_update_commit :log_action
|
18
|
+
#
|
19
|
+
# # bad
|
20
|
+
# # This won't be triggered.
|
21
|
+
# after_commit :log_action, on: :create
|
22
|
+
# # This won't be triggered.
|
23
|
+
# after_update_commit :log_action
|
24
|
+
# # This will override both previous callbacks.
|
25
|
+
# after_commit :log_action, on: :destroy
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# after_save_commit :log_action
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# after_create_commit :log_create_action
|
32
|
+
# after_update_commit :log_update_action
|
33
|
+
#
|
34
|
+
class AfterCommitOverride < Cop
|
35
|
+
MSG = 'There can only be one `after_*_commit :%<name>s` hook defined for a model.'
|
36
|
+
|
37
|
+
AFTER_COMMIT_CALLBACKS = %i[
|
38
|
+
after_commit
|
39
|
+
after_create_commit
|
40
|
+
after_update_commit
|
41
|
+
after_save_commit
|
42
|
+
after_destroy_commit
|
43
|
+
].freeze
|
44
|
+
|
45
|
+
def on_class(class_node)
|
46
|
+
seen_callback_names = {}
|
47
|
+
|
48
|
+
each_after_commit_callback(class_node) do |node|
|
49
|
+
callback_name = node.arguments[0].value
|
50
|
+
if seen_callback_names.key?(callback_name)
|
51
|
+
add_offense(node, message: format(MSG, name: callback_name))
|
52
|
+
else
|
53
|
+
seen_callback_names[callback_name] = true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def each_after_commit_callback(class_node)
|
61
|
+
class_send_nodes(class_node).each do |node|
|
62
|
+
yield node if after_commit_callback?(node)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def class_send_nodes(class_node)
|
67
|
+
class_def = class_node.body
|
68
|
+
|
69
|
+
return [] unless class_def
|
70
|
+
|
71
|
+
if class_def.send_type?
|
72
|
+
[class_def]
|
73
|
+
else
|
74
|
+
class_def.each_child_node(:send).to_a
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def after_commit_callback?(node)
|
79
|
+
AFTER_COMMIT_CALLBACKS.include?(node.method_name)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -43,7 +43,7 @@ module RuboCop
|
|
43
43
|
range = correction_range(node)
|
44
44
|
|
45
45
|
rest_args = node.arguments.drop(1)
|
46
|
-
replacement = "tag.#{node.first_argument.value}(#{rest_args.map(&:source).join(', ')})"
|
46
|
+
replacement = "tag.#{node.first_argument.value.to_s.underscore}(#{rest_args.map(&:source).join(', ')})"
|
47
47
|
|
48
48
|
corrector.replace(range, replacement)
|
49
49
|
else
|
@@ -57,7 +57,7 @@ module RuboCop
|
|
57
57
|
def method_name?(node)
|
58
58
|
return false unless node.str_type? || node.sym_type?
|
59
59
|
|
60
|
-
/^[a-zA-Z_][a-zA-
|
60
|
+
/^[a-zA-Z_][a-zA-Z_\-0-9]*$/.match?(node.value)
|
61
61
|
end
|
62
62
|
|
63
63
|
def correction_range(node)
|
@@ -52,11 +52,7 @@ module RuboCop
|
|
52
52
|
|
53
53
|
def on_send(node)
|
54
54
|
return if active_resource?(node.parent)
|
55
|
-
|
56
|
-
unless association_without_options?(node)
|
57
|
-
return if valid_options?(association_with_options?(node))
|
58
|
-
end
|
59
|
-
|
55
|
+
return if !association_without_options?(node) && valid_options?(association_with_options?(node))
|
60
56
|
return if valid_options_in_with_options_block?(node)
|
61
57
|
|
62
58
|
add_offense(node, location: :selector)
|
@@ -11,6 +11,7 @@ module RuboCop
|
|
11
11
|
# @example
|
12
12
|
# # bad
|
13
13
|
# [1, 2, 3].each_with_object({}) { |el, h| h[foo(el)] = el }
|
14
|
+
# [1, 2, 3].to_h { |el| [foo(el), el] }
|
14
15
|
# [1, 2, 3].map { |el| [foo(el), el] }.to_h
|
15
16
|
# Hash[[1, 2, 3].collect { |el| [foo(el), el] }]
|
16
17
|
#
|
@@ -26,6 +27,13 @@ module RuboCop
|
|
26
27
|
({send csend} (lvar _memo) :[]= $_ (lvar _el)))
|
27
28
|
PATTERN
|
28
29
|
|
30
|
+
def_node_matcher :on_bad_to_h, <<~PATTERN
|
31
|
+
(block
|
32
|
+
({send csend} _ :to_h)
|
33
|
+
(args (arg $_el))
|
34
|
+
(array $_ (lvar _el)))
|
35
|
+
PATTERN
|
36
|
+
|
29
37
|
def_node_matcher :on_bad_map_to_h, <<~PATTERN
|
30
38
|
({send csend}
|
31
39
|
(block
|
@@ -11,6 +11,7 @@ module RuboCop
|
|
11
11
|
# @example
|
12
12
|
# # bad
|
13
13
|
# [1, 2, 3].each_with_object({}) { |el, h| h[el] = foo(el) }
|
14
|
+
# [1, 2, 3].to_h { |el| [el, foo(el)] }
|
14
15
|
# [1, 2, 3].map { |el| [el, foo(el)] }.to_h
|
15
16
|
# Hash[[1, 2, 3].collect { |el| [el, foo(el)] }]
|
16
17
|
#
|
@@ -29,6 +30,13 @@ module RuboCop
|
|
29
30
|
({send csend} (lvar _memo) :[]= (lvar _el) $_))
|
30
31
|
PATTERN
|
31
32
|
|
33
|
+
def_node_matcher :on_bad_to_h, <<~PATTERN
|
34
|
+
(block
|
35
|
+
({send csend} _ :to_h)
|
36
|
+
(args (arg $_el))
|
37
|
+
(array (lvar _el) $_))
|
38
|
+
PATTERN
|
39
|
+
|
32
40
|
def_node_matcher :on_bad_map_to_h, <<~PATTERN
|
33
41
|
({send csend}
|
34
42
|
(block
|
@@ -26,7 +26,11 @@ module RuboCop
|
|
26
26
|
MSG = "Prefer Ruby's comparison operators over Active Support's `inquiry`."
|
27
27
|
|
28
28
|
def on_send(node)
|
29
|
-
|
29
|
+
return unless node.method?(:inquiry) && node.arguments.empty?
|
30
|
+
return unless (receiver = node.receiver)
|
31
|
+
return if !receiver.str_type? && !receiver.array_type?
|
32
|
+
|
33
|
+
add_offense(node, location: :selector)
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks for places where ordering by `id` column is used.
|
7
|
+
#
|
8
|
+
# Don't use the `id` column for ordering. The sequence of ids is not guaranteed
|
9
|
+
# to be in any particular order, despite often (incidentally) being chronological.
|
10
|
+
# Use a timestamp column to order chronologically. As a bonus the intent is clearer.
|
11
|
+
#
|
12
|
+
# NOTE: Make sure the changed order column does not introduce performance
|
13
|
+
# bottlenecks and appropriate database indexes are added.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# # bad
|
17
|
+
# scope :chronological, -> { order(id: :asc) }
|
18
|
+
# scope :chronological, -> { order(primary_key => :asc) }
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# scope :chronological, -> { order(created_at: :asc) }
|
22
|
+
#
|
23
|
+
class OrderById < Base
|
24
|
+
include RangeHelp
|
25
|
+
|
26
|
+
MSG = 'Do not use the `id` column for ordering. '\
|
27
|
+
'Use a timestamp column to order chronologically.'
|
28
|
+
|
29
|
+
def_node_matcher :order_by_id?, <<~PATTERN
|
30
|
+
(send _ :order
|
31
|
+
{
|
32
|
+
(sym :id)
|
33
|
+
(hash (pair (sym :id) _))
|
34
|
+
(send _ :primary_key)
|
35
|
+
(hash (pair (send _ :primary_key) _))
|
36
|
+
})
|
37
|
+
PATTERN
|
38
|
+
|
39
|
+
def on_send(node)
|
40
|
+
return unless node.method?(:order)
|
41
|
+
|
42
|
+
add_offense(offense_range(node)) if order_by_id?(node)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def offense_range(node)
|
48
|
+
range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -9,20 +9,42 @@ module RuboCop
|
|
9
9
|
# Since `pluck` is an eager method and hits the database immediately,
|
10
10
|
# using `select` helps to avoid additional database queries.
|
11
11
|
#
|
12
|
+
# This cop has two different enforcement modes. When the EnforcedStyle
|
13
|
+
# is conservative (the default) then only calls to `pluck` on a constant
|
14
|
+
# (i.e. a model class) in the `where` is used as offenses.
|
15
|
+
#
|
16
|
+
# When the EnforcedStyle is aggressive then all calls to `pluck` in the
|
17
|
+
# `where` is used as offenses. This may lead to false positives
|
18
|
+
# as the cop cannot replace to `select` between calls to `pluck` on an
|
19
|
+
# `ActiveRecord::Relation` instance vs a call to `pluck` on an `Array` instance.
|
20
|
+
#
|
12
21
|
# @example
|
13
22
|
# # bad
|
14
23
|
# Post.where(user_id: User.active.pluck(:id))
|
15
24
|
#
|
16
25
|
# # good
|
17
26
|
# Post.where(user_id: User.active.select(:id))
|
27
|
+
# Post.where(user_id: active_users.select(:id))
|
28
|
+
#
|
29
|
+
# @example EnforcedStyle: conservative (default)
|
30
|
+
# # good
|
31
|
+
# Post.where(user_id: active_users.pluck(:id))
|
32
|
+
#
|
33
|
+
# @example EnforcedStyle: aggressive
|
34
|
+
# # bad
|
35
|
+
# Post.where(user_id: active_users.pluck(:id))
|
18
36
|
#
|
19
37
|
class PluckInWhere < Cop
|
20
38
|
include ActiveRecordHelper
|
39
|
+
include ConfigurableEnforcedStyle
|
21
40
|
|
22
41
|
MSG = 'Use `select` instead of `pluck` within `where` query method.'
|
23
42
|
|
24
43
|
def on_send(node)
|
25
|
-
|
44
|
+
return unless node.method?(:pluck) && in_where?(node)
|
45
|
+
return if style == :conservative && !root_receiver(node)&.const_type?
|
46
|
+
|
47
|
+
add_offense(node, location: :selector)
|
26
48
|
end
|
27
49
|
|
28
50
|
def autocorrect(node)
|
@@ -30,6 +52,18 @@ module RuboCop
|
|
30
52
|
corrector.replace(node.loc.selector, 'select')
|
31
53
|
end
|
32
54
|
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def root_receiver(node)
|
59
|
+
receiver = node.receiver
|
60
|
+
|
61
|
+
if receiver&.send_type?
|
62
|
+
root_receiver(receiver)
|
63
|
+
else
|
64
|
+
receiver
|
65
|
+
end
|
66
|
+
end
|
33
67
|
end
|
34
68
|
end
|
35
69
|
end
|
@@ -17,7 +17,7 @@ module RuboCop
|
|
17
17
|
MSG = 'Use a string value for `class_name`.'
|
18
18
|
|
19
19
|
def_node_matcher :association_with_reflection, <<~PATTERN
|
20
|
-
(send nil? {:has_many :has_one :belongs_to} _
|
20
|
+
(send nil? {:has_many :has_one :belongs_to} _ _ ?
|
21
21
|
(hash <$#reflection_class_name ...>)
|
22
22
|
)
|
23
23
|
PATTERN
|
@@ -49,8 +49,7 @@ module RuboCop
|
|
49
49
|
|
50
50
|
relative_date?(value) do |method_name|
|
51
51
|
add_offense(node,
|
52
|
-
location:
|
53
|
-
value.loc.expression.end_pos),
|
52
|
+
location: offense_range(name, value),
|
54
53
|
message: format(MSG, method_name: method_name))
|
55
54
|
end
|
56
55
|
end
|
@@ -77,6 +76,10 @@ module RuboCop
|
|
77
76
|
|
78
77
|
private
|
79
78
|
|
79
|
+
def offense_range(name, value)
|
80
|
+
range_between(name.loc.expression.begin_pos, value.loc.expression.end_pos)
|
81
|
+
end
|
82
|
+
|
80
83
|
def_node_matcher :relative_date_assignment?, <<~PATTERN
|
81
84
|
{
|
82
85
|
(casgn _ _ (send _ ${:since :from_now :after :ago :until :before}))
|
@@ -129,6 +129,51 @@ module RuboCop
|
|
129
129
|
# end
|
130
130
|
# end
|
131
131
|
#
|
132
|
+
# @example
|
133
|
+
# # remove_columns
|
134
|
+
#
|
135
|
+
# # bad
|
136
|
+
# def change
|
137
|
+
# remove_columns :users, :name, :email
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# # good
|
141
|
+
# def change
|
142
|
+
# reversible do |dir|
|
143
|
+
# dir.up do
|
144
|
+
# remove_columns :users, :name, :email
|
145
|
+
# end
|
146
|
+
#
|
147
|
+
# dir.down do
|
148
|
+
# add_column :users, :name, :string
|
149
|
+
# add_column :users, :email, :string
|
150
|
+
# end
|
151
|
+
# end
|
152
|
+
# end
|
153
|
+
#
|
154
|
+
# # good (Rails >= 6.1, see https://github.com/rails/rails/pull/36589)
|
155
|
+
# def change
|
156
|
+
# remove_columns :users, :name, :email, type: :string
|
157
|
+
# end
|
158
|
+
#
|
159
|
+
# @example
|
160
|
+
# # remove_index
|
161
|
+
#
|
162
|
+
# # bad
|
163
|
+
# def change
|
164
|
+
# remove_index :users, name: :index_users_on_email
|
165
|
+
# end
|
166
|
+
#
|
167
|
+
# # good
|
168
|
+
# def change
|
169
|
+
# remove_index :users, :email
|
170
|
+
# end
|
171
|
+
#
|
172
|
+
# # good
|
173
|
+
# def change
|
174
|
+
# remove_index :users, column: :email
|
175
|
+
# end
|
176
|
+
#
|
132
177
|
# @see https://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html
|
133
178
|
class ReversibleMigration < Cop
|
134
179
|
MSG = '%<action>s is not reversible.'
|
@@ -153,6 +198,14 @@ module RuboCop
|
|
153
198
|
(send nil? :change_table $_ ...)
|
154
199
|
PATTERN
|
155
200
|
|
201
|
+
def_node_matcher :remove_columns_call, <<~PATTERN
|
202
|
+
(send nil? :remove_columns ... $_)
|
203
|
+
PATTERN
|
204
|
+
|
205
|
+
def_node_matcher :remove_index_call, <<~PATTERN
|
206
|
+
(send nil? :remove_index _ $_)
|
207
|
+
PATTERN
|
208
|
+
|
156
209
|
def on_send(node)
|
157
210
|
return unless within_change_method?(node)
|
158
211
|
return if within_reversible_or_up_only_block?(node)
|
@@ -162,6 +215,8 @@ module RuboCop
|
|
162
215
|
check_reversible_hash_node(node)
|
163
216
|
check_remove_column_node(node)
|
164
217
|
check_remove_foreign_key_node(node)
|
218
|
+
check_remove_columns_node(node)
|
219
|
+
check_remove_index_node(node)
|
165
220
|
end
|
166
221
|
|
167
222
|
def on_block(node)
|
@@ -237,6 +292,30 @@ module RuboCop
|
|
237
292
|
end
|
238
293
|
end
|
239
294
|
|
295
|
+
def check_remove_columns_node(node)
|
296
|
+
remove_columns_call(node) do |args|
|
297
|
+
unless all_hash_key?(args, :type) && target_rails_version >= 6.1
|
298
|
+
action = target_rails_version >= 6.1 ? 'remove_columns(without type)' : 'remove_columns'
|
299
|
+
|
300
|
+
add_offense(
|
301
|
+
node,
|
302
|
+
message: format(MSG, action: action)
|
303
|
+
)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def check_remove_index_node(node)
|
309
|
+
remove_index_call(node) do |args|
|
310
|
+
if args.hash_type? && !all_hash_key?(args, :column)
|
311
|
+
add_offense(
|
312
|
+
node,
|
313
|
+
message: format(MSG, action: 'remove_index(without column)')
|
314
|
+
)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
240
319
|
def check_change_table_offense(receiver, node)
|
241
320
|
method_name = node.method_name
|
242
321
|
return if receiver != node.receiver &&
|
@@ -138,7 +138,7 @@ module RuboCop
|
|
138
138
|
add_offense_for_node(node, CREATE_MSG)
|
139
139
|
end
|
140
140
|
|
141
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
141
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
142
142
|
def on_send(node)
|
143
143
|
return unless persist_method?(node)
|
144
144
|
return if return_value_assigned?(node)
|
@@ -150,7 +150,7 @@ module RuboCop
|
|
150
150
|
|
151
151
|
add_offense_for_node(node)
|
152
152
|
end
|
153
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
153
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
154
154
|
alias on_csend on_send
|
155
155
|
|
156
156
|
def autocorrect(node)
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
#
|
7
|
+
# Checks SQL heredocs to use `.squish`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# <<-SQL
|
12
|
+
# SELECT * FROM posts;
|
13
|
+
# SQL
|
14
|
+
#
|
15
|
+
# <<-SQL
|
16
|
+
# SELECT * FROM posts
|
17
|
+
# WHERE id = 1
|
18
|
+
# SQL
|
19
|
+
#
|
20
|
+
# execute(<<~SQL, "Post Load")
|
21
|
+
# SELECT * FROM posts
|
22
|
+
# WHERE post_id = 1
|
23
|
+
# SQL
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# <<-SQL.squish
|
27
|
+
# SELECT * FROM posts;
|
28
|
+
# SQL
|
29
|
+
#
|
30
|
+
# <<~SQL.squish
|
31
|
+
# SELECT * FROM table
|
32
|
+
# WHERE id = 1
|
33
|
+
# SQL
|
34
|
+
#
|
35
|
+
# execute(<<~SQL.squish, "Post Load")
|
36
|
+
# SELECT * FROM posts
|
37
|
+
# WHERE post_id = 1
|
38
|
+
# SQL
|
39
|
+
#
|
40
|
+
class SquishedSQLHeredocs < Cop
|
41
|
+
include Heredoc
|
42
|
+
|
43
|
+
SQL = 'SQL'
|
44
|
+
SQUISH = '.squish'
|
45
|
+
MSG = 'Use `%<expect>s` instead of `%<current>s`.'
|
46
|
+
|
47
|
+
def on_heredoc(node)
|
48
|
+
return unless offense_detected?(node)
|
49
|
+
|
50
|
+
add_offense(node)
|
51
|
+
end
|
52
|
+
|
53
|
+
def autocorrect(node)
|
54
|
+
lambda do |corrector|
|
55
|
+
corrector.insert_after(node, SQUISH)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def offense_detected?(node)
|
62
|
+
sql_heredoc?(node) && !using_squish?(node)
|
63
|
+
end
|
64
|
+
|
65
|
+
def sql_heredoc?(node)
|
66
|
+
delimiter_string(node) == SQL
|
67
|
+
end
|
68
|
+
|
69
|
+
def using_squish?(node)
|
70
|
+
node.parent&.send_type? && node.parent&.method?(:squish)
|
71
|
+
end
|
72
|
+
|
73
|
+
def message(node)
|
74
|
+
format(
|
75
|
+
MSG,
|
76
|
+
expect: "#{node.source}#{SQUISH}",
|
77
|
+
current: node.source
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -18,6 +18,8 @@ module RuboCop
|
|
18
18
|
# ActiveRecord::Relation vs a call to pluck on an
|
19
19
|
# ActiveRecord::Associations::CollectionProxy.
|
20
20
|
#
|
21
|
+
# This cop is unsafe because the behavior may change depending on the
|
22
|
+
# database collation.
|
21
23
|
# Autocorrect is disabled by default for this cop since it may generate
|
22
24
|
# false positives.
|
23
25
|
#
|
@@ -34,23 +34,25 @@ module RuboCop
|
|
34
34
|
return unless uniqueness_part(node)
|
35
35
|
return if condition_part?(node)
|
36
36
|
return unless schema
|
37
|
-
|
37
|
+
|
38
|
+
klass, table, names = find_schema_information(node)
|
39
|
+
return unless names
|
40
|
+
return if with_index?(klass, table, names)
|
38
41
|
|
39
42
|
add_offense(node)
|
40
43
|
end
|
41
44
|
|
42
45
|
private
|
43
46
|
|
44
|
-
def
|
47
|
+
def find_schema_information(node)
|
45
48
|
klass = class_node(node)
|
46
|
-
return true unless klass # Skip analysis
|
47
|
-
|
48
49
|
table = schema.table_by(name: table_name(klass))
|
49
|
-
return true unless table # Skip analysis if it can't find the table
|
50
|
-
|
51
50
|
names = column_names(node)
|
52
|
-
return true unless names
|
53
51
|
|
52
|
+
[klass, table, names]
|
53
|
+
end
|
54
|
+
|
55
|
+
def with_index?(klass, table, names)
|
54
56
|
# Compatibility for Rails 4.2.
|
55
57
|
add_indicies = schema.add_indicies_by(table_name: table_name(klass))
|
56
58
|
|
@@ -95,6 +97,8 @@ module RuboCop
|
|
95
97
|
scope = find_scope(uniq)
|
96
98
|
return unless scope
|
97
99
|
|
100
|
+
scope = unfreeze_scope(scope)
|
101
|
+
|
98
102
|
case scope.type
|
99
103
|
when :sym, :str
|
100
104
|
[scope.value]
|
@@ -112,6 +116,10 @@ module RuboCop
|
|
112
116
|
end
|
113
117
|
end
|
114
118
|
|
119
|
+
def unfreeze_scope(scope)
|
120
|
+
scope.send_type? && scope.method?(:freeze) ? scope.children.first : scope
|
121
|
+
end
|
122
|
+
|
115
123
|
def class_node(node)
|
116
124
|
node.each_ancestor.find(&:class_type?)
|
117
125
|
end
|
@@ -3,9 +3,15 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
# This cop enforces
|
6
|
+
# This cop enforces consistent style when using `exists?`.
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# Two styles are supported for this cop. When EnforcedStyle is 'exists'
|
9
|
+
# then the cop enforces `exists?(...)` over `where(...).exists?`.
|
10
|
+
#
|
11
|
+
# When EnforcedStyle is 'where' then the cop enforces
|
12
|
+
# `where(...).exists?` over `exists?(...)`.
|
13
|
+
#
|
14
|
+
# @example EnforcedStyle: exists (default)
|
9
15
|
# # bad
|
10
16
|
# User.where(name: 'john').exists?
|
11
17
|
# User.where(['name = ?', 'john']).exists?
|
@@ -17,15 +23,34 @@ module RuboCop
|
|
17
23
|
# User.where('length(name) > 10').exists?
|
18
24
|
# user.posts.exists?(published: true)
|
19
25
|
#
|
26
|
+
# @example EnforcedStyle: where
|
27
|
+
# # bad
|
28
|
+
# User.exists?(name: 'john')
|
29
|
+
# User.exists?(['name = ?', 'john'])
|
30
|
+
# User.exists?('name = ?', 'john')
|
31
|
+
# user.posts.exists?(published: true)
|
32
|
+
#
|
33
|
+
# # good
|
34
|
+
# User.where(name: 'john').exists?
|
35
|
+
# User.where(['name = ?', 'john']).exists?
|
36
|
+
# User.where('name = ?', 'john').exists?
|
37
|
+
# user.posts.where(published: true).exists?
|
38
|
+
# User.where('length(name) > 10').exists?
|
20
39
|
class WhereExists < Cop
|
40
|
+
include ConfigurableEnforcedStyle
|
41
|
+
|
21
42
|
MSG = 'Prefer `%<good_method>s` over `%<bad_method>s`.'
|
22
43
|
|
23
44
|
def_node_matcher :where_exists_call?, <<~PATTERN
|
24
45
|
(send (send _ :where $...) :exists?)
|
25
46
|
PATTERN
|
26
47
|
|
48
|
+
def_node_matcher :exists_with_args?, <<~PATTERN
|
49
|
+
(send _ :exists? $...)
|
50
|
+
PATTERN
|
51
|
+
|
27
52
|
def on_send(node)
|
28
|
-
|
53
|
+
find_offenses(node) do |args|
|
29
54
|
return unless convertable_args?(args)
|
30
55
|
|
31
56
|
range = correction_range(node)
|
@@ -35,7 +60,7 @@ module RuboCop
|
|
35
60
|
end
|
36
61
|
|
37
62
|
def autocorrect(node)
|
38
|
-
args =
|
63
|
+
args = find_offenses(node)
|
39
64
|
|
40
65
|
lambda do |corrector|
|
41
66
|
corrector.replace(
|
@@ -47,21 +72,59 @@ module RuboCop
|
|
47
72
|
|
48
73
|
private
|
49
74
|
|
75
|
+
def where_style?
|
76
|
+
style == :where
|
77
|
+
end
|
78
|
+
|
79
|
+
def exists_style?
|
80
|
+
style == :exists
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_offenses(node, &block)
|
84
|
+
if exists_style?
|
85
|
+
where_exists_call?(node, &block)
|
86
|
+
elsif where_style?
|
87
|
+
exists_with_args?(node, &block)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
50
91
|
def convertable_args?(args)
|
92
|
+
return false if args.empty?
|
93
|
+
|
51
94
|
args.size > 1 || args[0].hash_type? || args[0].array_type?
|
52
95
|
end
|
53
96
|
|
54
97
|
def correction_range(node)
|
55
|
-
|
98
|
+
if exists_style?
|
99
|
+
node.receiver.loc.selector.join(node.loc.selector)
|
100
|
+
elsif where_style?
|
101
|
+
node.loc.selector.with(end_pos: node.loc.expression.end_pos)
|
102
|
+
end
|
56
103
|
end
|
57
104
|
|
58
105
|
def build_good_method(args)
|
106
|
+
if exists_style?
|
107
|
+
build_good_method_exists(args)
|
108
|
+
elsif where_style?
|
109
|
+
build_good_method_where(args)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def build_good_method_exists(args)
|
59
114
|
if args.size > 1
|
60
115
|
"exists?([#{args.map(&:source).join(', ')}])"
|
61
116
|
else
|
62
117
|
"exists?(#{args[0].source})"
|
63
118
|
end
|
64
119
|
end
|
120
|
+
|
121
|
+
def build_good_method_where(args)
|
122
|
+
if args.size > 1
|
123
|
+
"where(#{args.map(&:source).join(', ')}).exists?"
|
124
|
+
else
|
125
|
+
"where(#{args[0].source}).exists?"
|
126
|
+
end
|
127
|
+
end
|
65
128
|
end
|
66
129
|
end
|
67
130
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop identifies places where manually constructed SQL
|
7
|
+
# in `where` can be replaced with `where.not(...)`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# User.where('name != ?', 'Gabe')
|
12
|
+
# User.where('name != :name', name: 'Gabe')
|
13
|
+
# User.where('name IS NOT NULL')
|
14
|
+
# User.where('name NOT IN (?)', ['john', 'jane'])
|
15
|
+
# User.where('name NOT IN (:names)', names: ['john', 'jane'])
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# User.where.not(name: 'Gabe')
|
19
|
+
# User.where.not(name: nil)
|
20
|
+
# User.where.not(name: ['john', 'jane'])
|
21
|
+
#
|
22
|
+
class WhereNot < Cop
|
23
|
+
include RangeHelp
|
24
|
+
|
25
|
+
MSG = 'Use `%<good_method>s` instead of manually constructing negated SQL in `where`.'
|
26
|
+
|
27
|
+
def_node_matcher :where_method_call?, <<~PATTERN
|
28
|
+
{
|
29
|
+
(send _ :where (array $str_type? $_ ?))
|
30
|
+
(send _ :where $str_type? $_ ?)
|
31
|
+
}
|
32
|
+
PATTERN
|
33
|
+
|
34
|
+
def on_send(node)
|
35
|
+
where_method_call?(node) do |template_node, value_node|
|
36
|
+
value_node = value_node.first
|
37
|
+
|
38
|
+
range = offense_range(node)
|
39
|
+
|
40
|
+
column_and_value = extract_column_and_value(template_node, value_node)
|
41
|
+
return unless column_and_value
|
42
|
+
|
43
|
+
good_method = build_good_method(*column_and_value)
|
44
|
+
message = format(MSG, good_method: good_method)
|
45
|
+
|
46
|
+
add_offense(node, location: range, message: message)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def autocorrect(node)
|
51
|
+
where_method_call?(node) do |template_node, value_node|
|
52
|
+
value_node = value_node.first
|
53
|
+
|
54
|
+
lambda do |corrector|
|
55
|
+
range = offense_range(node)
|
56
|
+
|
57
|
+
column, value = *extract_column_and_value(template_node, value_node)
|
58
|
+
replacement = build_good_method(column, value)
|
59
|
+
|
60
|
+
corrector.replace(range, replacement)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
NOT_EQ_ANONYMOUS_RE = /\A([\w.]+)\s+!=\s+\?\z/.freeze # column != ?
|
66
|
+
NOT_IN_ANONYMOUS_RE = /\A([\w.]+)\s+NOT\s+IN\s+\(\?\)\z/i.freeze # column NOT IN (?)
|
67
|
+
NOT_EQ_NAMED_RE = /\A([\w.]+)\s+!=\s+:(\w+)\z/.freeze # column != :column
|
68
|
+
NOT_IN_NAMED_RE = /\A([\w.]+)\s+NOT\s+IN\s+\(:(\w+)\)\z/i.freeze # column NOT IN (:column)
|
69
|
+
IS_NOT_NULL_RE = /\A([\w.]+)\s+IS\s+NOT\s+NULL\z/i.freeze # column IS NOT NULL
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def offense_range(node)
|
74
|
+
range_between(node.loc.selector.begin_pos, node.loc.expression.end_pos)
|
75
|
+
end
|
76
|
+
|
77
|
+
def extract_column_and_value(template_node, value_node)
|
78
|
+
value =
|
79
|
+
case template_node.value
|
80
|
+
when NOT_EQ_ANONYMOUS_RE, NOT_IN_ANONYMOUS_RE
|
81
|
+
value_node.source
|
82
|
+
when NOT_EQ_NAMED_RE, NOT_IN_NAMED_RE
|
83
|
+
return unless value_node.hash_type?
|
84
|
+
|
85
|
+
pair = value_node.pairs.find { |p| p.key.value.to_sym == Regexp.last_match(2).to_sym }
|
86
|
+
pair.value.source
|
87
|
+
when IS_NOT_NULL_RE
|
88
|
+
'nil'
|
89
|
+
else
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
93
|
+
[Regexp.last_match(1), value]
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_good_method(column, value)
|
97
|
+
if column.include?('.')
|
98
|
+
"where.not('#{column}' => #{value})"
|
99
|
+
else
|
100
|
+
"where.not(#{column}: #{value})"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -9,6 +9,7 @@ require_relative 'rails/active_record_aliases'
|
|
9
9
|
require_relative 'rails/active_record_callbacks_order'
|
10
10
|
require_relative 'rails/active_record_override'
|
11
11
|
require_relative 'rails/active_support_aliases'
|
12
|
+
require_relative 'rails/after_commit_override'
|
12
13
|
require_relative 'rails/application_controller'
|
13
14
|
require_relative 'rails/application_job'
|
14
15
|
require_relative 'rails/application_mailer'
|
@@ -48,6 +49,7 @@ require_relative 'rails/mailer_name'
|
|
48
49
|
require_relative 'rails/match_route'
|
49
50
|
require_relative 'rails/negate_include'
|
50
51
|
require_relative 'rails/not_null_column'
|
52
|
+
require_relative 'rails/order_by_id'
|
51
53
|
require_relative 'rails/output'
|
52
54
|
require_relative 'rails/output_safety'
|
53
55
|
require_relative 'rails/pick'
|
@@ -75,9 +77,11 @@ require_relative 'rails/save_bang'
|
|
75
77
|
require_relative 'rails/scope_args'
|
76
78
|
require_relative 'rails/short_i18n'
|
77
79
|
require_relative 'rails/skips_model_validations'
|
80
|
+
require_relative 'rails/squished_sql_heredocs'
|
78
81
|
require_relative 'rails/time_zone'
|
79
82
|
require_relative 'rails/uniq_before_pluck'
|
80
83
|
require_relative 'rails/unique_validation_without_index'
|
81
84
|
require_relative 'rails/unknown_env'
|
82
85
|
require_relative 'rails/validation'
|
83
86
|
require_relative 'rails/where_exists'
|
87
|
+
require_relative 'rails/where_not'
|
@@ -97,14 +97,12 @@ module RuboCop
|
|
97
97
|
end.compact
|
98
98
|
end
|
99
99
|
|
100
|
-
def each_content(node)
|
100
|
+
def each_content(node, &block)
|
101
101
|
return enum_for(__method__, node) unless block_given?
|
102
102
|
|
103
103
|
case node.body&.type
|
104
104
|
when :begin
|
105
|
-
node.body.children.each
|
106
|
-
yield(child)
|
107
|
-
end
|
105
|
+
node.body.children.each(&block)
|
108
106
|
else
|
109
107
|
yield(node.body)
|
110
108
|
end
|
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.8.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: 2020-
|
13
|
+
date: 2020-09-04 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -77,6 +77,7 @@ files:
|
|
77
77
|
- lib/rubocop/cop/rails/active_record_callbacks_order.rb
|
78
78
|
- lib/rubocop/cop/rails/active_record_override.rb
|
79
79
|
- lib/rubocop/cop/rails/active_support_aliases.rb
|
80
|
+
- lib/rubocop/cop/rails/after_commit_override.rb
|
80
81
|
- lib/rubocop/cop/rails/application_controller.rb
|
81
82
|
- lib/rubocop/cop/rails/application_job.rb
|
82
83
|
- lib/rubocop/cop/rails/application_mailer.rb
|
@@ -116,6 +117,7 @@ files:
|
|
116
117
|
- lib/rubocop/cop/rails/match_route.rb
|
117
118
|
- lib/rubocop/cop/rails/negate_include.rb
|
118
119
|
- lib/rubocop/cop/rails/not_null_column.rb
|
120
|
+
- lib/rubocop/cop/rails/order_by_id.rb
|
119
121
|
- lib/rubocop/cop/rails/output.rb
|
120
122
|
- lib/rubocop/cop/rails/output_safety.rb
|
121
123
|
- lib/rubocop/cop/rails/pick.rb
|
@@ -143,12 +145,14 @@ files:
|
|
143
145
|
- lib/rubocop/cop/rails/scope_args.rb
|
144
146
|
- lib/rubocop/cop/rails/short_i18n.rb
|
145
147
|
- lib/rubocop/cop/rails/skips_model_validations.rb
|
148
|
+
- lib/rubocop/cop/rails/squished_sql_heredocs.rb
|
146
149
|
- lib/rubocop/cop/rails/time_zone.rb
|
147
150
|
- lib/rubocop/cop/rails/uniq_before_pluck.rb
|
148
151
|
- lib/rubocop/cop/rails/unique_validation_without_index.rb
|
149
152
|
- lib/rubocop/cop/rails/unknown_env.rb
|
150
153
|
- lib/rubocop/cop/rails/validation.rb
|
151
154
|
- lib/rubocop/cop/rails/where_exists.rb
|
155
|
+
- lib/rubocop/cop/rails/where_not.rb
|
152
156
|
- lib/rubocop/cop/rails_cops.rb
|
153
157
|
- lib/rubocop/rails.rb
|
154
158
|
- lib/rubocop/rails/inject.rb
|