Migraine Ruby Gem
Introduction
Have you ever felt enthusiastic about migrating data from one
database to another? Me neither. Migraine is a simple gem to
assist in writing data migration scripts in Ruby. It supports
various database adapters (e.g. MySQL, SQLite3, PostgreSQL)
through its only dependency, the
Sequel Ruby gem.
Requirements
Installation
myproject.com
> gem install migraine
Todo
-
Migraine::Map#map
(or separate method) should be able to deal
with column type differences between source and destination. The
method should accept a block argument that takes source record
as input, and relies on developer to manipulate that and
return valid destination data.
migration.map 'table' do
map 'column' => 'new_column' do |source|
destination = "A new string from #{source}"
destination.upcase
destination
end
end
-
Provide further adapter support for schema generator, and thus
migration file generation. As of now, migration files can only
be generated for MySQL databases since different databases
require different ways of generating a schema (which is used
for analyzing difference between source and destination.)
See lib/migraine/generator.rb
for further details.
Using Migraine
Create migration file
myproject.com
> vim migrate.rb
The migration file
You must now tell Migraine what data you want to migrate, and
where. But first require the Migraine gem.
require 'migraine'
Create a migration, specifying source and destination databases.
For information about how to write the connection URI's, see the
documentation of Sequel.
It's very simple, I promise.
migration = Migraine::Migration.new(
from: "mysql://root:root@localhost/myproj_old",
to: "mysql://root:root@localhost/myproj"
)
Map source tables to destination tables. If the columns are
unchanged in the destination table, you do not need to provide
any block with instructions. Simply map source table to
destination table.
migration.map "products" => "spree_products"
If source and destination tables have the same name in both
databases, you can make it even shorter.
migration.map "products"
On the other hand, if the column names have changed at the
destination, you can provide instructions on how to map them.
migration.map "users" => "spree_users" do
map "crypted_password" => "encrypted_password"
map "salt" => "password_salt"
map "remember_token"
map "persistance_token"
map "perishable_token"
end
TODO: In the future, Migraine will provide methods for dealing
with differences in column types. See Todo section above.
When you have provided the mappings, tell Migraine that you want
to run the migration.
migration.run
Run migration file
myproject.com
> ruby migrate.rb
Generating migration files
Migraine can even generate migration files for you. This is
useful for databases with many tables, where you don't want to
type them all out manually.
Adapter support
At the moment, Migraine can only generate migrations
for MySQL databases. This is because to analyze the differences
between source and destination, we need to generate database
schemas as Hashes. Right now there is only a method for MySQL.
Please feel free to add methods for other adapters as well. You
can do this by forking Migraine, editing
lib/migraine/generator.rb
and add a new method. The file
is commented with further instructions.
Create generation file
You will need to create a file with generation instructions, just
like you create a file for migrations.
myproject.com
> vim generate.rb
The generation file
All you need to do in your new Ruby file is to create a
Migraine::Migration
with a source and destination, and tell
Migraine to generate a migration file for it.
migration = Migraine::Migration.new(
from: "mysql://root:root@localhost/myproj_old",
to: "mysql://root:root@localhost/myproj"
)
migration.generate 'migrate.rb'
When you run this file using ruby generate.rb
or similar, it
will create a migrate.rb file containing table and column
mappings.
Migraine will try to determine the mappings by analyzing the
differences between source and destination. When it can't, it
will simply leave the destinations empty for you to fill in.
Table prefixes
If the destination database has many of the same tables as
source, but with an added prefix (e.g. source table 'users'
should be mapped to destination table 'spree_users'), you can use
the prefix method to tell Migraine about it and, and it will
consider that when looking for destination tables.
Add the following before your call to Migration#generate
.
migration.prefix 'spree_'
Example
Following is a generation file and part of its resulting
migration file (abbreviated since Spree has many tables.)
generate.rb
require 'migraine'
migration = Migraine::Migration.new(
from: 'mysql://root:root@localhost/migraine_from',
to: 'mysql://root:root@localhost/migraine_to'
)
migration.prefix 'spree_'
migration.generate 'generated.rb'
After running ruby generate.rb
, Migraine will generate the
following file.
generated.rb
require 'migraine'
migration = Migraine::Migration.new(
from: 'mysql://root:root@localhost/migraine_from',
to: 'mysql://root:root@localhost/migraine_to'
)
migration.map 'addresses' => 'spree_addresses'
map 'id'
map 'firstname'
map 'lastname'
map 'address1'
map 'address2'
map 'city'
map 'state_id'
map 'zipcode'
map 'country_id'
map 'phone'
map 'created_at'
map 'updated_at'
map 'state_name'
map 'alternative_phone'
end
migration.map 'adjustments' => 'spree_adjustments'
map 'id'
map 'order_id' => ''
map 'type' => ''
map 'amount'
map 'description' => ''
map 'position' => ''
map 'created_at'
map 'updated_at'
map 'adjustment_source_id' => ''
map 'adjustment_source_type' => ''
end
migration.map 'assets' => 'spree_assets'
map 'id'
map 'viewable_id'
map 'viewable_type'
map 'attachment_content_type'
map 'attachment_file_name'
map 'attachment_size'
map 'position'
map 'type'
map 'attachment_updated_at'
map 'attachment_width'
map 'attachment_height'
map 'alt'
end
migration.map 'calculators' => 'spree_calculators'
map 'id'
map 'type'
map 'calculable_id'
map 'calculable_type'
map 'created_at'
map 'updated_at'
end
migration.map 'checkouts' => ''
map 'id' => ''
map 'order_id' => ''
map 'email' => ''
map 'ip_address' => ''
map 'special_instructions' => ''
map 'bill_address_id' => ''
map 'created_at' => ''
map 'updated_at' => ''
map 'state' => ''
map 'ship_address_id' => ''
map 'shipping_method_id' => ''
end