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.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +52 -5
  4. data/config/default.yml +726 -32
  5. data/config/obsoletion.yml +17 -0
  6. data/lib/rubocop/cop/mixin/active_record_helper.rb +106 -0
  7. data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +32 -0
  8. data/lib/rubocop/cop/mixin/class_send_node_helper.rb +20 -0
  9. data/lib/rubocop/cop/mixin/enforce_superclass.rb +40 -0
  10. data/lib/rubocop/cop/mixin/index_method.rb +165 -0
  11. data/lib/rubocop/cop/mixin/migrations_helper.rb +26 -0
  12. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +112 -0
  13. data/lib/rubocop/cop/rails/action_controller_test_case.rb +47 -0
  14. data/lib/rubocop/cop/rails/action_filter.rb +11 -21
  15. data/lib/rubocop/cop/rails/action_order.rb +116 -0
  16. data/lib/rubocop/cop/rails/active_record_aliases.rb +23 -24
  17. data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +143 -0
  18. data/lib/rubocop/cop/rails/active_record_override.rb +3 -6
  19. data/lib/rubocop/cop/rails/active_support_aliases.rb +13 -22
  20. data/lib/rubocop/cop/rails/active_support_on_load.rb +70 -0
  21. data/lib/rubocop/cop/rails/add_column_index.rb +61 -0
  22. data/lib/rubocop/cop/rails/after_commit_override.rb +81 -0
  23. data/lib/rubocop/cop/rails/application_controller.rb +36 -0
  24. data/lib/rubocop/cop/rails/application_job.rb +9 -4
  25. data/lib/rubocop/cop/rails/application_mailer.rb +39 -0
  26. data/lib/rubocop/cop/rails/application_record.rb +9 -9
  27. data/lib/rubocop/cop/rails/arel_star.rb +47 -0
  28. data/lib/rubocop/cop/rails/assert_not.rb +8 -10
  29. data/lib/rubocop/cop/rails/attribute_default_block_value.rb +90 -0
  30. data/lib/rubocop/cop/rails/belongs_to.rb +12 -24
  31. data/lib/rubocop/cop/rails/blank.rb +40 -36
  32. data/lib/rubocop/cop/rails/bulk_change_table.rb +40 -35
  33. data/lib/rubocop/cop/rails/compact_blank.rb +111 -0
  34. data/lib/rubocop/cop/rails/content_tag.rb +93 -0
  35. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +22 -15
  36. data/lib/rubocop/cop/rails/date.rb +41 -36
  37. data/lib/rubocop/cop/rails/default_scope.rb +61 -0
  38. data/lib/rubocop/cop/rails/delegate.rb +33 -29
  39. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +9 -10
  40. data/lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb +168 -0
  41. data/lib/rubocop/cop/rails/dot_separated_keys.rb +71 -0
  42. data/lib/rubocop/cop/rails/duplicate_association.rb +56 -0
  43. data/lib/rubocop/cop/rails/duplicate_scope.rb +46 -0
  44. data/lib/rubocop/cop/rails/duration_arithmetic.rb +98 -0
  45. data/lib/rubocop/cop/rails/dynamic_find_by.rb +76 -31
  46. data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +82 -0
  47. data/lib/rubocop/cop/rails/enum_hash.rb +75 -0
  48. data/lib/rubocop/cop/rails/enum_uniqueness.rb +30 -12
  49. data/lib/rubocop/cop/rails/environment_comparison.rb +70 -22
  50. data/lib/rubocop/cop/rails/environment_variable_access.rb +67 -0
  51. data/lib/rubocop/cop/rails/exit.rb +7 -13
  52. data/lib/rubocop/cop/rails/expanded_date_range.rb +102 -0
  53. data/lib/rubocop/cop/rails/file_path.rb +48 -31
  54. data/lib/rubocop/cop/rails/find_by.rb +43 -24
  55. data/lib/rubocop/cop/rails/find_by_id.rb +94 -0
  56. data/lib/rubocop/cop/rails/find_each.rb +42 -18
  57. data/lib/rubocop/cop/rails/freeze_time.rb +79 -0
  58. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +4 -3
  59. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +62 -25
  60. data/lib/rubocop/cop/rails/helper_instance_variable.rb +32 -4
  61. data/lib/rubocop/cop/rails/http_positional_arguments.rb +61 -32
  62. data/lib/rubocop/cop/rails/http_status.rb +27 -23
  63. data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +96 -0
  64. data/lib/rubocop/cop/rails/i18n_locale_assignment.rb +37 -0
  65. data/lib/rubocop/cop/rails/i18n_locale_texts.rb +110 -0
  66. data/lib/rubocop/cop/rails/ignored_columns_assignment.rb +50 -0
  67. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +9 -16
  68. data/lib/rubocop/cop/rails/index_by.rb +65 -0
  69. data/lib/rubocop/cop/rails/index_with.rb +68 -0
  70. data/lib/rubocop/cop/rails/inquiry.rb +39 -0
  71. data/lib/rubocop/cop/rails/inverse_of.rb +33 -27
  72. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +62 -32
  73. data/lib/rubocop/cop/rails/link_to_blank.rb +31 -32
  74. data/lib/rubocop/cop/rails/mailer_name.rb +90 -0
  75. data/lib/rubocop/cop/rails/match_route.rb +120 -0
  76. data/lib/rubocop/cop/rails/migration_class_name.rb +63 -0
  77. data/lib/rubocop/cop/rails/negate_include.rb +42 -0
  78. data/lib/rubocop/cop/rails/not_null_column.rb +16 -12
  79. data/lib/rubocop/cop/rails/order_by_id.rb +51 -0
  80. data/lib/rubocop/cop/rails/output.rb +29 -10
  81. data/lib/rubocop/cop/rails/output_safety.rb +9 -4
  82. data/lib/rubocop/cop/rails/pick.rb +64 -0
  83. data/lib/rubocop/cop/rails/pluck.rb +96 -0
  84. data/lib/rubocop/cop/rails/pluck_id.rb +59 -0
  85. data/lib/rubocop/cop/rails/pluck_in_where.rb +71 -0
  86. data/lib/rubocop/cop/rails/pluralization_grammar.rb +14 -19
  87. data/lib/rubocop/cop/rails/presence.rb +54 -26
  88. data/lib/rubocop/cop/rails/present.rb +40 -37
  89. data/lib/rubocop/cop/rails/rake_environment.rb +112 -0
  90. data/lib/rubocop/cop/rails/read_write_attribute.rb +56 -18
  91. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +33 -45
  92. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +77 -0
  93. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +257 -0
  94. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +34 -32
  95. data/lib/rubocop/cop/rails/redundant_travel_back.rb +57 -0
  96. data/lib/rubocop/cop/rails/reflection_class_name.rb +56 -7
  97. data/lib/rubocop/cop/rails/refute_methods.rb +56 -35
  98. data/lib/rubocop/cop/rails/relative_date_constant.rb +52 -33
  99. data/lib/rubocop/cop/rails/render_inline.rb +41 -0
  100. data/lib/rubocop/cop/rails/render_plain_text.rb +71 -0
  101. data/lib/rubocop/cop/rails/request_referer.rb +10 -11
  102. data/lib/rubocop/cop/rails/require_dependency.rb +38 -0
  103. data/lib/rubocop/cop/rails/response_parsed_body.rb +57 -0
  104. data/lib/rubocop/cop/rails/reversible_migration.rb +122 -82
  105. data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +66 -0
  106. data/lib/rubocop/cop/rails/root_join_chain.rb +72 -0
  107. data/lib/rubocop/cop/rails/root_pathname_methods.rb +238 -0
  108. data/lib/rubocop/cop/rails/root_public_path.rb +59 -0
  109. data/lib/rubocop/cop/rails/safe_navigation.rb +55 -43
  110. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +50 -0
  111. data/lib/rubocop/cop/rails/save_bang.rb +89 -63
  112. data/lib/rubocop/cop/rails/schema_comment.rb +104 -0
  113. data/lib/rubocop/cop/rails/scope_args.rb +8 -3
  114. data/lib/rubocop/cop/rails/short_i18n.rb +71 -0
  115. data/lib/rubocop/cop/rails/skips_model_validations.rb +53 -16
  116. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +87 -0
  117. data/lib/rubocop/cop/rails/strip_heredoc.rb +56 -0
  118. data/lib/rubocop/cop/rails/table_name_assignment.rb +44 -0
  119. data/lib/rubocop/cop/rails/three_state_boolean_column.rb +73 -0
  120. data/lib/rubocop/cop/rails/time_zone.rb +83 -67
  121. data/lib/rubocop/cop/rails/time_zone_assignment.rb +37 -0
  122. data/lib/rubocop/cop/rails/to_formatted_s.rb +46 -0
  123. data/lib/rubocop/cop/rails/to_s_with_argument.rb +78 -0
  124. data/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb +49 -0
  125. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +99 -0
  126. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +40 -49
  127. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +172 -0
  128. data/lib/rubocop/cop/rails/unknown_env.rb +52 -21
  129. data/lib/rubocop/cop/rails/unused_ignored_columns.rb +76 -0
  130. data/lib/rubocop/cop/rails/validation.rb +54 -23
  131. data/lib/rubocop/cop/rails/where_equals.rb +102 -0
  132. data/lib/rubocop/cop/rails/where_exists.rb +138 -0
  133. data/lib/rubocop/cop/rails/where_missing.rb +118 -0
  134. data/lib/rubocop/cop/rails/where_not.rb +101 -0
  135. data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +55 -0
  136. data/lib/rubocop/cop/rails_cops.rb +78 -8
  137. data/lib/rubocop/rails/inject.rb +1 -1
  138. data/lib/rubocop/rails/schema_loader/schema.rb +191 -0
  139. data/lib/rubocop/rails/schema_loader.rb +61 -0
  140. data/lib/rubocop/rails/version.rb +5 -1
  141. data/lib/rubocop/rails.rb +3 -1
  142. data/lib/rubocop-rails.rb +22 -0
  143. metadata +120 -19
  144. data/bin/setup +0 -7
