From 430b91cebbf6ba6cce9237832448eb94db29b587 Mon Sep 17 00:00:00 2001 From: r7kamura Date: Mon, 21 Jan 2019 12:31:27 +0900 Subject: [PATCH] Support more complex argument patterns on `Rails/Validation` auto-correction --- CHANGELOG.md | 1 + lib/rubocop/cop/rails/validation.rb | 13 +- spec/rubocop/cop/rails/validation_spec.rb | 152 ++++++++++++++++++---- 3 files changed, 134 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 354bab24b2c..47e4b611b0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * [#6254](https://github.com/rubocop-hq/rubocop/issues/6254): Fix `Layout/RescueEnsureAlignment` for non-local assignments. ([@marcotc][]) * [#6648](https://github.com/rubocop-hq/rubocop/issues/6648): Fix auto-correction of `Style/EmptyLiteral` when `Hash.new` is passed as the first argument to `super`. ([@rrosenblum][]) * [#6351](https://github.com/rubocop-hq/rubocop/pull/6351): Fix a false positive for `Layout/ClosingParenthesisIndentation` when first argument is multiline. ([@antonzaytsev][]) +* [#6689](https://github.com/rubocop-hq/rubocop/pull/6689): Support more complex argument patterns on `Rails/Validation` auto-correction. ([@r7kamura][]) ## 0.63.1 (2019-01-22) diff --git a/lib/rubocop/cop/rails/validation.rb b/lib/rubocop/cop/rails/validation.rb index c3f1fd4ff52..3a56205717a 100644 --- a/lib/rubocop/cop/rails/validation.rb +++ b/lib/rubocop/cop/rails/validation.rb @@ -57,6 +57,9 @@ def on_send(node) end def autocorrect(node) + last_argument = node.arguments.last + return if !last_argument.literal? && !last_argument.splat_type? + lambda do |corrector| corrector.replace(node.loc.selector, 'validates') correct_validate_type(corrector, node) @@ -75,12 +78,14 @@ def preferred_method(method) end def correct_validate_type(corrector, node) - options = node.arguments.find { |arg| !arg.sym_type? } + last_argument = node.arguments.last validate_type = node.method_name.to_s.split('_')[1] - if options - corrector.replace(options.loc.expression, - "#{validate_type}: #{braced_options(options)}") + if last_argument.hash_type? + corrector.replace( + last_argument.loc.expression, + "#{validate_type}: #{braced_options(last_argument)}" + ) else corrector.insert_after(node.loc.expression, ", #{validate_type}: true") diff --git a/spec/rubocop/cop/rails/validation_spec.rb b/spec/rubocop/cop/rails/validation_spec.rb index 6bff597fe19..829a6c6d731 100644 --- a/spec/rubocop/cop/rails/validation_spec.rb +++ b/spec/rubocop/cop/rails/validation_spec.rb @@ -20,34 +20,130 @@ end end - describe 'autocorrect' do - described_class::TYPES.each do |parameter| - it "corrects validates_#{parameter}_of" do - new_source = autocorrect_source( - "validates_#{parameter}_of :full_name, :birth_date" - ) - expect(new_source).to eq( - "validates :full_name, :birth_date, #{parameter}: true" - ) - end - end - - it 'corrects validates_numericality_of with options' do - new_source = autocorrect_source( - 'validates_numericality_of :age, minimum: 0, maximum: 122' - ) - expect(new_source).to eq( - 'validates :age, numericality: { minimum: 0, maximum: 122 }' - ) - end - - it 'autocorrect validates_numericality_of with options in braces' do - new_source = autocorrect_source( - 'validates_numericality_of :age, { minimum: 0, maximum: 122 }' - ) - expect(new_source).to eq( - 'validates :age, numericality: { minimum: 0, maximum: 122 }' - ) + describe '#autocorrect' do + shared_examples 'auto-corrects' do + it 'auto-corrects' do + expect(autocorrect_source(source)).to eq(auto_corrected_source) + end + end + + shared_examples 'does not auto-correct' do + it 'does not auto-correct' do + expect(autocorrect_source(source)).to eq(source) + end + end + + described_class::TYPES.each do |type| + context "with validates_#{type}_of" do + let(:auto_corrected_source) do + "validates :full_name, :birth_date, #{type}: true" + end + + let(:source) do + "validates_#{type}_of :full_name, :birth_date" + end + + include_examples 'auto-corrects' + end + end + + context 'with single attribute name' do + let(:auto_corrected_source) do + 'validates :a, numericality: true' + end + + let(:source) do + 'validates_numericality_of :a' + end + + include_examples 'auto-corrects' + end + + context 'with multi attribute names' do + let(:auto_corrected_source) do + 'validates :a, :b, numericality: true' + end + + let(:source) do + 'validates_numericality_of :a, :b' + end + + include_examples 'auto-corrects' + end + + context 'with non-braced hash literal' do + let(:auto_corrected_source) do + 'validates :a, :b, numericality: { minimum: 1 }' + end + + let(:source) do + 'validates_numericality_of :a, :b, minimum: 1' + end + + include_examples 'auto-corrects' + end + + context 'with braced hash literal' do + let(:auto_corrected_source) do + 'validates :a, :b, numericality: { minimum: 1 }' + end + + let(:source) do + 'validates_numericality_of :a, :b, { minimum: 1 }' + end + + include_examples 'auto-corrects' + end + + context 'with splat' do + let(:auto_corrected_source) do + 'validates :a, *b, numericality: true' + end + + let(:source) do + 'validates_numericality_of :a, *b' + end + + include_examples 'auto-corrects' + end + + context 'with splat and options' do + let(:auto_corrected_source) do + 'validates :a, *b, :c, numericality: { minimum: 1 }' + end + + let(:source) do + 'validates_numericality_of :a, *b, :c, minimum: 1' + end + + include_examples 'auto-corrects' + end + + context 'with trailing send node' do + let(:source) do + 'validates_numericality_of :a, b' + end + + include_examples 'does not auto-correct' + end + + context 'with trailing constant' do + let(:source) do + 'validates_numericality_of :a, B' + end + + include_examples 'does not auto-correct' + end + + context 'with trailing local variable' do + let(:source) do + <<-RUBY.strip_indent + b = { minimum: 1 } + validates_numericality_of :a, b + RUBY + end + + include_examples 'does not auto-correct' end end end