Commit 14ef40d4 authored by Patrick Bair's avatar Patrick Bair Committed by Mayra Cabrera

Require name for non-concurrent complex indexes

Improve upon an existing cop that requires an explicit name be provided
for indexes that are "complex", i.e. created with a hash of options
configuring their definition. The cop will no raise offenses at
non-concurrent index creation either via create_table or add_index.
parent 06a3f976
...@@ -10,8 +10,12 @@ module RuboCop ...@@ -10,8 +10,12 @@ module RuboCop
MSG = 'indexes added with custom options must be explicitly named' MSG = 'indexes added with custom options must be explicitly named'
def_node_matcher :match_create_table_index_with_options, <<~PATTERN
(send _ {:index } _ (hash $...))
PATTERN
def_node_matcher :match_add_index_with_options, <<~PATTERN def_node_matcher :match_add_index_with_options, <<~PATTERN
(send _ {:add_concurrent_index} _ _ (hash $...)) (send _ {:add_index :add_concurrent_index} _ _ (hash $...))
PATTERN PATTERN
def_node_matcher :name_option?, <<~PATTERN def_node_matcher :name_option?, <<~PATTERN
...@@ -26,7 +30,7 @@ module RuboCop ...@@ -26,7 +30,7 @@ module RuboCop
return unless in_migration?(node) return unless in_migration?(node)
node.each_descendant(:send) do |send_node| node.each_descendant(:send) do |send_node|
next unless add_index_offense?(send_node) next unless create_table_with_index_offense?(send_node) || add_index_offense?(send_node)
add_offense(send_node, location: :selector) add_offense(send_node, location: :selector)
end end
...@@ -34,6 +38,10 @@ module RuboCop ...@@ -34,6 +38,10 @@ module RuboCop
private private
def create_table_with_index_offense?(send_node)
match_create_table_index_with_options(send_node) { |option_nodes| needs_name_option?(option_nodes) }
end
def add_index_offense?(send_node) def add_index_offense?(send_node)
match_add_index_with_options(send_node) { |option_nodes| needs_name_option?(option_nodes) } match_add_index_with_options(send_node) { |option_nodes| needs_name_option?(option_nodes) }
end end
......
...@@ -14,40 +14,120 @@ RSpec.describe RuboCop::Cop::Migration::ComplexIndexesRequireName, type: :ruboco ...@@ -14,40 +14,120 @@ RSpec.describe RuboCop::Cop::Migration::ComplexIndexesRequireName, type: :ruboco
allow(cop).to receive(:in_migration?).and_return(true) allow(cop).to receive(:in_migration?).and_return(true)
end end
context 'when indexes are configured with an options hash, but no name' do context 'when creating complex indexes as part of create_table' do
it 'registers an offense' do context 'when indexes are configured with an options hash, but no name' do
expect_offense(<<~RUBY) it 'registers an offense' do
class TestComplexIndexes < ActiveRecord::Migration[6.0] expect_offense(<<~RUBY)
DOWNTIME = false class TestComplexIndexes < ActiveRecord::Migration[6.0]
DOWNTIME = false
def up
create_table :test_table do |t|
t.integer :column1, null: false
t.integer :column2, null: false
t.jsonb :column3
t.index :column1, unique: true
t.index :column2, where: 'column1 = 0'
^^^^^ #{described_class::MSG}
t.index :column3, using: :gin
^^^^^ #{described_class::MSG}
end
end
def down
drop_table :test_table
end
end
RUBY
expect(cop.offenses.map(&:cop_name)).to all(eq("Migration/#{described_class.name.demodulize}"))
end
end
context 'when indexes are configured with an options hash and name' do
it 'registers no offense' do
expect_no_offenses(<<~RUBY)
class TestComplexIndexes < ActiveRecord::Migration[6.0]
DOWNTIME = false
def up
create_table :test_table do |t|
t.integer :column1, null: false
t.integer :column2, null: false
t.jsonb :column3
t.index :column1, unique: true
t.index :column2, where: 'column1 = 0', name: 'my_index_1'
t.index :column3, using: :gin, name: 'my_gin_index'
end
end
def down
drop_table :test_table
end
end
RUBY
end
end
end
INDEX_NAME = 'my_test_name' context 'when indexes are added to an existing table' do
context 'when indexes are configured with an options hash, but no name' do
it 'registers an offense' do
expect_offense(<<~RUBY)
class TestComplexIndexes < ActiveRecord::Migration[6.0]
DOWNTIME = false
disable_ddl_transaction! disable_ddl_transaction!
def up def up
add_concurrent_index :test_indexes, :column1 add_index :test_indexes, :column1
add_concurrent_index :test_indexes, :column2, where: "column2 = 'value'", order: { column4: :desc } add_index :test_indexes, :column2, where: "column2 = 'value'", order: { column4: :desc }
^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG} ^^^^^^^^^ #{described_class::MSG}
end
add_concurrent_index :test_indexes, :column3, where: 'column3 = 10', name: 'idx_equal_to_10' def down
add_index :test_indexes, :column4, 'unique' => true, where: 'column4 IS NOT NULL'
^^^^^^^^^ #{described_class::MSG}
add_concurrent_index :test_indexes, :column6, using: :gin, opclass: :gin_trgm_ops
^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG}
end
end end
RUBY
def down expect(cop.offenses.map(&:cop_name)).to all(eq("Migration/#{described_class.name.demodulize}"))
add_concurrent_index :test_indexes, :column4, 'unique' => true end
end
add_concurrent_index :test_indexes, :column4, 'unique' => true, where: 'column4 IS NOT NULL' context 'when indexes are configured with an options hash and a name' do
^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG} it 'registers no offenses' do
expect_no_offenses(<<~RUBY)
class TestComplexIndexes < ActiveRecord::Migration[6.0]
DOWNTIME = false
add_concurrent_index :test_indexes, :column5, using: :gin, name: INDEX_NAME INDEX_NAME = 'my_test_name'
add_concurrent_index :test_indexes, :column6, using: :gin, opclass: :gin_trgm_ops disable_ddl_transaction!
^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG}
end def up
end add_index :test_indexes, :column1
RUBY
add_index :test_indexes, :column2, where: "column2 = 'value'", order: { column4: :desc }, name: 'my_index_1'
add_concurrent_index :test_indexes, :column3, where: 'column3 = 10', name: 'idx_equal_to_10'
end
def down
add_index :test_indexes, :column4, 'unique' => true, where: 'column4 IS NOT NULL', name: 'my_index_2'
expect(cop.offenses.map(&:cop_name)).to all(eq("Migration/#{described_class.name.demodulize}")) add_concurrent_index :test_indexes, :column6, using: :gin, opclass: :gin_trgm_ops, name: INDEX_NAME
end
end
RUBY
end
end end
end end
end end
...@@ -65,7 +145,13 @@ RSpec.describe RuboCop::Cop::Migration::ComplexIndexesRequireName, type: :ruboco ...@@ -65,7 +145,13 @@ RSpec.describe RuboCop::Cop::Migration::ComplexIndexesRequireName, type: :ruboco
disable_ddl_transaction! disable_ddl_transaction!
def up def up
add_concurrent_index :test_indexes, :column1, where: "some_column = 'value'" create_table :test_table do |t|
t.integer :column1
t.index :column1, where: 'column2 IS NOT NULL'
end
add_index :test_indexes, :column1, where: "some_column = 'value'"
end end
def down def down
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment