rubocop-rails 2.0.1 → 2.19.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +52 -5
  4. data/config/default.yml +726 -32
  5. data/config/obsoletion.yml +17 -0
  6. data/lib/rubocop/cop/mixin/active_record_helper.rb +106 -0
  7. data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +32 -0
  8. data/lib/rubocop/cop/mixin/class_send_node_helper.rb +20 -0
  9. data/lib/rubocop/cop/mixin/enforce_superclass.rb +40 -0
  10. data/lib/rubocop/cop/mixin/index_method.rb +165 -0
  11. data/lib/rubocop/cop/mixin/migrations_helper.rb +26 -0
  12. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +112 -0
  13. data/lib/rubocop/cop/rails/action_controller_test_case.rb +47 -0
  14. data/lib/rubocop/cop/rails/action_filter.rb +11 -21
  15. data/lib/rubocop/cop/rails/action_order.rb +116 -0
  16. data/lib/rubocop/cop/rails/active_record_aliases.rb +23 -24
  17. data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +143 -0
  18. data/lib/rubocop/cop/rails/active_record_override.rb +3 -6
  19. data/lib/rubocop/cop/rails/active_support_aliases.rb +13 -22
  20. data/lib/rubocop/cop/rails/active_support_on_load.rb +70 -0
  21. data/lib/rubocop/cop/rails/add_column_index.rb +61 -0
  22. data/lib/rubocop/cop/rails/after_commit_override.rb +81 -0
  23. data/lib/rubocop/cop/rails/application_controller.rb +36 -0
  24. data/lib/rubocop/cop/rails/application_job.rb +9 -4
  25. data/lib/rubocop/cop/rails/application_mailer.rb +39 -0
  26. data/lib/rubocop/cop/rails/application_record.rb +9 -9
  27. data/lib/rubocop/cop/rails/arel_star.rb +47 -0
  28. data/lib/rubocop/cop/rails/assert_not.rb +8 -10
  29. data/lib/rubocop/cop/rails/attribute_default_block_value.rb +90 -0
  30. data/lib/rubocop/cop/rails/belongs_to.rb +12 -24
  31. data/lib/rubocop/cop/rails/blank.rb +40 -36
  32. data/lib/rubocop/cop/rails/bulk_change_table.rb +40 -35
  33. data/lib/rubocop/cop/rails/compact_blank.rb +111 -0
  34. data/lib/rubocop/cop/rails/content_tag.rb +93 -0
  35. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +22 -15
  36. data/lib/rubocop/cop/rails/date.rb +41 -36
  37. data/lib/rubocop/cop/rails/default_scope.rb +61 -0
  38. data/lib/rubocop/cop/rails/delegate.rb +33 -29
  39. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +9 -10
  40. data/lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb +168 -0
  41. data/lib/rubocop/cop/rails/dot_separated_keys.rb +71 -0
  42. data/lib/rubocop/cop/rails/duplicate_association.rb +56 -0
  43. data/lib/rubocop/cop/rails/duplicate_scope.rb +46 -0
  44. data/lib/rubocop/cop/rails/duration_arithmetic.rb +98 -0
  45. data/lib/rubocop/cop/rails/dynamic_find_by.rb +76 -31
  46. data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +82 -0
  47. data/lib/rubocop/cop/rails/enum_hash.rb +75 -0
  48. data/lib/rubocop/cop/rails/enum_uniqueness.rb +30 -12
  49. data/lib/rubocop/cop/rails/environment_comparison.rb +70 -22
  50. data/lib/rubocop/cop/rails/environment_variable_access.rb +67 -0
  51. data/lib/rubocop/cop/rails/exit.rb +7 -13
  52. data/lib/rubocop/cop/rails/expanded_date_range.rb +102 -0
  53. data/lib/rubocop/cop/rails/file_path.rb +48 -31
  54. data/lib/rubocop/cop/rails/find_by.rb +43 -24
  55. data/lib/rubocop/cop/rails/find_by_id.rb +94 -0
  56. data/lib/rubocop/cop/rails/find_each.rb +42 -18
  57. data/lib/rubocop/cop/rails/freeze_time.rb +79 -0
  58. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +4 -3
  59. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +62 -25
  60. data/lib/rubocop/cop/rails/helper_instance_variable.rb +32 -4
  61. data/lib/rubocop/cop/rails/http_positional_arguments.rb +61 -32
  62. data/lib/rubocop/cop/rails/http_status.rb +27 -23
  63. data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +96 -0
  64. data/lib/rubocop/cop/rails/i18n_locale_assignment.rb +37 -0
  65. data/lib/rubocop/cop/rails/i18n_locale_texts.rb +110 -0
  66. data/lib/rubocop/cop/rails/ignored_columns_assignment.rb +50 -0
  67. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +9 -16
  68. data/lib/rubocop/cop/rails/index_by.rb +65 -0
  69. data/lib/rubocop/cop/rails/index_with.rb +68 -0
  70. data/lib/rubocop/cop/rails/inquiry.rb +39 -0
  71. data/lib/rubocop/cop/rails/inverse_of.rb +33 -27
  72. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +62 -32
  73. data/lib/rubocop/cop/rails/link_to_blank.rb +31 -32
  74. data/lib/rubocop/cop/rails/mailer_name.rb +90 -0
  75. data/lib/rubocop/cop/rails/match_route.rb +120 -0
  76. data/lib/rubocop/cop/rails/migration_class_name.rb +63 -0
  77. data/lib/rubocop/cop/rails/negate_include.rb +42 -0
  78. data/lib/rubocop/cop/rails/not_null_column.rb +16 -12
  79. data/lib/rubocop/cop/rails/order_by_id.rb +51 -0
  80. data/lib/rubocop/cop/rails/output.rb +29 -10
  81. data/lib/rubocop/cop/rails/output_safety.rb +9 -4
  82. data/lib/rubocop/cop/rails/pick.rb +64 -0
  83. data/lib/rubocop/cop/rails/pluck.rb +96 -0
  84. data/lib/rubocop/cop/rails/pluck_id.rb +59 -0
  85. data/lib/rubocop/cop/rails/pluck_in_where.rb +71 -0
  86. data/lib/rubocop/cop/rails/pluralization_grammar.rb +14 -19
  87. data/lib/rubocop/cop/rails/presence.rb +54 -26
  88. data/lib/rubocop/cop/rails/present.rb +40 -37
  89. data/lib/rubocop/cop/rails/rake_environment.rb +112 -0
  90. data/lib/rubocop/cop/rails/read_write_attribute.rb +56 -18
  91. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +33 -45
  92. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +77 -0
  93. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +257 -0
  94. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +34 -32
  95. data/lib/rubocop/cop/rails/redundant_travel_back.rb +57 -0
  96. data/lib/rubocop/cop/rails/reflection_class_name.rb +56 -7
  97. data/lib/rubocop/cop/rails/refute_methods.rb +56 -35
  98. data/lib/rubocop/cop/rails/relative_date_constant.rb +52 -33
  99. data/lib/rubocop/cop/rails/render_inline.rb +41 -0
  100. data/lib/rubocop/cop/rails/render_plain_text.rb +71 -0
  101. data/lib/rubocop/cop/rails/request_referer.rb +10 -11
  102. data/lib/rubocop/cop/rails/require_dependency.rb +38 -0
  103. data/lib/rubocop/cop/rails/response_parsed_body.rb +57 -0
  104. data/lib/rubocop/cop/rails/reversible_migration.rb +122 -82
  105. data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +66 -0
  106. data/lib/rubocop/cop/rails/root_join_chain.rb +72 -0
  107. data/lib/rubocop/cop/rails/root_pathname_methods.rb +238 -0
  108. data/lib/rubocop/cop/rails/root_public_path.rb +59 -0
  109. data/lib/rubocop/cop/rails/safe_navigation.rb +55 -43
  110. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +50 -0
  111. data/lib/rubocop/cop/rails/save_bang.rb +89 -63
  112. data/lib/rubocop/cop/rails/schema_comment.rb +104 -0
  113. data/lib/rubocop/cop/rails/scope_args.rb +8 -3
  114. data/lib/rubocop/cop/rails/short_i18n.rb +71 -0
  115. data/lib/rubocop/cop/rails/skips_model_validations.rb +53 -16
  116. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +87 -0
  117. data/lib/rubocop/cop/rails/strip_heredoc.rb +56 -0
  118. data/lib/rubocop/cop/rails/table_name_assignment.rb +44 -0
  119. data/lib/rubocop/cop/rails/three_state_boolean_column.rb +73 -0
  120. data/lib/rubocop/cop/rails/time_zone.rb +83 -67
  121. data/lib/rubocop/cop/rails/time_zone_assignment.rb +37 -0
  122. data/lib/rubocop/cop/rails/to_formatted_s.rb +46 -0
  123. data/lib/rubocop/cop/rails/to_s_with_argument.rb +78 -0
  124. data/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb +49 -0
  125. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +99 -0
  126. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +40 -49
  127. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +172 -0
  128. data/lib/rubocop/cop/rails/unknown_env.rb +52 -21
  129. data/lib/rubocop/cop/rails/unused_ignored_columns.rb +76 -0
  130. data/lib/rubocop/cop/rails/validation.rb +54 -23
  131. data/lib/rubocop/cop/rails/where_equals.rb +102 -0
  132. data/lib/rubocop/cop/rails/where_exists.rb +138 -0
  133. data/lib/rubocop/cop/rails/where_missing.rb +118 -0
  134. data/lib/rubocop/cop/rails/where_not.rb +101 -0
  135. data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +55 -0
  136. data/lib/rubocop/cop/rails_cops.rb +78 -8
  137. data/lib/rubocop/rails/inject.rb +1 -1
  138. data/lib/rubocop/rails/schema_loader/schema.rb +191 -0
  139. data/lib/rubocop/rails/schema_loader.rb +61 -0
  140. data/lib/rubocop/rails/version.rb +5 -1
  141. data/lib/rubocop/rails.rb +3 -1
  142. data/lib/rubocop-rails.rb +22 -0
  143. metadata +120 -19
  144. data/bin/setup +0 -7
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Looks for uses of `each_with_object({}) { ... }`,
7
+ # `map { ... }.to_h`, and `Hash[map { ... }]` that are transforming
8
+ # an enumerable into a hash where the keys are the original elements.
9
+ # Rails provides the `index_with` method for this purpose.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # [1, 2, 3].each_with_object({}) { |el, h| h[el] = foo(el) }
14
+ # [1, 2, 3].to_h { |el| [el, foo(el)] }
15
+ # [1, 2, 3].map { |el| [el, foo(el)] }.to_h
16
+ # Hash[[1, 2, 3].collect { |el| [el, foo(el)] }]
17
+ #
18
+ # # good
19
+ # [1, 2, 3].index_with { |el| foo(el) }
20
+ class IndexWith < Base
21
+ extend AutoCorrector
22
+ extend TargetRailsVersion
23
+ include IndexMethod
24
+
25
+ minimum_target_rails_version 6.0
26
+
27
+ def_node_matcher :on_bad_each_with_object, <<~PATTERN
28
+ (block
29
+ (call _ :each_with_object (hash))
30
+ (args (arg $_el) (arg _memo))
31
+ (call (lvar _memo) :[]= (lvar _el) $!`_memo))
32
+ PATTERN
33
+
34
+ def_node_matcher :on_bad_to_h, <<~PATTERN
35
+ (block
36
+ (call _ :to_h)
37
+ (args (arg $_el))
38
+ (array (lvar _el) $_))
39
+ PATTERN
40
+
41
+ def_node_matcher :on_bad_map_to_h, <<~PATTERN
42
+ (call
43
+ (block
44
+ (call _ {:map :collect})
45
+ (args (arg $_el))
46
+ (array (lvar _el) $_))
47
+ :to_h)
48
+ PATTERN
49
+
50
+ def_node_matcher :on_bad_hash_brackets_map, <<~PATTERN
51
+ (send
52
+ (const {nil? cbase} :Hash)
53
+ :[]
54
+ (block
55
+ (call _ {:map :collect})
56
+ (args (arg $_el))
57
+ (array (lvar _el) $_)))
58
+ PATTERN
59
+
60
+ private
61
+
62
+ def new_method_name
63
+ 'index_with'
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Checks that Active Support's `inquiry` method is not used.
7
+ #
8
+ # @example
9
+ # # bad - String#inquiry
10
+ # ruby = 'two'.inquiry
11
+ # ruby.two?
12
+ #
13
+ # # good
14
+ # ruby = 'two'
15
+ # ruby == 'two'
16
+ #
17
+ # # bad - Array#inquiry
18
+ # pets = %w(cat dog).inquiry
19
+ # pets.gopher?
20
+ #
21
+ # # good
22
+ # pets = %w(cat dog)
23
+ # pets.include? 'cat'
24
+ #
25
+ class Inquiry < Base
26
+ MSG = "Prefer Ruby's comparison operators over Active Support's `inquiry`."
27
+ RESTRICT_ON_SEND = %i[inquiry].freeze
28
+
29
+ def on_send(node)
30
+ return unless node.arguments.empty?
31
+ return unless (receiver = node.receiver)
32
+ return if !receiver.str_type? && !receiver.array_type?
33
+
34
+ add_offense(node.loc.selector)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop looks for has_(one|many) and belongs_to associations where
6
+ # Looks for has_(one|many) and belongs_to associations where
7
7
  # Active Record can't automatically determine the inverse association
8
8
  # because of a scope or the options used. Using the blog with order scope
9
9
  # example below, traversing the a Blog's association in both directions
@@ -126,50 +126,55 @@ module RuboCop
126
126
  # has_many :physicians, through: :appointments
127
127
  # end
128
128
  #
129
- # @see https://guides.rubyonrails.org/association_basics.html#bi-directional-associations
130
- # @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses
131
- class InverseOf < Cop
132
- extend TargetRailsVersion
133
-
134
- minimum_target_rails_version 4.1
135
-
129
+ # @example IgnoreScopes: false (default)
130
+ # # bad
131
+ # class Blog < ApplicationRecord
132
+ # has_many :posts, -> { order(published_at: :desc) }
133
+ # end
134
+ #
135
+ # @example IgnoreScopes: true
136
+ # # good
137
+ # class Blog < ApplicationRecord
138
+ # has_many :posts, -> { order(published_at: :desc) }
139
+ # end
140
+ class InverseOf < Base
136
141
  SPECIFY_MSG = 'Specify an `:inverse_of` option.'
137
- NIL_MSG = 'You specified `inverse_of: nil`, you probably meant to ' \
138
- 'use `inverse_of: false`.'
142
+ NIL_MSG = 'You specified `inverse_of: nil`, you probably meant to use `inverse_of: false`.'
143
+ RESTRICT_ON_SEND = %i[has_many has_one belongs_to].freeze
139
144
 
140
- def_node_matcher :association_recv_arguments, <<-PATTERN
145
+ def_node_matcher :association_recv_arguments, <<~PATTERN
141
146
  (send $_ {:has_many :has_one :belongs_to} _ $...)
142
147
  PATTERN
143
148
 
144
- def_node_matcher :options_from_argument, <<-PATTERN
149
+ def_node_matcher :options_from_argument, <<~PATTERN
145
150
  (hash $...)
146
151
  PATTERN
147
152
 
148
- def_node_matcher :conditions_option?, <<-PATTERN
153
+ def_node_matcher :conditions_option?, <<~PATTERN
149
154
  (pair (sym :conditions) !nil)
150
155
  PATTERN
151
156
 
152
- def_node_matcher :through_option?, <<-PATTERN
157
+ def_node_matcher :through_option?, <<~PATTERN
153
158
  (pair (sym :through) !nil)
154
159
  PATTERN
155
160
 
156
- def_node_matcher :polymorphic_option?, <<-PATTERN
161
+ def_node_matcher :polymorphic_option?, <<~PATTERN
157
162
  (pair (sym :polymorphic) !nil)
158
163
  PATTERN
159
164
 
160
- def_node_matcher :as_option?, <<-PATTERN
165
+ def_node_matcher :as_option?, <<~PATTERN
161
166
  (pair (sym :as) !nil)
162
167
  PATTERN
163
168
 
164
- def_node_matcher :foreign_key_option?, <<-PATTERN
169
+ def_node_matcher :foreign_key_option?, <<~PATTERN
165
170
  (pair (sym :foreign_key) !nil)
166
171
  PATTERN
167
172
 
168
- def_node_matcher :inverse_of_option?, <<-PATTERN
173
+ def_node_matcher :inverse_of_option?, <<~PATTERN
169
174
  (pair (sym :inverse_of) !nil)
170
175
  PATTERN
171
176
 
172
- def_node_matcher :inverse_of_nil_option?, <<-PATTERN
177
+ def_node_matcher :inverse_of_nil_option?, <<~PATTERN
173
178
  (pair (sym :inverse_of) nil)
174
179
  PATTERN
175
180
 
@@ -184,22 +189,20 @@ module RuboCop
184
189
  end
185
190
  return if options_ignoring_inverse_of?(options)
186
191
 
187
- return unless scope?(arguments) ||
188
- options_requiring_inverse_of?(options)
192
+ return unless scope?(arguments) || options_requiring_inverse_of?(options)
189
193
 
190
194
  return if options_contain_inverse_of?(options)
191
195
 
192
- add_offense(node, message: message(options), location: :selector)
196
+ add_offense(node.loc.selector, message: message(options))
193
197
  end
194
198
 
195
199
  def scope?(arguments)
196
- arguments.any?(&:block_type?)
200
+ !ignore_scopes? && arguments.any?(&:block_type?)
197
201
  end
198
202
 
199
203
  def options_requiring_inverse_of?(options)
200
204
  required = options.any? do |opt|
201
- conditions_option?(opt) ||
202
- foreign_key_option?(opt)
205
+ conditions_option?(opt) || foreign_key_option?(opt)
203
206
  end
204
207
 
205
208
  return required if target_rails_version >= 5.2
@@ -219,8 +222,7 @@ module RuboCop
219
222
 
220
223
  def with_options_arguments(recv, node)
221
224
  blocks = node.each_ancestor(:block).select do |block|
222
- block.send_node.command?(:with_options) &&
223
- same_context_in_with_options?(block.arguments.first, recv)
225
+ block.send_node.command?(:with_options) && same_context_in_with_options?(block.arguments.first, recv)
224
226
  end
225
227
  blocks.flat_map { |n| n.send_node.arguments }
226
228
  end
@@ -240,6 +242,10 @@ module RuboCop
240
242
  SPECIFY_MSG
241
243
  end
242
244
  end
245
+
246
+ def ignore_scopes?
247
+ cop_config['IgnoreScopes'] == true
248
+ end
243
249
  end
244
250
  end
245
251
  end
@@ -3,16 +3,17 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks that methods specified in the filter's `only` or
6
+ # Checks that methods specified in the filter's `only` or
7
7
  # `except` options are defined within the same class or module.
8
8
  #
9
- # You can technically specify methods of superclass or methods added by
10
- # mixins on the filter, but these can confuse developers. If you specify
11
- # methods that are defined in other classes or modules, you should
12
- # define the filter in that class or module.
9
+ # @safety
10
+ # You can technically specify methods of superclass or methods added by
11
+ # mixins on the filter, but these can confuse developers. If you specify
12
+ # methods that are defined in other classes or modules, you should
13
+ # define the filter in that class or module.
13
14
  #
14
- # If you rely on behaviour defined in the superclass actions, you must
15
- # remember to invoke `super` in the subclass actions.
15
+ # If you rely on behavior defined in the superclass actions, you must
16
+ # remember to invoke `super` in the subclass actions.
16
17
  #
17
18
  # @example
18
19
  # # bad
@@ -70,7 +71,7 @@ module RuboCop
70
71
  # class ArticlesController < ContentController
71
72
  # before_action :load_article, only: [:update]
72
73
  #
73
- # # the cop requires this method, but it relies on behaviour defined
74
+ # # the cop requires this method, but it relies on behavior defined
74
75
  # # in the superclass, so needs to invoke `super`
75
76
  # def update
76
77
  # super
@@ -82,26 +83,28 @@ module RuboCop
82
83
  # @content = Article.find(params[:article_id])
83
84
  # end
84
85
  # end
85
- class LexicallyScopedActionFilter < Cop
86
+ class LexicallyScopedActionFilter < Base
86
87
  MSG = '%<action>s not explicitly defined on the %<type>s.'
87
88
 
88
- FILTERS = %w[
89
- :after_action
90
- :append_after_action
91
- :append_around_action
92
- :append_before_action
93
- :around_action
94
- :before_action
95
- :prepend_after_action
96
- :prepend_around_action
97
- :prepend_before_action
98
- :skip_after_action
99
- :skip_around_action
100
- :skip_before_action
101
- :skip_action_callback
89
+ RESTRICT_ON_SEND = %i[
90
+ after_action
91
+ append_after_action
92
+ append_around_action
93
+ append_before_action
94
+ around_action
95
+ before_action
96
+ prepend_after_action
97
+ prepend_around_action
98
+ prepend_before_action
99
+ skip_after_action
100
+ skip_around_action
101
+ skip_before_action
102
+ skip_action_callback
102
103
  ].freeze
103
104
 
104
- def_node_matcher :only_or_except_filter_methods, <<-PATTERN
105
+ FILTERS = RESTRICT_ON_SEND.map { |method_name| ":#{method_name}" }
106
+
107
+ def_node_matcher :only_or_except_filter_methods, <<~PATTERN
105
108
  (send
106
109
  nil?
107
110
  {#{FILTERS.join(' ')}}
@@ -122,9 +125,10 @@ module RuboCop
122
125
  block = parent.each_child_node(:begin).first
123
126
  return unless block
124
127
 
125
- defined_methods = block.each_child_node(:def).map(&:method_name)
128
+ defined_action_methods = defined_action_methods(block)
129
+
126
130
  methods = array_values(methods_node).reject do |method|
127
- defined_methods.include?(method)
131
+ defined_action_methods.include?(method)
128
132
  end
129
133
 
130
134
  message = message(methods, parent)
@@ -133,6 +137,36 @@ module RuboCop
133
137
 
134
138
  private
135
139
 
140
+ def defined_action_methods(block)
141
+ defined_methods = block.each_child_node(:def).map(&:method_name)
142
+
143
+ defined_methods + aliased_action_methods(block, defined_methods)
144
+ end
145
+
146
+ def aliased_action_methods(node, defined_methods)
147
+ alias_methods = alias_methods(node)
148
+ defined_methods.each_with_object([]) do |defined_method, aliased_method|
149
+ if (new_method_name = alias_methods[defined_method])
150
+ aliased_method << new_method_name
151
+ end
152
+ end
153
+ end
154
+
155
+ def alias_methods(node)
156
+ result = {}
157
+ node.each_child_node(:send, :alias) do |child_node|
158
+ case child_node.type
159
+ when :send
160
+ if child_node.method?(:alias_method)
161
+ result[child_node.last_argument.value] = child_node.first_argument.value
162
+ end
163
+ when :alias
164
+ result[child_node.old_identifier.value] = child_node.new_identifier.value
165
+ end
166
+ end
167
+ result
168
+ end
169
+
136
170
  # @param node [RuboCop::AST::Node]
137
171
  # @return [Array<Symbol>]
138
172
  def array_values(node) # rubocop:disable Metrics/MethodLength
@@ -160,13 +194,9 @@ module RuboCop
160
194
  # @return [String]
161
195
  def message(methods, parent)
162
196
  if methods.size == 1
163
- format(MSG,
164
- action: "`#{methods[0]}` is",
165
- type: parent.type)
197
+ format(MSG, action: "`#{methods[0]}` is", type: parent.type)
166
198
  else
167
- format(MSG,
168
- action: "`#{methods.join('`, `')}` are",
169
- type: parent.type)
199
+ format(MSG, action: "`#{methods.join('`, `')}` are", type: parent.type)
170
200
  end
171
201
  end
172
202
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks for calls to `link_to` that contain a
6
+ # Checks for calls to `link_to` that contain a
7
7
  # `target: '_blank'` but no `rel: 'noopener'`. This can be a security
8
8
  # risk as the loaded page will have control over the previous page
9
9
  # and could change its location for phishing purposes.
@@ -20,68 +20,67 @@ module RuboCop
20
20
  #
21
21
  # # good
22
22
  # link_to 'Click here', url, target: '_blank', rel: 'noreferrer'
23
- class LinkToBlank < Cop
23
+ class LinkToBlank < Base
24
+ extend AutoCorrector
25
+
24
26
  MSG = 'Specify a `:rel` option containing noopener.'
27
+ RESTRICT_ON_SEND = %i[link_to].freeze
25
28
 
26
- def_node_matcher :blank_target?, <<-PATTERN
29
+ def_node_matcher :blank_target?, <<~PATTERN
27
30
  (pair {(sym :target) (str "target")} {(str "_blank") (sym :_blank)})
28
31
  PATTERN
29
32
 
30
- def_node_matcher :includes_noopener?, <<-PATTERN
33
+ def_node_matcher :includes_noopener?, <<~PATTERN
31
34
  (pair {(sym :rel) (str "rel")} ({str sym} #contains_noopener?))
32
35
  PATTERN
33
36
 
34
- def_node_matcher :rel_node?, <<-PATTERN
37
+ def_node_matcher :rel_node?, <<~PATTERN
35
38
  (pair {(sym :rel) (str "rel")} (str _))
36
39
  PATTERN
37
40
 
38
41
  def on_send(node)
39
- return unless node.method?(:link_to)
40
-
41
42
  option_nodes = node.each_child_node(:hash)
42
43
 
43
44
  option_nodes.map(&:children).each do |options|
44
45
  blank = options.find { |o| blank_target?(o) }
45
- if blank && options.none? { |o| includes_noopener?(o) }
46
- add_offense(blank)
46
+ next unless blank && options.none? { |o| includes_noopener?(o) }
47
+
48
+ add_offense(blank) do |corrector|
49
+ autocorrect(corrector, node, blank, option_nodes)
47
50
  end
48
51
  end
49
52
  end
50
53
 
51
- def autocorrect(node)
52
- lambda do |corrector|
53
- send_node = node.parent.parent
54
+ private
54
55
 
55
- option_nodes = send_node.each_child_node(:hash)
56
- rel_node = nil
57
- option_nodes.map(&:children).each do |options|
58
- rel_node ||= options.find { |o| rel_node?(o) }
59
- end
56
+ def autocorrect(corrector, send_node, node, option_nodes)
57
+ rel_node = nil
58
+ option_nodes.map(&:children).each do |options|
59
+ rel_node ||= options.find { |o| rel_node?(o) }
60
+ end
60
61
 
61
- if rel_node
62
- append_to_rel(rel_node, corrector)
63
- else
64
- add_rel(send_node, node, corrector)
65
- end
62
+ if rel_node
63
+ append_to_rel(rel_node, corrector)
64
+ else
65
+ add_rel(send_node, node, corrector)
66
66
  end
67
67
  end
68
68
 
69
- private
70
-
71
69
  def append_to_rel(rel_node, corrector)
72
70
  existing_rel = rel_node.children.last.value
73
- str_range = rel_node.children.last.loc.expression.adjust(
74
- begin_pos: 1,
75
- end_pos: -1
76
- )
71
+ str_range = rel_node.children.last.source_range.adjust(begin_pos: 1, end_pos: -1)
77
72
  corrector.replace(str_range, "#{existing_rel} noopener")
78
73
  end
79
74
 
80
- def add_rel(send_node, offence_node, corrector)
81
- opening_quote = offence_node.children.last.source[0]
75
+ def add_rel(send_node, offense_node, corrector)
76
+ opening_quote = offense_node.children.last.source[0]
82
77
  closing_quote = opening_quote == ':' ? '' : opening_quote
83
78
  new_rel_exp = ", rel: #{opening_quote}noopener#{closing_quote}"
84
- range = send_node.arguments.last.source_range
79
+ range = if (last_argument = send_node.last_argument).hash_type?
80
+ last_argument.pairs.last.source_range
81
+ else
82
+ last_argument.source_range
83
+ end
85
84
 
86
85
  corrector.insert_after(range, new_rel_exp)
87
86
  end
@@ -89,7 +88,7 @@ module RuboCop
89
88
  def contains_noopener?(value)
90
89
  return false unless value
91
90
 
92
- rel_array = value.to_s.split(' ')
91
+ rel_array = value.to_s.split
93
92
  rel_array.include?('noopener') || rel_array.include?('noreferrer')
94
93
  end
95
94
  end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Enforces that mailer names end with `Mailer` suffix.
7
+ #
8
+ # Without the `Mailer` suffix it isn't immediately apparent what's a mailer
9
+ # and which views are related to the mailer.
10
+ #
11
+ # @safety
12
+ # This cop's autocorrection is unsafe because renaming a constant is
13
+ # always an unsafe operation.
14
+ #
15
+ # @example
16
+ # # bad
17
+ # class User < ActionMailer::Base
18
+ # end
19
+ #
20
+ # class User < ApplicationMailer
21
+ # end
22
+ #
23
+ # # good
24
+ # class UserMailer < ActionMailer::Base
25
+ # end
26
+ #
27
+ # class UserMailer < ApplicationMailer
28
+ # end
29
+ #
30
+ class MailerName < Base
31
+ extend AutoCorrector
32
+
33
+ MSG = 'Mailer should end with `Mailer` suffix.'
34
+
35
+ def_node_matcher :mailer_base_class?, <<~PATTERN
36
+ {
37
+ (const (const {nil? cbase} :ActionMailer) :Base)
38
+ (const {nil? cbase} :ApplicationMailer)
39
+ }
40
+ PATTERN
41
+
42
+ def_node_matcher :class_definition?, <<~PATTERN
43
+ (class $(const _ !#mailer_suffix?) #mailer_base_class? ...)
44
+ PATTERN
45
+
46
+ def_node_matcher :class_new_definition?, <<~PATTERN
47
+ (send (const {nil? cbase} :Class) :new #mailer_base_class?)
48
+ PATTERN
49
+
50
+ def on_class(node)
51
+ class_definition?(node) do |name_node|
52
+ add_offense(name_node) do |corrector|
53
+ autocorrect(corrector, name_node)
54
+ end
55
+ end
56
+ end
57
+
58
+ def on_send(node)
59
+ return unless class_new_definition?(node)
60
+
61
+ casgn_parent = node.each_ancestor(:casgn).first
62
+ return unless casgn_parent
63
+
64
+ name = casgn_parent.children[1]
65
+ return if mailer_suffix?(name)
66
+
67
+ add_offense(casgn_parent.loc.name) do |corrector|
68
+ autocorrect(corrector, casgn_parent)
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def autocorrect(corrector, node)
75
+ if node.casgn_type?
76
+ name = node.children[1]
77
+ corrector.replace(node.loc.name, "#{name}Mailer")
78
+ else
79
+ name = node.children.last
80
+ corrector.replace(node, "#{name}Mailer")
81
+ end
82
+ end
83
+
84
+ def mailer_suffix?(mailer_name)
85
+ mailer_name.to_s.end_with?('Mailer')
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end