rubocop-rails 2.15.2 → 2.17.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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +120 -0
  3. data/config/obsoletion.yml +10 -0
  4. data/lib/rubocop/cop/mixin/active_record_helper.rb +1 -4
  5. data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +1 -3
  6. data/lib/rubocop/cop/mixin/index_method.rb +5 -15
  7. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +99 -0
  8. data/lib/rubocop/cop/rails/action_controller_test_case.rb +1 -1
  9. data/lib/rubocop/cop/rails/action_filter.rb +1 -1
  10. data/lib/rubocop/cop/rails/action_order.rb +81 -0
  11. data/lib/rubocop/cop/rails/active_record_aliases.rb +1 -4
  12. data/lib/rubocop/cop/rails/active_record_override.rb +2 -5
  13. data/lib/rubocop/cop/rails/active_support_on_load.rb +70 -0
  14. data/lib/rubocop/cop/rails/add_column_index.rb +1 -4
  15. data/lib/rubocop/cop/rails/blank.rb +1 -2
  16. data/lib/rubocop/cop/rails/bulk_change_table.rb +6 -20
  17. data/lib/rubocop/cop/rails/compact_blank.rb +5 -1
  18. data/lib/rubocop/cop/rails/content_tag.rb +3 -4
  19. data/lib/rubocop/cop/rails/date.rb +4 -9
  20. data/lib/rubocop/cop/rails/delegate.rb +2 -5
  21. data/lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb +17 -13
  22. data/lib/rubocop/cop/rails/dot_separated_keys.rb +1 -1
  23. data/lib/rubocop/cop/rails/dynamic_find_by.rb +8 -6
  24. data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +4 -0
  25. data/lib/rubocop/cop/rails/enum_uniqueness.rb +2 -5
  26. data/lib/rubocop/cop/rails/environment_comparison.rb +1 -2
  27. data/lib/rubocop/cop/rails/file_path.rb +2 -4
  28. data/lib/rubocop/cop/rails/find_each.rb +8 -2
  29. data/lib/rubocop/cop/rails/freeze_time.rb +74 -0
  30. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +1 -3
  31. data/lib/rubocop/cop/rails/http_positional_arguments.rb +4 -9
  32. data/lib/rubocop/cop/rails/http_status.rb +11 -11
  33. data/lib/rubocop/cop/rails/ignored_columns_assignment.rb +50 -0
  34. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +3 -10
  35. data/lib/rubocop/cop/rails/inverse_of.rb +3 -6
  36. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +2 -6
  37. data/lib/rubocop/cop/rails/link_to_blank.rb +1 -4
  38. data/lib/rubocop/cop/rails/output.rb +2 -5
  39. data/lib/rubocop/cop/rails/pluck.rb +8 -7
  40. data/lib/rubocop/cop/rails/pluralization_grammar.rb +1 -2
  41. data/lib/rubocop/cop/rails/presence.rb +21 -12
  42. data/lib/rubocop/cop/rails/present.rb +3 -6
  43. data/lib/rubocop/cop/rails/rake_environment.rb +1 -1
  44. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +2 -4
  45. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +1 -1
  46. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +3 -3
  47. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +30 -26
  48. data/lib/rubocop/cop/rails/reflection_class_name.rb +17 -0
  49. data/lib/rubocop/cop/rails/refute_methods.rb +1 -5
  50. data/lib/rubocop/cop/rails/relative_date_constant.rb +2 -5
  51. data/lib/rubocop/cop/rails/request_referer.rb +1 -2
  52. data/lib/rubocop/cop/rails/reversible_migration.rb +10 -33
  53. data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +1 -2
  54. data/lib/rubocop/cop/rails/root_pathname_methods.rb +214 -0
  55. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +1 -3
  56. data/lib/rubocop/cop/rails/save_bang.rb +10 -22
  57. data/lib/rubocop/cop/rails/short_i18n.rb +1 -4
  58. data/lib/rubocop/cop/rails/skips_model_validations.rb +1 -2
  59. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +1 -5
  60. data/lib/rubocop/cop/rails/time_zone.rb +10 -21
  61. data/lib/rubocop/cop/rails/to_s_with_argument.rb +41 -0
  62. data/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb +49 -0
  63. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +3 -6
  64. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +1 -3
  65. data/lib/rubocop/cop/rails/unknown_env.rb +2 -4
  66. data/lib/rubocop/cop/rails/validation.rb +4 -12
  67. data/lib/rubocop/cop/rails/where_missing.rb +111 -0
  68. data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +55 -0
  69. data/lib/rubocop/cop/rails_cops.rb +10 -0
  70. data/lib/rubocop/rails/version.rb +1 -1
  71. data/lib/rubocop-rails.rb +10 -0
  72. metadata +16 -8
  73. data/bin/console +0 -11
  74. data/bin/setup +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 169ed04b1f5378e9e08fbffdca151afaf4b84fd54b1867a173740aadaede60b3
4
- data.tar.gz: 1a2ac75c313cfcdeec1814d7e3aff4777c5013527d2cf4a2aa4f95aeb45446bf
3
+ metadata.gz: 2a7a516c82d0953f3f1995b190ddc427aa251583287a9232229e82a218c93ba6
4
+ data.tar.gz: f224a35df6de69c04c7c6f6314f9cfa713cc5accfba1bbce9d5554c7fd50a121
5
5
  SHA512:
6
- metadata.gz: 51d495edd3e825db49ece8f51bfc118c73887681f414b86d31cd47d85dc5a1a22a385a2a17ed4ebf7d1c6332989795e2cde999a364d911c60f452993dc926c4b
7
- data.tar.gz: 4546785b39af93e6c7fe5a6a3b637fa5c21d4b358c38ea8ad3ebd5c272d1d0a504e8206e8201cfe3771af9199c336649d033e32b324f16b06fc7d150054dc488
6
+ metadata.gz: 5b3277765f3819855d3e29d3226c81ebc6231b1b1aaf583e1ae86db9e18b7b450bbc2b3f9bccaaa85623ca57e0ac139abde604c35bccc81c3081a80c4392f35d
7
+ data.tar.gz: a3d7b79f10c2090b04a05d1b30cc3c639645aa6b3c2ad33247fecfa7597907238d8f6e727a8e0dd088b92d69842c488489e9883f9acfd4551e08a9b8f3dd1f8c
data/config/default.yml CHANGED
@@ -10,6 +10,12 @@ AllCops:
10
10
  # Exclude db/schema.rb and db/[CONFIGURATION_NAMESPACE]_schema.rb by default.
11
11
  # See: https://guides.rubyonrails.org/active_record_multiple_databases.html#setting-up-your-application
12
12
  - db/*schema.rb
13
+ - log/**/*
14
+ - public/**/*
15
+ - storage/**/*
16
+ # Enable checking Active Support extensions.
17
+ # See: https://docs.rubocop.org/rubocop/configuration.html#enable-checking-active-support-extensions
18
+ ActiveSupportExtensionsEnabled: true
13
19
  # What version of Rails is the inspected code using? If a value is specified
14
20
  # for TargetRailsVersion then it is used. Acceptable values are specified
15
21
  # as a float (i.e. 5.1); the patch version of Rails should not be included.
@@ -23,6 +29,24 @@ Lint/NumberConversion:
23
29
  # Add Rails' duration methods to the ignore list for `Lint/NumberConversion`
24
30
  # so that calling `to_i` on one of these does not register an offense.
25
31
  # See: https://github.com/rubocop/rubocop/issues/8950
32
+ AllowedMethods:
33
+ - ago
34
+ - from_now
35
+ - second
36
+ - seconds
37
+ - minute
38
+ - minutes
39
+ - hour
40
+ - hours
41
+ - day
42
+ - days
43
+ - week
44
+ - weeks
45
+ - fortnight
46
+ - fortnights
47
+ - in_milliseconds
48
+ AllowedPatterns: []
49
+ # Deprecated.
26
50
  IgnoredMethods:
27
51
  - ago
