rubocop-rails 2.14.2 → 2.19.1
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 +23 -2
- data/config/default.yml +190 -12
- data/config/obsoletion.yml +10 -0
- data/lib/rubocop/cop/mixin/active_record_helper.rb +3 -6
- data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +1 -3
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +1 -1
- data/lib/rubocop/cop/mixin/index_method.rb +7 -17
- data/lib/rubocop/cop/mixin/migrations_helper.rb +1 -1
- data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +112 -0
- data/lib/rubocop/cop/rails/action_controller_test_case.rb +2 -2
- data/lib/rubocop/cop/rails/action_filter.rb +2 -2
- data/lib/rubocop/cop/rails/action_order.rb +116 -0
- data/lib/rubocop/cop/rails/active_record_aliases.rb +3 -4
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +7 -4
- data/lib/rubocop/cop/rails/active_record_override.rb +2 -5
- data/lib/rubocop/cop/rails/active_support_aliases.rb +1 -1
- data/lib/rubocop/cop/rails/active_support_on_load.rb +70 -0
- data/lib/rubocop/cop/rails/add_column_index.rb +3 -6
- data/lib/rubocop/cop/rails/after_commit_override.rb +1 -1
- data/lib/rubocop/cop/rails/application_controller.rb +2 -2
- data/lib/rubocop/cop/rails/application_job.rb +3 -3
- data/lib/rubocop/cop/rails/application_mailer.rb +2 -2
- data/lib/rubocop/cop/rails/application_record.rb +2 -2
- data/lib/rubocop/cop/rails/arel_star.rb +2 -2
- data/lib/rubocop/cop/rails/assert_not.rb +1 -1
- data/lib/rubocop/cop/rails/attribute_default_block_value.rb +1 -1
- data/lib/rubocop/cop/rails/belongs_to.rb +2 -5
- data/lib/rubocop/cop/rails/blank.rb +10 -11
- data/lib/rubocop/cop/rails/bulk_change_table.rb +8 -25
- data/lib/rubocop/cop/rails/compact_blank.rb +6 -2
- data/lib/rubocop/cop/rails/content_tag.rb +6 -7
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +16 -3
- data/lib/rubocop/cop/rails/date.rb +12 -17
- data/lib/rubocop/cop/rails/default_scope.rb +1 -1
- data/lib/rubocop/cop/rails/delegate.rb +24 -18
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +2 -2
- data/lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb +63 -3
- data/lib/rubocop/cop/rails/dot_separated_keys.rb +71 -0
- data/lib/rubocop/cop/rails/duplicate_association.rb +2 -2
- data/lib/rubocop/cop/rails/duplicate_scope.rb +1 -1
- data/lib/rubocop/cop/rails/duration_arithmetic.rb +4 -4
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +26 -14
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +6 -2
- data/lib/rubocop/cop/rails/enum_hash.rb +3 -4
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +3 -6
- data/lib/rubocop/cop/rails/environment_comparison.rb +3 -4
- data/lib/rubocop/cop/rails/environment_variable_access.rb +1 -1
- data/lib/rubocop/cop/rails/exit.rb +1 -1
- data/lib/rubocop/cop/rails/expanded_date_range.rb +39 -23
- data/lib/rubocop/cop/rails/file_path.rb +41 -24
- data/lib/rubocop/cop/rails/find_by.rb +1 -1
- data/lib/rubocop/cop/rails/find_by_id.rb +3 -3
- data/lib/rubocop/cop/rails/find_each.rb +14 -4
- data/lib/rubocop/cop/rails/freeze_time.rb +79 -0
- data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +1 -1
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +14 -8
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +3 -3
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +23 -12
- data/lib/rubocop/cop/rails/http_status.rb +6 -11
- data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +3 -1
- data/lib/rubocop/cop/rails/i18n_locale_assignment.rb +1 -1
- data/lib/rubocop/cop/rails/i18n_locale_texts.rb +2 -2
- data/lib/rubocop/cop/rails/ignored_columns_assignment.rb +50 -0
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +5 -14
- data/lib/rubocop/cop/rails/index_by.rb +2 -2
- data/lib/rubocop/cop/rails/index_with.rb +2 -2
- data/lib/rubocop/cop/rails/inquiry.rb +1 -1
- data/lib/rubocop/cop/rails/inverse_of.rb +4 -10
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +22 -16
- data/lib/rubocop/cop/rails/link_to_blank.rb +2 -5
- data/lib/rubocop/cop/rails/mailer_name.rb +5 -5
- data/lib/rubocop/cop/rails/match_route.rb +1 -1
- data/lib/rubocop/cop/rails/migration_class_name.rb +2 -2
- data/lib/rubocop/cop/rails/negate_include.rb +2 -2
- data/lib/rubocop/cop/rails/not_null_column.rb +10 -7
- data/lib/rubocop/cop/rails/order_by_id.rb +2 -3
- data/lib/rubocop/cop/rails/output.rb +7 -9
- data/lib/rubocop/cop/rails/output_safety.rb +6 -2
- data/lib/rubocop/cop/rails/pick.rb +1 -1
- data/lib/rubocop/cop/rails/pluck.rb +45 -13
- data/lib/rubocop/cop/rails/pluck_id.rb +2 -2
- data/lib/rubocop/cop/rails/pluck_in_where.rb +1 -1
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +2 -3
- data/lib/rubocop/cop/rails/presence.rb +22 -13
- data/lib/rubocop/cop/rails/present.rb +10 -13
- data/lib/rubocop/cop/rails/rake_environment.rb +3 -3
- data/lib/rubocop/cop/rails/read_write_attribute.rb +2 -2
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +5 -7
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +3 -3
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +3 -3
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +31 -27
- data/lib/rubocop/cop/rails/redundant_travel_back.rb +1 -1
- data/lib/rubocop/cop/rails/reflection_class_name.rb +35 -2
- data/lib/rubocop/cop/rails/refute_methods.rb +1 -5
- data/lib/rubocop/cop/rails/relative_date_constant.rb +5 -8
- data/lib/rubocop/cop/rails/render_inline.rb +1 -1
- data/lib/rubocop/cop/rails/render_plain_text.rb +1 -1
- data/lib/rubocop/cop/rails/request_referer.rb +2 -3
- data/lib/rubocop/cop/rails/require_dependency.rb +2 -2
- data/lib/rubocop/cop/rails/response_parsed_body.rb +57 -0
- data/lib/rubocop/cop/rails/reversible_migration.rb +15 -63
- data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +5 -6
- data/lib/rubocop/cop/rails/root_join_chain.rb +1 -1
- 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 +8 -13
- data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +2 -4
- data/lib/rubocop/cop/rails/save_bang.rb +12 -24
- data/lib/rubocop/cop/rails/schema_comment.rb +1 -1
- data/lib/rubocop/cop/rails/scope_args.rb +1 -1
- data/lib/rubocop/cop/rails/short_i18n.rb +3 -6
- data/lib/rubocop/cop/rails/skips_model_validations.rb +3 -4
- data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +10 -7
- data/lib/rubocop/cop/rails/strip_heredoc.rb +56 -0
- data/lib/rubocop/cop/rails/table_name_assignment.rb +1 -1
- data/lib/rubocop/cop/rails/three_state_boolean_column.rb +73 -0
- data/lib/rubocop/cop/rails/time_zone.rb +34 -32
- data/lib/rubocop/cop/rails/time_zone_assignment.rb +4 -4
- 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 +17 -12
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +3 -6
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +14 -7
- data/lib/rubocop/cop/rails/unknown_env.rb +3 -5
- data/lib/rubocop/cop/rails/unused_ignored_columns.rb +7 -2
- data/lib/rubocop/cop/rails/validation.rb +5 -13
- data/lib/rubocop/cop/rails/where_equals.rb +2 -2
- data/lib/rubocop/cop/rails/where_exists.rb +3 -3
- data/lib/rubocop/cop/rails/where_missing.rb +118 -0
- data/lib/rubocop/cop/rails/where_not.rb +2 -2
- data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +55 -0
- data/lib/rubocop/cop/rails_cops.rb +16 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +8 -5
- data/lib/rubocop/rails/version.rb +1 -1
- data/lib/rubocop/rails.rb +1 -1
- data/lib/rubocop-rails.rb +19 -0
- metadata +23 -9
- data/bin/console +0 -11
- data/bin/setup +0 -7
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks if the value of the option `class_name`, in
|
7
7
|
# the definition of a reflection is a string.
|
8
8
|
#
|
9
9
|
# @safety
|
@@ -18,6 +18,8 @@ module RuboCop
|
|
18
18
|
# # good
|
19
19
|
# has_many :accounts, class_name: 'Account'
|
20
20
|
class ReflectionClassName < Base
|
21
|
+
extend AutoCorrector
|
22
|
+
|
21
23
|
MSG = 'Use a string value for `class_name`.'
|
22
24
|
RESTRICT_ON_SEND = %i[has_many has_one belongs_to].freeze
|
23
25
|
ALLOWED_REFLECTION_CLASS_TYPES = %i[dstr str sym].freeze
|
@@ -32,14 +34,37 @@ module RuboCop
|
|
32
34
|
(pair (sym :class_name) #reflection_class_value?)
|
33
35
|
PATTERN
|
34
36
|
|
37
|
+
def_node_matcher :const_or_string, <<~PATTERN
|
38
|
+
{$(const nil? _) (send $(const nil? _) :name) (send $(const nil? _) :to_s)}
|
39
|
+
PATTERN
|
40
|
+
|
35
41
|
def on_send(node)
|
36
42
|
association_with_reflection(node) do |reflection_class_name|
|
37
|
-
|
43
|
+
return if reflection_class_name.value.send_type? && reflection_class_name.value.receiver.nil?
|
44
|
+
return if reflection_class_name.value.lvar_type? && str_assigned?(reflection_class_name)
|
45
|
+
|
46
|
+
add_offense(reflection_class_name.source_range) do |corrector|
|
47
|
+
autocorrect(corrector, reflection_class_name)
|
48
|
+
end
|
38
49
|
end
|
39
50
|
end
|
40
51
|
|
41
52
|
private
|
42
53
|
|
54
|
+
def str_assigned?(reflection_class_name)
|
55
|
+
lvar = reflection_class_name.value.source
|
56
|
+
|
57
|
+
reflection_class_name.ancestors.each do |nodes|
|
58
|
+
return true if nodes.each_child_node(:lvasgn).detect do |node|
|
59
|
+
lhs, rhs = *node
|
60
|
+
|
61
|
+
lhs.to_s == lvar && ALLOWED_REFLECTION_CLASS_TYPES.include?(rhs.type)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
false
|
66
|
+
end
|
67
|
+
|
43
68
|
def reflection_class_value?(class_value)
|
44
69
|
if class_value.send_type?
|
45
70
|
!class_value.method?(:to_s) || class_value.receiver&.const_type?
|
@@ -47,6 +72,14 @@ module RuboCop
|
|
47
72
|
!ALLOWED_REFLECTION_CLASS_TYPES.include?(class_value.type)
|
48
73
|
end
|
49
74
|
end
|
75
|
+
|
76
|
+
def autocorrect(corrector, class_config)
|
77
|
+
class_value = class_config.value
|
78
|
+
replacement = const_or_string(class_value)
|
79
|
+
return unless replacement.present?
|
80
|
+
|
81
|
+
corrector.replace(class_value, replacement.source.inspect)
|
82
|
+
end
|
50
83
|
end
|
51
84
|
end
|
52
85
|
end
|
@@ -81,11 +81,7 @@ module RuboCop
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def offense_message(method_name)
|
84
|
-
format(
|
85
|
-
MSG,
|
86
|
-
bad_method: method_name,
|
87
|
-
good_method: convert_good_method(method_name)
|
88
|
-
)
|
84
|
+
format(MSG, bad_method: method_name, good_method: convert_good_method(method_name))
|
89
85
|
end
|
90
86
|
|
91
87
|
def convert_good_method(bad_method)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks whether constant value isn't relative date.
|
7
7
|
# Because the relative date will be evaluated only once.
|
8
8
|
#
|
9
9
|
# @safety
|
@@ -34,8 +34,7 @@ module RuboCop
|
|
34
34
|
include RangeHelp
|
35
35
|
extend AutoCorrector
|
36
36
|
|
37
|
-
MSG = 'Do not assign `%<method_name>s` to constants as it '
|
38
|
-
'will be evaluated only once.'
|
37
|
+
MSG = 'Do not assign `%<method_name>s` to constants as it will be evaluated only once.'
|
39
38
|
RELATIVE_DATE_METHODS = %i[since from_now after ago until before yesterday tomorrow].to_set.freeze
|
40
39
|
|
41
40
|
def on_casgn(node)
|
@@ -77,11 +76,9 @@ module RuboCop
|
|
77
76
|
return unless scope.nil?
|
78
77
|
|
79
78
|
indent = ' ' * node.loc.column
|
80
|
-
new_code = ["def self.#{const_name.downcase}",
|
81
|
-
"#{indent}#{value.source}",
|
82
|
-
'end'].join("\n#{indent}")
|
79
|
+
new_code = ["def self.#{const_name.downcase}", "#{indent}#{value.source}", 'end'].join("\n#{indent}")
|
83
80
|
|
84
|
-
corrector.replace(node
|
81
|
+
corrector.replace(node, new_code)
|
85
82
|
end
|
86
83
|
|
87
84
|
def message(method_name)
|
@@ -89,7 +86,7 @@ module RuboCop
|
|
89
86
|
end
|
90
87
|
|
91
88
|
def offense_range(name, value)
|
92
|
-
range_between(name.
|
89
|
+
range_between(name.source_range.begin_pos, value.source_range.end_pos)
|
93
90
|
end
|
94
91
|
|
95
92
|
def nested_relative_date(node, &callback)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks for consistent uses of `request.referer` or
|
7
7
|
# `request.referrer`, depending on the cop's configuration.
|
8
8
|
#
|
9
9
|
# @example EnforcedStyle: referer (default)
|
@@ -23,8 +23,7 @@ module RuboCop
|
|
23
23
|
include ConfigurableEnforcedStyle
|
24
24
|
extend AutoCorrector
|
25
25
|
|
26
|
-
MSG = 'Use `request.%<prefer>s` instead of '
|
27
|
-
'`request.%<current>s`.'
|
26
|
+
MSG = 'Use `request.%<prefer>s` instead of `request.%<current>s`.'
|
28
27
|
RESTRICT_ON_SEND = %i[referer referrer].freeze
|
29
28
|
|
30
29
|
def_node_matcher :referer?, <<~PATTERN
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks for the usage of `require_dependency`.
|
7
7
|
#
|
8
8
|
# `require_dependency` is an obsolete method for Rails applications running in Zeitwerk mode.
|
9
9
|
# In Zeitwerk mode, the semantics should match Ruby's and no need to be defensive with load order,
|
@@ -26,7 +26,7 @@ module RuboCop
|
|
26
26
|
RESTRICT_ON_SEND = %i[require_dependency].freeze
|
27
27
|
|
28
28
|
def_node_matcher :require_dependency_call?, <<~PATTERN
|
29
|
-
(send {nil? (const
|
29
|
+
(send {nil? (const {nil? cbase} :Kernel)} :require_dependency _)
|
30
30
|
PATTERN
|
31
31
|
|
32
32
|
def on_send(node)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Prefer `response.parsed_body` to `JSON.parse(response.body)`.
|
7
|
+
#
|
8
|
+
# @safety
|
9
|
+
# This cop is unsafe because Content-Type may not be `application/json`. For example, the proprietary
|
10
|
+
# Content-Type provided by corporate entities such as `application/vnd.github+json` is not supported at
|
11
|
+
# `response.parsed_body` by default, so you still have to use `JSON.parse(response.body)` there.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# JSON.parse(response.body)
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# response.parsed_body
|
19
|
+
class ResponseParsedBody < Base
|
20
|
+
extend AutoCorrector
|
21
|
+
extend TargetRailsVersion
|
22
|
+
|
23
|
+
MSG = 'Prefer `response.parsed_body` to `JSON.parse(response.body)`.'
|
24
|
+
|
25
|
+
RESTRICT_ON_SEND = %i[parse].freeze
|
26
|
+
|
27
|
+
minimum_target_rails_version 5.0
|
28
|
+
|
29
|
+
# @!method json_parse_response_body?(node)
|
30
|
+
def_node_matcher :json_parse_response_body?, <<~PATTERN
|
31
|
+
(send
|
32
|
+
(const {nil? cbase} :JSON)
|
33
|
+
:parse
|
34
|
+
(send
|
35
|
+
(send nil? :response)
|
36
|
+
:body
|
37
|
+
)
|
38
|
+
)
|
39
|
+
PATTERN
|
40
|
+
|
41
|
+
def on_send(node)
|
42
|
+
return unless json_parse_response_body?(node)
|
43
|
+
|
44
|
+
add_offense(node) do |corrector|
|
45
|
+
autocorrect(corrector, node)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def autocorrect(corrector, node)
|
52
|
+
corrector.replace(node, 'response.parsed_body')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks whether the change method of the migration file is
|
7
7
|
# reversible.
|
8
8
|
#
|
9
9
|
# @example
|
@@ -16,23 +16,15 @@ module RuboCop
|
|
16
16
|
#
|
17
17
|
# # good
|
18
18
|
# def change
|
19
|
-
#
|
20
|
-
# t.
|
19
|
+
# change_table :users do |t|
|
20
|
+
# t.remove :name, :string
|
21
21
|
# end
|
22
22
|
# end
|
23
23
|
#
|
24
24
|
# # good
|
25
25
|
# def change
|
26
|
-
#
|
27
|
-
#
|
28
|
-
# dir.up do
|
29
|
-
# t.column :name, :string
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# dir.down do
|
33
|
-
# t.remove :name
|
34
|
-
# end
|
35
|
-
# end
|
26
|
+
# create_table :users do |t|
|
27
|
+
# t.string :name
|
36
28
|
# end
|
37
29
|
# end
|
38
30
|
#
|
@@ -114,21 +106,6 @@ module RuboCop
|
|
114
106
|
# end
|
115
107
|
# end
|
116
108
|
#
|
117
|
-
# # good
|
118
|
-
# def change
|
119
|
-
# reversible do |dir|
|
120
|
-
# change_table :users do |t|
|
121
|
-
# dir.up do
|
122
|
-
# t.change :price, :string
|
123
|
-
# end
|
124
|
-
#
|
125
|
-
# dir.down do
|
126
|
-
# t.change :price, :integer
|
127
|
-
# end
|
128
|
-
# end
|
129
|
-
# end
|
130
|
-
# end
|
131
|
-
#
|
132
109
|
# @example
|
133
110
|
# # remove_columns
|
134
111
|
#
|
@@ -173,8 +150,6 @@ module RuboCop
|
|
173
150
|
# def change
|
174
151
|
# remove_index :users, column: :email
|
175
152
|
# end
|
176
|
-
#
|
177
|
-
# @see https://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html
|
178
153
|
class ReversibleMigration < Base
|
179
154
|
include MigrationsHelper
|
180
155
|
|
@@ -229,6 +204,8 @@ module RuboCop
|
|
229
204
|
check_change_table_node(node.send_node, node.body)
|
230
205
|
end
|
231
206
|
|
207
|
+
alias on_numblock on_block
|
208
|
+
|
232
209
|
private
|
233
210
|
|
234
211
|
def check_irreversible_schema_statement_node(node)
|
@@ -240,10 +217,7 @@ module RuboCop
|
|
240
217
|
def check_drop_table_node(node)
|
241
218
|
drop_table_call(node) do
|
242
219
|
unless node.parent.block_type? || node.last_argument.block_pass_type?
|
243
|
-
add_offense(
|
244
|
-
node,
|
245
|
-
message: format(MSG, action: 'drop_table(without block)')
|
246
|
-
)
|
220
|
+
add_offense(node, message: format(MSG, action: 'drop_table(without block)'))
|
247
221
|
end
|
248
222
|
end
|
249
223
|
end
|
@@ -251,22 +225,12 @@ module RuboCop
|
|
251
225
|
def check_reversible_hash_node(node)
|
252
226
|
return if reversible_change_table_call?(node)
|
253
227
|
|
254
|
-
add_offense(
|
255
|
-
node,
|
256
|
-
message: format(
|
257
|
-
MSG, action: "#{node.method_name}(without :from and :to)"
|
258
|
-
)
|
259
|
-
)
|
228
|
+
add_offense(node, message: format(MSG, action: "#{node.method_name}(without :from and :to)"))
|
260
229
|
end
|
261
230
|
|
262
231
|
def check_remove_column_node(node)
|
263
232
|
remove_column_call(node) do |args|
|
264
|
-
if args.to_a.size < 3
|
265
|
-
add_offense(
|
266
|
-
node,
|
267
|
-
message: format(MSG, action: 'remove_column(without type)')
|
268
|
-
)
|
269
|
-
end
|
233
|
+
add_offense(node, message: format(MSG, action: 'remove_column(without type)')) if args.to_a.size < 3
|
270
234
|
end
|
271
235
|
end
|
272
236
|
|
@@ -295,10 +259,7 @@ module RuboCop
|
|
295
259
|
unless all_hash_key?(args, :type) && target_rails_version >= 6.1
|
296
260
|
action = target_rails_version >= 6.1 ? 'remove_columns(without type)' : 'remove_columns'
|
297
261
|
|
298
|
-
add_offense(
|
299
|
-
node,
|
300
|
-
message: format(MSG, action: action)
|
301
|
-
)
|
262
|
+
add_offense(node, message: format(MSG, action: action))
|
302
263
|
end
|
303
264
|
end
|
304
265
|
end
|
@@ -306,18 +267,14 @@ module RuboCop
|
|
306
267
|
def check_remove_index_node(node)
|
307
268
|
remove_index_call(node) do |args|
|
308
269
|
if args.hash_type? && !all_hash_key?(args, :column)
|
309
|
-
add_offense(
|
310
|
-
node,
|
311
|
-
message: format(MSG, action: 'remove_index(without column)')
|
312
|
-
)
|
270
|
+
add_offense(node, message: format(MSG, action: 'remove_index(without column)'))
|
313
271
|
end
|
314
272
|
end
|
315
273
|
end
|
316
274
|
|
317
275
|
def check_change_table_offense(receiver, node)
|
318
276
|
method_name = node.method_name
|
319
|
-
return if receiver != node.receiver &&
|
320
|
-
reversible_change_table_call?(node)
|
277
|
+
return if receiver != node.receiver && reversible_change_table_call?(node)
|
321
278
|
|
322
279
|
action = if method_name == :remove
|
323
280
|
target_rails_version >= 6.1 ? 't.remove (without type)' : 't.remove'
|
@@ -325,10 +282,7 @@ module RuboCop
|
|
325
282
|
"change_table(with #{method_name})"
|
326
283
|
end
|
327
284
|
|
328
|
-
add_offense(
|
329
|
-
node,
|
330
|
-
message: format(MSG, action: action)
|
331
|
-
)
|
285
|
+
add_offense(node, message: format(MSG, action: action))
|
332
286
|
end
|
333
287
|
|
334
288
|
def reversible_change_table_call?(node)
|
@@ -353,9 +307,7 @@ module RuboCop
|
|
353
307
|
|
354
308
|
def within_reversible_or_up_only_block?(node)
|
355
309
|
node.each_ancestor(:block).any? do |ancestor|
|
356
|
-
(ancestor.block_type? &&
|
357
|
-
ancestor.send_node.method?(:reversible)) ||
|
358
|
-
ancestor.send_node.method?(:up_only)
|
310
|
+
(ancestor.block_type? && ancestor.send_node.method?(:reversible)) || ancestor.send_node.method?(:up_only)
|
359
311
|
end
|
360
312
|
end
|
361
313
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks whether the migration implements
|
7
7
|
# either a `change` method or both an `up` and a `down`
|
8
8
|
# method.
|
9
9
|
#
|
@@ -45,19 +45,18 @@ module RuboCop
|
|
45
45
|
class ReversibleMigrationMethodDefinition < Base
|
46
46
|
include MigrationsHelper
|
47
47
|
|
48
|
-
MSG = 'Migrations must contain either a `change` method, or '
|
49
|
-
'both an `up` and a `down` method.'
|
48
|
+
MSG = 'Migrations must contain either a `change` method, or both an `up` and a `down` method.'
|
50
49
|
|
51
50
|
def_node_matcher :change_method?, <<~PATTERN
|
52
|
-
|
51
|
+
`(def :change (args) _)
|
53
52
|
PATTERN
|
54
53
|
|
55
54
|
def_node_matcher :up_and_down_methods?, <<~PATTERN
|
56
|
-
[
|
55
|
+
[`(def :up (args) _) `(def :down (args) _)]
|
57
56
|
PATTERN
|
58
57
|
|
59
58
|
def on_class(node)
|
60
|
-
return if change_method?(node) || up_and_down_methods?(node)
|
59
|
+
return if !migration_class?(node) || change_method?(node) || up_and_down_methods?(node)
|
61
60
|
|
62
61
|
add_offense(node)
|
63
62
|
end
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
39
39
|
def on_send(node)
|
40
40
|
evidence(node) do |rails_node, args|
|
41
41
|
add_offense(node, message: format(MSG, root: rails_node.source)) do |corrector|
|
42
|
-
range = range_between(rails_node.loc.selector.end_pos, node.
|
42
|
+
range = range_between(rails_node.loc.selector.end_pos, node.source_range.end_pos)
|
43
43
|
replacement = ".join(#{args.map(&:source).join(', ')})"
|
44
44
|
|
45
45
|
corrector.replace(range, replacement)
|
@@ -0,0 +1,238 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Use `Rails.root` IO methods instead of passing it to `File`.
|
7
|
+
#
|
8
|
+
# `Rails.root` is an instance of `Pathname`
|
9
|
+
# so we can apply many IO methods directly.
|
10
|
+
#
|
11
|
+
# This cop works best when used together with
|
12
|
+
# `Style/FileRead`, `Style/FileWrite` and `Rails/RootJoinChain`.
|
13
|
+
#
|
14
|
+
# @safety
|
15
|
+
# This cop is unsafe for autocorrection because `Dir`'s `children`, `each_child`, `entries`, and `glob`
|
16
|
+
# methods return string element, but these methods of `Pathname` return `Pathname` element.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# # bad
|
20
|
+
# File.open(Rails.root.join('db', 'schema.rb'))
|
21
|
+
# File.open(Rails.root.join('db', 'schema.rb'), 'w')
|
22
|
+
# File.read(Rails.root.join('db', 'schema.rb'))
|
23
|
+
# File.binread(Rails.root.join('db', 'schema.rb'))
|
24
|
+
# File.write(Rails.root.join('db', 'schema.rb'), content)
|
25
|
+
# File.binwrite(Rails.root.join('db', 'schema.rb'), content)
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# Rails.root.join('db', 'schema.rb').open
|
29
|
+
# Rails.root.join('db', 'schema.rb').open('w')
|
30
|
+
# Rails.root.join('db', 'schema.rb').read
|
31
|
+
# Rails.root.join('db', 'schema.rb').binread
|
32
|
+
# Rails.root.join('db', 'schema.rb').write(content)
|
33
|
+
# Rails.root.join('db', 'schema.rb').binwrite(content)
|
34
|
+
#
|
35
|
+
class RootPathnameMethods < Base
|
36
|
+
extend AutoCorrector
|
37
|
+
include RangeHelp
|
38
|
+
|
39
|
+
MSG = '`%<rails_root>s` is a `Pathname` so you can just append `#%<method>s`.'
|
40
|
+
|
41
|
+
DIR_METHODS = %i[children delete each_child empty? entries exist? glob mkdir open rmdir unlink].to_set.freeze
|
42
|
+
|
43
|
+
FILE_METHODS = %i[
|
44
|
+
atime
|
45
|
+
basename
|
46
|
+
binread
|
47
|
+
binwrite
|
48
|
+
birthtime
|
49
|
+
blockdev?
|
50
|
+
chardev?
|
51
|
+
chmod
|
52
|
+
chown
|
53
|
+
ctime
|
54
|
+
delete
|
55
|
+
directory?
|
56
|
+
dirname
|
57
|
+
empty?
|
58
|
+
executable?
|
59
|
+
executable_real?
|
60
|
+
exist?
|
61
|
+
expand_path
|
62
|
+
extname
|
63
|
+
file?
|
64
|
+
fnmatch
|
65
|
+
fnmatch?
|
66
|
+
ftype
|
67
|
+
grpowned?
|
68
|
+
join
|
69
|
+
lchmod
|
70
|
+
lchown
|
71
|
+
lstat
|
72
|
+
mtime
|
73
|
+
open
|
74
|
+
owned?
|
75
|
+
pipe?
|
76
|
+
read
|
77
|
+
readable?
|
78
|
+
readable_real?
|
79
|
+
readlines
|
80
|
+
readlink
|
81
|
+
realdirpath
|
82
|
+
realpath
|
83
|
+
rename
|
84
|
+
setgid?
|
85
|
+
setuid?
|
86
|
+
size
|
87
|
+
size?
|
88
|
+
socket?
|
89
|
+
split
|
90
|
+
stat
|
91
|
+
sticky?
|
92
|
+
symlink?
|
93
|
+
sysopen
|
94
|
+
truncate
|
95
|
+
unlink
|
96
|
+
utime
|
97
|
+
world_readable?
|
98
|
+
world_writable?
|
99
|
+
writable?
|
100
|
+
writable_real?
|
101
|
+
write
|
102
|
+
zero?
|
103
|
+
].to_set.freeze
|
104
|
+
|
105
|
+
FILE_TEST_METHODS = %i[
|
106
|
+
blockdev?
|
107
|
+
chardev?
|
108
|
+
directory?
|
109
|
+
empty?
|
110
|
+
executable?
|
111
|
+
executable_real?
|
112
|
+
exist?
|
113
|
+
file?
|
114
|
+
grpowned?
|
115
|
+
owned?
|
116
|
+
pipe?
|
117
|
+
readable?
|
118
|
+
readable_real?
|
119
|
+
setgid?
|
120
|
+
setuid?
|
121
|
+
size
|
122
|
+
size?
|
123
|
+
socket?
|
124
|
+
sticky?
|
125
|
+
symlink?
|
126
|
+
world_readable?
|
127
|
+
world_writable?
|
128
|
+
writable?
|
129
|
+
writable_real?
|
130
|
+
zero?
|
131
|
+
].to_set.freeze
|
132
|
+
|
133
|
+
FILE_UTILS_METHODS = %i[chmod chown mkdir mkpath rmdir rmtree].to_set.freeze
|
134
|
+
|
135
|
+
RESTRICT_ON_SEND = (DIR_METHODS + FILE_METHODS + FILE_TEST_METHODS + FILE_UTILS_METHODS).to_set.freeze
|
136
|
+
|
137
|
+
def_node_matcher :pathname_method, <<~PATTERN
|
138
|
+
{
|
139
|
+
(send (const {nil? cbase} :Dir) $DIR_METHODS $_ $...)
|
140
|
+
(send (const {nil? cbase} {:IO :File}) $FILE_METHODS $_ $...)
|
141
|
+
(send (const {nil? cbase} :FileTest) $FILE_TEST_METHODS $_ $...)
|
142
|
+
(send (const {nil? cbase} :FileUtils) $FILE_UTILS_METHODS $_ $...)
|
143
|
+
}
|
144
|
+
PATTERN
|
145
|
+
|
146
|
+
def_node_matcher :dir_glob?, <<~PATTERN
|
147
|
+
(send
|
148
|
+
(const {cbase nil?} :Dir) :glob ...)
|
149
|
+
PATTERN
|
150
|
+
|
151
|
+
def_node_matcher :rails_root_pathname?, <<~PATTERN
|
152
|
+
{
|
153
|
+
$#rails_root?
|
154
|
+
(send $#rails_root? :join ...)
|
155
|
+
}
|
156
|
+
PATTERN
|
157
|
+
|
158
|
+
# @!method rails_root?(node)
|
159
|
+
def_node_matcher :rails_root?, <<~PATTERN
|
160
|
+
(send (const {nil? cbase} :Rails) {:root :public_path})
|
161
|
+
PATTERN
|
162
|
+
|
163
|
+
def on_send(node)
|
164
|
+
evidence(node) do |method, path, args, rails_root|
|
165
|
+
add_offense(node, message: format(MSG, method: method, rails_root: rails_root.source)) do |corrector|
|
166
|
+
replacement = if dir_glob?(node)
|
167
|
+
build_path_glob_replacement(path, method)
|
168
|
+
else
|
169
|
+
build_path_replacement(path, method, args)
|
170
|
+
end
|
171
|
+
|
172
|
+
corrector.replace(node, replacement)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def evidence(node)
|
180
|
+
return if node.method?(:open) && node.parent&.send_type?
|
181
|
+
return unless (method, path, args = pathname_method(node)) && (rails_root = rails_root_pathname?(path))
|
182
|
+
|
183
|
+
yield(method, path, args, rails_root)
|
184
|
+
end
|
185
|
+
|
186
|
+
def build_path_glob_replacement(path, method)
|
187
|
+
receiver = range_between(path.source_range.begin_pos, path.children.first.loc.selector.end_pos).source
|
188
|
+
|
189
|
+
argument = path.arguments.one? ? path.first_argument.source : join_arguments(path.arguments)
|
190
|
+
|
191
|
+
"#{receiver}.#{method}(#{argument})"
|
192
|
+
end
|
193
|
+
|
194
|
+
def build_path_replacement(path, method, args)
|
195
|
+
path_replacement = path.source
|
196
|
+
if path.arguments? && !path.parenthesized_call?
|
197
|
+
path_replacement[' '] = '('
|
198
|
+
path_replacement << ')'
|
199
|
+
end
|
200
|
+
|
201
|
+
replacement = "#{path_replacement}.#{method}"
|
202
|
+
replacement += "(#{args.map(&:source).join(', ')})" unless args.empty?
|
203
|
+
replacement
|
204
|
+
end
|
205
|
+
|
206
|
+
def include_interpolation?(arguments)
|
207
|
+
arguments.any? do |argument|
|
208
|
+
argument.children.any? { |child| child.respond_to?(:begin_type?) && child.begin_type? }
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def join_arguments(arguments)
|
213
|
+
use_interpolation = false
|
214
|
+
|
215
|
+
joined_arguments = arguments.map do |arg|
|
216
|
+
if arg.respond_to?(:value)
|
217
|
+
arg.value
|
218
|
+
else
|
219
|
+
use_interpolation = true
|
220
|
+
"\#{#{arg.source}}"
|
221
|
+
end
|
222
|
+
end.join('/')
|
223
|
+
quote = enforce_double_quotes? || include_interpolation?(arguments) || use_interpolation ? '"' : "'"
|
224
|
+
|
225
|
+
"#{quote}#{joined_arguments}#{quote}"
|
226
|
+
end
|
227
|
+
|
228
|
+
def enforce_double_quotes?
|
229
|
+
string_literals_config['EnforcedStyle'] == 'double_quotes'
|
230
|
+
end
|
231
|
+
|
232
|
+
def string_literals_config
|
233
|
+
config.for_cop('Style/StringLiterals')
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|