Skip to content

Instantly share code, notes, and snippets.

@DaanVanVugt
Created July 26, 2014 10:34
Show Gist options
  • Save DaanVanVugt/c2b2161770b3282eb3ab to your computer and use it in GitHub Desktop.
Save DaanVanVugt/c2b2161770b3282eb3ab to your computer and use it in GitHub Desktop.
Migration code to move to unique ids
def generate_uuid(prefix, id)
UUIDTools::UUID.sha1_create(UUIDTools::UUID_OID_NAMESPACE, "#{prefix}-#{id}")
end
class TransformToUuids < ActiveRecord::Migration
def change
models = [Contact, User, Version]
models.each do |model|
# Create new column for uuid
add_column model.table_name, :uuid, :uuid
# Create columns for associations
model.reflect_on_all_associations(:belongs_to).each do |assoc|
add_column model.table_name, "#{assoc.name}_uuid", :uuid # only works for associations where the column has not been renamed
end
model.reflect_on_all_associations(:has_and_belongs_to_many).each do |assoc|
if assoc.class_name == model.to_s
# self-referential, so add uuid for other side as well
# assoc.table_name is not the name of the join table, that's assoc.options[:table_name]
add_column assoc.options[:table_name], assoc.association_foreign_key.to_s.gsub("_id", "_uuid"), :uuid
end
# Add column to hold the foreign key to this object
add_column assoc.options[:table_name], assoc.foreign_key.to_s.gsub("_id", "_uuid"), :uuid
end
# Update all values
model.find_each do |obj|
# Generate uuid values
obj.update_column(:uuid, generate_uuid(model.to_s, obj.id))
model.reflect_on_all_associations(:belongs_to).each do |assoc|
# find correct classname to use
if assoc.polymorphic?
classname = obj.attributes["#{assoc.name}_type"]
else
classname = assoc.klass.to_s
end
obj.update_column("#{assoc.name}_uuid", generate_uuid(classname, obj.attributes["#{assoc.name}_id"]))
end
model.reflect_on_all_associations(:has_and_belongs_to_many).each do |assoc|
if assoc.class_name == model.to_s
# self-referential, update other side too
execute("UPDATE #{assoc.options[:table_name]} SET #{assoc.association_foreign_key.to_s.gsub("_id", "_uuid")}=#{obj.uuid.quoted_id} WHERE #{assoc.association_foreign_key}=#{obj.id}")
end
execute("UPDATE #{assoc.options[:table_name]} SET #{assoc.foreign_key.to_s.gsub("_id", "_uuid")}=#{obj.uuid.quoted_id} WHERE #{assoc.foreign_key}=#{obj.id}")
end
end
# Remove old columns and add index on these new foreign keys
remove_column model.table_name, :id
rename_column model.table_name, :uuid, :id
# Set column as primary key
# WARNING: is not reflected in db/schema.rb! Add there manually
# Related to https://github.com/jashmenn/activeuuid/pull/22, but that probably wont work for rails 4
execute("ALTER TABLE #{model.table_name} ADD PRIMARY KEY (id)")
# change_column model.table_name, :id, :uuid, primary_key: true # This does not work :(
model.reflect_on_all_associations(:belongs_to).each do |assoc|
remove_column model.table_name, "#{assoc.name}_id"
rename_column model.table_name, "#{assoc.name}_uuid", "#{assoc.name}_id"
add_index model.table_name, "#{assoc.name}_id"
end
model.reflect_on_all_associations(:has_and_belongs_to_many).each do |assoc|
# Drop the index because we'll get an error about duplicate values for the non-composite index after removing one of the component columns
remove_index assoc.options[:table_name], [assoc.foreign_key, assoc.association_foreign_key]
if assoc.class_name == model.to_s
# self-referential
remove_index assoc.options[:table_name], [assoc.association_foreign_key, assoc.foreign_key]
remove_column assoc.options[:table_name], assoc.association_foreign_key
rename_column assoc.options[:table_name], assoc.association_foreign_key.to_s.gsub("_id", "_uuid"), assoc.association_foreign_key
end
remove_column assoc.options[:table_name], assoc.foreign_key
rename_column assoc.options[:table_name], assoc.foreign_key.to_s.gsub("_id", "_uuid"), assoc.foreign_key
# Add composite index
add_index assoc.options[:table_name], [assoc.foreign_key, assoc.association_foreign_key], name: "index_#{assoc.table_name}_on_#{assoc.foreign_key}_and_#{assoc.association_foreign_key}", unique: true, using: :btree
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment