2007/06/24

Rails Migration

Migrationは物理的なデータベースによって使われるスキーマの変更を 管理するためのものです。新しい機能のために、 データベースにフィールドを追加する必要がある時 他の開発者にどのように変更を伝えるか、 商用サーバにそれをどう適用すればよいかを 伝えるための共通の問題に対するソリューションです。 Migrationを使う事で、変更をクラスとして記述する事ができ、 バージョン管理システムで管理し、他のデータベースに対して 1、2または5つ以上のバージョンを適用することができます。

Migrationの簡単な例は次のようにものです:

class AddSsl < ActiveRecord::Migration
    def self.up
      add_column :accounts, :ssl_enabled, :boolean, :default => 1
    end

    def self.down
      remove_column :accounts, :ssl_enabled
    end
  end

このMigrationはaccountテーブルにbooleanのフラグを追加し、 Migrationを戻すとそのフラグを削除するものです。 これは全てのMigrationが、移行に必要な実装/削除に必要な変更を記述するため、 2つのクラスメソッドupとdownを持つことを示しています。 これらのメソッドは、migrationのメソッドであるadd_columnやremove_columnだけでなく、 変更に必要なデータを生成するための通常のRubyコードを含む事ができます。

データの初期化が必要な場合のもう少し複雑なmigrationの例は次のようになります:

  class AddSystemSettings < ActiveRecord::Migration
    def self.up
      create_table :system_settings do |t|
        t.column :name,     :string
        t.column :label,    :string
        t.column :value,    :text
        t.column :type,     :string
        t.column :position, :integer
      end

      SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
    end

    def self.down
      drop_table :system_settings
    end
  end

このMigrationは最初にsystem_settingテーブルを追加し、 ActiveRecordモデルを利用して最初の行を作成しています。 また、これは完全なテーブルスキーマをブロックコールで作成する、 create_tableのより高度な文法を使用しています。

使用可能な変換

  • create_table(name, options):nameというテーブルを作成し、 ブロックでadd_columnと同じフォーマットで列を作成できるようにします。 上記の例を参照してください。このoptionsハッシュはDEFAULT CHARSET="UTF-8"のような フラグメントをcreate tableの定義に追加するためのものです。
  • drop_table(name):nameテーブルをdropします。
  • rename_table(old_name, new_name):old_nameというテーブルを new_nameにリネームします。
  • add_column(table_name, column_name, type, options): table_nameというテーブルにcolumn_nameという名前の新しい列を追加します。 typeは以下のタイプです:
    :string、:text、:integer、:float、:decimal、:datetime、:timestamp、:time、:date、:binary、 :boolean。
    デフォルト地はoptionsハッシュに{:default => 11}のようにして追加できます。 ほかにも:limitや:null(例えば{ :limit => 50, :null => false})を追加できます。 詳しくはActiveRecord::ConnectionAdapters::TableDefinition#columnを参照してください。
  • rename_column(table_name, column_name, new_column_name): 列をリネームします。タイプと内容は保持します。
  • change_column(table_name, column_name, type, options): add_columnと同じパラメータで、列を別のタイプに変更します。
  • remove_column(table_name, column_name):table_nameという名前の テーブルからcolumn_nameという列を削除します。
  • add_index(table_name, column_names, index_type, index_name): 新しいインデックスを列名または(もし指定されていれば)index_nameをつかって作成する。 index_type(例えばUNIQUE)も指定できる。
  • remove_index(table_name, index_name): index_nameで指定したindexを削除する。

復元不可能な変更

いくつかの変更は戻す事のできない破壊的なものがあります。 そのようなのMigrationはdownメソッドで IrreversibleMigration例外をraiseするべきです。

RailsからMigrationを実行する

RailsはMigrationを作ったり適用するのを助けるいくつかのツールを持っています。

新しいMigrationを作るには、script/generate migration MyNewMigration を使います。ここでMyNewMigrationはこのMigrationの名前です。 このジェネレータはnnn_my_new_migration.rbというファイルをdb/migrate/ディレクトリに 作成します。nnnはmigration番号です。n個のMyNewMigrationのself.upとself.downメソッドを 書き換える事になるでしょう。

現在設定されているデータベースに対してMigrationを実行するには rake migrateを実行します。このコマンドは全ての未実行の migrationを実行してデータベースを更新し、もし存在しなければschema_infoテーブルを 作成します。

データベースを前のmigrationバージョンにロールバックするには rake migrate VERSION=Xを実行します。 Xはダウングレードしたいバージョンの番号です。 もしmigrationがIrreversibleMigration例外をスローした場合 そのステップは手作業で行う必要があります。

データベースサポート

Migrationは現在MySQL, PostgreSQL, SQLite、SQL Server、 Sybase、そしてOracleを サポートしています。

サンプル

全てのmigrationがスキーマを変更するとは限りません。 データを修正するものもあります:

  class RemoveEmptyTags < ActiveRecord::Migration
    def self.up
      Tag.find(:all).each { |tag| tag.destroy if tag.pages.empty? }
    end

    def self.down
      # not much we can do to restore deleted data
      raise IrreversibleMigration
    end
  end

