Archive

Archive for February 26th, 2010

How to make rails migrations pretty

February 26th, 2010 5 comments

I have seen a lot of migrations, most of them was just doing what it had to do. But I found a nice way to make my migrations more pretty.

So I had an Article model, which had field :published of type :boolean , as comparing to other solutions, I found that it should be rather :publish_date of type date.
First I have created migration using “script/generate migration article_update” and got empty migration:

class UpdateArticles < ActiveRecord::Migration
  def self.up
  end
  def self.down
  end
end
&#91;/sourcecode&#93;
To be able to freely play with Fields we add our private implementation of article model, we change the fields and move data for them:
&#91;sourcecode language='ruby'&#93;
class UpdateArticles < ActiveRecord::Migration
  class Article < ActiveRecord::Base; end
  def self.up
    add_column :articles, :publish_date, :datetime, :null => true
    Article.reset_column_information
    Article.all.each { |article| article.update_attribute(:publish_date, :created_at) if article.publish }
    remove_column  :articles, :publish
  end
  def self.down
    add_column  :articles, :publish, :boolean
    Article.reset_column_information
    Article.all.each { |article| article.update_attribute(:publish, true) unless article.publish_date.nil? }
    remove_column :articles, :publish_date
  end
end

But this is not really what we wanted, if we have already a lot of data in Article tables then we will not know what is going on during update attribute action.
To make it display pretty we have to add say_with_time function:

class UpdateArticles < ActiveRecord::Migration
  class Article < ActiveRecord::Base; end

  def self.up
    add_column :articles, :publish_date, :datetime, :null => true
    say_with_time "Updating publish_date column" do
      Article.reset_column_information
      Article.all.each { |article| article.update_attribute(:publish_date, :created_at) if article.publish }
    end
    remove_column  :articles, :publish
  end

  def self.down
    add_column  :articles, :publish, :boolean
    say_with_time "Updating publish column" do
      Article.reset_column_information
      Article.all.each { |article| article.update_attribute(:publish, true) unless article.publish_date.nil? }
    end
    remove_column :articles, :publish_date
  end
end

And after running migration we will get he following result:

==  UpdateArticles: migrating =================================================
-- add_column(:articles, :publish_date, :datetime, {:null=>true})
-> 0.0007s
-- Updating publish_date column
-> 0.0034s
-- remove_column(:articles, :publish)
-> 0.0224s
==  UpdateArticles: migrated (0.0269s) ========================================

For me it was very small amount of data and the migration went fast, but if i would run it on production with thousands of records then it may take a bit more.

I know this exact code could be rewritten using SQL – and it would be almost always done in seconds, but even then It is nice to show progress, because person responsible for deployment might be confused seeing only blinking cursor.

Did You liked this post or maybe not, vote on it at dzone.

Categories: Development Tags: ,