diff --git a/CHANGELOG.md b/CHANGELOG.md index 566ac56..5cdb79b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ # main +* Added cop `Migration/StandaloneAddReference` ([#54](https://github.com/petalmd/rubocop-petal/pull/54)) * Update `Migration/ChangeTableReferences` on send alias and message to handle removing references. ([#55](https://github.com/petalmd/rubocop-petal/pull/55)) # v1.1.2 (2023-05-30) diff --git a/config/default.yml b/config/default.yml index 71a09b3..a83ff96 100644 --- a/config/default.yml +++ b/config/default.yml @@ -29,6 +29,12 @@ Migration/SchemaStatementsMethods: Include: - db/migrate/** +Migration/StandaloneAddReference: + Description: 'Prevent using `add_reference/belongs_to` outside of a change_table.' + Enabled: true + Include: + - db/migrate/** + RSpec/AuthenticatedAs: Description: 'Suggest to use authenticated_as instead of legacy api_key.' Enabled: true diff --git a/lib/rubocop/cop/migration/standalone_add_reference.rb b/lib/rubocop/cop/migration/standalone_add_reference.rb new file mode 100644 index 0000000..0db089b --- /dev/null +++ b/lib/rubocop/cop/migration/standalone_add_reference.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Migration + # Prevent using `add_reference` and `remove_reference` outside of + # a `change_table` block. `add_reference` create multiples `ALTER TABLE` + # statements. Using `change_table` with `bulk: true` is more efficient. + # + # # bad + # add_reference :products, :user, foreign_key: true + # + # # good + # change_table :products, bulk: true do |t| + # t.bigint :user_id, null: false + # t.index :user_id + # t.foreign_key :users, column: :user_id + # end + class StandaloneAddReference < Base + MSG = 'Modifying references must be done in a change_table block.' + + RESTRICT_ON_SEND = %i[add_reference add_belongs_to remove_reference remove_belongs_to].freeze + + def on_send(node) + reference_method = node.source_range.with(end_pos: node.child_nodes.first.source_range.begin_pos - 1) + + add_offense(reference_method) + end + end + end + end +end diff --git a/spec/rubocop/cop/migration/standalone_add_reference_spec.rb b/spec/rubocop/cop/migration/standalone_add_reference_spec.rb new file mode 100644 index 0000000..931f614 --- /dev/null +++ b/spec/rubocop/cop/migration/standalone_add_reference_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Migration::StandaloneAddReference, :config do + it 'registers an offense when modifing a references outside a change_table' do + expect_offense(<<~RUBY) + add_reference :products, :user + ^^^^^^^^^^^^^ Modifying references must be done in a change_table block. + RUBY + + expect_offense(<<~RUBY) + add_reference :products, :user, index: true + ^^^^^^^^^^^^^ Modifying references must be done in a change_table block. + RUBY + + expect_offense(<<~RUBY) + add_belongs_to :products, :user + ^^^^^^^^^^^^^^ Modifying references must be done in a change_table block. + RUBY + + expect_offense(<<~RUBY) + remove_reference :products, :user + ^^^^^^^^^^^^^^^^ Modifying references must be done in a change_table block. + RUBY + + expect_offense(<<~RUBY) + remove_belongs_to :products, :user + ^^^^^^^^^^^^^^^^^ Modifying references must be done in a change_table block. + RUBY + end + + it 'does not register an offense when not calling modifying references methods' do + expect_no_offenses(<<~RUBY) + add_index :users, :user_id + RUBY + end +end