他にはdownではなくupの時に列を削除することもあります:

class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
    def self.up
      remove_column :items, :incomplete_items_count
      remove_column :items, :completed_items_count
    end

    def self.down
      add_column :items, :incomplete_items_count
      add_column :items, :completed_items_count
    end
  end

そして、直接SQLを実行したい場合は

  class MakeJoinUnique < ActiveRecord::Migration
    def self.up
      execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
    end

    def self.down
      execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
    end
  end

テーブルを変えた後にそのモデルを使う場合

しばしばMigrationで列を追加して、その直後にデータを設定したい場合があるでしょう。 その場合、新しい列を追加した後、モデルにその列がある事を保証するために Base#reset_column_informationを呼ぶ必要があります。例えば:

 class AddPeopleSalary < ActiveRecord::Migration
    def self.up
      add_column :people, :salary, :integer
      Person.reset_column_information
      Person.find(:all).each do |p|
        p.update_attribute :salary, SalaryCalculator.compute(p)
      end
    end
  end

詳細の制御

デフォルトでは、Migrationは、なにを行ったかを各ステップにどのくらいかかったかの ベンチマークとともにコンソールに出力することで表します。

ActiveRecord::Migration.verbose = falseとすることでそれらを抑制する事ができます。

また、say_with_timeメソッドを使ってベンチマークに独自のメッセージを 挿入することができます:

  def self.up
    ...
    say_with_time "Updating salaries..." do
      Person.find(:all).each do |p|
        p.update_attribute :salary, SalaryCalculator.compute(p)
      end
    end
    ...
  end

"Updating salaries..."がブロックが終わったときのベンチマークとともに出力されます。

2007/06/19

JRuby On Rails

いよいよJRuby on Rails。 (うちのMacではNetBeansが遅すぎるのでWindowsに乗り換えたorz)

JRubyのインストール

NetBeansにバンドルされているが、最新版を使うためにインストールする。

  1. ここから最新版をダウンロードする。
    2007/6/19時点で1.0が最新
  2. 好きなディレクトリ(インストール先)に展開する
  3. 2のディレクトリを環境変数JRUBY_HOMEを設定する
  4. 環境変数PATHに%JRUBY_HOME%\binを追加する

Railsのインストール

コマンドプロンプトを開いて以下のコマンドを実行する。

jruby -S gem install rails -y

jruby -SでJRubyのbinにあるgemコマンドを実行してくれる。
-yは依存関係もダウンロード
次にJDBCでActibeRecordを使うためのライブラリのインストール

jruby -S gem install ActiveRecord-JDBC -y

NetBeansの設定

NetBeans 6.0 M9ではデフォルトで使用するJRubyがバンドルされている0.9.8なので最新版を使うようにする。
  1. NetBeansを起動する
  2. メニューからTools→Options
  3. Miscellaneous→Ruby Installationを選択
  4. インストールした1.0のパスに変更してOKを押す

Railsプロジェクトの作成

やっとRailsプロジェクトの作成。

  1. ProjectPaneで右クリックしてメニューからNew Projectを選択
  2. CategoliesからRubyを選択
  3. ProjectからRuby on Rails Applicationを選択してNext
  4. ProjectNameは任意でFinish

できたプロジェクトを右クリック→Run ProjectでWEBrickが起動しRailsアプリケーションが動く。

Scaffoldとかやりたいけど、とりあえず今日はここまで。

NetBeans 6.0 M9インストール

どうもNetBeansが最近なかなかイイということで、試しに使ってみようと思う。 なにより6.0からRubyとRuby on Railsサポートされているからだ。

インストールはダウンロードして展開して
netbeans-6.0m9-full-macosx-ppc.commandを実行。
ひたすらデフォルトで進める。ただそれだけ。

Java6が入っていればそちらを認識してくれる。

それにしても重い。Java6が悪さをしているのかと思ったがそうでもないようだ。 むぅ。

OSXでJava6

どうやらまだDeveloper Preview版しかないようだ。 (2006/09/12から更新が止まっている模様) とりあえずインストール方法は以下。 Tiger 10.4.5以降が必要(Intel / PPCどちらでも可)

  1. ADCに入会する。
  2. ADC Member Site→Download→Java
  3. Java SE 6.0 Release 1 DP6(Disk Image)をダウンロード ReleaseNoteは目を通すこと。
  4. ダウンロードしたJavaSE6Release1.pkgを実行する
  5. インストーラに従ってインストールする
Java6をいれてもJava5は残るらしい。 JVMの切り替えかたは以下の通り。
  1. /Application/Utilities/Java/Java SE 6にあるJava Preferences.appを実行する
  2. 基本タブを開く
  3. Use VersionをJava SE 6にする
  4. Java Application Runtime SettingsでJava SE6をドラッグして一番上にする
  5. Saveボタンをおして保存する
これでターミナルから実行するときもダブルクリックの時もJava6が使われる。 PPC版ではJITコンパイラが実装されてないらしい。 微妙だが、まぁ何とかなるだろう。