rubocop-rails 2.31.0 → 2.33.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba4876948bedd432dc186a3c060c26c3808f1c7126b8fe69072e056a5f160d94
4
- data.tar.gz: 7f890ec180a125abe4a40ad146661e71c088596eb093eacf12d322135c7f6907
3
+ metadata.gz: 3a61be89f227c5abed6543c4fb84957391ac66fdbadc7f7891a1876b7be0c193
4
+ data.tar.gz: f2e30c0c3e5a5cac32bae9ec0900a0e1fc382dda89f764f8a844b085ad2abffb
5
5
  SHA512:
6
- metadata.gz: 719fd043fd8ae7738eebb0c78abba01a23f2bc30c6d8c9825c89c7cbcc6cd48fd0077918e9bc84c9c471afecd62b69597d3f046c757606ecaec4af9daf8ef183
7
- data.tar.gz: b7173eeaf159ee8673fe2f2c16a1c92bcbb55601d1202db18e5b48d09d1d1ef0207c17a182dcfb2b39c6e686d2b82871b191d5f90c4416a1af7d7b038a12c218
6
+ metadata.gz: 8610629e8893c71c21d591cc68897f993699f20edab9cda08a18ce65d9bf67ac153830dffa144e18b2d9e4e204027684c18cbb02e822d01c9c40e35312709083
7
+ data.tar.gz: f7dc418f1cb2f4a38968b9a79ae17028368e10f2d529cba2de15954a2edc5c3f8ebf7bb35617b670cf7e8dab758ccb138c0387a0fd6caa31051ee5e803925c35
data/config/default.yml CHANGED
@@ -6,7 +6,7 @@ inherit_mode:
6
6
 
7
7
  AllCops:
8
8
  Exclude:
9
- - app/assets/**/*
9
+ - '**/app/assets/**/*'
10
10
  - bin/*
11
11
  # Exclude db/schema.rb and db/[CONFIGURATION_NAMESPACE]_schema.rb by default.
12
12
  # See: https://guides.rubyonrails.org/active_record_multiple_databases.html#setting-up-your-application
@@ -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
@@ -121,8 +127,8 @@ Rails/ActionFilter:
121
127
  - action
122
128
  - filter
123
129
  Include:
124
- - app/controllers/**/*.rb
125
- - app/mailers/**/*.rb
130
+ - '**/app/controllers/**/*.rb'
131
+ - '**/app/mailers/**/*.rb'
126
132
 
127
133
  Rails/ActionOrder:
128
134
  Description: 'Enforce consistent ordering of controller actions.'
@@ -137,7 +143,7 @@ Rails/ActionOrder:
137
143
  - update
138
144
  - destroy
139
145
  Include:
140
- - app/controllers/**/*.rb
146
+ - '**/app/controllers/**/*.rb'
141
147
 
142
148
  Rails/ActiveRecordAliases:
143
149
  Description: >-
@@ -154,7 +160,7 @@ Rails/ActiveRecordCallbacksOrder:
154
160
  Enabled: 'pending'
155
161
  VersionAdded: '2.7'
156
162
  Include:
157
- - app/models/**/*.rb
163
+ - '**/app/models/**/*.rb'
158
164
 
159
165
  Rails/ActiveRecordOverride:
160
166
  Description: >-
@@ -165,7 +171,7 @@ Rails/ActiveRecordOverride:
165
171
  VersionAdded: '0.67'
166
172
  VersionChanged: '2.18'
167
173
  Include:
168
- - app/models/**/*.rb
174
+ - '**/app/models/**/*.rb'
169
175
 
170
176
  Rails/ActiveSupportAliases:
171
177
  Description: >-
@@ -252,7 +258,7 @@ Rails/AttributeDefaultBlockValue:
252
258
  Enabled: pending
253
259
  VersionAdded: '2.9'
254
260
  Include:
255
- - 'app/models/**/*'
261
+ - '**/app/models/**/*'
256
262
 
257
263
  Rails/BelongsTo:
258
264
  Description: >-
@@ -311,8 +317,8 @@ Rails/ContentTag:
311
317
  # https://puma.io/puma/Puma/DSL.html#tag-instance_method
312
318
  # No helpers are used in normal models and configs.
313
319
  Exclude:
314
- - app/models/**/*.rb
315
- - config/**/*.rb
320
+ - '**/app/models/**/*.rb'
321
+ - '**/config/**/*.rb'
316
322
 
317
323
  Rails/CreateTableWithTimestamps:
318
324
  Description: >-
