Skip to content

Commit

Permalink
Merge pull request rubocop#688 from koic/support_autocorrect_for_rail…
Browse files Browse the repository at this point in the history
…s_deprecated_active_model_errors_methods

Support auto-correction for `Rails/DeprecatedActiveModelErrorsMethods`
  • Loading branch information
koic committed May 1, 2022
2 parents 5b643d8 + 41d890d commit 8119838
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#688](https://github.com/rubocop/rubocop-rails/pull/688): Support auto-correction for `Rails/DeprecatedActiveModelErrorsMethods`. ([@koic][])
1 change: 1 addition & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ Rails/DeprecatedActiveModelErrorsMethods:
Enabled: pending
Safe: false
VersionAdded: '2.14'
VersionChanged: '<<next>>'

Rails/DuplicateAssociation:
Description: "Don't repeat associations in a model."
Expand Down
41 changes: 40 additions & 1 deletion lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ module Rails
# user.errors.delete(:name)
#
class DeprecatedActiveModelErrorsMethods < Base
include RangeHelp
extend AutoCorrector

MSG = 'Avoid manipulating ActiveModel errors as hash directly.'
AUTOCORECTABLE_METHODS = %i[<< clear].freeze

MANIPULATIVE_METHODS = Set[
*%i[
Expand Down Expand Up @@ -89,12 +93,47 @@ class DeprecatedActiveModelErrorsMethods < Base

def on_send(node)
any_manipulation?(node) do
add_offense(node)
add_offense(node) do |corrector|
next unless AUTOCORECTABLE_METHODS.include?(node.method_name)

autocorrect(corrector, node)
end
end
end

private

def autocorrect(corrector, node)
receiver = node.receiver

if receiver.receiver.method?(:messages)
corrector.remove(receiver.receiver.loc.dot)
corrector.remove(receiver.receiver.loc.selector)
end

range = offense_range(node, receiver)
replacement = replacement(node, receiver)

corrector.replace(range, replacement)
end

def offense_range(node, receiver)
range_between(receiver.receiver.source_range.end_pos, node.source_range.end_pos)
end

def replacement(node, receiver)
key = receiver.first_argument.source

case node.method_name
when :<<
value = node.first_argument.source

".add(#{key}, #{value})"
when :clear
".delete(#{key})"
end
end

def receiver_matcher(node)
model_file? ? receiver_matcher_inside_model(node) : receiver_matcher_outside_model(node)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
RSpec.describe RuboCop::Cop::Rails::DeprecatedActiveModelErrorsMethods, :config do
shared_examples 'errors call with explicit receiver' do
context 'when modifying errors' do
it 'registers an offense' do
it 'registers and corrects an offense' do
expect_offense(<<~RUBY, file_path)
user.errors[:name] << 'msg'
^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid manipulating ActiveModel errors as hash directly.
RUBY

expect_correction(<<~RUBY)
user.errors.add(:name, 'msg')
RUBY
end

context 'when assigning' do
Expand All @@ -18,14 +22,31 @@
RUBY
end
end

context 'when using `clear` method' do
it 'registers and corrects an offense' do
expect_offense(<<~RUBY, file_path)
user.errors[:name].clear
^^^^^^^^^^^^^^^^^^^^^^^^ Avoid manipulating ActiveModel errors as hash directly.
RUBY

expect_correction(<<~RUBY)
user.errors.delete(:name)
RUBY
end
end
end

context 'when modifying errors.messages' do
it 'registers an offense' do
it 'registers and corrects an offense' do
expect_offense(<<~RUBY, file_path)
user.errors.messages[:name] << 'msg'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid manipulating ActiveModel errors as hash directly.
RUBY

expect_correction(<<~RUBY)
user.errors.add(:name, 'msg')
RUBY
end

context 'when assigning' do
Expand Down

0 comments on commit 8119838

Please sign in to comment.