読者です 読者をやめる 読者になる 読者になる

おもしろwebサービス開発日記

Ruby や Rails を中心に、web技術について書いています

Rails 3.1 の migration の変更点について

またまたRails 3.1 ネタです。Rails 3.1 では migration ファイルのデフォルトフォーマットが変わりました。

rails g model blog title:string body:text

としたとき、Rails 3.0 では下記のファイルが生成されます

class CreateBlogs < ActiveRecord::Migration
  def self.up
    create_table :blogs do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end

  def self.down
    drop_table :blogs
  end
end

Rails 3.1 では下記のファイルが生成されます。

class CreateBlogs < ActiveRecord::Migration
  def change
    create_table :blogs do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end
end

だいぶすっきりしましたね。Rails 3.1 では、上記のように migration の up, down 両方を change メソッド中にまとめるようになりました。change メソッド中に定義された情報は、rollbackされたときに自動的に反転されます。例えば上記の例なら rollback 時には drop_table が実行されることになります。

ただ、なんでも change メソッドを使えばOKかというとそうではありません。例えば、下記のような migration ファイルを作り、rake db:rollback とするとエラー(ActiveRecord::IrreversibleMigration)となってしまいます。どうしてエラーになるのでしょうか?

class RemoveTitleFromBlog < ActiveRecord::Migration
  def change
    remove_column :blogs, :title
  end
end

rake db:rollback した場合は、普通に考えれば remove_column の逆のメソッドである add_column が実行されるはずですが、上記の記述では追加するカラムの型の情報が足りず、add_column することができません。このような場合は、従来通りの up メソッドや down メソッドを定義してあげる必要があります。ただ、up メソッドや down メソッドRails 3.0 までとは違いインスタンスメソッドとして定義してあげなければなりません。

class RemoveTitleFromBlog < ActiveRecord::Migration
  def up
    remove_column :blogs, :title
  end

  def down
    add_column :blogs, :title, :string
  end
end

個人的には down 中の定義をtypoして、たまにrollbackした際に時間をロスすることがあるので、この仕様変更はありがたいです。RailsのDRY具合もだいぶ極まってきた感じがしますね。