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
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Using `flash` assignment before `render` in Rails controllers will persist the message for too long.
|
7
|
+
# Check https://guides.rubyonrails.org/action_controller_overview.html#flash-now
|
8
|
+
#
|
9
|
+
# @safety
|
10
|
+
# This cop's autocorrection is unsafe because it replaces `flash` by `flash.now`.
|
11
|
+
# Even though it is usually a mistake, it might be used intentionally.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# class HomeController < ApplicationController
|
17
|
+
# def create
|
18
|
+
# flash[:alert] = "msg"
|
19
|
+
# render :index
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# class HomeController < ApplicationController
|
25
|
+
# def create
|
26
|
+
# flash.now[:alert] = "msg"
|
27
|
+
# render :index
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
class ActionControllerFlashBeforeRender < Base
|
32
|
+
extend AutoCorrector
|
33
|
+
|
34
|
+
MSG = 'Use `flash.now` before `render`.'
|
35
|
+
|
36
|
+
def_node_search :flash_assignment?, <<~PATTERN
|
37
|
+
^(send (send nil? :flash) :[]= ...)
|
38
|
+
PATTERN
|
39
|
+
|
40
|
+
def_node_search :render?, <<~PATTERN
|
41
|
+
(send nil? :render ...)
|
42
|
+
PATTERN
|
43
|
+
|
44
|
+
def_node_search :action_controller?, <<~PATTERN
|
45
|
+
{
|
46
|
+
(const {nil? cbase} :ApplicationController)
|
47
|
+
(const (const {nil? cbase} :ActionController) :Base)
|
48
|
+
}
|
49
|
+
PATTERN
|
50
|
+
|
51
|
+
RESTRICT_ON_SEND = [:flash].freeze
|
52
|
+
|
53
|
+
def on_send(flash_node)
|
54
|
+
return unless flash_assignment?(flash_node)
|
55
|
+
|
56
|
+
return unless followed_by_render?(flash_node)
|
57
|
+
|
58
|
+
return unless instance_method_or_block?(flash_node)
|
59
|
+
|
60
|
+
return unless inherit_action_controller_base?(flash_node)
|
61
|
+
|
62
|
+
add_offense(flash_node) do |corrector|
|
63
|
+
corrector.replace(flash_node, 'flash.now')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def followed_by_render?(flash_node)
|
70
|
+
flash_assignment_node = find_ancestor(flash_node, type: :send)
|
71
|
+
context = flash_assignment_node
|
72
|
+
if (node = context.each_ancestor(:if, :rescue).first)
|
73
|
+
return false if use_redirect_to?(context)
|
74
|
+
|
75
|
+
context = node
|
76
|
+
elsif context.right_siblings.empty?
|
77
|
+
return true
|
78
|
+
end
|
79
|
+
context = context.right_siblings
|
80
|
+
|
81
|
+
context.compact.any? do |render_candidate|
|
82
|
+
render?(render_candidate)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def inherit_action_controller_base?(node)
|
87
|
+
class_node = find_ancestor(node, type: :class)
|
88
|
+
return unless class_node
|
89
|
+
|
90
|
+
action_controller?(class_node)
|
91
|
+
end
|
92
|
+
|
93
|
+
def instance_method_or_block?(node)
|
94
|
+
def_node = find_ancestor(node, type: :def)
|
95
|
+
block_node = find_ancestor(node, type: :block)
|
96
|
+
|
97
|
+
def_node || block_node
|
98
|
+
end
|
99
|
+
|
100
|
+
def use_redirect_to?(context)
|
101
|
+
context.right_siblings.compact.any? do |sibling|
|
102
|
+
sibling.send_type? && sibling.method?(:redirect_to)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def find_ancestor(node, type:)
|
107
|
+
node.each_ancestor(type).first
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -30,8 +30,8 @@ module RuboCop
|
|
30
30
|
|
31
31
|
def_node_matcher :action_controller_test_case?, <<~PATTERN
|
32
32
|
(class
|
33
|
-
(const
|
34
|
-
(const (const {nil? cbase} :ActionController) :TestCase)
|
33
|
+
(const _ _)
|
34
|
+
(const (const {nil? cbase} :ActionController) :TestCase) _)
|
35
35
|
PATTERN
|
36
36
|
|
37
37
|
def on_class(node)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Enforces the consistent use of action filter methods.
|
7
7
|
#
|
8
8
|
# The cop is configurable and can enforce the use of the older
|
9
9
|
# something_filter methods or the newer something_action methods.
|
@@ -69,7 +69,7 @@ module RuboCop
|
|
69
69
|
|
70
70
|
RESTRICT_ON_SEND = FILTER_METHODS + ACTION_METHODS
|
71
71
|
|
72
|
-
def on_block(node)
|
72
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
73
73
|
check_method_node(node.send_node)
|
74
74
|
end
|
75
75
|
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Enforces consistent ordering of the standard Rails RESTful controller actions.
|
7
|
+
#
|
8
|
+
# The cop is configurable and can enforce any ordering of the standard actions.
|
9
|
+
# All other methods are ignored. So, the actions specified in `ExpectedOrder` should be
|
10
|
+
# defined before actions not specified.
|
11
|
+
#
|
12
|
+
# [source,yaml]
|
13
|
+
# ----
|
14
|
+
# Rails/ActionOrder:
|
15
|
+
# ExpectedOrder:
|
16
|
+
# - index
|
17
|
+
# - show
|
18
|
+
# - new
|
19
|
+
# - edit
|
20
|
+
# - create
|
21
|
+
# - update
|
22
|
+
# - destroy
|
23
|
+
# ----
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# # bad
|
27
|
+
# def index; end
|
28
|
+
# def destroy; end
|
29
|
+
# def show; end
|
30
|
+
#
|
31
|
+
# # good
|
32
|
+
# def index; end
|
33
|
+
# def show; end
|
34
|
+
# def destroy; end
|
35
|
+
class ActionOrder < Base
|
36
|
+
extend AutoCorrector
|
37
|
+
include VisibilityHelp
|
38
|
+
include DefNode
|
39
|
+
include RangeHelp
|
40
|
+
|
41
|
+
MSG = 'Action `%<current>s` should appear before `%<previous>s`.'
|
42
|
+
|
43
|
+
def_node_search :action_declarations, '(def {%1} ...)'
|
44
|
+
|
45
|
+
def on_class(node)
|
46
|
+
action_declarations(node, actions).each_cons(2) do |previous, current|
|
47
|
+
next if node_visibility(current) != :public || non_public?(current)
|
48
|
+
next if find_index(current) >= find_index(previous)
|
49
|
+
|
50
|
+
register_offense(previous, current)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def expected_order
|
57
|
+
cop_config['ExpectedOrder'].map(&:to_sym)
|
58
|
+
end
|
59
|
+
|
60
|
+
def actions
|
61
|
+
@actions ||= Set.new(expected_order)
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_index(node)
|
65
|
+
expected_order.find_index(node.method_name)
|
66
|
+
end
|
67
|
+
|
68
|
+
def register_offense(previous, current)
|
69
|
+
message = format(
|
70
|
+
MSG,
|
71
|
+
expected_order: expected_order.join(', '),
|
72
|
+
previous: previous.method_name,
|
73
|
+
current: current.method_name
|
74
|
+
)
|
75
|
+
add_offense(current, message: message) do |corrector|
|
76
|
+
current = correction_target(current)
|
77
|
+
previous = correction_target(previous)
|
78
|
+
|
79
|
+
swap_range(corrector, current, previous)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def correction_target(def_node)
|
84
|
+
range_with_comments_and_lines(def_node.each_ancestor(:if).first || def_node)
|
85
|
+
end
|
86
|
+
|
87
|
+
def add_range(range1, range2)
|
88
|
+
range1.with(
|
89
|
+
begin_pos: [range1.begin_pos, range2.begin_pos].min,
|
90
|
+
end_pos: [range1.end_pos, range2.end_pos].max
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
def range_with_comments(node)
|
95
|
+
# rubocop:todo InternalAffairs/LocationExpression
|
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
|
100
|
+
ranges.reduce do |result, range|
|
101
|
+
add_range(result, range)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def range_with_comments_and_lines(node)
|
106
|
+
range_by_whole_lines(range_with_comments(node), include_final_newline: true)
|
107
|
+
end
|
108
|
+
|
109
|
+
def swap_range(corrector, range1, range2)
|
110
|
+
corrector.insert_before(range2, range1.source)
|
111
|
+
corrector.remove(range1)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -21,14 +21,13 @@ module RuboCop
|
|
21
21
|
|
22
22
|
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
23
23
|
|
24
|
-
ALIASES = {
|
25
|
-
update_attributes: :update,
|
26
|
-
update_attributes!: :update!
|
27
|
-
}.freeze
|
24
|
+
ALIASES = { update_attributes: :update, update_attributes!: :update! }.freeze
|
28
25
|
|
29
26
|
RESTRICT_ON_SEND = ALIASES.keys.freeze
|
30
27
|
|
31
28
|
def on_send(node)
|
29
|
+
return if node.arguments.empty?
|
30
|
+
|
32
31
|
method_name = node.method_name
|
33
32
|
alias_method = ALIASES[method_name]
|
34
33
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks that Active Record callbacks are declared
|
7
7
|
# in the order in which they will be executed.
|
8
8
|
#
|
9
9
|
# @example
|
@@ -104,7 +104,7 @@ module RuboCop
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def end_position_for(node)
|
107
|
-
end_line = buffer.line_for_position(node.
|
107
|
+
end_line = buffer.line_for_position(node.source_range.end_pos)
|
108
108
|
buffer.line_range(end_line).end_pos
|
109
109
|
end
|
110
110
|
|
@@ -112,8 +112,7 @@ module RuboCop
|
|
112
112
|
annotation_line = node.first_line - 1
|
113
113
|
first_comment = nil
|
114
114
|
|
115
|
-
processed_source.
|
116
|
-
.reverse_each do |comment|
|
115
|
+
processed_source.each_comment_in_lines(0..annotation_line).reverse_each do |comment|
|
117
116
|
if comment.location.line == annotation_line && !inline_comment?(comment)
|
118
117
|
first_comment = comment
|
119
118
|
annotation_line -= 1
|
@@ -124,7 +123,11 @@ module RuboCop
|
|
124
123
|
end
|
125
124
|
|
126
125
|
def inline_comment?(comment)
|
126
|
+
# rubocop:todo InternalAffairs/LocationExpression
|
127
|
+
# Using `RuboCop::Ext::Comment#source_range` requires RuboCop > 1.46,
|
128
|
+
# which introduces https://github.com/rubocop/rubocop/pull/11630.
|
127
129
|
!comment_line?(comment.loc.expression.source_line)
|
130
|
+
# rubocop:enable InternalAffairs/LocationExpression
|
128
131
|
end
|
129
132
|
|
130
133
|
def start_line_position(node)
|
@@ -25,12 +25,9 @@ module RuboCop
|
|
25
25
|
# end
|
26
26
|
#
|
27
27
|
class ActiveRecordOverride < Base
|
28
|
-
MSG =
|
29
|
-
'Use %<prefer>s callbacks instead of overriding the Active Record ' \
|
30
|
-
'method `%<bad>s`.'
|
28
|
+
MSG = 'Use %<prefer>s callbacks instead of overriding the Active Record method `%<bad>s`.'
|
31
29
|
BAD_METHODS = %i[create destroy save update].freeze
|
32
|
-
ACTIVE_RECORD_CLASSES = %w[ApplicationRecord ActiveModel::Base
|
33
|
-
ActiveRecord::Base].freeze
|
30
|
+
ACTIVE_RECORD_CLASSES = %w[ApplicationRecord ActiveModel::Base ActiveRecord::Base].freeze
|
34
31
|
|
35
32
|
def on_def(node)
|
36
33
|
return unless BAD_METHODS.include?(node.method_name)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Checks for Rails framework classes that are patched directly instead of using Active Support load hooks. Direct
|
7
|
+
# patching forcibly loads the framework referenced, using hooks defers loading until it's actually needed.
|
8
|
+
#
|
9
|
+
# @safety
|
10
|
+
# While using lazy load hooks is recommended, it changes the order in which is code is loaded and may reveal
|
11
|
+
# load order dependency bugs.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# ActiveRecord::Base.include(MyClass)
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# ActiveSupport.on_load(:active_record) { include MyClass }
|
20
|
+
class ActiveSupportOnLoad < Base
|
21
|
+
extend AutoCorrector
|
22
|
+
|
23
|
+
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
24
|
+
RESTRICT_ON_SEND = %i[prepend include extend].freeze
|
25
|
+
LOAD_HOOKS = {
|
26
|
+
'ActionCable' => 'action_cable',
|
27
|
+
'ActionCable::Channel::Base' => 'action_cable_channel',
|
28
|
+
'ActionCable::Connection::Base' => 'action_cable_connection',
|
29
|
+
'ActionCable::Connection::TestCase' => 'action_cable_connection_test_case',
|
30
|
+
'ActionController::API' => 'action_controller',
|
31
|
+
'ActionController::Base' => 'action_controller',
|
32
|
+
'ActionController::TestCase' => 'action_controller_test_case',
|
33
|
+
'ActionDispatch::IntegrationTest' => 'action_dispatch_integration_test',
|
34
|
+
'ActionDispatch::Request' => 'action_dispatch_request',
|
35
|
+
'ActionDispatch::Response' => 'action_dispatch_response',
|
36
|
+
'ActionDispatch::SystemTestCase' => 'action_dispatch_system_test_case',
|
37
|
+
'ActionMailbox::Base' => 'action_mailbox',
|
38
|
+
'ActionMailbox::InboundEmail' => 'action_mailbox_inbound_email',
|
39
|
+
'ActionMailbox::Record' => 'action_mailbox_record',
|
40
|
+
'ActionMailbox::TestCase' => 'action_mailbox_test_case',
|
41
|
+
'ActionMailer::Base' => 'action_mailer',
|
42
|
+
'ActionMailer::TestCase' => 'action_mailer_test_case',
|
43
|
+
'ActionText::Content' => 'action_text_content',
|
44
|
+
'ActionText::Record' => 'action_text_record',
|
45
|
+
'ActionText::RichText' => 'action_text_rich_text',
|
46
|
+
'ActionView::Base' => 'action_view',
|
47
|
+
'ActionView::TestCase' => 'action_view_test_case',
|
48
|
+
'ActiveJob::Base' => 'active_job',
|
49
|
+
'ActiveJob::TestCase' => 'active_job_test_case',
|
50
|
+
'ActiveRecord::Base' => 'active_record',
|
51
|
+
'ActiveStorage::Attachment' => 'active_storage_attachment',
|
52
|
+
'ActiveStorage::Blob' => 'active_storage_blob',
|
53
|
+
'ActiveStorage::Record' => 'active_storage_record',
|
54
|
+
'ActiveStorage::VariantRecord' => 'active_storage_variant_record',
|
55
|
+
'ActiveSupport::TestCase' => 'active_support_test_case'
|
56
|
+
}.freeze
|
57
|
+
|
58
|
+
def on_send(node)
|
59
|
+
receiver, method, arguments = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
60
|
+
return unless receiver && (hook = LOAD_HOOKS[receiver.const_name])
|
61
|
+
|
62
|
+
preferred = "ActiveSupport.on_load(:#{hook}) { #{method} #{arguments.source} }"
|
63
|
+
add_offense(node, message: format(MSG, prefer: preferred, current: node.source)) do |corrector|
|
64
|
+
corrector.replace(node, preferred)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks for migrations using `add_column` that have an `index`
|
7
7
|
# key. `add_column` does not accept `index`, but also does not raise an
|
8
8
|
# error for extra keys, so it is possible to mistakenly add the key without
|
9
9
|
# realizing it will not actually add an index.
|
@@ -42,7 +42,7 @@ module RuboCop
|
|
42
42
|
add_index_opts = ''
|
43
43
|
|
44
44
|
if value.hash_type?
|
45
|
-
hash = value.
|
45
|
+
hash = value.source_range.adjust(begin_pos: 1, end_pos: -1).source.strip
|
46
46
|
add_index_opts = ", #{hash}"
|
47
47
|
end
|
48
48
|
|
@@ -53,10 +53,7 @@ module RuboCop
|
|
53
53
|
private
|
54
54
|
|
55
55
|
def index_range(pair_node)
|
56
|
-
range_with_surrounding_comma(
|
57
|
-
range_with_surrounding_space(range: pair_node.loc.expression, side: :left),
|
58
|
-
:left
|
59
|
-
)
|
56
|
+
range_with_surrounding_comma(range_with_surrounding_space(pair_node.source_range, side: :left), :left)
|
60
57
|
end
|
61
58
|
end
|
62
59
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Enforces that there is only one call to `after_commit`
|
7
7
|
# (and its aliases - `after_create_commit`, `after_update_commit`,
|
8
8
|
# and `after_destroy_commit`) with the same callback name per model.
|
9
9
|
#
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks that controllers subclass `ApplicationController`.
|
7
7
|
#
|
8
8
|
# @safety
|
9
9
|
# This cop's autocorrection is unsafe because it may let the logic from `ApplicationController`
|
@@ -25,7 +25,7 @@ module RuboCop
|
|
25
25
|
|
26
26
|
MSG = 'Controllers should subclass `ApplicationController`.'
|
27
27
|
SUPERCLASS = 'ApplicationController'
|
28
|
-
BASE_PATTERN = '(const (const nil? :ActionController) :Base)'
|
28
|
+
BASE_PATTERN = '(const (const {nil? cbase} :ActionController) :Base)'
|
29
29
|
|
30
30
|
# rubocop:disable Layout/ClassStructure
|
31
31
|
include RuboCop::Cop::EnforceSuperclass
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks that jobs subclass `ApplicationJob` with Rails 5.0.
|
7
7
|
#
|
8
8
|
# @safety
|
9
9
|
# This cop's autocorrection is unsafe because it may let the logic from `ApplicationJob`
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
28
28
|
|
29
29
|
MSG = 'Jobs should subclass `ApplicationJob`.'
|
30
30
|
SUPERCLASS = 'ApplicationJob'
|
31
|
-
BASE_PATTERN = '(const (const nil? :ActiveJob) :Base)'
|
31
|
+
BASE_PATTERN = '(const (const {nil? cbase} :ActiveJob) :Base)'
|
32
32
|
|
33
33
|
# rubocop:disable Layout/ClassStructure
|
34
34
|
include RuboCop::Cop::EnforceSuperclass
|
@@ -36,7 +36,7 @@ module RuboCop
|
|
36
36
|
|
37
37
|
def autocorrect(node)
|
38
38
|
lambda do |corrector|
|
39
|
-
corrector.replace(node
|
39
|
+
corrector.replace(node, self.class::SUPERCLASS)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks that mailers subclass `ApplicationMailer` with Rails 5.0.
|
7
7
|
#
|
8
8
|
# @safety
|
9
9
|
# This cop's autocorrection is unsafe because it may let the logic from `ApplicationMailer`
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
28
28
|
|
29
29
|
MSG = 'Mailers should subclass `ApplicationMailer`.'
|
30
30
|
SUPERCLASS = 'ApplicationMailer'
|
31
|
-
BASE_PATTERN = '(const (const nil? :ActionMailer) :Base)'
|
31
|
+
BASE_PATTERN = '(const (const {nil? cbase} :ActionMailer) :Base)'
|
32
32
|
|
33
33
|
# rubocop:disable Layout/ClassStructure
|
34
34
|
include RuboCop::Cop::EnforceSuperclass
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks that models subclass `ApplicationRecord` with Rails 5.0.
|
7
7
|
#
|
8
8
|
# @safety
|
9
9
|
# This cop's autocorrection is unsafe because it may let the logic from `ApplicationRecord`
|
@@ -29,7 +29,7 @@ module RuboCop
|
|
29
29
|
|
30
30
|
MSG = 'Models should subclass `ApplicationRecord`.'
|
31
31
|
SUPERCLASS = 'ApplicationRecord'
|
32
|
-
BASE_PATTERN = '(const (const nil? :ActiveRecord) :Base)'
|
32
|
+
BASE_PATTERN = '(const (const {nil? cbase} :ActiveRecord) :Base)'
|
33
33
|
|
34
34
|
# rubocop:disable Layout/ClassStructure
|
35
35
|
include RuboCop::Cop::EnforceSuperclass
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Prevents usage of `"*"` on an Arel::Table column reference.
|
7
7
|
#
|
8
8
|
# Using `arel_table["*"]` causes the outputted string to be a literal
|
9
9
|
# quoted asterisk (e.g. <tt>`my_model`.`*`</tt>). This causes the
|
@@ -38,7 +38,7 @@ module RuboCop
|
|
38
38
|
return unless (star = star_bracket?(node))
|
39
39
|
|
40
40
|
add_offense(star) do |corrector|
|
41
|
-
corrector.replace(star
|
41
|
+
corrector.replace(star, 'Arel.star')
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Looks for `attribute` class methods that specify a `:default` option
|
7
7
|
# which value is an array, string literal or method call without a block.
|
8
8
|
# It will accept all other values, such as string, symbol, integer and float literals
|
9
9
|
# as well as constants.
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Looks for belongs_to associations where we control whether the
|
7
7
|
# association is required via the deprecated `required` option instead.
|
8
8
|
#
|
9
9
|
# Since Rails 5, belongs_to associations are required by default and this
|
@@ -47,9 +47,6 @@ module RuboCop
|
|
47
47
|
# class Post < ApplicationRecord
|
48
48
|
# belongs_to :blog, optional: false
|
49
49
|
# end
|
50
|
-
#
|
51
|
-
# @see https://guides.rubyonrails.org/5_0_release_notes.html
|
52
|
-
# @see https://github.com/rails/rails/pull/18937
|
53
50
|
class BelongsTo < Base
|
54
51
|
extend AutoCorrector
|
55
52
|
extend TargetRailsVersion
|
@@ -83,7 +80,7 @@ module RuboCop
|
|
83
80
|
end
|
84
81
|
|
85
82
|
add_offense(node.loc.selector, message: message) do |corrector|
|
86
|
-
corrector.replace(option_node
|
83
|
+
corrector.replace(option_node, replacement)
|
87
84
|
end
|
88
85
|
end
|
89
86
|
end
|
@@ -3,17 +3,17 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks for code that can be written with simpler conditionals
|
7
7
|
# using `Object#blank?` defined by Active Support.
|
8
8
|
#
|
9
9
|
# Interaction with `Style/UnlessElse`:
|
10
10
|
# The configuration of `NotPresent` will not produce an offense in the
|
11
|
-
# context of `unless else` if `Style/UnlessElse` is
|
12
|
-
# to prevent interference between the
|
11
|
+
# context of `unless else` if `Style/UnlessElse` is enabled. This is
|
12
|
+
# to prevent interference between the autocorrection of the two cops.
|
13
13
|
#
|
14
14
|
# @safety
|
15
|
-
# This cop is unsafe
|
16
|
-
# but `' '.blank?` returns true. Therefore,
|
15
|
+
# This cop is unsafe autocorrection, because `' '.empty?` returns false,
|
16
|
+
# but `' '.blank?` returns true. Therefore, autocorrection is not compatible
|
17
17
|
# if the receiver is a non-empty blank string, tab, or newline meta characters.
|
18
18
|
#
|
19
19
|
# @example NilOrEmpty: true (default)
|
@@ -63,8 +63,7 @@ module RuboCop
|
|
63
63
|
|
64
64
|
MSG_NIL_OR_EMPTY = 'Use `%<prefer>s` instead of `%<current>s`.'
|
65
65
|
MSG_NOT_PRESENT = 'Use `%<prefer>s` instead of `%<current>s`.'
|
66
|
-
MSG_UNLESS_PRESENT = 'Use `if %<prefer>s` instead of '
|
67
|
-
'`%<current>s`.'
|
66
|
+
MSG_UNLESS_PRESENT = 'Use `if %<prefer>s` instead of `%<current>s`.'
|
68
67
|
RESTRICT_ON_SEND = %i[!].freeze
|
69
68
|
|
70
69
|
# `(send nil $_)` is not actually a valid match for an offense. Nodes
|
@@ -143,10 +142,10 @@ module RuboCop
|
|
143
142
|
|
144
143
|
if method_call
|
145
144
|
corrector.replace(node.loc.keyword, 'if')
|
146
|
-
range = method_call.
|
145
|
+
range = method_call.source_range
|
147
146
|
else
|
148
147
|
variable1, _variable2 = nil_or_empty?(node) || not_present?(node)
|
149
|
-
range = node.
|
148
|
+
range = node.source_range
|
150
149
|
end
|
151
150
|
|
152
151
|
corrector.replace(range, replacement(variable1))
|
@@ -154,9 +153,9 @@ module RuboCop
|
|
154
153
|
|
155
154
|
def unless_condition(node, method_call)
|
156
155
|
if node.modifier_form?
|
157
|
-
node.loc.keyword.join(node.
|
156
|
+
node.loc.keyword.join(node.source_range.end)
|
158
157
|
else
|
159
|
-
node.
|
158
|
+
node.source_range.begin.join(method_call.source_range)
|
160
159
|
end
|
161
160
|
end
|
162
161
|
|