28
52
  - from_now
@@ -40,6 +64,18 @@ Lint/NumberConversion:
40
64
  - fortnights
41
65
  - in_milliseconds
42
66
 
67
+ Rails:
68
+ Enabled: true
69
+ DocumentationBaseURL: https://docs.rubocop.org/rubocop-rails
70
+
71
+ Rails/ActionControllerFlashBeforeRender:
72
+ Description: 'Use `flash.now` instead of `flash` before `render`.'
73
+ StyleGuide: 'https://rails.rubystyle.guide/#flash-before-render'
74
+ Reference: 'https://api.rubyonrails.org/classes/ActionController/FlashBeforeRender.html'
75
+ Enabled: 'pending'
76
+ SafeAutocorrect: false
77
+ VersionAdded: '2.16'
78
+
43
79
  Rails/ActionControllerTestCase:
44
80
  Description: 'Use `ActionDispatch::IntegrationTest` instead of `ActionController::TestCase`.'
45
81
  StyleGuide: 'https://rails.rubystyle.guide/#integration-testing'
@@ -62,6 +98,21 @@ Rails/ActionFilter:
62
98
  - app/controllers/**/*.rb
63
99
  - app/mailers/**/*.rb
64
100
 
101
+ Rails/ActionOrder:
102
+ Description: 'Enforce consistent ordering of controller actions.'
103
+ Enabled: pending
104
+ VersionAdded: '2.17'
105
+ ExpectedOrder:
106
+ - index
107
+ - show
108
+ - new
109
+ - edit
110
+ - create
111
+ - update
112
+ - destroy
113
+ Include:
114
+ - app/controllers/**/*.rb
115
+
65
116
  Rails/ActiveRecordAliases:
66
117
  Description: >-
67
118
  Avoid Active Record aliases:
@@ -96,6 +147,15 @@ Rails/ActiveSupportAliases:
96
147
  Enabled: true
97
148
  VersionAdded: '0.48'
98
149
 
150
+ Rails/ActiveSupportOnLoad:
151
+ Description: 'Use `ActiveSupport.on_load(...)` to patch Rails framework classes.'
152
+ Enabled: 'pending'
153
+ Reference:
154
+ - 'https://api.rubyonrails.org/classes/ActiveSupport/LazyLoadHooks.html'
155
+ - 'https://guides.rubyonrails.org/engines.html#available-load-hooks'
156
+ SafeAutoCorrect: false
157
+ VersionAdded: '2.16'
158
+
99
159
  Rails/AddColumnIndex:
100
160
  Description: >-
101
161
  Rails migrations don't make use of a given `index` key, but also
@@ -306,10 +366,13 @@ Rails/DynamicFindBy:
306
366
  # The `Whitelist` has been deprecated, Please use `AllowedMethods` instead.
307
367
  Whitelist:
308
368
  - find_by_sql
369
+ - find_by_token_for
309
370
  AllowedMethods:
310
371
  - find_by_sql
372
+ - find_by_token_for
311
373
  AllowedReceivers:
312
374
  - Gem::Specification
375
+ - page # Prevents a warning for `page.find_by_id`. See: https://github.com/rubocop/rubocop-rails/issues/778
313
376
 
314
377
  Rails/EagerEvaluationLogMessage:
315
378
  Description: 'Checks that blocks are used for interpolated strings passed to `Rails.logger.debug`.'
@@ -407,6 +470,14 @@ Rails/FindEach:
407
470
  VersionChanged: '2.9'
408
471
  Include:
409
472
  - app/models/**/*.rb
473
+ AllowedMethods:
474
+ # Methods that don't work well with `find_each`.
475
+ - order
476
+ - limit
477
+ - select
478
+ - lock
479
+ AllowedPatterns: []
480
+ # Deprecated.
410
481
  IgnoredMethods:
411
482
  # Methods that don't work well with `find_each`.
412
483
  - order
@@ -414,6 +485,13 @@ Rails/FindEach:
414
485
  - select
415
486
  - lock
416
487
 
488
+ Rails/FreezeTime:
489
+ Description: 'Prefer `freeze_time` over `travel_to` with an argument of the current time.'
490
+ StyleGuide: 'https://rails.rubystyle.guide/#freeze-time'
491
+ Enabled: pending
492
+ VersionAdded: '2.16'
493
+ SafeAutoCorrect: false
494
+
417
495
  Rails/HasAndBelongsToMany:
418
496
  Description: 'Prefer has_many :through to has_and_belongs_to_many.'
419
497
  StyleGuide: 'https://rails.rubystyle.guide#has-many-through'
@@ -478,6 +556,13 @@ Rails/I18nLocaleTexts:
478
556
  Enabled: pending
479
557
  VersionAdded: '2.14'
480
558
 
559
+ Rails/IgnoredColumnsAssignment:
560
+ Description: 'Looks for assignments of `ignored_columns` that override previous assignments.'
561
+ StyleGuide: 'https://rails.rubystyle.guide/#append-ignored-columns'
562
+ Enabled: pending
563
+ SafeAutoCorrect: false
564
+ VersionAdded: '2.17'
565
+
481
566
  Rails/IgnoredSkipActionFilterOption:
482
567
  Description: 'Checks that `if` and `only` (or `except`) are not used together as options of `skip_*` action filter.'
483
568
  Reference: 'https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-_normalize_callback_options'
@@ -785,6 +870,12 @@ Rails/RootJoinChain:
785
870
  Enabled: pending
786
871
  VersionAdded: '2.13'
787
872
 
873
+ Rails/RootPathnameMethods:
874
+ Description: 'Use `Rails.root` IO methods instead of passing it to `File`.'
875
+ Enabled: pending
876
+ SafeAutocorrect: false
877
+ VersionAdded: '2.16'
878
+
788
879
  Rails/RootPublicPath:
789
880
  Description: "Favor `Rails.public_path` over `Rails.root` with `'public'`."
790
881
  Enabled: pending
@@ -937,6 +1028,18 @@ Rails/ToFormattedS:
937
1028
  - to_formatted_s
938
1029
  VersionAdded: '2.15'
939
1030
 
1031
+ Rails/ToSWithArgument:
1032
+ Description: 'Identifies passing any argument to `#to_s`.'
1033
+ Enabled: pending
1034
+ Safe: false
1035
+ VersionAdded: '2.16'
1036
+
1037
+ Rails/TopLevelHashWithIndifferentAccess:
1038
+ Description: 'Identifies top-level `HashWithIndifferentAccess`.'
1039
+ Reference: 'https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#top-level-hashwithindifferentaccess-is-soft-deprecated'
1040
+ Enabled: pending
1041
+ VersionAdded: '2.16'
1042
+
940
1043
  Rails/TransactionExitStatement:
941
1044
  Description: 'Avoid the usage of `return`, `break` and `throw` in transaction blocks.'
942
1045
  Enabled: pending
@@ -1003,12 +1106,29 @@ Rails/WhereExists:
1003
1106
  VersionAdded: '2.7'
1004
1107
  VersionChanged: '2.10'
1005
1108
 
1109
+ Rails/WhereMissing:
1110
+ Description: 'Use `where.missing(...)` to find missing relationship records.'
1111
+ StyleGuide: 'https://rails.rubystyle.guide/#finding-missing-relationship-records'
1112
+ Enabled: pending
1113
+ VersionAdded: '2.16'
1114
+
1006
1115
  Rails/WhereNot:
1007
1116
  Description: 'Use `where.not(...)` instead of manually constructing negated SQL in `where`.'
1008
1117
  StyleGuide: 'https://rails.rubystyle.guide/#hash-conditions'
1009
1118
  Enabled: 'pending'
1010
1119
  VersionAdded: '2.8'
1011
1120
 
1121
+ Rails/WhereNotWithMultipleConditions:
1122
+ Description: 'Do not use `where.not(...)` with multiple conditions.'
1123
+ Enabled: 'pending'
1124
+ VersionAdded: '2.17'
1125
+
1012
1126
  # Accept `redirect_to(...) and return` and similar cases.
1013
1127
  Style/AndOr:
1014
1128
  EnforcedStyle: conditionals
1129
+
1130
+ Style/SymbolProc:
1131
+ AllowedMethods:
1132
+ - define_method
1133
+ - mail
1134
+ - respond_to
@@ -5,3 +5,13 @@
5
5
  #
6
6
  extracted:
7
7
  Rails/*: ~
8
+
9
+ # Cop parameters that have been changed
10
+ # Can be treated as a warning instead of a failure with `severity: warning`
11
+ changed_parameters:
12
+ - cops: Rails/FindEach
13
+ parameters: IgnoredMethods
14
+ alternatives:
15
+ - AllowedMethods
16
+ - AllowedPatterns
17
+ severity: warning
@@ -48,10 +48,7 @@ module RuboCop
48
48
 
49
49
  class_nodes = class_node.defined_module.each_node
50
50
  namespaces = class_node.each_ancestor(:class, :module).map(&:identifier)
51
- [*class_nodes, *namespaces]
52
- .reverse
53
- .map { |node| node.children[1] }.join('_')
54
- .tableize
51
+ [*class_nodes, *namespaces].reverse.map { |node| node.children[1] }.join('_').tableize
55
52
  end
56
53
 
57
54
  # Resolve relation into column name.
@@ -10,9 +10,7 @@ module RuboCop
10
10
  bigint binary boolean date datetime decimal float integer json string
11
11
  text time timestamp virtual
12
12
  ].freeze
13
- RAILS_ABSTRACT_SCHEMA_DEFINITIONS_HELPERS = %i[
14
- column references belongs_to primary_key numeric
15
- ].freeze
13
+ RAILS_ABSTRACT_SCHEMA_DEFINITIONS_HELPERS = %i[column references belongs_to primary_key numeric].freeze
16
14
  POSTGRES_SCHEMA_DEFINITIONS = %i[
17
15
  bigserial bit bit_varying cidr citext daterange hstore inet interval
18
16
  int4range int8range jsonb ltree macaddr money numrange oid point line
@@ -6,7 +6,7 @@ module RuboCop
6
6
  module IndexMethod # rubocop:disable Metrics/ModuleLength
7
7
  RESTRICT_ON_SEND = %i[each_with_object to_h map collect []].freeze
8
8
 
9
- def on_block(node)
9
+ def on_block(node) # rubocop:todo InternalAffairs/NumblockHandler
10
10
  on_bad_each_with_object(node) do |*match|
11
11
  handle_possible_offense(node, match, 'each_with_object')
12
12
  end
@@ -98,10 +98,7 @@ module RuboCop
98
98
 
99
99
  captures = extract_captures(correction.match)
100
100
  correction.set_new_arg_name(captures.transformed_argname, corrector)
101
- correction.set_new_body_expression(
102
- captures.transforming_body_expr,
103
- corrector
104
- )
101
+ correction.set_new_body_expression(captures.transforming_body_expr, corrector)
105
102
  end
106
103
 
107
104
  # Internal helper class to hold match data
@@ -110,8 +107,7 @@ module RuboCop
110
107
  :transforming_body_expr
111
108
  ) do
112
109
  def noop_transformation?
113
- transforming_body_expr.lvar_type? &&
114
- transforming_body_expr.children == [transformed_argname]
110
+ transforming_body_expr.lvar_type? && transforming_body_expr.children == [transformed_argname]
115
111
  end
116
112
  end
117
113
 
@@ -157,17 +153,11 @@ module RuboCop
157
153
  end
158
154
 
159
155
  def set_new_arg_name(transformed_argname, corrector)
160
- corrector.replace(
161
- block_node.arguments.loc.expression,
162
- "|#{transformed_argname}|"
163
- )
156
+ corrector.replace(block_node.arguments.loc.expression, "|#{transformed_argname}|")
164
157
  end
165
158
 
166
159
  def set_new_body_expression(transforming_body_expr, corrector)
167
- corrector.replace(
168
- block_node.body.loc.expression,
169
- transforming_body_expr.loc.expression.source
170
- )
160
+ corrector.replace(block_node.body.loc.expression, transforming_body_expr.loc.expression.source)
171
161
  end
172
162
  end
173
163
  end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Using `flash` assignment before `render` in Rails controllers will persist the message for too long.
7
+ # Check https://guides.rubyonrails.org/action_controller_overview.html#flash-now
8
+ #
9
+ # @safety
10
+ # This cop's autocorrection is unsafe because it replaces `flash` by `flash.now`.
11
+ # Even though it is usually a mistake, it might be used intentionally.
12
+ #
13
+ # @example
14
+ #
15
+ # # bad
16
+ # class HomeController < ApplicationController
17
+ # def create
18
+ # flash[:alert] = "msg"
19
+ # render :index
20
+ # end
21
+ # end
22
+ #
23
+ # # good
24
+ # class HomeController < ApplicationController
25
+ # def create
26
+ # flash.now[:alert] = "msg"
27
+ # render :index
28
+ # end
29
+ # end
30
+ #
31
+ class ActionControllerFlashBeforeRender < Base
32
+ extend AutoCorrector
33
+
34
+ MSG = 'Use `flash.now` before `render`.'
35
+
36
+ def_node_search :flash_assignment?, <<~PATTERN
37
+ ^(send (send nil? :flash) :[]= ...)
38
+ PATTERN
39
+
40
+ def_node_search :redirect_to?, <<~PATTERN
41
+ (send nil? :redirect_to ...)
42
+ PATTERN
43
+
44
+ def_node_search :action_controller?, <<~PATTERN
45
+ {
46
+ (const nil? :ApplicationController)
47
+ (const (const nil? :ActionController) :Base)
48
+ }
49
+ PATTERN
50
+
51
+ RESTRICT_ON_SEND = [:flash].freeze
52
+
53
+ def on_send(flash_node)
54
+ return unless flash_assignment?(flash_node)
55
+
56
+ return if followed_by_redirect_to?(flash_node)
57
+
58
+ return unless instance_method_or_block?(flash_node)
59
+
60
+ return unless inherit_action_controller_base?(flash_node)
61
+
62
+ add_offense(flash_node) do |corrector|
63
+ corrector.replace(flash_node, 'flash.now')
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def followed_by_redirect_to?(flash_node)
70
+ flash_assigment_node = find_ancestor(flash_node, type: :send)
71
+ context = flash_assigment_node.parent
72
+
73
+ flash_index = context.children.index(flash_assigment_node)
74
+ context.each_child_node.with_index.any? do |node, index|
75
+ index > flash_index && redirect_to?(node)
76
+ end
77
+ end
78
+
79
+ def inherit_action_controller_base?(node)
80
+ class_node = find_ancestor(node, type: :class)
81
+ return unless class_node
82
+
83
+ action_controller?(class_node)
84
+ end
85
+
86
+ def instance_method_or_block?(node)
87
+ def_node = find_ancestor(node, type: :def)
88
+ block_node = find_ancestor(node, type: :block)
89
+
90
+ def_node || block_node
91
+ end
92
+
93
+ def find_ancestor(node, type:)
94
+ node.each_ancestor(type).first
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -31,7 +31,7 @@ module RuboCop
31
31
  def_node_matcher :action_controller_test_case?, <<~PATTERN
32
32
  (class
33
33
  (const nil? _)
34
- (const (const {nil? cbase} :ActionController) :TestCase) nil?)
34
+ (const (const {nil? cbase} :ActionController) :TestCase) _)
35
35
  PATTERN
36
36
 
37
37
  def on_class(node)
@@ -69,7 +69,7 @@ module RuboCop
69
69
 
70
70
  RESTRICT_ON_SEND = FILTER_METHODS + ACTION_METHODS
71
71
 
72
- def on_block(node)
72
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
73
73
  check_method_node(node.send_node)
74
74
  end
75
75
 
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Enforces consistent ordering of the standard Rails RESTful controller actions.
7
+ #
8
+ # The cop is configurable and can enforce any ordering of the standard actions.
9
+ # All other methods are ignored.
10
+ #
11
+ # [source,yaml]
12
+ # ----
13
+ # Rails/ActionOrder:
14
+ # ExpectedOrder:
15
+ # - index
16
+ # - show
17
+ # - new
18
+ # - edit
19
+ # - create
20
+ # - update
21
+ # - destroy
22
+ # ----
23
+ #
24
+ # @example
25
+ # # bad
26
+ # def index; end
27
+ # def destroy; end
28
+ # def show; end
29
+ #
30
+ # # good
31
+ # def index; end
32
+ # def show; end
33
+ # def destroy; end
34
+ class ActionOrder < Base
35
+ extend AutoCorrector
36
+ include VisibilityHelp
37
+ include DefNode
38
+
39
+ MSG = 'Action `%<current>s` should appear before `%<previous>s`.'
40
+
41
+ def_node_search :action_declarations, '(def {%1} ...)'
42
+
43
+ def on_class(node)
44
+ action_declarations(node, actions).each_cons(2) do |previous, current|
45
+ next if node_visibility(current) != :public || non_public?(current)
46
+ next if find_index(current) >= find_index(previous)
47
+
48
+ register_offense(previous, current)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def expected_order
55
+ cop_config['ExpectedOrder'].map(&:to_sym)
56
+ end
57
+
58
+ def actions
59
+ @actions ||= Set.new(expected_order)
60
+ end
61
+
62
+ def find_index(node)
63
+ expected_order.find_index(node.method_name)
64
+ end
65
+
66
+ def register_offense(previous, current)
67
+ message = format(
68
+ MSG,
69
+ expected_order: expected_order.join(', '),
70
+ previous: previous.method_name,
71
+ current: current.method_name
72
+ )
73
+ add_offense(current, message: message) do |corrector|
74
+ corrector.replace(current, previous.source)
75
+ corrector.replace(previous, current.source)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -21,10 +21,7 @@ module RuboCop
21
21
 
22
22
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
23
23
 
24
- ALIASES = {
25
- update_attributes: :update,
26
- update_attributes!: :update!
27
- }.freeze
24
+ ALIASES = { update_attributes: :update, update_attributes!: :update! }.freeze
28
25
 
29
26
  RESTRICT_ON_SEND = ALIASES.keys.freeze
30
27
 
@@ -25,12 +25,9 @@ module RuboCop
25
25
  # end
26
26
  #
27
27
  class ActiveRecordOverride < Base
28
- MSG =
29
- 'Use %<prefer>s callbacks instead of overriding the Active Record ' \
30
- 'method `%<bad>s`.'
28
+ MSG = 'Use %<prefer>s callbacks instead of overriding the Active Record method `%<bad>s`.'
31
29
  BAD_METHODS = %i[create destroy save update].freeze
32
- ACTIVE_RECORD_CLASSES = %w[ApplicationRecord ActiveModel::Base
33
- ActiveRecord::Base].freeze
30
+ ACTIVE_RECORD_CLASSES = %w[ApplicationRecord ActiveModel::Base ActiveRecord::Base].freeze
34
31
 
35
32
  def on_def(node)
36
33
  return unless BAD_METHODS.include?(node.method_name)
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Checks for Rails framework classes that are patched directly instead of using Active Support load hooks. Direct
7
+ # patching forcibly loads the framework referenced, using hooks defers loading until it's actually needed.
8
+ #
9
+ # @safety
10
+ # While using lazy load hooks is recommended, it changes the order in which is code is loaded and may reveal
11
+ # load order dependency bugs.
12
+ #
13
+ # @example
14
+ #
15
+ # # bad
16
+ # ActiveRecord::Base.include(MyClass)
17
+ #
18
+ # # good
19
+ # ActiveSupport.on_load(:active_record) { include MyClass }
20
+ class ActiveSupportOnLoad < Base
21
+ extend AutoCorrector
22
+
23
+ MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
24
+ RESTRICT_ON_SEND = %i[prepend include extend].freeze
25
+ LOAD_HOOKS = {
26
+ 'ActionCable' => 'action_cable',
27
+ 'ActionCable::Channel::Base' => 'action_cable_channel',
28
+ 'ActionCable::Connection::Base' => 'action_cable_connection',
29
+ 'ActionCable::Connection::TestCase' => 'action_cable_connection_test_case',
30
+ 'ActionController::API' => 'action_controller',
31
+ 'ActionController::Base' => 'action_controller',
32
+ 'ActionController::TestCase' => 'action_controller_test_case',
33
+ 'ActionDispatch::IntegrationTest' => 'action_dispatch_integration_test',
34
+ 'ActionDispatch::Request' => 'action_dispatch_request',
35
+ 'ActionDispatch::Response' => 'action_dispatch_response',
36
+ 'ActionDispatch::SystemTestCase' => 'action_dispatch_system_test_case',
37
+ 'ActionMailbox::Base' => 'action_mailbox',
38
+ 'ActionMailbox::InboundEmail' => 'action_mailbox_inbound_email',
39
+ 'ActionMailbox::Record' => 'action_mailbox_record',
40
+ 'ActionMailbox::TestCase' => 'action_mailbox_test_case',
41
+ 'ActionMailer::Base' => 'action_mailer',
42
+ 'ActionMailer::TestCase' => 'action_mailer_test_case',
43
+ 'ActionText::Content' => 'action_text_content',
44
+ 'ActionText::Record' => 'action_text_record',
45
+ 'ActionText::RichText' => 'action_text_rich_text',
46
+ 'ActionView::Base' => 'action_view',
47
+ 'ActionView::TestCase' => 'action_view_test_case',
48
+ 'ActiveJob::Base' => 'active_job',
49
+ 'ActiveJob::TestCase' => 'active_job_test_case',
50
+ 'ActiveRecord::Base' => 'active_record',
51
+ 'ActiveStorage::Attachment' => 'active_storage_attachment',
52
+ 'ActiveStorage::Blob' => 'active_storage_blob',
53
+ 'ActiveStorage::Record' => 'active_storage_record',
54
+ 'ActiveStorage::VariantRecord' => 'active_storage_variant_record',
55
+ 'ActiveSupport::TestCase' => 'active_support_test_case'
56
+ }.freeze
57
+
58
+ def on_send(node)
59
+ receiver, method, arguments = *node # rubocop:disable InternalAffairs/NodeDestructuring
60
+ return unless receiver && (hook = LOAD_HOOKS[receiver.const_name])
61
+
62
+ preferred = "ActiveSupport.on_load(:#{hook}) { #{method} #{arguments.source} }"
63
+ add_offense(node, message: format(MSG, prefer: preferred, current: node.source)) do |corrector|
64
+ corrector.replace(node, preferred)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -53,10 +53,7 @@ module RuboCop
53
53
  private
54
54
 
55
55
  def index_range(pair_node)
56
- range_with_surrounding_comma(
57
- range_with_surrounding_space(range: pair_node.loc.expression, side: :left),
58
- :left
59
- )
56
+ range_with_surrounding_comma(range_with_surrounding_space(pair_node.loc.expression, side: :left), :left)
60
57
  end
61
58
  end
62
59
  end
@@ -63,8 +63,7 @@ module RuboCop
63
63
 
64
64
  MSG_NIL_OR_EMPTY = 'Use `%<prefer>s` instead of `%<current>s`.'
65
65
  MSG_NOT_PRESENT = 'Use `%<prefer>s` instead of `%<current>s`.'
66
- MSG_UNLESS_PRESENT = 'Use `if %<prefer>s` instead of ' \
67
- '`%<current>s`.'
66
+ MSG_UNLESS_PRESENT = 'Use `if %<prefer>s` instead of `%<current>s`.'
68
67
  RESTRICT_ON_SEND = %i[!].freeze
69
68
 
70
69
  # `(send nil $_)` is not actually a valid match for an offense. Nodes