Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
module RuboCop
module Cop
module Migration
class AddIndexName < RuboCop::Cop::Cop
# Postgres and MySQL have different naming conventions, so if we need to remove them we cannot predict accurately what the constraint name would be.
MSG = 'Please explicitly name your index or constraint.'.freeze
CONSTRAINT_METHODS = %i{
add_unique_constraint add_constraint add_foreign_key add_index add_primary_key add_full_text_index add_spatial_index
unique_constraint constraint foreign_key index primary_key full_text_index spatial_index
}.freeze
COLUMN_ADDING_METHODS = %i{
add_column column String Integer
}.freeze
def on_block(node)
node.each_descendant(:send) do |send_node|
method = method_name(send_node)
# Test houndci output
add_offense(send_node, location: :expression) if method == :each
next unless constraint_adding_method?(method) || column_adding_method?(method)
opts = send_node.children.last
missing_named_constraint = true
if opts
if constraint_adding_method?(method)
missing_named_constraint = add_constraint_missing_name?(opts)
elsif column_adding_method?(method)
missing_named_constraint = add_column_missing_name?(opts)
end
end
add_offense(send_node, location: :expression) if missing_named_constraint
end
end
private
def constraint_adding_method?(method)
CONSTRAINT_METHODS.include?(method)
end
def column_adding_method?(method)
COLUMN_ADDING_METHODS.include?(method)
end
def add_constraint_missing_name?(opts)
return true unless opts.type == :hash
opts.each_node(:pair) do |pair|
return false if hash_key_type(pair) == :sym && hash_key_name(pair) == :name
end
true
end
def add_column_missing_name?(opts)
return true if opts.type == :sym && %i{index primary_key unique}.include?(sym_opts_name(opts))
needs_named_index = false
needs_named_primary_key = false
needs_named_unique_constraint = false
opts.each_node(:pair) do |pair|
next unless hash_key_type(pair) == :sym
case hash_key_name(pair)
when :index then needs_named_index = true
when :primary_key then needs_named_primary_key = true
when :unique then needs_named_unique_constraint = true
end
end
opts.each_node(:pair) do |pair|
next unless hash_key_type(pair) == :sym
case hash_key_name(pair)
when :name then needs_named_index = false
when :primary_key_constraint_name then needs_named_primary_key = false
when :unique_constraint_name then needs_named_unique_constraint = false
end
end
[needs_named_index, needs_named_primary_key, needs_named_unique_constraint].any?
end
def method_name(node)
node.children[1]
end
def hash_key_type(pair)
pair.children[0].type
end
def hash_key_name(pair)
pair.children[0].children[0]
end
def sym_opts_name(opts)
opts.children[0]
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.