rubocop-rails 2.32.0 → 2.35.1
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/LICENSE.txt +1 -1
- data/config/default.yml +59 -15
- data/lib/rubocop/cop/mixin/active_record_helper.rb +1 -1
- data/lib/rubocop/cop/mixin/database_type_resolvable.rb +3 -3
- data/lib/rubocop/cop/mixin/index_method.rb +4 -0
- data/lib/rubocop/cop/mixin/target_rails_version.rb +7 -17
- data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +4 -2
- data/lib/rubocop/cop/rails/action_filter.rb +1 -1
- data/lib/rubocop/cop/rails/delegate.rb +4 -4
- data/lib/rubocop/cop/rails/duplicate_association.rb +1 -1
- data/lib/rubocop/cop/rails/duplicate_scope.rb +2 -2
- data/lib/rubocop/cop/rails/env.rb +57 -0
- data/lib/rubocop/cop/rails/env_local.rb +50 -26
- data/lib/rubocop/cop/rails/environment_comparison.rb +56 -48
- data/lib/rubocop/cop/rails/exit.rb +7 -4
- data/lib/rubocop/cop/rails/file_path.rb +2 -2
- data/lib/rubocop/cop/rails/find_by.rb +1 -1
- data/lib/rubocop/cop/rails/find_by_or_assignment_memoization.rb +124 -0
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +16 -17
- data/lib/rubocop/cop/rails/http_status_name_consistency.rb +80 -0
- data/lib/rubocop/cop/rails/i18n_locale_texts.rb +24 -2
- data/lib/rubocop/cop/rails/index_with.rb +5 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +7 -0
- data/lib/rubocop/cop/rails/not_null_column.rb +2 -0
- data/lib/rubocop/cop/rails/order_arguments.rb +84 -0
- data/lib/rubocop/cop/rails/output.rb +3 -0
- data/lib/rubocop/cop/rails/output_safety.rb +3 -1
- data/lib/rubocop/cop/rails/pluck.rb +6 -3
- data/lib/rubocop/cop/rails/presence.rb +76 -20
- data/lib/rubocop/cop/rails/rake_environment.rb +1 -1
- data/lib/rubocop/cop/rails/read_write_attribute.rb +1 -1
- data/lib/rubocop/cop/rails/redirect_back_or_to.rb +99 -0
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +3 -3
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -1
- data/lib/rubocop/cop/rails/response_parsed_body.rb +59 -50
- data/lib/rubocop/cop/rails/save_bang.rb +2 -2
- data/lib/rubocop/cop/rails/select_map.rb +26 -4
- data/lib/rubocop/cop/rails/strong_parameters_expect.rb +78 -1
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +4 -1
- data/lib/rubocop/cop/rails/unknown_env.rb +39 -12
- data/lib/rubocop/cop/rails/where_exists.rb +5 -5
- data/lib/rubocop/cop/rails_cops.rb +5 -0
- 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: 409bf447cc0d5a3b6529407611f53ad1331a5c1bd908e35b7f50193e53bb2c95
|
|
4
|
+
data.tar.gz: 5d3b83383d138616bcfbbe292413f6ddd6f49b60be33dbe57fdcd481bb7d1c1c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ea50f3fad3d83f62ec110dd0a5b32688f92fcd0963d486714e4a71867343316b67842476f67dd64a5c6d7ccde5f43aeca5c1980ec20e96f097703fdf6ad7c15c
|
|
7
|
+
data.tar.gz: eb75cb4c5b5a21562ff42dd194fea691e2feec9563095a1a1509b3263a41693ce072a075a269e1416c8f150cf201a602e2301ac1d993dc7a237c3bbe19083d04
|
data/LICENSE.txt
CHANGED
data/config/default.yml
CHANGED
|
@@ -91,6 +91,12 @@ Lint/UselessAccessModifier:
|
|
|
91
91
|
- concern
|
|
92
92
|
- concerning
|
|
93
93
|
|
|
94
|
+
Lint/UselessMethodDefinition:
|
|
95
|
+
# Avoids conflict with `Rails/LexicallyScopedActionFilter` cop.
|
|
96
|
+
Exclude:
|
|
97
|
+
- '**/app/controllers/**/*.rb'
|
|
98
|
+
- '**/app/mailers/**/*.rb'
|
|
99
|
+
|
|
94
100
|
Rails:
|
|
95
101
|
Enabled: true
|
|
96
102
|
DocumentationBaseURL: https://docs.rubocop.org/rubocop-rails
|
|
@@ -402,7 +408,7 @@ Rails/DuplicateAssociation:
|
|
|
402
408
|
VersionChanged: '2.18'
|
|
403
409
|
|
|
404
410
|
Rails/DuplicateScope:
|
|
405
|
-
Description: 'Multiple scopes share this same
|
|
411
|
+
Description: 'Multiple scopes share this same expression.'
|
|
406
412
|
Enabled: pending
|
|
407
413
|
Severity: warning
|
|
408
414
|
VersionAdded: '2.14'
|
|
@@ -453,6 +459,7 @@ Rails/EnumSyntax:
|
|
|
453
459
|
VersionAdded: '2.26'
|
|
454
460
|
Include:
|
|
455
461
|
- '**/app/models/**/*.rb'
|
|
462
|
+
- '**/lib/**/*.rb'
|
|
456
463
|
|
|
457
464
|
Rails/EnumUniqueness:
|
|
458
465
|
Description: 'Avoid duplicate integers in hash-syntax `enum` declaration.'
|
|
@@ -461,6 +468,11 @@ Rails/EnumUniqueness:
|
|
|
461
468
|
Include:
|
|
462
469
|
- '**/app/models/**/*.rb'
|
|
463
470
|
|
|
471
|
+
Rails/Env:
|
|
472
|
+
Description: 'Use Feature Flags or config instead of `Rails.env`.'
|
|
473
|
+
Enabled: false
|
|
474
|
+
VersionAdded: '2.34'
|
|
475
|
+
|
|
464
476
|
Rails/EnvLocal:
|
|
465
477
|
Description: 'Use `Rails.env.local?` instead of `Rails.env.development? || Rails.env.test?`.'
|
|
466
478
|
Enabled: pending
|
|
@@ -532,6 +544,13 @@ Rails/FindById:
|
|
|
532
544
|
Enabled: 'pending'
|
|
533
545
|
VersionAdded: '2.7'
|
|
534
546
|
|
|
547
|
+
Rails/FindByOrAssignmentMemoization:
|
|
548
|
+
Description: 'Avoid memoizing `find_by` results with `||=`.'
|
|
549
|
+
StyleGuide: 'https://rails.rubystyle.guide/#find-by-memoization'
|
|
550
|
+
Enabled: pending
|
|
551
|
+
Safe: false
|
|
552
|
+
VersionAdded: '2.33'
|
|
553
|
+
|
|
535
554
|
Rails/FindEach:
|
|
536
555
|
Description: 'Prefer all.find_each over all.each.'
|
|
537
556
|
StyleGuide: 'https://rails.rubystyle.guide#find-each'
|
|
@@ -582,8 +601,8 @@ Rails/HttpPositionalArguments:
|
|
|
582
601
|
Enabled: true
|
|
583
602
|
VersionAdded: '0.44'
|
|
584
603
|
Include:
|
|
585
|
-
- 'spec/**/*'
|
|
586
|
-
- 'test/**/*'
|
|
604
|
+
- '**/spec/**/*'
|
|
605
|
+
- '**/test/**/*'
|
|
587
606
|
|
|
588
607
|
Rails/HttpStatus:
|
|
589
608
|
Description: 'Enforces use of symbolic or numeric value to define HTTP status.'
|
|
@@ -595,6 +614,14 @@ Rails/HttpStatus:
|
|
|
595
614
|
- numeric
|
|
596
615
|
- symbolic
|
|
597
616
|
|
|
617
|
+
Rails/HttpStatusNameConsistency:
|
|
618
|
+
Description: 'Enforces consistency by using the current HTTP status names.'
|
|
619
|
+
Enabled: pending
|
|
620
|
+
Severity: warning
|
|
621
|
+
VersionAdded: '2.34'
|
|
622
|
+
Include:
|
|
623
|
+
- '**/app/controllers/**/*.rb'
|
|
624
|
+
|
|
598
625
|
Rails/I18nLazyLookup:
|
|
599
626
|
Description: 'Checks for places where I18n "lazy" lookup can be used.'
|
|
600
627
|
StyleGuide: 'https://rails.rubystyle.guide/#lazy-lookup'
|
|
@@ -613,8 +640,8 @@ Rails/I18nLocaleAssignment:
|
|
|
613
640
|
Enabled: 'pending'
|
|
614
641
|
VersionAdded: '2.11'
|
|
615
642
|
Include:
|
|
616
|
-
- spec/**/*.rb
|
|
617
|
-
- test/**/*.rb
|
|
643
|
+
- '**/spec/**/*.rb'
|
|
644
|
+
- '**/test/**/*.rb'
|
|
618
645
|
|
|
619
646
|
Rails/I18nLocaleTexts:
|
|
620
647
|
Description: 'Enforces use of I18n and locale files instead of locale specific strings.'
|
|
@@ -647,8 +674,9 @@ Rails/IndexBy:
|
|
|
647
674
|
Rails/IndexWith:
|
|
648
675
|
Description: 'Prefer `index_with` over `each_with_object`, `to_h`, or `map`.'
|
|
649
676
|
Enabled: true
|
|
677
|
+
SafeAutoCorrect: false
|
|
650
678
|
VersionAdded: '2.5'
|
|
651
|
-
VersionChanged: '2.
|
|
679
|
+
VersionChanged: '2.33'
|
|
652
680
|
|
|
653
681
|
Rails/Inquiry:
|
|
654
682
|
Description: "Prefer Ruby's comparison operators over Active Support's `Array#inquiry` and `String#inquiry`."
|
|
@@ -742,6 +770,13 @@ Rails/NotNullColumn:
|
|
|
742
770
|
Include:
|
|
743
771
|
- db/**/*.rb
|
|
744
772
|
|
|
773
|
+
Rails/OrderArguments:
|
|
774
|
+
Description: 'Prefer symbol arguments over strings in `order` method.'
|
|
775
|
+
StyleGuide: 'https://rails.rubystyle.guide/#order-arguments'
|
|
776
|
+
Enabled: pending
|
|
777
|
+
VersionAdded: '2.33'
|
|
778
|
+
Safe: false
|
|
779
|
+
|
|
745
780
|
Rails/OrderById:
|
|
746
781
|
Description: >-
|
|
747
782
|
Do not use the `id` column for ordering.
|
|
@@ -809,6 +844,7 @@ Rails/Presence:
|
|
|
809
844
|
Description: 'Checks code that can be written more easily using `Object#presence` defined by Active Support.'
|
|
810
845
|
Enabled: true
|
|
811
846
|
VersionAdded: '0.52'
|
|
847
|
+
VersionChanged: '2.34'
|
|
812
848
|
|
|
813
849
|
Rails/Present:
|
|
814
850
|
Description: 'Enforces use of `present?`.'
|
|
@@ -845,6 +881,14 @@ Rails/ReadWriteAttribute:
|
|
|
845
881
|
Include:
|
|
846
882
|
- '**/app/models/**/*.rb'
|
|
847
883
|
|
|
884
|
+
Rails/RedirectBackOrTo:
|
|
885
|
+
Description: >-
|
|
886
|
+
Use `redirect_back_or_to` instead of `redirect_back` with
|
|
887
|
+
`fallback_location` option.
|
|
888
|
+
Enabled: pending
|
|
889
|
+
Severity: warning
|
|
890
|
+
VersionAdded: '2.34'
|
|
891
|
+
|
|
848
892
|
Rails/RedundantActiveRecordAllMethod:
|
|
849
893
|
Description: Detect redundant `all` used as a receiver for Active Record query methods.
|
|
850
894
|
StyleGuide: 'https://rails.rubystyle.guide/#redundant-all'
|
|
@@ -885,8 +929,8 @@ Rails/RedundantTravelBack:
|
|
|
885
929
|
Enabled: pending
|
|
886
930
|
VersionAdded: '2.12'
|
|
887
931
|
Include:
|
|
888
|
-
- spec/**/*.rb
|
|
889
|
-
- test/**/*.rb
|
|
932
|
+
- '**/spec/**/*.rb'
|
|
933
|
+
- '**/test/**/*.rb'
|
|
890
934
|
|
|
891
935
|
Rails/ReflectionClassName:
|
|
892
936
|
Description: 'Use a string for `class_name` option value in the definition of a reflection.'
|
|
@@ -949,10 +993,10 @@ Rails/ResponseParsedBody:
|
|
|
949
993
|
VersionAdded: '2.18'
|
|
950
994
|
VersionChanged: '2.19'
|
|
951
995
|
Include:
|
|
952
|
-
- spec/controllers/**/*.rb
|
|
953
|
-
- spec/requests/**/*.rb
|
|
954
|
-
- test/controllers/**/*.rb
|
|
955
|
-
- test/integration/**/*.rb
|
|
996
|
+
- '**/spec/controllers/**/*.rb'
|
|
997
|
+
- '**/spec/requests/**/*.rb'
|
|
998
|
+
- '**/test/controllers/**/*.rb'
|
|
999
|
+
- '**/test/integration/**/*.rb'
|
|
956
1000
|
|
|
957
1001
|
Rails/ReversibleMigration:
|
|
958
1002
|
Description: 'Checks whether the change method of the migration file is reversible.'
|
|
@@ -1126,7 +1170,7 @@ Rails/ThreeStateBooleanColumn:
|
|
|
1126
1170
|
Rails/TimeZone:
|
|
1127
1171
|
Description: 'Checks the correct usage of time zone aware methods.'
|
|
1128
1172
|
StyleGuide: 'https://rails.rubystyle.guide#time'
|
|
1129
|
-
Reference: '
|
|
1173
|
+
Reference: 'https://danilenko.org/2012/7/6/rails_timezones'
|
|
1130
1174
|
Enabled: true
|
|
1131
1175
|
SafeAutoCorrect: false
|
|
1132
1176
|
VersionAdded: '0.30'
|
|
@@ -1146,8 +1190,8 @@ Rails/TimeZoneAssignment:
|
|
|
1146
1190
|
Enabled: 'pending'
|
|
1147
1191
|
VersionAdded: '2.10'
|
|
1148
1192
|
Include:
|
|
1149
|
-
- spec/**/*.rb
|
|
1150
|
-
- test/**/*.rb
|
|
1193
|
+
- '**/spec/**/*.rb'
|
|
1194
|
+
- '**/test/**/*.rb'
|
|
1151
1195
|
|
|
1152
1196
|
Rails/ToFormattedS:
|
|
1153
1197
|
Description: 'Checks for consistent uses of `to_fs` or `to_formatted_s`.'
|
|
@@ -106,7 +106,7 @@ module RuboCop
|
|
|
106
106
|
send_node = node.each_ancestor(:call).first
|
|
107
107
|
return false unless send_node
|
|
108
108
|
|
|
109
|
-
return true if WHERE_METHODS.include?(send_node.method_name)
|
|
109
|
+
return true if WHERE_METHODS.include?(send_node.method_name) && send_node.receiver != node
|
|
110
110
|
|
|
111
111
|
receiver = send_node.receiver
|
|
112
112
|
return false unless receiver&.send_type?
|
|
@@ -40,7 +40,7 @@ module RuboCop
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
def database_yaml
|
|
43
|
+
def database_yaml(environment = 'development')
|
|
44
44
|
return unless File.exist?('config/database.yml')
|
|
45
45
|
|
|
46
46
|
yaml = if YAML.respond_to?(:unsafe_load_file)
|
|
@@ -50,7 +50,7 @@ module RuboCop
|
|
|
50
50
|
end
|
|
51
51
|
return unless yaml.is_a? Hash
|
|
52
52
|
|
|
53
|
-
config = yaml[
|
|
53
|
+
config = yaml[environment]
|
|
54
54
|
return unless config.is_a?(Hash)
|
|
55
55
|
|
|
56
56
|
config
|
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def database_adapter
|
|
62
|
-
database_yaml['adapter'] || database_yaml.first.last['adapter']
|
|
62
|
+
database_yaml['adapter'] || database_yaml('shared')&.dig('adapter') || database_yaml.first.last['adapter']
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
end
|
|
@@ -132,9 +132,13 @@ module RuboCop
|
|
|
132
132
|
add_offense(
|
|
133
133
|
node, message: "Prefer `#{new_method_name}` over `#{match_desc}`."
|
|
134
134
|
) do |corrector|
|
|
135
|
+
next if part_of_ignored_node?(node)
|
|
136
|
+
|
|
135
137
|
correction = prepare_correction(node)
|
|
136
138
|
execute_correction(corrector, node, correction)
|
|
137
139
|
end
|
|
140
|
+
|
|
141
|
+
ignore_node(node)
|
|
138
142
|
end
|
|
139
143
|
|
|
140
144
|
def extract_captures(match)
|
|
@@ -12,29 +12,19 @@ module RuboCop
|
|
|
12
12
|
TARGET_GEM_NAME = 'railties' # :nodoc:
|
|
13
13
|
|
|
14
14
|
def minimum_target_rails_version(version)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
when String then requires_gem(TARGET_GEM_NAME, version)
|
|
19
|
-
end
|
|
20
|
-
else
|
|
21
|
-
# Fallback path for previous versions of RuboCop which don't support the `requires_gem` API yet.
|
|
22
|
-
@minimum_target_rails_version = version
|
|
15
|
+
case version
|
|
16
|
+
when Integer, Float then requires_gem(TARGET_GEM_NAME, ">= #{version}")
|
|
17
|
+
when String then requires_gem(TARGET_GEM_NAME, version)
|
|
23
18
|
end
|
|
24
19
|
end
|
|
25
20
|
|
|
26
21
|
def support_target_rails_version?(version)
|
|
27
|
-
|
|
28
|
-
return false unless gem_requirements
|
|
22
|
+
return false unless gem_requirements
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
gem_requirement = gem_requirements[TARGET_GEM_NAME]
|
|
25
|
+
return true unless gem_requirement # If we have no requirement, then we support all versions
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
else
|
|
35
|
-
# Fallback path for previous versions of RuboCop which don't support the `requires_gem` API yet.
|
|
36
|
-
@minimum_target_rails_version <= version
|
|
37
|
-
end
|
|
27
|
+
gem_requirement.satisfied_by?(Gem::Version.new(version))
|
|
38
28
|
end
|
|
39
29
|
end
|
|
40
30
|
end
|
|
@@ -73,7 +73,7 @@ module RuboCop
|
|
|
73
73
|
return false if use_redirect_to?(context)
|
|
74
74
|
|
|
75
75
|
context = node
|
|
76
|
-
elsif context.right_siblings.empty?
|
|
76
|
+
elsif context.right_siblings.empty? && !use_redirect_to?(context.parent)
|
|
77
77
|
return true
|
|
78
78
|
end
|
|
79
79
|
context = context.right_siblings
|
|
@@ -98,7 +98,9 @@ module RuboCop
|
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def use_redirect_to?(context)
|
|
101
|
-
context.right_siblings.
|
|
101
|
+
context.right_siblings.any? do |sibling|
|
|
102
|
+
next unless sibling.is_a?(AST::Node)
|
|
103
|
+
|
|
102
104
|
# Unwrap `return redirect_to :index`
|
|
103
105
|
sibling = sibling.children.first if sibling.return_type? && sibling.children.one?
|
|
104
106
|
sibling.send_type? && sibling.method?(:redirect_to)
|
|
@@ -72,7 +72,7 @@ module RuboCop
|
|
|
72
72
|
|
|
73
73
|
RESTRICT_ON_SEND = FILTER_METHODS + ACTION_METHODS
|
|
74
74
|
|
|
75
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
75
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
76
76
|
check_method_node(node.send_node)
|
|
77
77
|
end
|
|
78
78
|
|
|
@@ -76,7 +76,7 @@ module RuboCop
|
|
|
76
76
|
|
|
77
77
|
def on_def(node)
|
|
78
78
|
return unless trivial_delegate?(node)
|
|
79
|
-
return if private_or_protected_delegation(node)
|
|
79
|
+
return if private_or_protected_delegation?(node)
|
|
80
80
|
return if module_function_declared?(node)
|
|
81
81
|
|
|
82
82
|
register_offense(node)
|
|
@@ -163,8 +163,8 @@ module RuboCop
|
|
|
163
163
|
end
|
|
164
164
|
end
|
|
165
165
|
|
|
166
|
-
def private_or_protected_delegation(node)
|
|
167
|
-
private_or_protected_inline(node) || node_visibility(node) != :public
|
|
166
|
+
def private_or_protected_delegation?(node)
|
|
167
|
+
private_or_protected_inline?(node) || node_visibility(node) != :public
|
|
168
168
|
end
|
|
169
169
|
|
|
170
170
|
def module_function_declared?(node)
|
|
@@ -173,7 +173,7 @@ module RuboCop
|
|
|
173
173
|
end
|
|
174
174
|
end
|
|
175
175
|
|
|
176
|
-
def private_or_protected_inline(node)
|
|
176
|
+
def private_or_protected_inline?(node)
|
|
177
177
|
processed_source[node.first_line - 1].strip.match?(/\A(private )|(protected )/)
|
|
178
178
|
end
|
|
179
179
|
end
|
|
@@ -94,7 +94,7 @@ module RuboCop
|
|
|
94
94
|
filtered_nodes = association_nodes.reject { |node| node.method?(:belongs_to) }
|
|
95
95
|
grouped_associations = filtered_nodes.group_by do |node|
|
|
96
96
|
arguments = association(node).last
|
|
97
|
-
next unless arguments.
|
|
97
|
+
next unless arguments.one?
|
|
98
98
|
|
|
99
99
|
if (class_name = class_name(arguments.first))
|
|
100
100
|
class_name.source
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Rails
|
|
6
|
-
# Checks for multiple scopes in a model that have the same
|
|
6
|
+
# Checks for multiple scopes in a model that have the same expression. This
|
|
7
7
|
# often means you copy/pasted a scope, updated the name, and forgot to change the condition.
|
|
8
8
|
#
|
|
9
9
|
# @example
|
|
@@ -19,7 +19,7 @@ module RuboCop
|
|
|
19
19
|
class DuplicateScope < Base
|
|
20
20
|
include ClassSendNodeHelper
|
|
21
21
|
|
|
22
|
-
MSG = 'Multiple scopes share this same
|
|
22
|
+
MSG = 'Multiple scopes share this same expression.'
|
|
23
23
|
|
|
24
24
|
def_node_matcher :scope, <<~PATTERN
|
|
25
25
|
(send nil? :scope _ $...)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Rails
|
|
6
|
+
# Checks for usage of `Rails.env` which can be replaced with Feature Flags
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
#
|
|
10
|
+
# # bad
|
|
11
|
+
# Rails.env.production? || Rails.env.local?
|
|
12
|
+
#
|
|
13
|
+
# # good
|
|
14
|
+
# if FeatureFlag.enabled?(:new_feature)
|
|
15
|
+
# # new feature code
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
class Env < Base
|
|
19
|
+
MSG = 'Use Feature Flags or config instead of `Rails.env`.'
|
|
20
|
+
RESTRICT_ON_SEND = %i[env].freeze
|
|
21
|
+
# This allow list is derived from:
|
|
22
|
+
# (Rails.env.methods - Object.instance_methods).select { |m| m.to_s.end_with?('?') }
|
|
23
|
+
# and then removing the environment specific methods like development?, test?, production?, local?
|
|
24
|
+
ALLOWED_LIST = Set.new(
|
|
25
|
+
%i[
|
|
26
|
+
unicode_normalized?
|
|
27
|
+
exclude?
|
|
28
|
+
empty?
|
|
29
|
+
acts_like_string?
|
|
30
|
+
include?
|
|
31
|
+
is_utf8?
|
|
32
|
+
casecmp?
|
|
33
|
+
match?
|
|
34
|
+
starts_with?
|
|
35
|
+
ends_with?
|
|
36
|
+
start_with?
|
|
37
|
+
end_with?
|
|
38
|
+
valid_encoding?
|
|
39
|
+
ascii_only?
|
|
40
|
+
between?
|
|
41
|
+
]
|
|
42
|
+
).freeze
|
|
43
|
+
|
|
44
|
+
def on_send(node)
|
|
45
|
+
return unless node.receiver&.const_name == 'Rails'
|
|
46
|
+
|
|
47
|
+
parent = node.parent
|
|
48
|
+
return unless parent.respond_to?(:predicate_method?) && parent.predicate_method?
|
|
49
|
+
|
|
50
|
+
return if ALLOWED_LIST.include?(parent.method_name)
|
|
51
|
+
|
|
52
|
+
add_offense(parent)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -24,45 +24,69 @@ module RuboCop
|
|
|
24
24
|
|
|
25
25
|
minimum_target_rails_version 7.1
|
|
26
26
|
|
|
27
|
-
# @!method
|
|
28
|
-
def_node_matcher :
|
|
29
|
-
(
|
|
30
|
-
(send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
|
|
31
|
-
(send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
|
|
32
|
-
)
|
|
27
|
+
# @!method rails_env_local?(node)
|
|
28
|
+
def_node_matcher :rails_env_local?, <<~PATTERN
|
|
29
|
+
(send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
|
|
33
30
|
PATTERN
|
|
34
31
|
|
|
35
|
-
# @!method
|
|
36
|
-
def_node_matcher :
|
|
37
|
-
(
|
|
38
|
-
(send
|
|
39
|
-
(send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
|
|
40
|
-
:!)
|
|
41
|
-
(send
|
|
42
|
-
(send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
|
|
43
|
-
:!)
|
|
44
|
-
)
|
|
32
|
+
# @!method not_rails_env_local?(node)
|
|
33
|
+
def_node_matcher :not_rails_env_local?, <<~PATTERN
|
|
34
|
+
(send #rails_env_local? :!)
|
|
45
35
|
PATTERN
|
|
46
36
|
|
|
47
37
|
def on_or(node)
|
|
48
|
-
|
|
49
|
-
|
|
38
|
+
lhs, rhs = *node.children
|
|
39
|
+
return unless rails_env_local?(rhs)
|
|
50
40
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
41
|
+
nodes = [rhs]
|
|
42
|
+
|
|
43
|
+
if rails_env_local?(lhs)
|
|
44
|
+
nodes << lhs
|
|
45
|
+
elsif lhs.or_type? && rails_env_local?(lhs.rhs)
|
|
46
|
+
nodes << lhs.rhs
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
return unless environments(nodes).to_set == LOCAL_ENVIRONMENTS
|
|
50
|
+
|
|
51
|
+
range = offense_range(nodes)
|
|
52
|
+
add_offense(range) do |corrector|
|
|
53
|
+
corrector.replace(range, 'Rails.env.local?')
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def on_and(node)
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
lhs, rhs = *node.children
|
|
59
|
+
return unless not_rails_env_local?(rhs)
|
|
60
|
+
|
|
61
|
+
nodes = [rhs]
|
|
62
|
+
|
|
63
|
+
if not_rails_env_local?(lhs)
|
|
64
|
+
nodes << lhs
|
|
65
|
+
elsif lhs.operator_keyword? && not_rails_env_local?(lhs.rhs)
|
|
66
|
+
nodes << lhs.rhs
|
|
67
|
+
end
|
|
60
68
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
return unless environments(nodes).to_set == LOCAL_ENVIRONMENTS
|
|
70
|
+
|
|
71
|
+
range = offense_range(nodes)
|
|
72
|
+
add_offense(range, message: MSG_NEGATED) do |corrector|
|
|
73
|
+
corrector.replace(range, '!Rails.env.local?')
|
|
64
74
|
end
|
|
65
75
|
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def environments(nodes)
|
|
80
|
+
if nodes[0].method?(:!)
|
|
81
|
+
nodes.map { |node| node.receiver.method_name }
|
|
82
|
+
else
|
|
83
|
+
nodes.map(&:method_name)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def offense_range(nodes)
|
|
88
|
+
nodes[1].source_range.begin.join(nodes[0].source_range.end)
|
|
89
|
+
end
|
|
66
90
|
end
|
|
67
91
|
end
|
|
68
92
|
end
|