From 1e66f4370d07e67541d9b884b0995ac54114cffb Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Sat, 3 Oct 2020 22:43:56 -0400 Subject: [PATCH] Added `IgnoredMethods` configuration to `Style/CombinableLoops`. --- CHANGELOG.md | 3 ++ config/default.yml | 2 ++ docs/modules/ROOT/pages/cops_style.adoc | 28 ++++++++++++++++++- lib/rubocop/cop/style/combinable_loops.rb | 16 +++++++++++ .../cop/style/combinable_loops_spec.rb | 20 +++++++++++-- 5 files changed, 65 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4e409bd6bf..720d3562648 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ * [#8782](https://github.com/rubocop-hq/rubocop/issues/8782): Fix incorrect autocorrection for `Style/TernaryParentheses` with `defined?`. ([@dvandersluis][]) * [#8864](https://github.com/rubocop-hq/rubocop/issues/8864): Fix false positive for `Style/RedundantBegin` with a postfix `while` or `until`. ([@dvandersluis][]) +### Changes +* [#8852](https://github.com/rubocop-hq/rubocop/pull/8852): Add `IgnoredMethods` configuration to `Style/CombinableLoops`. ([@dvandersluis][]) + ## 0.93.0 (2020-10-08) ### New features diff --git a/config/default.yml b/config/default.yml index 1f93ea75b06..3304ed8ac6b 100644 --- a/config/default.yml +++ b/config/default.yml @@ -2797,7 +2797,9 @@ Style/CombinableLoops: can be combined into a single loop. Enabled: pending Safe: false + IgnoredMethods: [] VersionAdded: '0.90' + VersionChanged: '0.94' Style/CommandLiteral: Description: 'Use `` or %x around command literals.' diff --git a/docs/modules/ROOT/pages/cops_style.adoc b/docs/modules/ROOT/pages/cops_style.adoc index 7b0b980ecb2..eab11c1f74c 100644 --- a/docs/modules/ROOT/pages/cops_style.adoc +++ b/docs/modules/ROOT/pages/cops_style.adoc @@ -1429,7 +1429,7 @@ end | No | No | 0.90 -| - +| 0.94 |=== This cop checks for places where multiple consecutive loops over the same data @@ -1488,6 +1488,32 @@ def method end ---- +==== IgnoredMethods: [validates_each] + +[source,ruby] +---- +# good +class Foo + validates_each :attr1, :attr2 do |record, attribute, value| + record.errors.add(attribute, :invalid) if invalid?(value) + end + + validates_each :attr1, :attr2 do |record, attribute, value| + record.errors.add(attribute, :empty) if empty?(value) + end +end +---- + +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| IgnoredMethods +| `[]` +| Array +|=== + == Style/CommandLiteral |=== diff --git a/lib/rubocop/cop/style/combinable_loops.rb b/lib/rubocop/cop/style/combinable_loops.rb index 7099436f8ee..01a11a229d2 100644 --- a/lib/rubocop/cop/style/combinable_loops.rb +++ b/lib/rubocop/cop/style/combinable_loops.rb @@ -55,7 +55,21 @@ module Style # each_slice(3) { |slice| do_something(slice) } # end # + # @example IgnoredMethods: [validates_each] + # + # # good + # class Foo + # validates_each :attr1, :attr2 do |record, attribute, value| + # record.errors.add(attribute, :invalid) if invalid?(value) + # end + # + # validates_each :attr1, :attr2 do |record, attribute, value| + # record.errors.add(attribute, :empty) if empty?(value) + # end + # end class CombinableLoops < Base + include IgnoredMethods + MSG = 'Combine this loop with the previous loop.' def on_block(node) @@ -76,6 +90,8 @@ def on_for(node) def collection_looping_method?(node) method_name = node.send_node.method_name + return false if ignored_method?(method_name) + method_name.match?(/^each/) || method_name.match?(/_each$/) end diff --git a/spec/rubocop/cop/style/combinable_loops_spec.rb b/spec/rubocop/cop/style/combinable_loops_spec.rb index e1f6b9ad2e7..fac08a0955d 100644 --- a/spec/rubocop/cop/style/combinable_loops_spec.rb +++ b/spec/rubocop/cop/style/combinable_loops_spec.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -RSpec.describe RuboCop::Cop::Style::CombinableLoops do - subject(:cop) { described_class.new } - +RSpec.describe RuboCop::Cop::Style::CombinableLoops, :config do context 'when looping method' do it 'registers an offense when looping over the same data as previous loop' do expect_offense(<<~RUBY) @@ -98,4 +96,20 @@ RUBY end end + + context 'when IgnoredMethods is set' do + let(:cop_config) { { 'IgnoredMethods' => %w[validates_each] } } + + it 'does not register an offense for ignored methods' do + expect_no_offenses(<<~RUBY) + validates_each :foo, :bar do |record, attribute, value| + record.errors.add(attribute, :invalid) if invalid?(value) + end + + validates_each :foo, :bar do |record, attribute, value| + record.errors.add(attribute, :empty) if empty?(value) + end + RUBY + end + end end