Skip to content

Instantly share code, notes, and snippets.

@dinhhuydh
Created September 13, 2018 10:25
Show Gist options
  • Save dinhhuydh/8d779b31eb76e1a22272092ead95445c to your computer and use it in GitHub Desktop.
Save dinhhuydh/8d779b31eb76e1a22272092ead95445c to your computer and use it in GitHub Desktop.
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