rubocop-rails 2.25.1 → 2.32.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 +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +60 -8
- data/config/default.yml +103 -51
- data/lib/rubocop/cop/mixin/active_record_helper.rb +2 -2
- data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +2 -2
- data/lib/rubocop/cop/mixin/database_type_resolvable.rb +2 -2
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +6 -1
- data/lib/rubocop/cop/mixin/index_method.rb +69 -61
- data/lib/rubocop/cop/mixin/routes_helper.rb +20 -0
- data/lib/rubocop/cop/mixin/target_rails_version.rb +3 -5
- data/lib/rubocop/cop/rails/action_order.rb +1 -5
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +1 -5
- data/lib/rubocop/cop/rails/add_column_index.rb +1 -0
- data/lib/rubocop/cop/rails/application_record.rb +4 -0
- data/lib/rubocop/cop/rails/arel_star.rb +5 -5
- data/lib/rubocop/cop/rails/belongs_to.rb +1 -1
- data/lib/rubocop/cop/rails/blank.rb +1 -1
- data/lib/rubocop/cop/rails/bulk_change_table.rb +3 -2
- data/lib/rubocop/cop/rails/compact_blank.rb +29 -8
- data/lib/rubocop/cop/rails/content_tag.rb +1 -1
- data/lib/rubocop/cop/rails/dangerous_column_names.rb +2 -0
- data/lib/rubocop/cop/rails/date.rb +2 -2
- data/lib/rubocop/cop/rails/delegate.rb +53 -7
- data/lib/rubocop/cop/rails/duplicate_association.rb +8 -4
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +1 -3
- data/lib/rubocop/cop/rails/enum_hash.rb +31 -8
- data/lib/rubocop/cop/rails/enum_syntax.rb +130 -0
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +29 -7
- data/lib/rubocop/cop/rails/env_local.rb +26 -3
- data/lib/rubocop/cop/rails/file_path.rb +62 -10
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +7 -0
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +1 -1
- data/lib/rubocop/cop/rails/index_by.rb +37 -12
- data/lib/rubocop/cop/rails/index_with.rb +37 -12
- data/lib/rubocop/cop/rails/inquiry.rb +1 -1
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +11 -1
- data/lib/rubocop/cop/rails/match_route.rb +1 -9
- data/lib/rubocop/cop/rails/multiple_route_paths.rb +50 -0
- data/lib/rubocop/cop/rails/not_null_column.rb +6 -2
- data/lib/rubocop/cop/rails/output.rb +1 -2
- data/lib/rubocop/cop/rails/pluck.rb +30 -4
- data/lib/rubocop/cop/rails/pluck_in_where.rb +17 -8
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +30 -16
- data/lib/rubocop/cop/rails/presence.rb +1 -1
- data/lib/rubocop/cop/rails/present.rb +1 -3
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +1 -30
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +9 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +7 -2
- data/lib/rubocop/cop/rails/reflection_class_name.rb +3 -3
- data/lib/rubocop/cop/rails/relative_date_constant.rb +1 -1
- data/lib/rubocop/cop/rails/render_plain_text.rb +6 -3
- data/lib/rubocop/cop/rails/request_referer.rb +1 -1
- data/lib/rubocop/cop/rails/reversible_migration.rb +4 -1
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +21 -12
- data/lib/rubocop/cop/rails/save_bang.rb +8 -7
- data/lib/rubocop/cop/rails/schema_comment.rb +2 -1
- data/lib/rubocop/cop/rails/select_map.rb +3 -2
- data/lib/rubocop/cop/rails/skips_model_validations.rb +5 -3
- data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +1 -1
- data/lib/rubocop/cop/rails/strip_heredoc.rb +1 -1
- data/lib/rubocop/cop/rails/strong_parameters_expect.rb +104 -0
- data/lib/rubocop/cop/rails/three_state_boolean_column.rb +3 -2
- data/lib/rubocop/cop/rails/time_zone.rb +16 -7
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +7 -2
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +10 -33
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +1 -1
- data/lib/rubocop/cop/rails/validation.rb +1 -1
- data/lib/rubocop/cop/rails/where_equals.rb +28 -12
- data/lib/rubocop/cop/rails/where_not.rb +11 -6
- data/lib/rubocop/cop/rails/where_range.rb +7 -2
- data/lib/rubocop/cop/rails_cops.rb +4 -0
- data/lib/rubocop/rails/migration_file_skippable.rb +54 -0
- data/lib/rubocop/rails/plugin.rb +48 -0
- data/lib/rubocop/rails/version.rb +1 -1
- data/lib/rubocop/rails.rb +1 -8
- data/lib/rubocop-rails.rb +4 -5
- metadata +29 -12
- data/lib/rubocop/rails/inject.rb +0 -18
@@ -6,7 +6,72 @@ 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
|
-
|
9
|
+
# Internal helper class to hold match data
|
10
|
+
Captures = ::Struct.new(
|
11
|
+
:transformed_argname,
|
12
|
+
:transforming_body_expr
|
13
|
+
) do
|
14
|
+
def noop_transformation?
|
15
|
+
transforming_body_expr.lvar_type? && transforming_body_expr.children == [transformed_argname]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Internal helper class to hold autocorrect data
|
20
|
+
Autocorrection = ::Struct.new(:match, :block_node, :leading, :trailing) do
|
21
|
+
def self.from_each_with_object(node, match)
|
22
|
+
new(match, node, 0, 0)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.from_to_h(node, match)
|
26
|
+
new(match, node, 0, 0)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.from_map_to_h(node, match)
|
30
|
+
if node.block_literal?
|
31
|
+
strip_trailing_chars = 0
|
32
|
+
else
|
33
|
+
map_range = node.children.first.source_range
|
34
|
+
node_range = node.source_range
|
35
|
+
strip_trailing_chars = node_range.end_pos - map_range.end_pos
|
36
|
+
end
|
37
|
+
|
38
|
+
new(match, node.children.first, 0, strip_trailing_chars)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.from_hash_brackets_map(node, match)
|
42
|
+
new(match, node.children.last, "#{node.receiver.source}[".length, ']'.length)
|
43
|
+
end
|
44
|
+
|
45
|
+
def strip_prefix_and_suffix(node, corrector)
|
46
|
+
expression = node.source_range
|
47
|
+
corrector.remove_leading(expression, leading)
|
48
|
+
corrector.remove_trailing(expression, trailing)
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_new_method_name(new_method_name, corrector)
|
52
|
+
range = block_node.send_node.loc.selector
|
53
|
+
if (send_end = block_node.send_node.loc.end)
|
54
|
+
# If there are arguments (only true in the `each_with_object` case)
|
55
|
+
range = range.begin.join(send_end)
|
56
|
+
end
|
57
|
+
corrector.replace(range, new_method_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
def set_new_arg_name(transformed_argname, corrector)
|
61
|
+
return unless block_node.block_type?
|
62
|
+
|
63
|
+
corrector.replace(block_node.arguments, "|#{transformed_argname}|")
|
64
|
+
end
|
65
|
+
|
66
|
+
def set_new_body_expression(transforming_body_expr, corrector)
|
67
|
+
body_source = transforming_body_expr.source
|
68
|
+
body_source = "{ #{body_source} }" if transforming_body_expr.hash_type? && !transforming_body_expr.braces?
|
69
|
+
|
70
|
+
corrector.replace(block_node.body, body_source)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def on_block(node)
|
10
75
|
on_bad_each_with_object(node) do |*match|
|
11
76
|
handle_possible_offense(node, match, 'each_with_object')
|
12
77
|
end
|
@@ -18,6 +83,9 @@ module RuboCop
|
|
18
83
|
end
|
19
84
|
end
|
20
85
|
|
86
|
+
alias on_numblock on_block
|
87
|
+
alias on_itblock on_block
|
88
|
+
|
21
89
|
def on_send(node)
|
22
90
|
on_bad_map_to_h(node) do |*match|
|
23
91
|
handle_possible_offense(node, match, 'map { ... }.to_h')
|
@@ -100,66 +168,6 @@ module RuboCop
|
|
100
168
|
correction.set_new_arg_name(captures.transformed_argname, corrector)
|
101
169
|
correction.set_new_body_expression(captures.transforming_body_expr, corrector)
|
102
170
|
end
|
103
|
-
|
104
|
-
# Internal helper class to hold match data
|
105
|
-
Captures = ::Struct.new(
|
106
|
-
:transformed_argname,
|
107
|
-
:transforming_body_expr
|
108
|
-
) do
|
109
|
-
def noop_transformation?
|
110
|
-
transforming_body_expr.lvar_type? && transforming_body_expr.children == [transformed_argname]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
# Internal helper class to hold autocorrect data
|
115
|
-
Autocorrection = ::Struct.new(:match, :block_node, :leading, :trailing) do
|
116
|
-
def self.from_each_with_object(node, match)
|
117
|
-
new(match, node, 0, 0)
|
118
|
-
end
|
119
|
-
|
120
|
-
def self.from_to_h(node, match)
|
121
|
-
new(match, node, 0, 0)
|
122
|
-
end
|
123
|
-
|
124
|
-
def self.from_map_to_h(node, match)
|
125
|
-
strip_trailing_chars = 0
|
126
|
-
|
127
|
-
unless node.parent&.block_type?
|
128
|
-
map_range = node.children.first.source_range
|
129
|
-
node_range = node.source_range
|
130
|
-
strip_trailing_chars = node_range.end_pos - map_range.end_pos
|
131
|
-
end
|
132
|
-
|
133
|
-
new(match, node.children.first, 0, strip_trailing_chars)
|
134
|
-
end
|
135
|
-
|
136
|
-
def self.from_hash_brackets_map(node, match)
|
137
|
-
new(match, node.children.last, "#{node.receiver.source}[".length, ']'.length)
|
138
|
-
end
|
139
|
-
|
140
|
-
def strip_prefix_and_suffix(node, corrector)
|
141
|
-
expression = node.source_range
|
142
|
-
corrector.remove_leading(expression, leading)
|
143
|
-
corrector.remove_trailing(expression, trailing)
|
144
|
-
end
|
145
|
-
|
146
|
-
def set_new_method_name(new_method_name, corrector)
|
147
|
-
range = block_node.send_node.loc.selector
|
148
|
-
if (send_end = block_node.send_node.loc.end)
|
149
|
-
# If there are arguments (only true in the `each_with_object` case)
|
150
|
-
range = range.begin.join(send_end)
|
151
|
-
end
|
152
|
-
corrector.replace(range, new_method_name)
|
153
|
-
end
|
154
|
-
|
155
|
-
def set_new_arg_name(transformed_argname, corrector)
|
156
|
-
corrector.replace(block_node.arguments, "|#{transformed_argname}|")
|
157
|
-
end
|
158
|
-
|
159
|
-
def set_new_body_expression(transforming_body_expr, corrector)
|
160
|
-
corrector.replace(block_node.body, transforming_body_expr.source)
|
161
|
-
end
|
162
|
-
end
|
163
171
|
end
|
164
172
|
end
|
165
173
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# Common functionality for cops working with routes.
|
6
|
+
module RoutesHelper
|
7
|
+
extend NodePattern::Macros
|
8
|
+
|
9
|
+
HTTP_METHODS = %i[get post put patch delete].freeze
|
10
|
+
|
11
|
+
def_node_matcher :routes_draw?, <<~PATTERN
|
12
|
+
(send (send _ :routes) :draw)
|
13
|
+
PATTERN
|
14
|
+
|
15
|
+
def within_routes?(node)
|
16
|
+
node.each_ancestor(:block).any? { |block| routes_draw?(block.send_node) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -7,6 +7,9 @@ module RuboCop
|
|
7
7
|
# Informs the base RuboCop gem that it the Rails version is checked via `requires_gem` API,
|
8
8
|
# without needing to call this `#support_target_rails_version` method.
|
9
9
|
USES_REQUIRES_GEM_API = true
|
10
|
+
# Look for `railties` instead of `rails`, to support apps that only use a subset of `rails`
|
11
|
+
# See https://github.com/rubocop/rubocop/pull/11289
|
12
|
+
TARGET_GEM_NAME = 'railties' # :nodoc:
|
10
13
|
|
11
14
|
def minimum_target_rails_version(version)
|
12
15
|
if respond_to?(:requires_gem)
|
@@ -33,11 +36,6 @@ module RuboCop
|
|
33
36
|
@minimum_target_rails_version <= version
|
34
37
|
end
|
35
38
|
end
|
36
|
-
|
37
|
-
# Look for `railties` instead of `rails`, to support apps that only use a subset of `rails`
|
38
|
-
# See https://github.com/rubocop/rubocop/pull/11289
|
39
|
-
TARGET_GEM_NAME = 'railties'
|
40
|
-
private_constant :TARGET_GEM_NAME
|
41
39
|
end
|
42
40
|
end
|
43
41
|
end
|
@@ -92,11 +92,7 @@ module RuboCop
|
|
92
92
|
end
|
93
93
|
|
94
94
|
def range_with_comments(node)
|
95
|
-
|
96
|
-
# Using `RuboCop::Ext::Comment#source_range` requires RuboCop > 1.46,
|
97
|
-
# which introduces https://github.com/rubocop/rubocop/pull/11630.
|
98
|
-
ranges = [node, *processed_source.ast_with_comments[node]].map { |comment| comment.loc.expression }
|
99
|
-
# rubocop:enable InternalAffairs/LocationExpression
|
95
|
+
ranges = [node, *processed_source.ast_with_comments[node]].map(&:source_range)
|
100
96
|
ranges.reduce do |result, range|
|
101
97
|
add_range(result, range)
|
102
98
|
end
|
@@ -123,11 +123,7 @@ module RuboCop
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def inline_comment?(comment)
|
126
|
-
|
127
|
-
# Using `RuboCop::Ext::Comment#source_range` requires RuboCop > 1.46,
|
128
|
-
# which introduces https://github.com/rubocop/rubocop/pull/11630.
|
129
|
-
!comment_line?(comment.loc.expression.source_line)
|
130
|
-
# rubocop:enable InternalAffairs/LocationExpression
|
126
|
+
!comment_line?(comment.source_range.source_line)
|
131
127
|
end
|
132
128
|
|
133
129
|
def start_line_position(node)
|
@@ -5,6 +5,10 @@ module RuboCop
|
|
5
5
|
module Rails
|
6
6
|
# Checks that models subclass `ApplicationRecord` with Rails 5.0.
|
7
7
|
#
|
8
|
+
# It is a common practice to define models inside migrations in order to retain forward
|
9
|
+
# compatibility by avoiding loading any application code. And so migration files are excluded
|
10
|
+
# by default for this cop.
|
11
|
+
#
|
8
12
|
# @safety
|
9
13
|
# This cop's autocorrection is unsafe because it may let the logic from `ApplicationRecord`
|
10
14
|
# sneak into an Active Record model that is not purposed to inherit logic common among other
|
@@ -5,14 +5,14 @@ module RuboCop
|
|
5
5
|
module Rails
|
6
6
|
# Prevents usage of `"*"` on an Arel::Table column reference.
|
7
7
|
#
|
8
|
-
# Using `arel_table["
|
9
|
-
# quoted asterisk (e.g.
|
10
|
-
# database to look for a column named
|
8
|
+
# Using `arel_table["\*"]` causes the outputted string to be a literal
|
9
|
+
# quoted asterisk (e.g. `my_model`.`*`). This causes the
|
10
|
+
# database to look for a column named `\*` (or `"*"`) as opposed
|
11
11
|
# to expanding the column list as one would likely expect.
|
12
12
|
#
|
13
13
|
# @safety
|
14
|
-
# This cop's autocorrection is unsafe because it turns a quoted
|
15
|
-
# an SQL `*`, unquoted.
|
14
|
+
# This cop's autocorrection is unsafe because it turns a quoted `\*` into
|
15
|
+
# an SQL `*`, unquoted. `\*` is a valid column name in certain databases
|
16
16
|
# supported by Rails, and even though it is usually a mistake,
|
17
17
|
# it might denote legitimate access to a column named `*`.
|
18
18
|
#
|
@@ -123,7 +123,7 @@ module RuboCop
|
|
123
123
|
def on_if(node)
|
124
124
|
return unless cop_config['UnlessPresent']
|
125
125
|
return unless node.unless?
|
126
|
-
return if node.else? && config.
|
126
|
+
return if node.else? && config.cop_enabled?('Style/UnlessElse')
|
127
127
|
|
128
128
|
unless_present?(node) do |method_call, receiver|
|
129
129
|
range = unless_condition(node, method_call)
|
@@ -65,6 +65,7 @@ module RuboCop
|
|
65
65
|
# end
|
66
66
|
class BulkChangeTable < Base
|
67
67
|
include DatabaseTypeResolvable
|
68
|
+
include MigrationsHelper
|
68
69
|
|
69
70
|
MSG_FOR_CHANGE_TABLE = <<~MSG.chomp
|
70
71
|
You can combine alter queries using `bulk: true` options.
|
@@ -140,9 +141,9 @@ module RuboCop
|
|
140
141
|
return unless support_bulk_alter?
|
141
142
|
return unless node.command?(:change_table)
|
142
143
|
return if include_bulk_options?(node)
|
143
|
-
return unless node.block_node
|
144
|
+
return unless (body = node.block_node&.body)
|
144
145
|
|
145
|
-
send_nodes = send_nodes_from_change_table_block(
|
146
|
+
send_nodes = send_nodes_from_change_table_block(body)
|
146
147
|
|
147
148
|
add_offense_for_change_table(node) if count_transformations(send_nodes) > 1
|
148
149
|
end
|
@@ -16,7 +16,6 @@ module RuboCop
|
|
16
16
|
# And `compact_blank!` has different implementations for `Array`, `Hash`, and
|
17
17
|
# `ActionController::Parameters`.
|
18
18
|
# `Array#compact_blank!`, `Hash#compact_blank!` are equivalent to `delete_if(&:blank?)`.
|
19
|
-
# `ActionController::Parameters#compact_blank!` is equivalent to `reject!(&:blank?)`.
|
20
19
|
# If the cop makes a mistake, autocorrected code may get unexpected behavior.
|
21
20
|
#
|
22
21
|
# @example
|
@@ -24,6 +23,10 @@ module RuboCop
|
|
24
23
|
# # bad
|
25
24
|
# collection.reject(&:blank?)
|
26
25
|
# collection.reject { |_k, v| v.blank? }
|
26
|
+
# collection.select(&:present?)
|
27
|
+
# collection.select { |_k, v| v.present? }
|
28
|
+
# collection.filter(&:present?)
|
29
|
+
# collection.filter { |_k, v| v.present? }
|
27
30
|
#
|
28
31
|
# # good
|
29
32
|
# collection.compact_blank
|
@@ -31,8 +34,8 @@ module RuboCop
|
|
31
34
|
# # bad
|
32
35
|
# collection.delete_if(&:blank?) # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
|
33
36
|
# collection.delete_if { |_k, v| v.blank? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
|
34
|
-
# collection.
|
35
|
-
# collection.
|
37
|
+
# collection.keep_if(&:present?) # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
|
38
|
+
# collection.keep_if { |_k, v| v.present? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
|
36
39
|
#
|
37
40
|
# # good
|
38
41
|
# collection.compact_blank!
|
@@ -43,25 +46,41 @@ module RuboCop
|
|
43
46
|
extend TargetRailsVersion
|
44
47
|
|
45
48
|
MSG = 'Use `%<preferred_method>s` instead.'
|
46
|
-
RESTRICT_ON_SEND = %i[reject delete_if
|
49
|
+
RESTRICT_ON_SEND = %i[reject delete_if select filter keep_if].freeze
|
50
|
+
DESTRUCTIVE_METHODS = %i[delete_if keep_if].freeze
|
47
51
|
|
48
52
|
minimum_target_rails_version 6.1
|
49
53
|
|
50
54
|
def_node_matcher :reject_with_block?, <<~PATTERN
|
51
55
|
(block
|
52
|
-
(send _ {:reject :delete_if
|
56
|
+
(send _ {:reject :delete_if})
|
53
57
|
$(args ...)
|
54
58
|
(send
|
55
59
|
$(lvar _) :blank?))
|
56
60
|
PATTERN
|
57
61
|
|
58
62
|
def_node_matcher :reject_with_block_pass?, <<~PATTERN
|
59
|
-
(send _ {:reject :delete_if
|
63
|
+
(send _ {:reject :delete_if}
|
60
64
|
(block_pass
|
61
65
|
(sym :blank?)))
|
62
66
|
PATTERN
|
63
67
|
|
68
|
+
def_node_matcher :select_with_block?, <<~PATTERN
|
69
|
+
(block
|
70
|
+
(send _ {:select :filter :keep_if})
|
71
|
+
$(args ...)
|
72
|
+
(send
|
73
|
+
$(lvar _) :present?))
|
74
|
+
PATTERN
|
75
|
+
|
76
|
+
def_node_matcher :select_with_block_pass?, <<~PATTERN
|
77
|
+
(send _ {:select :filter :keep_if}
|
78
|
+
(block-pass
|
79
|
+
(sym :present?)))
|
80
|
+
PATTERN
|
81
|
+
|
64
82
|
def on_send(node)
|
83
|
+
return if target_ruby_version < 2.6 && node.method?(:filter)
|
65
84
|
return unless bad_method?(node)
|
66
85
|
|
67
86
|
range = offense_range(node)
|
@@ -75,8 +94,10 @@ module RuboCop
|
|
75
94
|
|
76
95
|
def bad_method?(node)
|
77
96
|
return true if reject_with_block_pass?(node)
|
97
|
+
return true if select_with_block_pass?(node)
|
78
98
|
|
79
|
-
|
99
|
+
arguments, receiver_in_block = reject_with_block?(node.parent) || select_with_block?(node.parent)
|
100
|
+
if arguments
|
80
101
|
return use_single_value_block_argument?(arguments, receiver_in_block) ||
|
81
102
|
use_hash_value_block_argument?(arguments, receiver_in_block)
|
82
103
|
end
|
@@ -103,7 +124,7 @@ module RuboCop
|
|
103
124
|
end
|
104
125
|
|
105
126
|
def preferred_method(node)
|
106
|
-
|
127
|
+
DESTRUCTIVE_METHODS.include?(node.method_name) ? 'compact_blank!' : 'compact_blank'
|
107
128
|
end
|
108
129
|
end
|
109
130
|
end
|
@@ -12,10 +12,10 @@ module RuboCop
|
|
12
12
|
# The cop also reports warnings when you are using `to_time` method,
|
13
13
|
# because it doesn't know about Rails time zone either.
|
14
14
|
#
|
15
|
-
# Two styles are supported for this cop. When `EnforcedStyle` is
|
15
|
+
# Two styles are supported for this cop. When `EnforcedStyle` is `strict`
|
16
16
|
# then the Date methods `today`, `current`, `yesterday`, and `tomorrow`
|
17
17
|
# are prohibited and the usage of both `to_time`
|
18
|
-
# and
|
18
|
+
# and `to_time_in_current_zone` are reported as warning.
|
19
19
|
#
|
20
20
|
# When `EnforcedStyle` is `flexible` then only `Date.today` is prohibited.
|
21
21
|
#
|
@@ -15,6 +15,9 @@ module RuboCop
|
|
15
15
|
# without using the `delegate` method will be a violation.
|
16
16
|
# When set to `false`, this case is legal.
|
17
17
|
#
|
18
|
+
# It is disabled for controllers in order to keep controller actions
|
19
|
+
# explicitly defined.
|
20
|
+
#
|
18
21
|
# @example
|
19
22
|
# # bad
|
20
23
|
# def bar
|
@@ -68,12 +71,13 @@ module RuboCop
|
|
68
71
|
|
69
72
|
def_node_matcher :delegate?, <<~PATTERN
|
70
73
|
(def _method_name _args
|
71
|
-
(send {(send nil? _) (self)} _ ...))
|
74
|
+
(send {(send nil? _) (self) (send (self) :class) ({cvar gvar ivar} _) (const _ _)} _ ...))
|
72
75
|
PATTERN
|
73
76
|
|
74
77
|
def on_def(node)
|
75
78
|
return unless trivial_delegate?(node)
|
76
79
|
return if private_or_protected_delegation(node)
|
80
|
+
return if module_function_declared?(node)
|
77
81
|
|
78
82
|
register_offense(node)
|
79
83
|
end
|
@@ -82,15 +86,40 @@ module RuboCop
|
|
82
86
|
|
83
87
|
def register_offense(node)
|
84
88
|
add_offense(node.loc.keyword) do |corrector|
|
85
|
-
|
89
|
+
receiver = determine_register_offense_receiver(node.body.receiver)
|
90
|
+
delegation = build_delegation(node, receiver)
|
86
91
|
|
87
|
-
|
92
|
+
corrector.replace(node, delegation)
|
93
|
+
end
|
94
|
+
end
|
88
95
|
|
89
|
-
|
90
|
-
|
96
|
+
def determine_register_offense_receiver(receiver)
|
97
|
+
case receiver.type
|
98
|
+
when :self
|
99
|
+
'self'
|
100
|
+
when :const
|
101
|
+
full_name = full_const_name(receiver)
|
102
|
+
full_name.include?('::') ? ":'#{full_name}'" : ":#{full_name}"
|
103
|
+
when :cvar, :gvar, :ivar
|
104
|
+
":#{receiver.source}"
|
105
|
+
else
|
106
|
+
":#{receiver.method_name}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def build_delegation(node, receiver)
|
111
|
+
delegation = ["delegate :#{node.body.method_name}", "to: #{receiver}"]
|
112
|
+
delegation << ['prefix: true'] if node.method?(prefixed_method_name(node.body))
|
113
|
+
delegation.join(', ')
|
114
|
+
end
|
91
115
|
|
92
|
-
|
116
|
+
def full_const_name(node)
|
117
|
+
return unless node.const_type?
|
118
|
+
unless node.namespace
|
119
|
+
return node.absolute? ? "::#{node.source}" : node.source
|
93
120
|
end
|
121
|
+
|
122
|
+
"#{full_const_name(node.namespace)}::#{node.short_name}"
|
94
123
|
end
|
95
124
|
|
96
125
|
def trivial_delegate?(def_node)
|
@@ -120,13 +149,30 @@ module RuboCop
|
|
120
149
|
def prefixed_method_name(body)
|
121
150
|
return '' if body.receiver.self_type?
|
122
151
|
|
123
|
-
[body.receiver
|
152
|
+
[determine_prefixed_method_receiver_name(body.receiver), body.method_name].join('_').to_sym
|
153
|
+
end
|
154
|
+
|
155
|
+
def determine_prefixed_method_receiver_name(receiver)
|
156
|
+
case receiver.type
|
157
|
+
when :cvar, :gvar, :ivar
|
158
|
+
receiver.source
|
159
|
+
when :const
|
160
|
+
full_const_name(receiver)
|
161
|
+
else
|
162
|
+
receiver.method_name.to_s
|
163
|
+
end
|
124
164
|
end
|
125
165
|
|
126
166
|
def private_or_protected_delegation(node)
|
127
167
|
private_or_protected_inline(node) || node_visibility(node) != :public
|
128
168
|
end
|
129
169
|
|
170
|
+
def module_function_declared?(node)
|
171
|
+
node.each_ancestor(:module, :begin).any? do |ancestor|
|
172
|
+
ancestor.children.any? { |child| child.send_type? && child.method?(:module_function) }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
130
176
|
def private_or_protected_inline(node)
|
131
177
|
processed_source[node.first_line - 1].strip.match?(/\A(private )|(protected )/)
|
132
178
|
end
|
@@ -63,11 +63,15 @@ module RuboCop
|
|
63
63
|
private
|
64
64
|
|
65
65
|
def register_offense(name, nodes, message_template)
|
66
|
-
nodes.
|
67
|
-
add_offense(node, message: format(message_template, name: name)) do |corrector|
|
68
|
-
next if same_line?(nodes.last, node)
|
66
|
+
last_node = nodes.last
|
69
67
|
|
70
|
-
|
68
|
+
nodes.each_with_index do |node, index|
|
69
|
+
add_offense(node, message: format(message_template, name: name)) do |corrector|
|
70
|
+
if index.zero?
|
71
|
+
corrector.replace(node, last_node.source)
|
72
|
+
else
|
73
|
+
corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
|
74
|
+
end
|
71
75
|
end
|
72
76
|
end
|
73
77
|
end
|
@@ -45,12 +45,10 @@ module RuboCop
|
|
45
45
|
return if node.parent&.block_type?
|
46
46
|
|
47
47
|
interpolated_string_passed_to_debug(node) do |arguments|
|
48
|
-
message = format(MSG)
|
49
|
-
|
50
48
|
range = replacement_range(node)
|
51
49
|
replacement = replacement_source(node, arguments)
|
52
50
|
|
53
|
-
add_offense(range
|
51
|
+
add_offense(range) do |corrector|
|
54
52
|
corrector.replace(range, replacement)
|
55
53
|
end
|
56
54
|
end
|
@@ -12,6 +12,12 @@ module RuboCop
|
|
12
12
|
#
|
13
13
|
# @example
|
14
14
|
# # bad
|
15
|
+
# enum :status, [:active, :archived]
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# enum :status, { active: 0, archived: 1 }
|
19
|
+
#
|
20
|
+
# # bad
|
15
21
|
# enum status: [:active, :archived]
|
16
22
|
#
|
17
23
|
# # good
|
@@ -23,7 +29,11 @@ module RuboCop
|
|
23
29
|
MSG = 'Enum defined as an array found in `%<enum>s` enum declaration. Use hash syntax instead.'
|
24
30
|
RESTRICT_ON_SEND = %i[enum].freeze
|
25
31
|
|
26
|
-
def_node_matcher :
|
32
|
+
def_node_matcher :enum_with_array?, <<~PATTERN
|
33
|
+
(send nil? :enum $_ ${array} ...)
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def_node_matcher :enum_with_old_syntax?, <<~PATTERN
|
27
37
|
(send nil? :enum (hash $...))
|
28
38
|
PATTERN
|
29
39
|
|
@@ -32,17 +42,19 @@ module RuboCop
|
|
32
42
|
PATTERN
|
33
43
|
|
34
44
|
def on_send(node)
|
35
|
-
|
45
|
+
target_rails_version >= 7.0 && enum_with_array?(node) do |key, array|
|
46
|
+
add_offense(array, message: message(key)) do |corrector|
|
47
|
+
corrector.replace(array, build_hash(array))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
enum_with_old_syntax?(node) do |pairs|
|
36
52
|
pairs.each do |pair|
|
37
53
|
key, array = array_pair?(pair)
|
38
54
|
next unless key
|
39
55
|
|
40
|
-
add_offense(array, message:
|
41
|
-
|
42
|
-
"#{source(elem)} => #{index}"
|
43
|
-
end.join(', ')
|
44
|
-
|
45
|
-
corrector.replace(array, "{#{hash}}")
|
56
|
+
add_offense(array, message: message(key)) do |corrector|
|
57
|
+
corrector.replace(array, build_hash(array))
|
46
58
|
end
|
47
59
|
end
|
48
60
|
end
|
@@ -50,6 +62,10 @@ module RuboCop
|
|
50
62
|
|
51
63
|
private
|
52
64
|
|
65
|
+
def message(key)
|
66
|
+
format(MSG, enum: enum_name(key))
|
67
|
+
end
|
68
|
+
|
53
69
|
def enum_name(key)
|
54
70
|
case key.type
|
55
71
|
when :sym, :str
|
@@ -69,6 +85,13 @@ module RuboCop
|
|
69
85
|
elem.source
|
70
86
|
end
|
71
87
|
end
|
88
|
+
|
89
|
+
def build_hash(array)
|
90
|
+
hash = array.children.each_with_index.map do |elem, index|
|
91
|
+
"#{source(elem)} => #{index}"
|
92
|
+
end.join(', ')
|
93
|
+
"{#{hash}}"
|
94
|
+
end
|
72
95
|
end
|
73
96
|
end
|
74
97
|
end
|