@@ -373,7 +379,7 @@ Rails/Delegate:
373
379
  # violation. When set to false, this case is legal.
374
380
  EnforceForPrefixed: true
375
381
  Exclude:
376
- - app/controllers/**/*.rb
382
+ - '**/app/controllers/**/*.rb'
377
383
 
378
384
  Rails/DelegateAllowBlank:
379
385
  Description: 'Do not use allow_blank as an option to delegate.'
@@ -444,7 +450,7 @@ Rails/EnumHash:
444
450
  Enabled: true
445
451
  VersionAdded: '2.3'
446
452
  Include:
447
- - app/models/**/*.rb
453
+ - '**/app/models/**/*.rb'
448
454
 
449
455
  Rails/EnumSyntax:
450
456
  Description: 'Use positional arguments over keyword arguments when defining enums.'
@@ -452,14 +458,15 @@ Rails/EnumSyntax:
452
458
  Severity: warning
453
459
  VersionAdded: '2.26'
454
460
  Include:
455
- - app/models/**/*.rb
461
+ - '**/app/models/**/*.rb'
462
+ - '**/lib/**/*.rb'
456
463
 
457
464
  Rails/EnumUniqueness:
458
465
  Description: 'Avoid duplicate integers in hash-syntax `enum` declaration.'
459
466
  Enabled: true
460
467
  VersionAdded: '0.46'
461
468
  Include:
462
- - app/models/**/*.rb
469
+ - '**/app/models/**/*.rb'
463
470
 
464
471
  Rails/EnvLocal:
465
472
  Description: 'Use `Rails.env.local?` instead of `Rails.env.development? || Rails.env.test?`.'
@@ -478,11 +485,11 @@ Rails/EnvironmentVariableAccess:
478
485
  VersionAdded: '2.10'
479
486
  VersionChanged: '2.24'
480
487
  Include:
481
- - app/**/*.rb
482
- - config/initializers/**/*.rb
483
- - lib/**/*.rb
488
+ - '**/app/**/*.rb'
489
+ - '**/config/initializers/**/*.rb'
490
+ - '**/lib/**/*.rb'
484
491
  Exclude:
485
- - lib/**/*.rake
492
+ - '**/lib/**/*.rake'
486
493
  AllowReads: false
487
494
  AllowWrites: false
488
495
 
@@ -494,11 +501,11 @@ Rails/Exit:
494
501
  Enabled: true
495
502
  VersionAdded: '0.41'
496
503
  Include:
497
- - app/**/*.rb
498
- - config/**/*.rb
499
- - lib/**/*.rb
504
+ - '**/app/**/*.rb'
505
+ - '**/config/**/*.rb'
506
+ - '**/lib/**/*.rb'
500
507
  Exclude:
501
- - lib/**/*.rake
508
+ - '**/lib/**/*.rake'
502
509
 
503
510
  Rails/ExpandedDateRange:
504
511
  Description: 'Checks for expanded date range.'
@@ -532,6 +539,13 @@ Rails/FindById:
532
539
  Enabled: 'pending'
533
540
  VersionAdded: '2.7'
534
541
 
542
+ Rails/FindByOrAssignmentMemoization:
543
+ Description: 'Avoid memoizing `find_by` results with `||=`.'
544
+ StyleGuide: 'https://rails.rubystyle.guide/#find-by-memoization'
545
+ Enabled: pending
546
+ Safe: false
547
+ VersionAdded: '2.33'
548
+
535
549
  Rails/FindEach:
536
550
  Description: 'Prefer all.find_each over all.each.'
537
551
  StyleGuide: 'https://rails.rubystyle.guide#find-each'
@@ -560,7 +574,7 @@ Rails/HasAndBelongsToMany:
560
574
  Enabled: true
561
575
  VersionAdded: '0.12'
562
576
  Include:
563
- - app/models/**/*.rb
577
+ - '**/app/models/**/*.rb'
564
578
 
565
579
  Rails/HasManyOrHasOneDependent:
566
580
  Description: 'Define the dependent option to the has_many and has_one associations.'
@@ -568,14 +582,14 @@ Rails/HasManyOrHasOneDependent:
568
582
  Enabled: true
569
583
  VersionAdded: '0.50'
570
584
  Include:
571
- - app/models/**/*.rb
585
+ - '**/app/models/**/*.rb'
572
586
 
573
587
  Rails/HelperInstanceVariable:
574
588
  Description: 'Do not use instance variables in helpers.'
575
589
  Enabled: true
576
590
  VersionAdded: '2.0'
577
591
  Include:
