diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e4d02a..de290a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ # main +* Added cop `Rails/DestroyAllBang`(Fix [#42](https://github.com/petalmd/rubocop-petal/issues/42)). ([#65](https://github.com/petalmd/rubocop-petal/pull/65)) * Added cop `Chewy/FieldType`. ([#64](https://github.com/petalmd/rubocop-petal/pull/64)) * Fix `Migration/ChangeTableReferences` offense location (Fix [#61](https://github.com/petalmd/rubocop-petal/issues/61)) ([#62](https://github.com/petalmd/rubocop-petal/pull/62)) * Added cop `Sidekiq/NoEarlyNilReturn` ([#58](https://github.com/petalmd/rubocop-petal/pull/58)) diff --git a/config/default.yml b/config/default.yml index 1b428df..830da66 100644 --- a/config/default.yml +++ b/config/default.yml @@ -80,6 +80,11 @@ Grape/UnnecessaryNamespace: Include: - app/api/**/* +Rails/DestroyAllBang: + Description: 'Prevent using `destroy_all` in favor of `each(&:destroy!)` to go along Rails/SaveBang cop.' + StyleGuide: https://rails.rubystyle.guide#save-bang + Enabled: true + Rails/EnumPrefix: Description: 'Set prefix options when using enums.' Enabled: true diff --git a/lib/rubocop/cop/rails/destroy_all_bang.rb b/lib/rubocop/cop/rails/destroy_all_bang.rb new file mode 100644 index 0000000..2a53c61 --- /dev/null +++ b/lib/rubocop/cop/rails/destroy_all_bang.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Rails + # Cop to enforce the use of `each(&:destroy!)` over `destroy_all`. + # https://docs.rubocop.org/rubocop-rails/cops_rails.html#railssavebang + # https://github.com/rails/rails/pull/37782 + # https://discuss.rubyonrails.org/t/proposal-add-destroy-all-method-to-activerecord-relation/80959 + # + # # bad + # User.destroy_all + # + # # good + # User.each(&:destroy!) + class DestroyAllBang < Base + extend AutoCorrector + MSG = 'Use `each(&:destroy!)` instead of `destroy_all`.' + RESTRICT_ON_SEND = %i[destroy_all].freeze + + def on_send(node) + destroy_all_range = node.loc.selector + add_offense(destroy_all_range) do |corrector| + corrector.replace(node.loc.selector, 'each(&:destroy!)') + end + end + end + end + end +end diff --git a/spec/rubocop/cop/rails/destroy_all_bang_spec.rb b/spec/rubocop/cop/rails/destroy_all_bang_spec.rb new file mode 100644 index 0000000..011a474 --- /dev/null +++ b/spec/rubocop/cop/rails/destroy_all_bang_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Rails::DestroyAllBang, :config do + it 'registers an offense when using `destroy_all`' do + expect_offense(<<~RUBY) + User.destroy_all + ^^^^^^^^^^^ Use `each(&:destroy!)` instead of `destroy_all`. + RUBY + + expect_correction(<<~RUBY) + User.each(&:destroy!) + RUBY + + expect_offense(<<~RUBY) + User.where(desactivated: true).destroy_all + ^^^^^^^^^^^ Use `each(&:destroy!)` instead of `destroy_all`. + RUBY + + expect_correction(<<~RUBY) + User.where(desactivated: true).each(&:destroy!) + RUBY + + expect_offense(<<~RUBY) + User + .where(desactivated: true) + .destroy_all + ^^^^^^^^^^^ Use `each(&:destroy!)` instead of `destroy_all`. + RUBY + + expect_correction(<<~RUBY) + User + .where(desactivated: true) + .each(&:destroy!) + RUBY + end + + it 'does not register an offense when using `#each(&:destroy!)`' do + expect_no_offenses(<<~RUBY) + User.each(&:destroy!) + RUBY + end +end