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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/config/default.yml +59 -15
  4. data/lib/rubocop/cop/mixin/active_record_helper.rb +1 -1
  5. data/lib/rubocop/cop/mixin/database_type_resolvable.rb +3 -3
  6. data/lib/rubocop/cop/mixin/index_method.rb +4 -0
  7. data/lib/rubocop/cop/mixin/target_rails_version.rb +7 -17
  8. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +4 -2
  9. data/lib/rubocop/cop/rails/action_filter.rb +1 -1
  10. data/lib/rubocop/cop/rails/delegate.rb +4 -4
  11. data/lib/rubocop/cop/rails/duplicate_association.rb +1 -1
  12. data/lib/rubocop/cop/rails/duplicate_scope.rb +2 -2
  13. data/lib/rubocop/cop/rails/env.rb +57 -0
  14. data/lib/rubocop/cop/rails/env_local.rb +50 -26
  15. data/lib/rubocop/cop/rails/environment_comparison.rb +56 -48
  16. data/lib/rubocop/cop/rails/exit.rb +7 -4
  17. data/lib/rubocop/cop/rails/file_path.rb +2 -2
  18. data/lib/rubocop/cop/rails/find_by.rb +1 -1
  19. data/lib/rubocop/cop/rails/find_by_or_assignment_memoization.rb +124 -0
  20. data/lib/rubocop/cop/rails/helper_instance_variable.rb +16 -17
  21. data/lib/rubocop/cop/rails/http_status_name_consistency.rb +80 -0
  22. data/lib/rubocop/cop/rails/i18n_locale_texts.rb +24 -2
  23. data/lib/rubocop/cop/rails/index_with.rb +5 -0
  24. data/lib/rubocop/cop/rails/inverse_of.rb +7 -0
  25. data/lib/rubocop/cop/rails/not_null_column.rb +2 -0
  26. data/lib/rubocop/cop/rails/order_arguments.rb +84 -0
  27. data/lib/rubocop/cop/rails/output.rb +3 -0
  28. data/lib/rubocop/cop/rails/output_safety.rb +3 -1
  29. data/lib/rubocop/cop/rails/pluck.rb +6 -3
  30. data/lib/rubocop/cop/rails/presence.rb +76 -20
  31. data/lib/rubocop/cop/rails/rake_environment.rb +1 -1
  32. data/lib/rubocop/cop/rails/read_write_attribute.rb +1 -1
  33. data/lib/rubocop/cop/rails/redirect_back_or_to.rb +99 -0
  34. data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +1 -1
  35. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +3 -3
  36. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -1
  37. data/lib/rubocop/cop/rails/response_parsed_body.rb +59 -50
  38. data/lib/rubocop/cop/rails/save_bang.rb +2 -2
  39. data/lib/rubocop/cop/rails/select_map.rb +26 -4
  40. data/lib/rubocop/cop/rails/strong_parameters_expect.rb +78 -1
  41. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +4 -1
  42. data/lib/rubocop/cop/rails/unknown_env.rb +39 -12
  43. data/lib/rubocop/cop/rails/where_exists.rb +5 -5
  44. data/lib/rubocop/cop/rails_cops.rb +5 -0
  45. data/lib/rubocop/rails/version.rb +1 -1
  46. metadata +9 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '009a04a87f7692e278ce16b061ff93814ada6644691ce2fdd4ac4c7659e68c4d'
4
- data.tar.gz: 313e26b30279cf9ebba24f5f99bc78b2b44433b24bc3522a6d4261de1cf72282
3
+ metadata.gz: 409bf447cc0d5a3b6529407611f53ad1331a5c1bd908e35b7f50193e53bb2c95
4
+ data.tar.gz: 5d3b83383d138616bcfbbe292413f6ddd6f49b60be33dbe57fdcd481bb7d1c1c
5
5
  SHA512:
6
- metadata.gz: 1f7397609be46c75da8ff050ed1325db850c39df7d097d9faeb6984162b706ed92e203f151a30a68aa41b3af8fcdd63b7e76c51ebdcfe31980129a853d7cd8a3
7
- data.tar.gz: 46006ebeb148838e1ae7f3dc56614b261fc90779c5e3772e163c9431034a89dbc614ef1cb1b913d5236fbc2b04708643fc27b593d8c0c4147898271c045cb4c7
6
+ metadata.gz: ea50f3fad3d83f62ec110dd0a5b32688f92fcd0963d486714e4a71867343316b67842476f67dd64a5c6d7ccde5f43aeca5c1980ec20e96f097703fdf6ad7c15c
7
+ data.tar.gz: eb75cb4c5b5a21562ff42dd194fea691e2feec9563095a1a1509b3263a41693ce072a075a269e1416c8f150cf201a602e2301ac1d993dc7a237c3bbe19083d04
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-25 Bozhidar Batsov
1
+ Copyright (c) 2012-26 Bozhidar Batsov
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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 where clause.'
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.8'
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: 'http://danilenko.org/2012/7/6/rails_timezones'
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['development']
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
- if respond_to?(:requires_gem)
16
- case version
17
- when Integer, Float then requires_gem(TARGET_GEM_NAME, ">= #{version}")
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
- if respond_to?(:requires_gem)
28
- return false unless gem_requirements
22
+ return false unless gem_requirements
29
23
 
30
- gem_requirement = gem_requirements[TARGET_GEM_NAME]
31
- return true unless gem_requirement # If we have no requirement, then we support all versions
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
- gem_requirement.satisfied_by?(Gem::Version.new(version))
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.compact.any? do |sibling|
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.count == 1
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 `where` clause. This
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 where clause.'
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 rails_env_local_or?(node)
28
- def_node_matcher :rails_env_local_or?, <<~PATTERN
29
- (or
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 rails_env_local_and?(node)
36
- def_node_matcher :rails_env_local_and?, <<~PATTERN
37
- (and
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
- rails_env_local_or?(node) do |*environments|
49
- next unless environments.to_set == LOCAL_ENVIRONMENTS
38
+ lhs, rhs = *node.children
39
+ return unless rails_env_local?(rhs)
50
40
 
51
- add_offense(node) do |corrector|
52
- corrector.replace(node, 'Rails.env.local?')
53
- end
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
- rails_env_local_and?(node) do |*environments|
59
- next unless environments.to_set == LOCAL_ENVIRONMENTS
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
- add_offense(node, message: MSG_NEGATED) do |corrector|
62
- corrector.replace(node, '!Rails.env.local?')
63
- end
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