rubocop-rails 2.8.0 → 2.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +18 -2
  4. data/config/default.yml +101 -5
  5. data/config/obsoletion.yml +7 -0
  6. data/lib/rubocop/cop/mixin/active_record_helper.rb +16 -3
  7. data/lib/rubocop/cop/mixin/enforce_superclass.rb +40 -0
  8. data/lib/rubocop/cop/mixin/index_method.rb +8 -11
  9. data/lib/rubocop/cop/rails/action_filter.rb +10 -14
  10. data/lib/rubocop/cop/rails/active_record_aliases.rb +13 -17
  11. data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +17 -12
  12. data/lib/rubocop/cop/rails/active_record_override.rb +1 -1
  13. data/lib/rubocop/cop/rails/active_support_aliases.rb +12 -21
  14. data/lib/rubocop/cop/rails/after_commit_override.rb +9 -2
  15. data/lib/rubocop/cop/rails/application_controller.rb +3 -7
  16. data/lib/rubocop/cop/rails/application_job.rb +2 -1
  17. data/lib/rubocop/cop/rails/application_mailer.rb +2 -7
  18. data/lib/rubocop/cop/rails/application_record.rb +2 -7
  19. data/lib/rubocop/cop/rails/arel_star.rb +41 -0
  20. data/lib/rubocop/cop/rails/assert_not.rb +8 -10
  21. data/lib/rubocop/cop/rails/attribute_default_block_value.rb +90 -0
  22. data/lib/rubocop/cop/rails/belongs_to.rb +10 -19
  23. data/lib/rubocop/cop/rails/blank.rb +31 -27
  24. data/lib/rubocop/cop/rails/bulk_change_table.rb +1 -1
  25. data/lib/rubocop/cop/rails/content_tag.rb +33 -18
  26. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +2 -1
  27. data/lib/rubocop/cop/rails/date.rb +10 -11
  28. data/lib/rubocop/cop/rails/default_scope.rb +11 -4
  29. data/lib/rubocop/cop/rails/delegate.rb +9 -9
  30. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +7 -8
  31. data/lib/rubocop/cop/rails/dynamic_find_by.rb +15 -12
  32. data/lib/rubocop/cop/rails/enum_hash.rb +11 -10
  33. data/lib/rubocop/cop/rails/enum_uniqueness.rb +2 -1
  34. data/lib/rubocop/cop/rails/environment_comparison.rb +18 -14
  35. data/lib/rubocop/cop/rails/environment_variable_access.rb +67 -0
  36. data/lib/rubocop/cop/rails/exit.rb +4 -10
  37. data/lib/rubocop/cop/rails/file_path.rb +6 -7
  38. data/lib/rubocop/cop/rails/find_by.rb +13 -13
  39. data/lib/rubocop/cop/rails/find_by_id.rb +12 -21
  40. data/lib/rubocop/cop/rails/find_each.rb +19 -18
  41. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +3 -2
  42. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +37 -6
  43. data/lib/rubocop/cop/rails/helper_instance_variable.rb +29 -3
  44. data/lib/rubocop/cop/rails/http_positional_arguments.rb +32 -21
  45. data/lib/rubocop/cop/rails/http_status.rb +7 -9
  46. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +8 -6
  47. data/lib/rubocop/cop/rails/index_by.rb +3 -2
  48. data/lib/rubocop/cop/rails/index_with.rb +3 -2
  49. data/lib/rubocop/cop/rails/inquiry.rb +4 -3
  50. data/lib/rubocop/cop/rails/inverse_of.rb +3 -2
  51. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +17 -15
  52. data/lib/rubocop/cop/rails/link_to_blank.rb +25 -23
  53. data/lib/rubocop/cop/rails/mailer_name.rb +19 -13
  54. data/lib/rubocop/cop/rails/match_route.rb +14 -13
  55. data/lib/rubocop/cop/rails/negate_include.rb +10 -8
  56. data/lib/rubocop/cop/rails/not_null_column.rb +2 -1
  57. data/lib/rubocop/cop/rails/order_by_id.rb +1 -2
  58. data/lib/rubocop/cop/rails/output.rb +5 -2
  59. data/lib/rubocop/cop/rails/output_safety.rb +3 -2
  60. data/lib/rubocop/cop/rails/pick.rb +14 -12
  61. data/lib/rubocop/cop/rails/pluck.rb +6 -9
  62. data/lib/rubocop/cop/rails/pluck_id.rb +4 -6
  63. data/lib/rubocop/cop/rails/pluck_in_where.rb +7 -7
  64. data/lib/rubocop/cop/rails/pluralization_grammar.rb +10 -14
  65. data/lib/rubocop/cop/rails/presence.rb +12 -13
  66. data/lib/rubocop/cop/rails/present.rb +30 -24
  67. data/lib/rubocop/cop/rails/rake_environment.rb +8 -10
  68. data/lib/rubocop/cop/rails/read_write_attribute.rb +12 -11
  69. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +29 -31
  70. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +9 -12
  71. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +11 -10
  72. data/lib/rubocop/cop/rails/reflection_class_name.rb +17 -3
  73. data/lib/rubocop/cop/rails/refute_methods.rb +9 -10
  74. data/lib/rubocop/cop/rails/relative_date_constant.rb +30 -21
  75. data/lib/rubocop/cop/rails/render_inline.rb +2 -1
  76. data/lib/rubocop/cop/rails/render_plain_text.rb +9 -14
  77. data/lib/rubocop/cop/rails/request_referer.rb +7 -7
  78. data/lib/rubocop/cop/rails/require_dependency.rb +38 -0
  79. data/lib/rubocop/cop/rails/reversible_migration.rb +4 -8
  80. data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +75 -0
  81. data/lib/rubocop/cop/rails/safe_navigation.rb +30 -11
  82. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +5 -10
  83. data/lib/rubocop/cop/rails/save_bang.rb +17 -20
  84. data/lib/rubocop/cop/rails/scope_args.rb +2 -1
  85. data/lib/rubocop/cop/rails/short_i18n.rb +7 -9
  86. data/lib/rubocop/cop/rails/skips_model_validations.rb +4 -4
  87. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +5 -6
  88. data/lib/rubocop/cop/rails/time_zone.rb +35 -25
  89. data/lib/rubocop/cop/rails/time_zone_assignment.rb +37 -0
  90. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +4 -6
  91. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +4 -2
  92. data/lib/rubocop/cop/rails/unknown_env.rb +3 -3
  93. data/lib/rubocop/cop/rails/validation.rb +15 -14
  94. data/lib/rubocop/cop/rails/where_equals.rb +98 -0
  95. data/lib/rubocop/cop/rails/where_exists.rb +19 -13
  96. data/lib/rubocop/cop/rails/where_not.rb +14 -19
  97. data/lib/rubocop/cop/rails_cops.rb +8 -0
  98. data/lib/rubocop/rails.rb +2 -0
  99. data/lib/rubocop/rails/schema_loader.rb +4 -4
  100. data/lib/rubocop/rails/schema_loader/schema.rb +2 -4
  101. data/lib/rubocop/rails/version.rb +5 -1
  102. metadata +29 -14
