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
@@ -3,9 +3,13 @@
|
|
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
|
+
# @safety
|
10
|
+
# This cop is unsafe because it cannot be determined whether
|
11
|
+
# constant or method return value specified to `class_name` is a string.
|
12
|
+
#
|
9
13
|
# @example
|
10
14
|
# # bad
|
11
15
|
# has_many :accounts, class_name: Account
|
@@ -13,23 +17,68 @@ module RuboCop
|
|
13
17
|
#
|
14
18
|
# # good
|
15
19
|
# has_many :accounts, class_name: 'Account'
|
16
|
-
class ReflectionClassName <
|
20
|
+
class ReflectionClassName < Base
|
21
|
+
extend AutoCorrector
|
22
|
+
|
17
23
|
MSG = 'Use a string value for `class_name`.'
|
24
|
+
RESTRICT_ON_SEND = %i[has_many has_one belongs_to].freeze
|
25
|
+
ALLOWED_REFLECTION_CLASS_TYPES = %i[dstr str sym].freeze
|
18
26
|
|
19
|
-
def_node_matcher :association_with_reflection,
|
20
|
-
(send nil? {:has_many :has_one :belongs_to} _
|
27
|
+
def_node_matcher :association_with_reflection, <<~PATTERN
|
28
|
+
(send nil? {:has_many :has_one :belongs_to} _ _ ?
|
21
29
|
(hash <$#reflection_class_name ...>)
|
22
30
|
)
|
23
31
|
PATTERN
|
24
32
|
|
25
|
-
def_node_matcher :reflection_class_name,
|
26
|
-
(pair (sym :class_name)
|
33
|
+
def_node_matcher :reflection_class_name, <<~PATTERN
|
34
|
+
(pair (sym :class_name) #reflection_class_value?)
|
35
|
+
PATTERN
|
36
|
+
|
37
|
+
def_node_matcher :const_or_string, <<~PATTERN
|
38
|
+
{$(const nil? _) (send $(const nil? _) :name) (send $(const nil? _) :to_s)}
|
27
39
|
PATTERN
|
28
40
|
|
29
41
|
def on_send(node)
|
30
42
|
association_with_reflection(node) do |reflection_class_name|
|
31
|
-
|
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
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
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
|
32
63
|
end
|
64
|
+
|
65
|
+
false
|
66
|
+
end
|
67
|
+
|
68
|
+
def reflection_class_value?(class_value)
|
69
|
+
if class_value.send_type?
|
70
|
+
!class_value.method?(:to_s) || class_value.receiver&.const_type?
|
71
|
+
else
|
72
|
+
!ALLOWED_REFLECTION_CLASS_TYPES.include?(class_value.type)
|
73
|
+
end
|
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)
|
33
82
|
end
|
34
83
|
end
|
35
84
|
end
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
6
6
|
#
|
7
7
|
# Use `assert_not` methods instead of `refute` methods.
|
8
8
|
#
|
9
|
-
# @example
|
9
|
+
# @example EnforcedStyle: assert_not (default)
|
10
10
|
# # bad
|
11
11
|
# refute false
|
12
12
|
# refute_empty [1, 2, 3]
|
@@ -17,58 +17,79 @@ module RuboCop
|
|
17
17
|
# assert_not_empty [1, 2, 3]
|
18
18
|
# assert_not_equal true, false
|
19
19
|
#
|
20
|
-
|
21
|
-
|
20
|
+
# @example EnforcedStyle: refute
|
21
|
+
# # bad
|
22
|
+
# assert_not false
|
23
|
+
# assert_not_empty [1, 2, 3]
|
24
|
+
# assert_not_equal true, false
|
25
|
+
#
|
26
|
+
# # good
|
27
|
+
# refute false
|
28
|
+
# refute_empty [1, 2, 3]
|
29
|
+
# refute_equal true, false
|
30
|
+
#
|
31
|
+
class RefuteMethods < Base
|
32
|
+
include ConfigurableEnforcedStyle
|
33
|
+
extend AutoCorrector
|
34
|
+
|
35
|
+
MSG = 'Prefer `%<good_method>s` over `%<bad_method>s`.'
|
22
36
|
|
23
37
|
CORRECTIONS = {
|
24
|
-
refute:
|
25
|
-
refute_empty:
|
26
|
-
refute_equal:
|
27
|
-
refute_in_delta:
|
28
|
-
refute_in_epsilon:
|
29
|
-
refute_includes:
|
30
|
-
refute_instance_of:
|
31
|
-
refute_kind_of:
|
32
|
-
refute_nil:
|
33
|
-
refute_operator:
|
34
|
-
refute_predicate:
|
35
|
-
refute_respond_to:
|
36
|
-
refute_same:
|
37
|
-
refute_match:
|
38
|
+
refute: :assert_not,
|
39
|
+
refute_empty: :assert_not_empty,
|
40
|
+
refute_equal: :assert_not_equal,
|
41
|
+
refute_in_delta: :assert_not_in_delta,
|
42
|
+
refute_in_epsilon: :assert_not_in_epsilon,
|
43
|
+
refute_includes: :assert_not_includes,
|
44
|
+
refute_instance_of: :assert_not_instance_of,
|
45
|
+
refute_kind_of: :assert_not_kind_of,
|
46
|
+
refute_nil: :assert_not_nil,
|
47
|
+
refute_operator: :assert_not_operator,
|
48
|
+
refute_predicate: :assert_not_predicate,
|
49
|
+
refute_respond_to: :assert_not_respond_to,
|
50
|
+
refute_same: :assert_not_same,
|
51
|
+
refute_match: :assert_no_match
|
38
52
|
}.freeze
|
39
53
|
|
40
|
-
|
54
|
+
REFUTE_METHODS = CORRECTIONS.keys.freeze
|
55
|
+
ASSERT_NOT_METHODS = CORRECTIONS.values.freeze
|
41
56
|
|
42
|
-
|
57
|
+
RESTRICT_ON_SEND = REFUTE_METHODS + ASSERT_NOT_METHODS
|
58
|
+
|
59
|
+
def_node_matcher :offensive?, '(send nil? #bad_method? ...)'
|
43
60
|
|
44
61
|
def on_send(node)
|
45
62
|
return unless offensive?(node)
|
46
63
|
|
47
|
-
|
48
|
-
|
49
|
-
|
64
|
+
method_name = node.method_name
|
65
|
+
message = offense_message(method_name)
|
66
|
+
range = node.loc.selector
|
50
67
|
|
51
|
-
|
52
|
-
|
53
|
-
corrector.replace(
|
54
|
-
node.loc.selector,
|
55
|
-
CORRECTIONS[node.method_name]
|
56
|
-
)
|
68
|
+
add_offense(range, message: message) do |corrector|
|
69
|
+
corrector.replace(range, convert_good_method(method_name))
|
57
70
|
end
|
58
71
|
end
|
59
72
|
|
60
73
|
private
|
61
74
|
|
62
|
-
def
|
63
|
-
|
75
|
+
def bad_method?(method_name)
|
76
|
+
if style == :assert_not
|
77
|
+
REFUTE_METHODS.include?(method_name)
|
78
|
+
else
|
79
|
+
ASSERT_NOT_METHODS.include?(method_name)
|
80
|
+
end
|
64
81
|
end
|
65
82
|
|
66
83
|
def offense_message(method_name)
|
67
|
-
format(
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
84
|
+
format(MSG, bad_method: method_name, good_method: convert_good_method(method_name))
|
85
|
+
end
|
86
|
+
|
87
|
+
def convert_good_method(bad_method)
|
88
|
+
if style == :assert_not
|
89
|
+
CORRECTIONS.fetch(bad_method)
|
90
|
+
else
|
91
|
+
CORRECTIONS.invert.fetch(bad_method)
|
92
|
+
end
|
72
93
|
end
|
73
94
|
end
|
74
95
|
end
|
@@ -3,9 +3,12 @@
|
|
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
|
+
# @safety
|
10
|
+
# This cop's autocorrection is unsafe because its dependence on the constant is not corrected.
|
11
|
+
#
|
9
12
|
# @example
|
10
13
|
# # bad
|
11
14
|
# class SomeClass
|
@@ -14,19 +17,31 @@ module RuboCop
|
|
14
17
|
#
|
15
18
|
# # good
|
16
19
|
# class SomeClass
|
20
|
+
# EXPIRES = 1.week
|
21
|
+
#
|
22
|
+
# def self.expired_at
|
23
|
+
# EXPIRES.since
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# class SomeClass
|
17
29
|
# def self.expired_at
|
18
30
|
# 1.week.since
|
19
31
|
# end
|
20
32
|
# end
|
21
|
-
class RelativeDateConstant <
|
33
|
+
class RelativeDateConstant < Base
|
22
34
|
include RangeHelp
|
35
|
+
extend AutoCorrector
|
23
36
|
|
24
|
-
MSG = 'Do not assign
|
25
|
-
|
37
|
+
MSG = 'Do not assign `%<method_name>s` to constants as it will be evaluated only once.'
|
38
|
+
RELATIVE_DATE_METHODS = %i[since from_now after ago until before yesterday tomorrow].to_set.freeze
|
26
39
|
|
27
40
|
def on_casgn(node)
|
28
|
-
|
29
|
-
add_offense(node, message:
|
41
|
+
nested_relative_date(node) do |method_name|
|
42
|
+
add_offense(node, message: message(method_name)) do |corrector|
|
43
|
+
autocorrect(corrector, node)
|
44
|
+
end
|
30
45
|
end
|
31
46
|
end
|
32
47
|
|
@@ -38,54 +53,58 @@ module RuboCop
|
|
38
53
|
lhs.children.zip(rhs.children).each do |(name, value)|
|
39
54
|
next unless name.casgn_type?
|
40
55
|
|
41
|
-
|
42
|
-
add_offense(
|
43
|
-
|
44
|
-
|
45
|
-
message: format(MSG, method_name: method_name))
|
56
|
+
nested_relative_date(value) do |method_name|
|
57
|
+
add_offense(offense_range(name, value), message: message(method_name)) do |corrector|
|
58
|
+
autocorrect(corrector, node)
|
59
|
+
end
|
46
60
|
end
|
47
61
|
end
|
48
62
|
end
|
49
63
|
|
50
64
|
def on_or_asgn(node)
|
51
|
-
relative_date_or_assignment
|
65
|
+
relative_date_or_assignment(node) do |method_name|
|
52
66
|
add_offense(node, message: format(MSG, method_name: method_name))
|
53
67
|
end
|
54
68
|
end
|
55
69
|
|
56
|
-
|
70
|
+
private
|
71
|
+
|
72
|
+
def autocorrect(corrector, node)
|
57
73
|
return unless node.casgn_type?
|
58
74
|
|
59
75
|
scope, const_name, value = *node
|
60
76
|
return unless scope.nil?
|
61
77
|
|
62
78
|
indent = ' ' * node.loc.column
|
63
|
-
new_code = ["def self.#{const_name.downcase}",
|
64
|
-
|
65
|
-
|
66
|
-
->(corrector) { corrector.replace(node.source_range, new_code) }
|
79
|
+
new_code = ["def self.#{const_name.downcase}", "#{indent}#{value.source}", 'end'].join("\n#{indent}")
|
80
|
+
|
81
|
+
corrector.replace(node, new_code)
|
67
82
|
end
|
68
83
|
|
69
|
-
|
84
|
+
def message(method_name)
|
85
|
+
format(MSG, method_name: method_name)
|
86
|
+
end
|
70
87
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
88
|
+
def offense_range(name, value)
|
89
|
+
range_between(name.source_range.begin_pos, value.source_range.end_pos)
|
90
|
+
end
|
91
|
+
|
92
|
+
def nested_relative_date(node, &callback)
|
93
|
+
return if node.nil? || node.block_type?
|
94
|
+
|
95
|
+
node.each_child_node do |child|
|
96
|
+
nested_relative_date(child, &callback)
|
97
|
+
end
|
98
|
+
|
99
|
+
relative_date(node, &callback)
|
100
|
+
end
|
78
101
|
|
79
|
-
def_node_matcher :relative_date_or_assignment
|
80
|
-
(:or_asgn (casgn _ _) (send _ $
|
102
|
+
def_node_matcher :relative_date_or_assignment, <<~PATTERN
|
103
|
+
(:or_asgn (casgn _ _) (send _ $RELATIVE_DATE_METHODS))
|
81
104
|
PATTERN
|
82
105
|
|
83
|
-
def_node_matcher :relative_date
|
84
|
-
|
85
|
-
({erange irange} _ (send _ ${:since :from_now :after :ago :until :before}))
|
86
|
-
({erange irange} (send _ ${:since :from_now :after :ago :until :before}) _)
|
87
|
-
(send _ ${:since :from_now :after :ago :until :before})
|
88
|
-
}
|
106
|
+
def_node_matcher :relative_date, <<~PATTERN
|
107
|
+
(send _ $RELATIVE_DATE_METHODS)
|
89
108
|
PATTERN
|
90
109
|
end
|
91
110
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Looks for inline rendering within controller actions.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# class ProductsController < ApplicationController
|
11
|
+
# def index
|
12
|
+
# render inline: "<% products.each do |p| %><p><%= p.name %></p><% end %>", type: :erb
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# # app/views/products/index.html.erb
|
18
|
+
# # <% products.each do |p| %>
|
19
|
+
# # <p><%= p.name %></p>
|
20
|
+
# # <% end %>
|
21
|
+
#
|
22
|
+
# class ProductsController < ApplicationController
|
23
|
+
# def index
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
class RenderInline < Base
|
28
|
+
MSG = 'Prefer using a template over inline rendering.'
|
29
|
+
RESTRICT_ON_SEND = %i[render].freeze
|
30
|
+
|
31
|
+
def_node_matcher :render_with_inline_option?, <<~PATTERN
|
32
|
+
(send nil? :render (hash <(pair {(sym :inline) (str "inline")} _) ...>))
|
33
|
+
PATTERN
|
34
|
+
|
35
|
+
def on_send(node)
|
36
|
+
add_offense(node) if render_with_inline_option?(node)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Identifies places where `render text:` can be
|
7
|
+
# replaced with `render plain:`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad - explicit MIME type to `text/plain`
|
11
|
+
# render text: 'Ruby!', content_type: 'text/plain'
|
12
|
+
#
|
13
|
+
# # good - short and precise
|
14
|
+
# render plain: 'Ruby!'
|
15
|
+
#
|
16
|
+
# # good - explicit MIME type not to `text/plain`
|
17
|
+
# render text: 'Ruby!', content_type: 'text/html'
|
18
|
+
#
|
19
|
+
# @example ContentTypeCompatibility: true (default)
|
20
|
+
# # good - sets MIME type to `text/html`
|
21
|
+
# render text: 'Ruby!'
|
22
|
+
#
|
23
|
+
# @example ContentTypeCompatibility: false
|
24
|
+
# # bad - sets MIME type to `text/html`
|
25
|
+
# render text: 'Ruby!'
|
26
|
+
#
|
27
|
+
class RenderPlainText < Base
|
28
|
+
extend AutoCorrector
|
29
|
+
|
30
|
+
MSG = 'Prefer `render plain:` over `render text:`.'
|
31
|
+
RESTRICT_ON_SEND = %i[render].freeze
|
32
|
+
|
33
|
+
def_node_matcher :render_plain_text?, <<~PATTERN
|
34
|
+
(send nil? :render $(hash <$(pair (sym :text) $_) ...>))
|
35
|
+
PATTERN
|
36
|
+
|
37
|
+
def on_send(node)
|
38
|
+
render_plain_text?(node) do |options_node, option_node, option_value|
|
39
|
+
content_type_node = find_content_type(options_node)
|
40
|
+
return unless compatible_content_type?(content_type_node)
|
41
|
+
|
42
|
+
add_offense(node) do |corrector|
|
43
|
+
rest_options = options_node.pairs - [option_node, content_type_node].compact
|
44
|
+
|
45
|
+
corrector.replace(node, replacement(rest_options, option_value))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def find_content_type(node)
|
53
|
+
node.pairs.find { |p| p.key.value.to_sym == :content_type }
|
54
|
+
end
|
55
|
+
|
56
|
+
def compatible_content_type?(node)
|
57
|
+
(node && node.value.value == 'text/plain') ||
|
58
|
+
(!node && !cop_config['ContentTypeCompatibility'])
|
59
|
+
end
|
60
|
+
|
61
|
+
def replacement(rest_options, option_value)
|
62
|
+
if rest_options.any?
|
63
|
+
"render plain: #{option_value.source}, #{rest_options.map(&:source).join(', ')}"
|
64
|
+
else
|
65
|
+
"render plain: #{option_value.source}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -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)
|
@@ -19,13 +19,14 @@ module RuboCop
|
|
19
19
|
#
|
20
20
|
# # good
|
21
21
|
# request.referrer
|
22
|
-
class RequestReferer <
|
22
|
+
class RequestReferer < Base
|
23
23
|
include ConfigurableEnforcedStyle
|
24
|
+
extend AutoCorrector
|
24
25
|
|
25
|
-
MSG = 'Use `request.%<prefer>s` instead of '
|
26
|
-
|
26
|
+
MSG = 'Use `request.%<prefer>s` instead of `request.%<current>s`.'
|
27
|
+
RESTRICT_ON_SEND = %i[referer referrer].freeze
|
27
28
|
|
28
|
-
def_node_matcher :referer?,
|
29
|
+
def_node_matcher :referer?, <<~PATTERN
|
29
30
|
(send (send nil? :request) {:referer :referrer})
|
30
31
|
PATTERN
|
31
32
|
|
@@ -33,17 +34,15 @@ module RuboCop
|
|
33
34
|
referer?(node) do
|
34
35
|
return unless node.method?(wrong_method_name)
|
35
36
|
|
36
|
-
add_offense(node.source_range
|
37
|
+
add_offense(node.source_range) do |corrector|
|
38
|
+
corrector.replace(node, "request.#{style}")
|
39
|
+
end
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
40
|
-
def autocorrect(node)
|
41
|
-
->(corrector) { corrector.replace(node, "request.#{style}") }
|
42
|
-
end
|
43
|
-
|
44
43
|
private
|
45
44
|
|
46
|
-
def message(
|
45
|
+
def message(_range)
|
47
46
|
format(MSG, prefer: style, current: wrong_method_name)
|
48
47
|
end
|
49
48
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Checks for the usage of `require_dependency`.
|
7
|
+
#
|
8
|
+
# `require_dependency` is an obsolete method for Rails applications running in Zeitwerk mode.
|
9
|
+
# In Zeitwerk mode, the semantics should match Ruby's and no need to be defensive with load order,
|
10
|
+
# just refer to classes and modules normally.
|
11
|
+
# If the constant name is dynamic, camelize if needed, and constantize.
|
12
|
+
#
|
13
|
+
# Applications running in Zeitwerk mode should not use `require_dependency`.
|
14
|
+
#
|
15
|
+
# NOTE: This cop is disabled by default. Please enable it if you are using Zeitwerk mode.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# # bad
|
19
|
+
# require_dependency 'some_lib'
|
20
|
+
class RequireDependency < Base
|
21
|
+
extend TargetRailsVersion
|
22
|
+
|
23
|
+
minimum_target_rails_version 6.0
|
24
|
+
|
25
|
+
MSG = 'Do not use `require_dependency` with Zeitwerk mode.'
|
26
|
+
RESTRICT_ON_SEND = %i[require_dependency].freeze
|
27
|
+
|
28
|
+
def_node_matcher :require_dependency_call?, <<~PATTERN
|
29
|
+
(send {nil? (const {nil? cbase} :Kernel)} :require_dependency _)
|
30
|
+
PATTERN
|
31
|
+
|
32
|
+
def on_send(node)
|
33
|
+
require_dependency_call?(node) { add_offense(node) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -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
|