Created
July 26, 2014 10:34
-
-
Save DaanVanVugt/c2b2161770b3282eb3ab to your computer and use it in GitHub Desktop.
Migration code to move to unique ids
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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