578
- - app/helpers/**/*.rb
592
+ - '**/app/helpers/**/*.rb'
579
593
 
580
594
  Rails/HttpPositionalArguments:
581
595
  Description: 'Use keyword arguments instead of positional arguments in http method calls.'
@@ -606,7 +620,7 @@ Rails/I18nLazyLookup:
606
620
  - lazy
607
621
  - explicit
608
622
  Include:
609
- - 'app/controllers/**/*.rb'
623
+ - '**/app/controllers/**/*.rb'
610
624
 
611
625
  Rails/I18nLocaleAssignment:
612
626
  Description: 'Prefer the usage of `I18n.with_locale` instead of manually updating `I18n.locale` value.'
@@ -635,8 +649,8 @@ Rails/IgnoredSkipActionFilterOption:
635
649
  Enabled: true
636
650
  VersionAdded: '0.63'
637
651
  Include:
638
- - app/controllers/**/*.rb
639
- - app/mailers/**/*.rb
652
+ - '**/app/controllers/**/*.rb'
653
+ - '**/app/mailers/**/*.rb'
640
654
 
641
655
  Rails/IndexBy:
642
656
  Description: 'Prefer `index_by` over `each_with_object`, `to_h`, or `map`.'
@@ -647,8 +661,9 @@ Rails/IndexBy:
647
661
  Rails/IndexWith:
648
662
  Description: 'Prefer `index_with` over `each_with_object`, `to_h`, or `map`.'
649
663
  Enabled: true
664
+ SafeAutoCorrect: false
650
665
  VersionAdded: '2.5'
651
- VersionChanged: '2.8'
666
+ VersionChanged: '2.33'
652
667
 
653
668
  Rails/Inquiry:
654
669
  Description: "Prefer Ruby's comparison operators over Active Support's `Array#inquiry` and `String#inquiry`."
@@ -665,7 +680,7 @@ Rails/InverseOf:
665
680
  VersionAdded: '0.52'
666
681
  IgnoreScopes: false
667
682
  Include:
668
- - app/models/**/*.rb
683
+ - '**/app/models/**/*.rb'
669
684
 
670
685
  Rails/LexicallyScopedActionFilter:
671
686
  Description: "Checks that methods specified in the filter's `only` or `except` options are explicitly defined in the class."
@@ -674,8 +689,8 @@ Rails/LexicallyScopedActionFilter:
674
689
  Safe: false
675
690
  VersionAdded: '0.52'
676
691
  Include:
677
- - app/controllers/**/*.rb
678
- - app/mailers/**/*.rb
692
+ - '**/app/controllers/**/*.rb'
693
+ - '**/app/mailers/**/*.rb'
679
694
 
680
695
  Rails/LinkToBlank:
681
696
  Description: 'Checks that `link_to` with a `target: "_blank"` have a `rel: "noopener"` option passed to them.'
@@ -693,7 +708,7 @@ Rails/MailerName:
693
708
  SafeAutoCorrect: false
694
709
  VersionAdded: '2.7'
695
710
  Include:
696
- - app/mailers/**/*.rb
711
+ - '**/app/mailers/**/*.rb'
697
712
 
698
713
  Rails/MatchRoute:
699
714
  Description: >-
@@ -703,8 +718,8 @@ Rails/MatchRoute:
703
718
  Enabled: 'pending'
704
719
  VersionAdded: '2.7'
705
720
  Include:
706
- - config/routes.rb
707
- - config/routes/**/*.rb
721
+ - '**/config/routes.rb'
722
+ - '**/config/routes/**/*.rb'
708
723
 
709
724
  Rails/MigrationClassName:
710
725
  Description: 'The class name of the migration should match its file name.'
@@ -720,8 +735,8 @@ Rails/MultipleRoutePaths:
720
735
  Severity: warning
721
736
  VersionAdded: '2.29'
722
737
  Include:
723
- - config/routes.rb
724
- - config/routes/**/*.rb
738
+ - '**/config/routes.rb'
739
+ - '**/config/routes/**/*.rb'
725
740
 
726
741
  Rails/NegateInclude:
727
742
  Description: 'Prefer `collection.exclude?(obj)` over `!collection.include?(obj)`.'
@@ -742,6 +757,13 @@ Rails/NotNullColumn:
742
757
  Include:
743
758
  - db/**/*.rb
744
759
 
760
+ Rails/OrderArguments:
761
+ Description: 'Prefer symbol arguments over strings in `order` method.'
762
+ StyleGuide: 'https://rails.rubystyle.guide/#order-arguments'
763
+ Enabled: pending
764
+ VersionAdded: '2.33'
765
+ Safe: false
766
+
745
767
  Rails/OrderById:
746
768
  Description: >-
747
769
  Do not use the `id` column for ordering.
@@ -757,10 +779,10 @@ Rails/Output:
757
779
  VersionAdded: '0.15'
758
780
  VersionChanged: '0.19'
759
781
  Include:
760
- - app/**/*.rb
761
- - config/**/*.rb
782
+ - '**/app/**/*.rb'
783
+ - '**/config/**/*.rb'
762
784
  - db/**/*.rb
763
- - lib/**/*.rb
785
+ - '**/lib/**/*.rb'
764
786
 
765
787
  Rails/OutputSafety:
766
788
  Description: 'The use of `html_safe` or `raw` may be a security risk.'
@@ -832,7 +854,7 @@ Rails/RakeEnvironment:
832
854
  - '**/Rakefile'
833
855
  - '**/*.rake'
834
856
  Exclude:
835
- - 'lib/capistrano/tasks/**/*.rake'
857
+ - '**/lib/capistrano/tasks/**/*.rake'
836
858
 
837
859
  Rails/ReadWriteAttribute:
838
860
  Description: >-
@@ -843,7 +865,7 @@ Rails/ReadWriteAttribute:
843
865
  VersionAdded: '0.20'
844
866
  VersionChanged: '0.29'
845
867
  Include:
846
- - app/models/**/*.rb
868
+ - '**/app/models/**/*.rb'
847
869
 
848
870
  Rails/RedundantActiveRecordAllMethod:
849
871
  Description: Detect redundant `all` used as a receiver for Active Record query methods.
@@ -862,7 +884,7 @@ Rails/RedundantAllowNil:
862
884
  Enabled: true
863
885
  VersionAdded: '0.67'
864
886
  Include:
865
- - app/models/**/*.rb
887
+ - '**/app/models/**/*.rb'
866
888
 
867
889
  Rails/RedundantForeignKey:
868
890
  Description: 'Checks for associations where the `:foreign_key` option is redundant.'
@@ -1033,7 +1055,7 @@ Rails/ScopeArgs:
1033
1055
  VersionAdded: '0.19'
1034
1056
  VersionChanged: '2.12'
1035
1057
  Include:
1036
- - app/models/**/*.rb
1058
+ - '**/app/models/**/*.rb'
1037
1059
 
1038
1060
  Rails/SelectMap:
1039
1061
  Description: 'Checks for uses of `select(:column_name)` with `map(&:column_name)`.'
@@ -1102,7 +1124,7 @@ Rails/StrongParametersExpect:
1102
1124
  Reference: 'https://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-expect'
1103
1125
  Enabled: pending
1104
1126
  Include:
1105
- - app/controllers/**/*.rb
1127
+ - '**/app/controllers/**/*.rb'
1106
1128
  SafeAutoCorrect: false
1107
1129
  VersionAdded: '2.29'
1108
1130
 
@@ -1113,7 +1135,7 @@ Rails/TableNameAssignment:
1113
1135
  Enabled: false
1114
1136
  VersionAdded: '2.14'
1115
1137
  Include:
1116
- - app/models/**/*.rb
1138
+ - '**/app/models/**/*.rb'
1117
1139
 
1118
1140
  Rails/ThreeStateBooleanColumn:
1119
1141
  Description: 'Add a default value and a `NOT NULL` constraint to boolean columns.'
@@ -1126,7 +1148,7 @@ Rails/ThreeStateBooleanColumn:
1126
1148
  Rails/TimeZone:
1127
1149
  Description: 'Checks the correct usage of time zone aware methods.'
1128
1150
  StyleGuide: 'https://rails.rubystyle.guide#time'
1129
- Reference: 'http://danilenko.org/2012/7/6/rails_timezones'
1151
+ Reference: 'https://danilenko.org/2012/7/6/rails_timezones'
1130
1152
  Enabled: true
1131
1153
  SafeAutoCorrect: false
1132
1154
  VersionAdded: '0.30'
@@ -1197,7 +1219,7 @@ Rails/UniqueValidationWithoutIndex:
1197
1219
  Enabled: true
1198
1220
  VersionAdded: '2.5'
1199
1221
  Include:
1200
- - app/models/**/*.rb
1222
+ - '**/app/models/**/*.rb'
1201
1223
 
1202
1224
  Rails/UnknownEnv:
1203
1225
  Description: 'Use correct environment name.'
@@ -1216,7 +1238,7 @@ Rails/UnusedIgnoredColumns:
1216
1238
  VersionAdded: '2.11'
1217
1239
  VersionChanged: '2.25'
1218
1240
  Include:
1219
- - app/models/**/*.rb
1241
+ - '**/app/models/**/*.rb'
1220
1242
 
1221
1243
  Rails/UnusedRenderContent:
1222
1244
  Description: 'Do not specify body content for a response with a non-content status code.'
@@ -1230,7 +1252,7 @@ Rails/Validation:
1230
1252
  VersionAdded: '0.9'
1231
1253
  VersionChanged: '0.41'
1232
1254
  Include:
1233
- - app/models/**/*.rb
1255
+ - '**/app/models/**/*.rb'
1234
1256
 
1235
1257
  Rails/WhereEquals:
1236
1258
  Description: 'Pass conditions to `where` and `where.not` as a hash instead of manually constructing SQL.'
@@ -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?
@@ -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)
@@ -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
@@ -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
@@ -190,7 +190,7 @@ module RuboCop
190
190
  else
191
191
  replace_with_rails_root_join(corrector, rails_root_node, argument_source)
192
192
  end
193
- node.children[rails_root_index + 1..].each { |child| corrector.remove(child) }
193
+ node.children[(rails_root_index + 1)..].each { |child| corrector.remove(child) }
194
194
  end
195
195
 
196
196
  def autocorrect_extension_after_rails_root_join_in_dstr(corrector, node, rails_root_index, extension_node)
@@ -281,7 +281,7 @@ module RuboCop
281
281
  end
282
282
 
283
283
  def extract_rails_root_join_argument_source(node, rails_root_index)
284
- node.children[rails_root_index + 1..].map(&:source).join.delete_prefix(File::SEPARATOR)
284
+ node.children[(rails_root_index + 1)..].map(&:source).join.delete_prefix(File::SEPARATOR)
285
285
  end
286
286
 
287
287
  def extension_node?(node)
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Avoid memoizing `find_by` results with `||=`.
7
+ #
8
+ # It is common to see code that attempts to memoize `find_by` result by `||=`,
9
+ # but `find_by` may return `nil`, in which case it is not memoized as intended.
10
+ #
11
+ # @safety
12
+ # This cop is unsafe because detected `find_by` may not be Active Record's method,
13
+ # or the code may have a different purpose than memoization.
14
+ #
15
+ # @example
16
+ # # bad
17
+ # def current_user
18
+ # @current_user ||= User.find_by(id: session[:user_id])
19
+ # end
20
+ #
21
+ # # good
22
+ # def current_user
23
+ # if instance_variable_defined?(:@current_user)
24
+ # @current_user
25
+ # else
26
+ # @current_user = User.find_by(id: session[:user_id])
27
+ # end
28
+ # end
29
+ class FindByOrAssignmentMemoization < Base
30
+ extend AutoCorrector
31
+
32
+ MSG = 'Avoid memoizing `find_by` results with `||=`.'
33
+
34
+ RESTRICT_ON_SEND = %i[find_by].freeze
35
+
36
+ def_node_matcher :find_by_or_assignment_memoization, <<~PATTERN
37
+ (or_asgn
38
+ (ivasgn $_)
39
+ $(send _ :find_by ...)
40
+ )
41
+ PATTERN
42
+
43
+ def on_send(node)
44
+ assignment_node = node.parent
45
+ find_by_or_assignment_memoization(assignment_node) do |varible_name, find_by|
46
+ next if assignment_node.each_ancestor(:if).any?
47
+
48
+ add_offense(assignment_node) do |corrector|
49
+ corrector.replace(
50
+ assignment_node,
51
+ <<~RUBY.rstrip
52
+ if instance_variable_defined?(:#{varible_name})
53
+ #{varible_name}
54
+ else
55
+ #{varible_name} = #{find_by.source}
56
+ end
57
+ RUBY
58
+ )
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -8,6 +8,11 @@ module RuboCop
8
8
  # an enumerable into a hash where the keys are the original elements.
9
9
  # Rails provides the `index_with` method for this purpose.
10
10
  #
11
+ # @safety
12
+ # This cop is marked as unsafe autocorrection, because `nil.to_h` returns {}
13
+ # but `nil.with_index` throws `NoMethodError`. Therefore, autocorrection is not
14
+ # compatible if the receiver is nil.
15
+ #
11
16
  # @example
12
17
  # # bad
13
18
  # [1, 2, 3].each_with_object({}) { |el, h| h[el] = foo(el) }
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Prefer symbol arguments over strings in `order` method.
7
+ #
8
+ # @safety
9
+ # Cop is unsafe because the receiver might not be an Active Record query.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # User.order('name')
14
+ # User.order('name DESC')
15
+ #
16
+ # # good
17
+ # User.order(:name)
18
+ # User.order(name: :desc)
19
+ #
20
+ class OrderArguments < Base
21
+ extend AutoCorrector
22
+
23
+ MSG = 'Prefer `%<prefer>s` instead.'
24
+
25
+ RESTRICT_ON_SEND = %i[order].freeze
26
+
27
+ def_node_matcher :string_order, <<~PATTERN
28
+ (call _ :order (str $_value)+)
29
+ PATTERN
30
+
31
+ ORDER_EXPRESSION_REGEX = /\A(\w+) ?(asc|desc)?\z/i.freeze
32
+
33
+ def on_send(node)
34
+ return unless (current_expressions = string_order(node))
35
+ return unless (preferred_expressions = replacement(current_expressions))
36
+
37
+ offense_range = find_offense_range(node)
38
+ add_offense(offense_range, message: format(MSG, prefer: preferred_expressions)) do |corrector|
39
+ corrector.replace(offense_range, preferred_expressions)
40
+ end
41
+ end
42
+ alias on_csend on_send
43
+
44
+ private
45
+
46
+ def find_offense_range(node)
47
+ node.first_argument.source_range.join(node.last_argument.source_range)
48
+ end
49
+
50
+ def replacement(order_expressions)
51
+ order_arguments = order_expressions.flat_map { |expr| expr.split(',') }
52
+ order_arguments.map! { |arg| extract_column_and_direction(arg.strip) }
53
+
54
+ return if order_arguments.any?(&:nil?)
55
+
56
+ convert_to_preferred_arguments(order_arguments).join(', ')
57
+ end
58
+
59
+ def convert_to_preferred_arguments(order_expressions)
60
+ use_hash = false
61
+ order_expressions.map do |column, direction|
62
+ if direction == :asc && !use_hash
63
+ ":#{column}"
64
+ else
65
+ use_hash = true
66
+ "#{column}: :#{direction}"
67
+ end
68
+ end
69
+ end
70
+
71
+ def extract_column_and_direction(order_expression)
72
+ return unless (column, direction = ORDER_EXPRESSION_REGEX.match(order_expression)&.captures)
73
+
74
+ [column.downcase, direction&.downcase&.to_sym || :asc]
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -38,9 +38,11 @@ module RuboCop
38
38
  ...)
39
39
  PATTERN
40
40
 
41
+ # rubocop:disable Metrics/CyclomaticComplexity
41
42
  def on_send(node)
42
43
  return if node.parent&.call_type? || node.block_node
43
44
  return if !output?(node) && !io_output?(node)
45
+ return if node.arguments.any? { |arg| arg.type?(:hash, :block_pass) }
44
46
 
45
47
  range = offense_range(node)
46
48
 
@@ -48,6 +50,7 @@ module RuboCop
48
50
  corrector.replace(range, 'Rails.logger.debug')
49
51
  end
50
52
  end
53
+ # rubocop:enable Metrics/CyclomaticComplexity
51
54
 
52
55
  private
53
56
 
@@ -27,6 +27,9 @@ module RuboCop
27
27
  # end
28
28
  # ----
29
29
  #
30
+ # If a method call has no receiver, like `do_something { users.map { |user| user[:foo] }`,
31
+ # it is not considered part of an iteration and will be detected.
32
+ #
30
33
  # @safety
31
34
  # This cop is unsafe because model can use column aliases.
32
35
  #
@@ -59,9 +62,9 @@ module RuboCop
59
62
  (any_block (call _ {:map :collect}) $_argument (send lvar :[] $_key))
60
63
  PATTERN
61
64
 
62
- # rubocop:disable Metrics/AbcSize
65
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
63
66
  def on_block(node)
64
- return if node.each_ancestor(:any_block).any?
67
+ return if node.each_ancestor(:any_block).first&.receiver
65
68
 
66
69
  pluck_candidate?(node) do |argument, key|
67
70
  next if key.regexp_type? || !use_one_block_argument?(argument)
@@ -79,7 +82,7 @@ module RuboCop
79
82
  register_offense(node, key)
80
83
  end
81
84
  end
82
- # rubocop:enable Metrics/AbcSize
85
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
83
86
  alias on_numblock on_block
84
87
  alias on_itblock on_block
85
88
 