@@ -0,0 +1,17 @@
1
+ #
2
+ # Configuration for obsoletion.
3
+ #
4
+ # See: https://docs.rubocop.org/rubocop/extensions.html#config-obsoletions
5
+ #
6
+ extracted:
7
+ Rails/*: ~
8
+
9
+ # Cop parameters that have been changed
10
+ # Can be treated as a warning instead of a failure with `severity: warning`
11
+ changed_parameters:
12
+ - cops: Rails/FindEach
13
+ parameters: IgnoredMethods
14
+ alternatives:
15
+ - AllowedMethods
16
+ - AllowedPatterns
17
+ severity: warning
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # A mixin to extend cops for Active Record features
6
+ module ActiveRecordHelper
7
+ extend NodePattern::Macros
8
+
9
+ WHERE_METHODS = %i[where rewhere].freeze
10
+
11
+ def_node_matcher :active_record?, <<~PATTERN
12
+ {
13
+ (const {nil? cbase} :ApplicationRecord)
14
+ (const (const {nil? cbase} :ActiveRecord) :Base)
15
+ }
16
+ PATTERN
17
+
18
+ def_node_search :find_set_table_name, <<~PATTERN
19
+ (send self :table_name= {str sym})
20
+ PATTERN
21
+
22
+ def_node_search :find_belongs_to, <<~PATTERN
23
+ (send nil? :belongs_to {str sym} ...)
24
+ PATTERN
25
+
26
+ def inherit_active_record_base?(node)
27
+ node.each_ancestor(:class).any? { |class_node| active_record?(class_node.parent_class) }
28
+ end
29
+
30
+ def external_dependency_checksum
31
+ return @external_dependency_checksum if defined?(@external_dependency_checksum)
32
+
33
+ schema_path = RuboCop::Rails::SchemaLoader.db_schema_path
34
+ return nil if schema_path.nil?
35
+
36
+ schema_code = File.read(schema_path)
37
+
38
+ @external_dependency_checksum ||= Digest::SHA1.hexdigest(schema_code)
39
+ end
40
+
41
+ def schema
42
+ RuboCop::Rails::SchemaLoader.load(target_ruby_version)
43
+ end
44
+
45
+ def table_name(class_node)
46
+ table_name = find_set_table_name(class_node).to_a.last&.first_argument
47
+ return table_name.value.to_s if table_name
48
+
49
+ class_nodes = class_node.defined_module.each_node
50
+ namespaces = class_node.each_ancestor(:class, :module).map(&:identifier)
51
+ [*class_nodes, *namespaces].reverse.map { |node| node.children[1] }.join('_').tableize
52
+ end
53
+
54
+ # Resolve relation into column name.
55
+ # It just returns column_name if the column exists.
56
+ # Or it tries to resolve column_name as a relation.
57
+ # Returns an array of column names if the relation is polymorphic.
58
+ # It returns `nil` if it can't resolve.
59
+ #
60
+ # @param name [String]
61
+ # @param class_node [RuboCop::AST::Node]
62
+ # @param table [RuboCop::Rails::SchemaLoader::Table]
63
+ # @return [Array, String, nil]
64
+ def resolve_relation_into_column(name:, class_node:, table:)
65
+ return unless table
66
+ return name if table.with_column?(name: name)
67
+
68
+ find_belongs_to(class_node) do |belongs_to|
69
+ next unless belongs_to.first_argument.value.to_s == name
70
+
71
+ fk = foreign_key_of(belongs_to) || "#{name}_id"
72
+ next unless table.with_column?(name: fk)
73
+
74
+ return polymorphic?(belongs_to) ? [fk, "#{name}_type"] : fk
75
+ end
76
+ nil
77
+ end
78
+
79
+ def foreign_key_of(belongs_to)
80
+ options = belongs_to.last_argument
81
+ return unless options.hash_type?
82
+
83
+ options.each_pair.find do |pair|
84
+ next unless pair.key.sym_type? && pair.key.value == :foreign_key
85
+ next unless pair.value.sym_type? || pair.value.str_type?
86
+
87
+ break pair.value.value.to_s
88
+ end
89
+ end
90
+
91
+ def polymorphic?(belongs_to)
92
+ options = belongs_to.last_argument
93
+ return false unless options.hash_type?
94
+
95
+ options.each_pair.any? do |pair|
96
+ pair.key.sym_type? && pair.key.value == :polymorphic && pair.value.true_type?
97
+ end
98
+ end
99
+
100
+ def in_where?(node)
101
+ send_node = node.each_ancestor(:send).first
102
+ send_node && WHERE_METHODS.include?(send_node.method_name)
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # A mixin to extend cops for Active Record features
6
+ module ActiveRecordMigrationsHelper
7
+ extend NodePattern::Macros
8
+
9
+ RAILS_ABSTRACT_SCHEMA_DEFINITIONS = %i[
10
+ bigint binary boolean date datetime decimal float integer json string
11
+ text time timestamp virtual
12
+ ].freeze
13
+ RAILS_ABSTRACT_SCHEMA_DEFINITIONS_HELPERS = %i[column references belongs_to primary_key numeric].freeze
14
+ POSTGRES_SCHEMA_DEFINITIONS = %i[
15
+ bigserial bit bit_varying cidr citext daterange hstore inet interval
16
+ int4range int8range jsonb ltree macaddr money numrange oid point line
17
+ lseg box path polygon circle serial tsrange tstzrange tsvector uuid xml
18
+ ].freeze
19
+ MYSQL_SCHEMA_DEFINITIONS = %i[
20
+ blob tinyblob mediumblob longblob tinytext mediumtext longtext
21
+ unsigned_integer unsigned_bigint unsigned_float unsigned_decimal
22
+ ].freeze
23
+
24
+ def_node_matcher :create_table_with_block?, <<~PATTERN
25
+ (block
26
+ (send nil? :create_table ...)
27
+ (args (arg _var))
28
+ _)
29
+ PATTERN
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # A mixin to return all of the class send nodes.
6
+ module ClassSendNodeHelper
7
+ def class_send_nodes(class_node)
8
+ class_def = class_node.body
9
+
10
+ return [] unless class_def
11
+
12
+ if class_def.send_type?
13
+ [class_def]
14
+ else
15
+ class_def.each_child_node(:send)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for enforcing a specific superclass.
6
+ module EnforceSuperclass
7
+ def self.included(base)
8
+ base.def_node_matcher :class_definition, <<~PATTERN
9
+ (class (const _ !:#{base::SUPERCLASS}) #{base::BASE_PATTERN} ...)
10
+ PATTERN
11
+
12
+ base.def_node_matcher :class_new_definition, <<~PATTERN
13
+ [!^(casgn {nil? cbase} :#{base::SUPERCLASS} ...)
14
+ !^^(casgn {nil? cbase} :#{base::SUPERCLASS} (block ...))
15
+ (send (const {nil? cbase} :Class) :new #{base::BASE_PATTERN})]
16
+ PATTERN
17
+ end
18
+
19
+ def on_class(node)
20
+ class_definition(node) do
21
+ register_offense(node.children[1])
22
+ end
23
+ end
24
+
25
+ def on_send(node)
26
+ class_new_definition(node) do
27
+ register_offense(node.children.last)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def register_offense(offense_node)
34
+ add_offense(offense_node) do |corrector|
35
+ corrector.replace(offense_node, self.class::SUPERCLASS)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for Rails/IndexBy and Rails/IndexWith
6
+ module IndexMethod # rubocop:disable Metrics/ModuleLength
7
+ RESTRICT_ON_SEND = %i[each_with_object to_h map collect []].freeze
8
+
9
+ def on_block(node) # rubocop:todo InternalAffairs/NumblockHandler
10
+ on_bad_each_with_object(node) do |*match|
11
+ handle_possible_offense(node, match, 'each_with_object')
12
+ end
13
+
14
+ return if target_ruby_version < 2.6
15
+
16
+ on_bad_to_h(node) do |*match|
17
+ handle_possible_offense(node, match, 'to_h { ... }')
18
+ end
19
+ end
20
+
21
+ def on_send(node)
22
+ on_bad_map_to_h(node) do |*match|
23
+ handle_possible_offense(node, match, 'map { ... }.to_h')
24
+ end
25
+
26
+ on_bad_hash_brackets_map(node) do |*match|
27
+ handle_possible_offense(node, match, 'Hash[map { ... }]')
28
+ end
29
+ end
30
+
31
+ def on_csend(node)
32
+ on_bad_map_to_h(node) do |*match|
33
+ handle_possible_offense(node, match, 'map { ... }.to_h')
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ # @abstract Implemented with `def_node_matcher`
40
+ def on_bad_each_with_object(_node)
41
+ raise NotImplementedError
42
+ end
43
+
44
+ # @abstract Implemented with `def_node_matcher`
45
+ def on_bad_to_h(_node)
46
+ raise NotImplementedError
47
+ end
48
+
49
+ # @abstract Implemented with `def_node_matcher`
50
+ def on_bad_map_to_h(_node)
51
+ raise NotImplementedError
52
+ end
53
+
54
+ # @abstract Implemented with `def_node_matcher`
55
+ def on_bad_hash_brackets_map(_node)
56
+ raise NotImplementedError
57
+ end
58
+
59
+ def handle_possible_offense(node, match, match_desc)
60
+ captures = extract_captures(match)
61
+
62
+ return if captures.noop_transformation?
63
+
64
+ add_offense(
65
+ node, message: "Prefer `#{new_method_name}` over `#{match_desc}`."
66
+ ) do |corrector|
67
+ correction = prepare_correction(node)
68
+ execute_correction(corrector, node, correction)
69
+ end
70
+ end
71
+
72
+ def extract_captures(match)
73
+ argname, body_expr = *match
74
+ Captures.new(argname, body_expr)
75
+ end
76
+
77
+ def new_method_name
78
+ raise NotImplementedError
79
+ end
80
+
81
+ def prepare_correction(node)
82
+ if (match = on_bad_each_with_object(node))
83
+ Autocorrection.from_each_with_object(node, match)
84
+ elsif (match = on_bad_to_h(node))
85
+ Autocorrection.from_to_h(node, match)
86
+ elsif (match = on_bad_map_to_h(node))
87
+ Autocorrection.from_map_to_h(node, match)
88
+ elsif (match = on_bad_hash_brackets_map(node))
89
+ Autocorrection.from_hash_brackets_map(node, match)
90
+ else
91
+ raise 'unreachable'
92
+ end
93
+ end
94
+
95
+ def execute_correction(corrector, node, correction)
96
+ correction.strip_prefix_and_suffix(node, corrector)
97
+ correction.set_new_method_name(new_method_name, corrector)
98
+
99
+ captures = extract_captures(correction.match)
100
+ correction.set_new_arg_name(captures.transformed_argname, corrector)
101
+ correction.set_new_body_expression(captures.transforming_body_expr, corrector)
102
+ 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
+ end
164
+ end
165
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for cops working with migrations
6
+ module MigrationsHelper
7
+ extend NodePattern::Macros
8
+
9
+ def_node_matcher :migration_class?, <<~PATTERN
10
+ (class
11
+ (const {nil? cbase} _)
12
+ (send
13
+ (const (const {nil? cbase} :ActiveRecord) :Migration)
14
+ :[]
15
+ (float _))
16
+ _)
17
+ PATTERN
18
+
19
+ def in_migration?(node)
20
+ node.each_ancestor(:class).any? do |class_node|
21
+ migration_class?(class_node)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -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
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Using `ActionController::TestCase`` is discouraged and should be replaced by
7
+ # `ActionDispatch::IntegrationTest``. Controller tests are too close to the
8
+ # internals of a controller whereas integration tests mimic the browser/user.
9
+ #
10
+ # @safety
11
+ # This cop's autocorrection is unsafe because the API of each test case class is different.
12
+ # Make sure to update each test of your controller test cases after changing the superclass.
13
+ #
14
+ # @example
15
+ # # bad
16
+ # class MyControllerTest < ActionController::TestCase
17
+ # end
18
+ #
19
+ # # good
20
+ # class MyControllerTest < ActionDispatch::IntegrationTest
21
+ # end
22
+ #
23
+ class ActionControllerTestCase < Base
24
+ extend AutoCorrector
25
+ extend TargetRailsVersion
26
+
27
+ MSG = 'Use `ActionDispatch::IntegrationTest` instead.'
28
+
29
+ minimum_target_rails_version 5.0
30
+
31
+ def_node_matcher :action_controller_test_case?, <<~PATTERN
32
+ (class
33
+ (const _ _)
34
+ (const (const {nil? cbase} :ActionController) :TestCase) _)
35
+ PATTERN
36
+
37
+ def on_class(node)
38
+ return unless action_controller_test_case?(node)
39
+
40
+ add_offense(node.parent_class) do |corrector|
41
+ corrector.replace(node.parent_class, 'ActionDispatch::IntegrationTest')
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -3,14 +3,11 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop enforces the consistent use of action filter methods.
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.
10
10
  #
11
- # If the TargetRailsVersion is set to less than 4.0, the cop will enforce
12
- # the use of filter methods.
13
- #
14
11
  # @example EnforcedStyle: action (default)
15
12
  # # bad
16
13
  # after_filter :do_stuff
@@ -32,9 +29,9 @@ module RuboCop
32
29
  # after_filter :do_stuff
33
30
  # append_around_filter :do_stuff
34
31
  # skip_after_filter :do_stuff
35
- class ActionFilter < Cop
36
- extend TargetRailsVersion
32
+ class ActionFilter < Base
37
33
  include ConfigurableEnforcedStyle
34
+ extend AutoCorrector
38
35
 
39
36
  MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
40
37
 
@@ -70,9 +67,9 @@ module RuboCop
70
67
  skip_action_callback
71
68
  ].freeze
72
69
 
73
- minimum_target_rails_version 4.0
70
+ RESTRICT_ON_SEND = FILTER_METHODS + ACTION_METHODS
74
71
 
75
- def on_block(node)
72
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
76
73
  check_method_node(node.send_node)
77
74
  end
78
75
 
@@ -80,24 +77,17 @@ module RuboCop
80
77
  check_method_node(node) unless node.receiver
81
78
  end
82
79
 
83
- def autocorrect(node)
84
- lambda do |corrector|
85
- corrector.replace(node.loc.selector,
86
- preferred_method(node.loc.selector.source).to_s)
87
- end
88
- end
89
-
90
80
  private
91
81
 
92
82
  def check_method_node(node)
93
- return unless bad_methods.include?(node.method_name)
83
+ method_name = node.method_name
84
+ return unless bad_methods.include?(method_name)
94
85
 
95
- add_offense(node, location: :selector)
96
- end
86
+ message = format(MSG, prefer: preferred_method(method_name), current: method_name)
97
87
 
98
- def message(node)
99
- format(MSG, prefer: preferred_method(node.method_name),
100
- current: node.method_name)
88
+ add_offense(node.loc.selector, message: message) do |corrector|
89
+ corrector.replace(node.loc.selector, preferred_method(node.loc.selector.source))
90
+ end
101
91
  end
102
92
 
103
93
  def bad_methods