@@ -50,7 +50,8 @@ module RuboCop
50
50
  #
51
51
  # @see https://guides.rubyonrails.org/5_0_release_notes.html
52
52
  # @see https://github.com/rails/rails/pull/18937
53
- class BelongsTo < Cop
53
+ class BelongsTo < Base
54
+ extend AutoCorrector
54
55
  extend TargetRailsVersion
55
56
 
56
57
  minimum_target_rails_version 5.0
@@ -64,35 +65,25 @@ module RuboCop
64
65
  'option is deprecated and you want to use `optional: false`. ' \
65
66
  'In most configurations, this is the default and you can omit ' \
66
67
  'this option altogether'
68
+ RESTRICT_ON_SEND = %i[belongs_to].freeze
67
69
 
68
70
  def_node_matcher :match_belongs_to_with_options, <<~PATTERN
69
- (send _ :belongs_to _
71
+ (send _ :belongs_to ...
70
72
  (hash <$(pair (sym :required) ${true false}) ...>)
71
73
  )
72
74
  PATTERN
73
75
 
74
76
  def on_send(node)
75
- match_belongs_to_with_options(node) do |_option_node, option_value|
76
- message =
77
+ match_belongs_to_with_options(node) do |option_node, option_value|
78
+ message, replacement =
77
79
  if option_value.true_type?
78
- SUPERFLOUS_REQUIRE_TRUE_MSG
80
+ [SUPERFLOUS_REQUIRE_TRUE_MSG, 'optional: false']
79
81
  elsif option_value.false_type?
80
- SUPERFLOUS_REQUIRE_FALSE_MSG
82
+ [SUPERFLOUS_REQUIRE_FALSE_MSG, 'optional: true']
81
83
  end
82
84
 
83
- add_offense(node, message: message, location: :selector)
84
- end
85
- end
86
-
87
- def autocorrect(node)
88
- option_node, option_value = match_belongs_to_with_options(node)
89
- return unless option_node
90
-
91
- lambda do |corrector|
92
- if option_value.true_type?
93
- corrector.replace(option_node.loc.expression, 'optional: false')
94
- elsif option_value.false_type?
95
- corrector.replace(option_node.loc.expression, 'optional: true')
85
+ add_offense(node.loc.selector, message: message) do |corrector|
86
+ corrector.replace(option_node.loc.expression, replacement)
96
87
  end
97
88
  end
98
89
  end
@@ -6,6 +6,10 @@ module RuboCop
6
6
  # This cop checks for code that can be written with simpler conditionals
7
7
  # using `Object#blank?` defined by Active Support.
8
8
  #
9
+ # This cop is marked as unsafe auto-correction, because `' '.empty?` returns false,
10
+ # but `' '.blank?` returns true. Therefore, auto-correction is not compatible
11
+ # if the receiver is a non-empty blank string, tab, or newline meta characters.
12
+ #
9
13
  # Interaction with `Style/UnlessElse`:
10
14
  # The configuration of `NotPresent` will not produce an offense in the
11
15
  # context of `unless else` if `Style/UnlessElse` is inabled. This is
@@ -53,11 +57,14 @@ module RuboCop
53
57
  # def blank?
54
58
  # !present?
55
59
  # end
56
- class Blank < Cop
60
+ class Blank < Base
61
+ extend AutoCorrector
62
+
57
63
  MSG_NIL_OR_EMPTY = 'Use `%<prefer>s` instead of `%<current>s`.'
58
64
  MSG_NOT_PRESENT = 'Use `%<prefer>s` instead of `%<current>s`.'
59
65
  MSG_UNLESS_PRESENT = 'Use `if %<prefer>s` instead of ' \
60
66
  '`%<current>s`.'
67
+ RESTRICT_ON_SEND = %i[!].freeze
61
68
 
62
69
  # `(send nil $_)` is not actually a valid match for an offense. Nodes
63
70
  # that have a single method call on the left hand side
@@ -93,10 +100,10 @@ module RuboCop
93
100
  # accepts !present? if its in the body of a `blank?` method
94
101
  next if defining_blank?(node.parent)
95
102
 
96
- add_offense(node,
97
- message: format(MSG_NOT_PRESENT,
98
- prefer: replacement(receiver),
99
- current: node.source))
103
+ message = format(MSG_NOT_PRESENT, prefer: replacement(receiver), current: node.source)
104
+ add_offense(node, message: message) do |corrector|
105
+ autocorrect(corrector, node)
106
+ end
100
107
  end
101
108
  end
102
109
 
@@ -106,10 +113,10 @@ module RuboCop
106
113
  nil_or_empty?(node) do |var1, var2|
107
114
  return unless var1 == var2
108
115
 
109
- add_offense(node,
110
- message: format(MSG_NIL_OR_EMPTY,
111
- prefer: replacement(var1),
112
- current: node.source))
116
+ message = format(MSG_NIL_OR_EMPTY, prefer: replacement(var1), current: node.source)
117
+ add_offense(node, message: message) do |corrector|
118
+ autocorrect(corrector, node)
119
+ end
113
120
  end
114
121
  end
115
122
 
@@ -121,31 +128,28 @@ module RuboCop
121
128
  unless_present?(node) do |method_call, receiver|
122
129
  range = unless_condition(node, method_call)
123
130
 
124
- add_offense(node,
125
- location: range,
126
- message: format(MSG_UNLESS_PRESENT,
127
- prefer: replacement(receiver),
128
- current: range.source))
131
+ message = format(MSG_UNLESS_PRESENT, prefer: replacement(receiver), current: range.source)
132
+ add_offense(range, message: message) do |corrector|
133
+ autocorrect(corrector, node)
134
+ end
129
135
  end
130
136
  end
131
137
 
132
- def autocorrect(node)
133
- lambda do |corrector|
134
- method_call, variable1 = unless_present?(node)
138
+ private
135
139
 
136
- if method_call
137
- corrector.replace(node.loc.keyword, 'if')
138
- range = method_call.loc.expression
139
- else
140
- variable1, _variable2 = nil_or_empty?(node) || not_present?(node)
141
- range = node.loc.expression
142
- end
140
+ def autocorrect(corrector, node)
141
+ method_call, variable1 = unless_present?(node)
143
142
 
144
- corrector.replace(range, replacement(variable1))
143
+ if method_call
144
+ corrector.replace(node.loc.keyword, 'if')
145
+ range = method_call.loc.expression
146
+ else
147
+ variable1, _variable2 = nil_or_empty?(node) || not_present?(node)
148
+ range = node.loc.expression
145
149
  end
146
- end
147
150
 
148
- private
151
+ corrector.replace(range, replacement(variable1))
152
+ end
149
153
 
150
154
  def unless_condition(node, method_call)
151
155
  if node.modifier_form?
@@ -65,7 +65,7 @@ module RuboCop
65
65
  #
66
66
  # @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_table
67
67
  # @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html
68
- class BulkChangeTable < Cop
68
+ class BulkChangeTable < Base
69
69
  MSG_FOR_CHANGE_TABLE = <<~MSG.chomp
70
70
  You can combine alter queries using `bulk: true` options.
71
71
  MSG
@@ -18,42 +18,57 @@ module RuboCop
18
18
  # tag.p('Hello world!')
19
19
  # tag.br
20
20
  # content_tag(name, 'Hello world!')
21
- class ContentTag < Cop
21
+ class ContentTag < Base
22
22
  include RangeHelp
23
+ extend AutoCorrector
23
24
  extend TargetRailsVersion
24
25
 
25
26
  minimum_target_rails_version 5.1
26
27
 
27
28
  MSG = 'Use `tag` instead of `content_tag`.'
29
+ RESTRICT_ON_SEND = %i[content_tag].freeze
28
30
 
29
- def on_send(node)
30
- return unless node.method?(:content_tag)
31
+ def on_new_investigation
32
+ @corrected_nodes = nil
33
+ end
31
34
 
35
+ def on_send(node)
32
36
  first_argument = node.first_argument
33
- return unless first_argument
37
+ return if !first_argument ||
38
+ allowed_argument?(first_argument) ||
39
+ corrected_ancestor?(node)
40
+
41
+ add_offense(node) do |corrector|
42
+ autocorrect(corrector, node)
43
+
44
+ @corrected_nodes ||= Set.new.compare_by_identity
45
+ @corrected_nodes.add(node)
46
+ end
47
+ end
48
+
49
+ private
34
50
 
35
- return if first_argument.variable? || first_argument.send_type? || first_argument.const_type?
51
+ def corrected_ancestor?(node)
52
+ node.each_ancestor(:send).any? { |ancestor| @corrected_nodes&.include?(ancestor) }
53
+ end
36
54
 
37
- add_offense(node)
55
+ def allowed_argument?(argument)
56
+ argument.variable? || argument.send_type? || argument.const_type? || argument.splat_type?
38
57
  end
39
58
 
40
- def autocorrect(node)
41
- lambda do |corrector|
42
- if method_name?(node.first_argument)
43
- range = correction_range(node)
59
+ def autocorrect(corrector, node)
60
+ if method_name?(node.first_argument)
61
+ range = correction_range(node)
44
62
 
45
- rest_args = node.arguments.drop(1)
46
- replacement = "tag.#{node.first_argument.value.to_s.underscore}(#{rest_args.map(&:source).join(', ')})"
63
+ rest_args = node.arguments.drop(1)
64
+ replacement = "tag.#{node.first_argument.value.to_s.underscore}(#{rest_args.map(&:source).join(', ')})"
47
65
 
48
- corrector.replace(range, replacement)
49
- else
50
- corrector.replace(node.loc.selector, 'tag')
51
- end
66
+ corrector.replace(range, replacement)
67
+ else
68
+ corrector.replace(node.loc.selector, 'tag')
52
69
  end
53
70
  end
54
71
 
55
- private
56
-
57
72
  def method_name?(node)
58
73
  return false unless node.str_type? || node.sym_type?
59
74
 
@@ -40,8 +40,9 @@ module RuboCop
40
40
  #
41
41
  # t.datetime :updated_at, default: -> { 'CURRENT_TIMESTAMP' }
42
42
  # end
43
- class CreateTableWithTimestamps < Cop
43
+ class CreateTableWithTimestamps < Base
44
44
  MSG = 'Add timestamps when creating a new table.'
45
+ RESTRICT_ON_SEND = %i[create_table].freeze
45
46
 
46
47
  def_node_matcher :create_table_with_block?, <<~PATTERN
47
48
  (block
@@ -43,7 +43,7 @@ module RuboCop
43
43
  # Date.yesterday
44
44
  # date.in_time_zone
45
45
  #
46
- class Date < Cop
46
+ class Date < Base
47
47
  include ConfigurableEnforcedStyle
48
48
 
49
49
  MSG = 'Do not use `Date.%<method_called>s` without zone. Use ' \
@@ -52,6 +52,8 @@ module RuboCop
52
52
  MSG_SEND = 'Do not use `%<method>s` on Date objects, because they ' \
53
53
  'know nothing about the time zone in use.'
54
54
 
55
+ RESTRICT_ON_SEND = %i[to_time to_time_in_current_zone].freeze
56
+
55
57
  BAD_DAYS = %i[today current yesterday tomorrow].freeze
56
58
 
57
59
  DEPRECATED_METHODS = [
@@ -76,8 +78,7 @@ module RuboCop
76
78
 
77
79
  check_deprecated_methods(node)
78
80
 
79
- add_offense(node, location: :selector,
80
- message: format(MSG_SEND, method: node.method_name))
81
+ add_offense(node.loc.selector, message: format(MSG_SEND, method: node.method_name))
81
82
  end
82
83
  alias on_csend on_send
83
84
 
@@ -87,10 +88,9 @@ module RuboCop
87
88
  DEPRECATED_METHODS.each do |method|
88
89
  next unless node.method?(method[:deprecated].to_sym)
89
90
 
90
- add_offense(node, location: :selector,
91
- message: format(DEPRECATED_MSG,
92
- deprecated: method[:deprecated],
93
- relevant: method[:relevant]))
91
+ message = format(DEPRECATED_MSG, deprecated: method[:deprecated], relevant: method[:relevant])
92
+
93
+ add_offense(node.loc.selector, message: message)
94
94
  end
95
95
  end
96
96
 
@@ -104,10 +104,9 @@ module RuboCop
104
104
  day = method_name
105
105
  day = 'today' if method_name == 'current'
106
106
 
107
- add_offense(node, location: :selector,
108
- message: format(MSG,
109
- method_called: method_name,
110
- day: day))
107
+ message = format(MSG, method_called: method_name, day: day)
108
+
109
+ add_offense(node.loc.selector, message: message)
111
110
  end
112
111
 
113
112
  def extract_method_chain(node)
@@ -22,8 +22,9 @@ module RuboCop
22
22
  # where(hidden: false)
23
23
  # end
24
24
  #
25
- class DefaultScope < Cop
25
+ class DefaultScope < Base
26
26
  MSG = 'Avoid use of `default_scope`. It is better to use explicitly named scopes.'
27
+ RESTRICT_ON_SEND = %i[default_scope].freeze
27
28
 
28
29
  def_node_matcher :method_call?, <<~PATTERN
29
30
  (send nil? :default_scope ...)
@@ -38,15 +39,21 @@ module RuboCop
38
39
  PATTERN
39
40
 
40
41
  def on_send(node)
41
- add_offense(node, location: :selector) if method_call?(node)
42
+ return unless method_call?(node)
43
+
44
+ add_offense(node.loc.selector)
42
45
  end
43
46
 
44
47
  def on_defs(node)
45
- add_offense(node, location: :name) if class_method_definition?(node)
48
+ return unless class_method_definition?(node)
49
+
50
+ add_offense(node.loc.name)
46
51
  end
47
52
 
48
53
  def on_sclass(node)
49
- eigenclass_method_definition?(node) { |default_scope| add_offense(default_scope, location: :name) }
54
+ eigenclass_method_definition?(node) do |default_scope|
55
+ add_offense(default_scope.loc.name)
56
+ end
50
57
  end
51
58
  end
52
59
  end
@@ -52,7 +52,9 @@ module RuboCop
52
52
  #
53
53
  # # good
54
54
  # delegate :bar, to: :foo, prefix: true
55
- class Delegate < Cop
55
+ class Delegate < Base
56
+ extend AutoCorrector
57
+
56
58
  MSG = 'Use `delegate` to define delegations.'
57
59
 
58
60
  def_node_matcher :delegate?, <<~PATTERN
@@ -64,22 +66,20 @@ module RuboCop
64
66
  return unless trivial_delegate?(node)
65
67
  return if private_or_protected_delegation(node)
66
68
 
67
- add_offense(node, location: :keyword)
69
+ register_offense(node)
68
70
  end
69
71
 
70
- def autocorrect(node)
71
- delegation = ["delegate :#{node.body.method_name}",
72
- "to: :#{node.body.receiver.method_name}"]
72
+ private
73
73
 
74
- delegation << ['prefix: true'] if node.method?(prefixed_method_name(node.body))
74
+ def register_offense(node)
75
+ add_offense(node.loc.keyword) do |corrector|
76
+ delegation = ["delegate :#{node.body.method_name}", "to: :#{node.body.receiver.method_name}"]
77
+ delegation << ['prefix: true'] if node.method?(prefixed_method_name(node.body))
75
78
 
76
- lambda do |corrector|
77
79
  corrector.replace(node.source_range, delegation.join(', '))
78
80
  end
79
81
  end
80
82
 
81
- private
82
-
83
83
  def trivial_delegate?(def_node)
84
84
  delegate?(def_node) &&
85
85
  method_name_matches?(def_node.method_name, def_node.body) &&
@@ -13,22 +13,21 @@ module RuboCop
13
13
  #
14
14
  # # good
15
15
  # delegate :foo, to: :bar, allow_nil: true
16
- class DelegateAllowBlank < Cop
16
+ class DelegateAllowBlank < Base
17
+ extend AutoCorrector
18
+
17
19
  MSG = '`allow_blank` is not a valid option, use `allow_nil`.'
20
+ RESTRICT_ON_SEND = %i[delegate].freeze
18
21
 
19
22
  def_node_matcher :allow_blank_option, <<~PATTERN
20
23
  (send nil? :delegate _ (hash <$(pair (sym :allow_blank) true) ...>))
21
24
  PATTERN
22
25
 
23
26
  def on_send(node)
24
- allow_blank_option(node) do |offending_node|
25
- add_offense(offending_node)
26
- end
27
- end
27
+ return unless (offending_node = allow_blank_option(node))
28
28
 
29
- def autocorrect(pair_node)
30
- lambda do |corrector|
31
- corrector.replace(pair_node.key.source_range, 'allow_nil')
29
+ add_offense(offending_node) do |corrector|
30
+ corrector.replace(offending_node.key.source_range, 'allow_nil')
32
31
  end
33
32
  end
34
33
  end
@@ -31,36 +31,39 @@ module RuboCop
31
31
  #
32
32
  # # good
33
33
  # Gem::Specification.find_by_name('backend').gem_dir
34
- class DynamicFindBy < Cop
34
+ class DynamicFindBy < Base
35
+ include ActiveRecordHelper
36
+ extend AutoCorrector
37
+
35
38
  MSG = 'Use `%<static_name>s` instead of dynamic `%<method>s`.'
36
39
  METHOD_PATTERN = /^find_by_(.+?)(!)?$/.freeze
37
40
 
38
41
  def on_send(node)
39
- return if allowed_invocation?(node)
42
+ return if node.receiver.nil? && !inherit_active_record_base?(node) || allowed_invocation?(node)
40
43
 
41
44
  method_name = node.method_name
42
45
  static_name = static_method_name(method_name)
43
46
  return unless static_name
47
+ return if node.arguments.any?(&:splat_type?)
44
48
 
45
- add_offense(node,
46
- message: format(MSG, static_name: static_name,
47
- method: method_name))
49
+ message = format(MSG, static_name: static_name, method: method_name)
50
+ add_offense(node, message: message) do |corrector|
51
+ autocorrect(corrector, node)
52
+ end
48
53
  end
49
54
  alias on_csend on_send
50
55
 
51
- def autocorrect(node)
56
+ private
57
+
58
+ def autocorrect(corrector, node)
52
59
  keywords = column_keywords(node.method_name)
53
60
 
54
61
  return if keywords.size != node.arguments.size
55
62
 
56
- lambda do |corrector|
57
- autocorrect_method_name(corrector, node)
58
- autocorrect_argument_keywords(corrector, node, keywords)
59
- end
63
+ autocorrect_method_name(corrector, node)
64
+ autocorrect_argument_keywords(corrector, node, keywords)
60
65
  end
61
66
 
62
- private
63
-
64
67
  def allowed_invocation?(node)
65
68
  allowed_method?(node) || allowed_receiver?(node) ||
66
69
  whitelisted?(node)