@@ -74,7 +74,7 @@ module RuboCop
74
74
  $[
75
75
  (hash <$(pair (sym :presence) true) ...>) # presence: true
76
76
  !(hash <$(pair (sym :strict) {true const}) ...>) # strict: true
77
- !(hash <$(pair (sym {:if :unless}) _) ...>) # if: some_condition or unless: some_condition
77
+ !(hash <$(pair (sym {:if}) _) ...>) # if: some_condition or unless: some_condition
78
78
  ]
79
79
  )
80
80
  PATTERN
@@ -211,12 +211,12 @@ module RuboCop
211
211
 
212
212
  def non_optional_belongs_to(node, keys)
213
213
  keys.select do |key|
214
- belongs_to = belongs_to_for(node, key)
214
+ belongs_to = belongs_to_for?(node, key)
215
215
  belongs_to && !optional?(belongs_to)
216
216
  end
217
217
  end
218
218
 
219
- def belongs_to_for(model_class_node, key)
219
+ def belongs_to_for?(model_class_node, key)
220
220
  if key.to_s.end_with?('_id')
221
221
  normalized_key = key.to_s.delete_suffix('_id').to_sym
222
222
  belongs_to?(model_class_node, key: normalized_key, fk: key)
@@ -71,7 +71,7 @@ module RuboCop
71
71
  def on_block(node)
72
72
  return unless node.method?(:with_options)
73
73
  return unless (body = node.body)
74
- return unless all_block_nodes_in(body).count.zero?
74
+ return unless all_block_nodes_in(body).none?
75
75
 
76
76
  send_nodes = all_send_nodes_in(body)
77
77
  return unless redundant_receiver?(send_nodes, node)
@@ -156,7 +156,7 @@ module RuboCop
156
156
  return unless persist_method?(node)
157
157
  return if return_value_assigned?(node)
158
158
  return if implicit_return?(node)
159
- return if check_used_in_condition_or_compound_boolean(node)
159
+ return if check_used_in_condition_or_compound_boolean?(node)
160
160
  return if argument?(node)
161
161
  return if explicit_return?(node)
162
162
  return if checked_immediately?(node)
@@ -227,7 +227,7 @@ module RuboCop
227
227
  array
228
228
  end
229
229
 
230
- def check_used_in_condition_or_compound_boolean(node)
230
+ def check_used_in_condition_or_compound_boolean?(node)
231
231
  return false unless in_condition_or_compound_boolean?(node)
232
232
 
233
233
  register_offense(node, CREATE_CONDITIONAL_MSG) unless MODIFY_PERSIST_METHODS.include?(node.method_name)
@@ -39,7 +39,7 @@ module RuboCop
39
39
 
40
40
  # @!method comment_present?(node)
41
41
  def_node_matcher :comment_present?, <<~PATTERN
42
- (hash <(pair {(sym :comment) (str "comment")} (_ !blank?)) ...>)
42
+ (hash <(pair {(sym :comment) (str "comment")} !{nil (str blank?)}) ...>)
43
43
  PATTERN
44
44
 
45
45
  # @!method add_column?(node)
@@ -45,7 +45,7 @@ module RuboCop
45
45
 
46
46
  return if required_options?(options_node)
47
47
 
48
- def_node = node.each_ancestor(:def, :defs).first
48
+ def_node = node.each_ancestor(:any_def).first
49
49
  table_node = table_node(node)
50
50
  return if def_node && (table_node.nil? || change_column_null?(def_node, table_node, column_node))
51
51
 
@@ -135,7 +135,9 @@ module RuboCop
135
135
  end
136
136
 
137
137
  def attach_timezone_specifier?(date)
138
- date.respond_to?(:value) && TIMEZONE_SPECIFIER.match?(date.value.to_s)
138
+ return false unless date.respond_to?(:value)
139
+
140
+ !date.value.to_s.valid_encoding? || TIMEZONE_SPECIFIER.match?(date.value.to_s)
139
141
  end
140
142
 
141
143
  def build_message(klass, method_name, node)
@@ -97,9 +97,11 @@ module RuboCop
97
97
 
98
98
  def in_transaction_block?(node)
99
99
  return false unless transaction_method_name?(node.method_name)
100
- return false unless (parent = node.parent)
100
+ return false unless node.parent&.body
101
101
 
102
- parent.any_block_type? && parent.body
102
+ node.right_siblings.none? do |sibling|
103
+ sibling.respond_to?(:loop_keyword?) && sibling.loop_keyword?
104
+ end
103
105
  end
104
106
 
105
107
  def statement(statement_node)
@@ -63,7 +63,7 @@ module RuboCop
63
63
  PATTERN
