Nov/07 3 4:52 pm
migrations
no comments
Ruby on Rails migrations allows the complete definition of an Object Relationship Mapping in just one place. Using a more traditional approach of Java and Hibernate would require DDL to make the database table changes, possibly SQL to populate default values, XML mapping files and finally a Java class complete with getter and setter methods.
Rails achieves all of this in just one migration file. Each migration contains a couple of methods - one to move up to this version of the database model, and one to move down. Typically these might respectively contain code to create a new table and drop it.
For example, to add a simple users table:
class CreateUsers < ActiveRecord:Migration
def self.up
create_table :users do |t|
t.column :username, :string, :limit => 12,
:null => false
t.column :full_name, :string, :limit => 40,
:null => false
end
end
def self.down
drop_table :users
end
end
The migration file does the job of the DDL and SQL allowing both database structure changes and data setup to occur. Rails’ axiom of convention over configuration removes the need for any mapping file or even the definition of the underlying class.
To show what else is possible in a migration it’s possible to add a unique constraint:
def self.up ... add_index :users, :username, :unique => true end
Or add an admin user:
def self.up ... User.create :username => 'admin', :full_name => 'Admin' end
As the contents of the up method are simply executed in sequence it’s possible to get around chicken and egg type scenarios. Suppose for audit purposes our user table holds a couple of fields to indicate which user created a new user and updated an existing user. Normally these fields would not allow nulls because they can always be populated. But who created the default admin user? It makes sense to be admin itself however, how can we defined the creator as a user who doesn’t exist? The solution is simple. Create the table allowing the created by field to allow nulls, add the admin user, set the created by field to this new user then update the table to no longer allow nulls.
def self.up
create_table :users do |t|
...
t.column :created_by, :integer
end
User.create :username => 'admin', :full_name => 'Admin'
admin_user = User.find_by_username('admin')
admin_user.created_by = admin_user
admin_user.save
change_column :users, :created_by, :integer,
:null => false