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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +52 -5
- data/config/default.yml +726 -32
- data/config/obsoletion.yml +17 -0
- data/lib/rubocop/cop/mixin/active_record_helper.rb +106 -0
- data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +32 -0
- data/lib/rubocop/cop/mixin/class_send_node_helper.rb +20 -0
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +40 -0
- data/lib/rubocop/cop/mixin/index_method.rb +165 -0
- data/lib/rubocop/cop/mixin/migrations_helper.rb +26 -0
- data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +112 -0
- data/lib/rubocop/cop/rails/action_controller_test_case.rb +47 -0
- data/lib/rubocop/cop/rails/action_filter.rb +11 -21
- data/lib/rubocop/cop/rails/action_order.rb +116 -0
- data/lib/rubocop/cop/rails/active_record_aliases.rb +23 -24
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +143 -0
- data/lib/rubocop/cop/rails/active_record_override.rb +3 -6
- data/lib/rubocop/cop/rails/active_support_aliases.rb +13 -22
- data/lib/rubocop/cop/rails/active_support_on_load.rb +70 -0
- data/lib/rubocop/cop/rails/add_column_index.rb +61 -0
- data/lib/rubocop/cop/rails/after_commit_override.rb +81 -0
- data/lib/rubocop/cop/rails/application_controller.rb +36 -0
- data/lib/rubocop/cop/rails/application_job.rb +9 -4
- data/lib/rubocop/cop/rails/application_mailer.rb +39 -0
- data/lib/rubocop/cop/rails/application_record.rb +9 -9
- data/lib/rubocop/cop/rails/arel_star.rb +47 -0
- data/lib/rubocop/cop/rails/assert_not.rb +8 -10
- data/lib/rubocop/cop/rails/attribute_default_block_value.rb +90 -0
- data/lib/rubocop/cop/rails/belongs_to.rb +12 -24
- data/lib/rubocop/cop/rails/blank.rb +40 -36
- data/lib/rubocop/cop/rails/bulk_change_table.rb +40 -35
- data/lib/rubocop/cop/rails/compact_blank.rb +111 -0
- data/lib/rubocop/cop/rails/content_tag.rb +93 -0
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +22 -15
- data/lib/rubocop/cop/rails/date.rb +41 -36
- data/lib/rubocop/cop/rails/default_scope.rb +61 -0
- data/lib/rubocop/cop/rails/delegate.rb +33 -29
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +9 -10
- data/lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb +168 -0
- data/lib/rubocop/cop/rails/dot_separated_keys.rb +71 -0
- data/lib/rubocop/cop/rails/duplicate_association.rb +56 -0
- data/lib/rubocop/cop/rails/duplicate_scope.rb +46 -0
- data/lib/rubocop/cop/rails/duration_arithmetic.rb +98 -0
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +76 -31
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +82 -0
- data/lib/rubocop/cop/rails/enum_hash.rb +75 -0
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +30 -12
- data/lib/rubocop/cop/rails/environment_comparison.rb +70 -22
- data/lib/rubocop/cop/rails/environment_variable_access.rb +67 -0
- data/lib/rubocop/cop/rails/exit.rb +7 -13
- data/lib/rubocop/cop/rails/expanded_date_range.rb +102 -0
- data/lib/rubocop/cop/rails/file_path.rb +48 -31
- data/lib/rubocop/cop/rails/find_by.rb +43 -24
- data/lib/rubocop/cop/rails/find_by_id.rb +94 -0
- data/lib/rubocop/cop/rails/find_each.rb +42 -18
- data/lib/rubocop/cop/rails/freeze_time.rb +79 -0
- data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +4 -3
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +62 -25
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +32 -4
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +61 -32
- data/lib/rubocop/cop/rails/http_status.rb +27 -23
- data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +96 -0
- data/lib/rubocop/cop/rails/i18n_locale_assignment.rb +37 -0
- data/lib/rubocop/cop/rails/i18n_locale_texts.rb +110 -0
- data/lib/rubocop/cop/rails/ignored_columns_assignment.rb +50 -0
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +9 -16
- data/lib/rubocop/cop/rails/index_by.rb +65 -0
- data/lib/rubocop/cop/rails/index_with.rb +68 -0
- data/lib/rubocop/cop/rails/inquiry.rb +39 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +33 -27
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +62 -32
- data/lib/rubocop/cop/rails/link_to_blank.rb +31 -32
- data/lib/rubocop/cop/rails/mailer_name.rb +90 -0
- data/lib/rubocop/cop/rails/match_route.rb +120 -0
- data/lib/rubocop/cop/rails/migration_class_name.rb +63 -0
- data/lib/rubocop/cop/rails/negate_include.rb +42 -0
- data/lib/rubocop/cop/rails/not_null_column.rb +16 -12
- data/lib/rubocop/cop/rails/order_by_id.rb +51 -0
- data/lib/rubocop/cop/rails/output.rb +29 -10
- data/lib/rubocop/cop/rails/output_safety.rb +9 -4
- data/lib/rubocop/cop/rails/pick.rb +64 -0
- data/lib/rubocop/cop/rails/pluck.rb +96 -0
- data/lib/rubocop/cop/rails/pluck_id.rb +59 -0
- data/lib/rubocop/cop/rails/pluck_in_where.rb +71 -0
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +14 -19
- data/lib/rubocop/cop/rails/presence.rb +54 -26
- data/lib/rubocop/cop/rails/present.rb +40 -37
- data/lib/rubocop/cop/rails/rake_environment.rb +112 -0
- data/lib/rubocop/cop/rails/read_write_attribute.rb +56 -18
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +33 -45
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +77 -0
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +257 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +34 -32
- data/lib/rubocop/cop/rails/redundant_travel_back.rb +57 -0
- data/lib/rubocop/cop/rails/reflection_class_name.rb +56 -7
- data/lib/rubocop/cop/rails/refute_methods.rb +56 -35
- data/lib/rubocop/cop/rails/relative_date_constant.rb +52 -33
- data/lib/rubocop/cop/rails/render_inline.rb +41 -0
- data/lib/rubocop/cop/rails/render_plain_text.rb +71 -0
- data/lib/rubocop/cop/rails/request_referer.rb +10 -11
- data/lib/rubocop/cop/rails/require_dependency.rb +38 -0
- data/lib/rubocop/cop/rails/response_parsed_body.rb +57 -0
- data/lib/rubocop/cop/rails/reversible_migration.rb +122 -82
- data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +66 -0
- data/lib/rubocop/cop/rails/root_join_chain.rb +72 -0
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +238 -0
- data/lib/rubocop/cop/rails/root_public_path.rb +59 -0
- data/lib/rubocop/cop/rails/safe_navigation.rb +55 -43
- data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +50 -0
- data/lib/rubocop/cop/rails/save_bang.rb +89 -63
- data/lib/rubocop/cop/rails/schema_comment.rb +104 -0
- data/lib/rubocop/cop/rails/scope_args.rb +8 -3
- data/lib/rubocop/cop/rails/short_i18n.rb +71 -0
- data/lib/rubocop/cop/rails/skips_model_validations.rb +53 -16
- data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +87 -0
- data/lib/rubocop/cop/rails/strip_heredoc.rb +56 -0
- data/lib/rubocop/cop/rails/table_name_assignment.rb +44 -0
- data/lib/rubocop/cop/rails/three_state_boolean_column.rb +73 -0
- data/lib/rubocop/cop/rails/time_zone.rb +83 -67
- data/lib/rubocop/cop/rails/time_zone_assignment.rb +37 -0
- data/lib/rubocop/cop/rails/to_formatted_s.rb +46 -0
- data/lib/rubocop/cop/rails/to_s_with_argument.rb +78 -0
- data/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb +49 -0
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +99 -0
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +40 -49
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +172 -0
- data/lib/rubocop/cop/rails/unknown_env.rb +52 -21
- data/lib/rubocop/cop/rails/unused_ignored_columns.rb +76 -0
- data/lib/rubocop/cop/rails/validation.rb +54 -23
- data/lib/rubocop/cop/rails/where_equals.rb +102 -0
- data/lib/rubocop/cop/rails/where_exists.rb +138 -0
- data/lib/rubocop/cop/rails/where_missing.rb +118 -0
- data/lib/rubocop/cop/rails/where_not.rb +101 -0
- data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +55 -0
- data/lib/rubocop/cop/rails_cops.rb +78 -8
- data/lib/rubocop/rails/inject.rb +1 -1
- data/lib/rubocop/rails/schema_loader/schema.rb +191 -0
- data/lib/rubocop/rails/schema_loader.rb +61 -0
- data/lib/rubocop/rails/version.rb +5 -1
- data/lib/rubocop/rails.rb +3 -1
- data/lib/rubocop-rails.rb +22 -0
- metadata +120 -19
- 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
|
-
#
|
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
|
-
# @
|
130
|
-
#
|
131
|
-
class
|
132
|
-
|
133
|
-
|
134
|
-
|
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
|
-
|
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,
|
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,
|
149
|
+
def_node_matcher :options_from_argument, <<~PATTERN
|
145
150
|
(hash $...)
|
146
151
|
PATTERN
|
147
152
|
|
148
|
-
def_node_matcher :conditions_option?,
|
153
|
+
def_node_matcher :conditions_option?, <<~PATTERN
|
149
154
|
(pair (sym :conditions) !nil)
|
150
155
|
PATTERN
|
151
156
|
|
152
|
-
def_node_matcher :through_option?,
|
157
|
+
def_node_matcher :through_option?, <<~PATTERN
|
153
158
|
(pair (sym :through) !nil)
|
154
159
|
PATTERN
|
155
160
|
|
156
|
-
def_node_matcher :polymorphic_option?,
|
161
|
+
def_node_matcher :polymorphic_option?, <<~PATTERN
|
157
162
|
(pair (sym :polymorphic) !nil)
|
158
163
|
PATTERN
|
159
164
|
|
160
|
-
def_node_matcher :as_option?,
|
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?,
|
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?,
|
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?,
|
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)
|
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
|
-
#
|
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
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
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
|
-
#
|
15
|
-
#
|
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
|
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 <
|
86
|
+
class LexicallyScopedActionFilter < Base
|
86
87
|
MSG = '%<action>s not explicitly defined on the %<type>s.'
|
87
88
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
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
|
-
|
128
|
+
defined_action_methods = defined_action_methods(block)
|
129
|
+
|
126
130
|
methods = array_values(methods_node).reject do |method|
|
127
|
-
|
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
|
-
#
|
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 <
|
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?,
|
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?,
|
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?,
|
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
|
-
|
46
|
-
|
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
|
-
|
52
|
-
lambda do |corrector|
|
53
|
-
send_node = node.parent.parent
|
54
|
+
private
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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.
|
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,
|
81
|
-
opening_quote =
|
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.
|
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
|