64
64
 
65
65
  def on_send(node)
66
- find_offenses(node) do |args|
66
+ find_offenses?(node) do |args|
67
67
  return unless convertable_args?(args)
68
68
 
69
69
  range = correction_range(node)
@@ -87,7 +87,7 @@ module RuboCop
87
87
  style == :exists
88
88
  end
89
89
 
90
- def find_offenses(node, &block)
90
+ def find_offenses?(node, &block)
91
91
  if exists_style?
92
92
  where_exists_call?(node, &block)
93
93
  elsif where_style?
@@ -58,6 +58,7 @@ require_relative 'rails/expanded_date_range'
58
58
  require_relative 'rails/file_path'
59
59
  require_relative 'rails/find_by'
60
60
  require_relative 'rails/find_by_id'
61
+ require_relative 'rails/find_by_or_assignment_memoization'
61
62
  require_relative 'rails/find_each'
62
63
  require_relative 'rails/freeze_time'
63
64
  require_relative 'rails/has_and_belongs_to_many'
@@ -82,6 +83,7 @@ require_relative 'rails/migration_class_name'
82
83
  require_relative 'rails/multiple_route_paths'
83
84
  require_relative 'rails/negate_include'
84
85
  require_relative 'rails/not_null_column'
86
+ require_relative 'rails/order_arguments'
85
87
  require_relative 'rails/order_by_id'
86
88
  require_relative 'rails/output'
87
89
  require_relative 'rails/output_safety'
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Rails
5
5
  # This module holds the RuboCop Rails version information.
6
6
  module Version
7
- STRING = '2.31.0'
7
+ STRING = '2.33.0'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
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.31.0
4
+ version: 2.33.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -9,7 +9,7 @@ authors:
9
9
  - Yuji Nakayama
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2025-04-01 00:00:00.000000000 Z
12
+ date: 1980-01-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -79,7 +79,7 @@ dependencies:
79
79
  requirements:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: 1.38.0
82
+ version: 1.44.0
83
83
  - - "<"
84
84
  - !ruby/object:Gem::Version
85
85
  version: '2.0'
@@ -89,7 +89,7 @@ dependencies:
89
89
  requirements:
90
90
  - - ">="
91
91
  - !ruby/object:Gem::Version
92
- version: 1.38.0
92
+ version: 1.44.0
93
93
  - - "<"
94
94
  - !ruby/object:Gem::Version
95
95
  version: '2.0'
@@ -164,6 +164,7 @@ files:
164
164
  - lib/rubocop/cop/rails/file_path.rb
165
165
  - lib/rubocop/cop/rails/find_by.rb
166
166
  - lib/rubocop/cop/rails/find_by_id.rb
167
+ - lib/rubocop/cop/rails/find_by_or_assignment_memoization.rb
167
168
  - lib/rubocop/cop/rails/find_each.rb
168
169
  - lib/rubocop/cop/rails/freeze_time.rb
169
170
  - lib/rubocop/cop/rails/has_and_belongs_to_many.rb
@@ -188,6 +189,7 @@ files:
188
189
  - lib/rubocop/cop/rails/multiple_route_paths.rb
189
190
  - lib/rubocop/cop/rails/negate_include.rb
190
191
  - lib/rubocop/cop/rails/not_null_column.rb
192
+ - lib/rubocop/cop/rails/order_arguments.rb
191
193
  - lib/rubocop/cop/rails/order_by_id.rb
192
194
  - lib/rubocop/cop/rails/output.rb
193
195
  - lib/rubocop/cop/rails/output_safety.rb
@@ -264,7 +266,7 @@ metadata:
264
266
  homepage_uri: https://docs.rubocop.org/rubocop-rails/
265
267
  changelog_uri: https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md
266
268
  source_code_uri: https://github.com/rubocop/rubocop-rails/
267
- documentation_uri: https://docs.rubocop.org/rubocop-rails/2.31/
269
+ documentation_uri: https://docs.rubocop.org/rubocop-rails/2.33/
268
270
  bug_tracker_uri: https://github.com/rubocop/rubocop-rails/issues
269
271
  rubygems_mfa_required: 'true'
270
272
  default_lint_roller_plugin: RuboCop::Rails::Plugin
@@ -282,7 +284,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
282
284
  - !ruby/object:Gem::Version
283
285
  version: '0'
284
286
  requirements: []
285
- rubygems_version: 3.6.2
287
+ rubygems_version: 3.6.9
286
288
  specification_version: 4
287
289
  summary: Automatic Rails code style checking tool.
288
290
  test_files: []