Rails Guides Active Record Migrations

http://guides.rubyonrails.org/migrations.html

これ日本語に訳してみました。

最後の方よくわからなかった。

sql とか
rubyとかのとこ。

ちょっと動かしてもう一回書き直します。

Active Record Migrations

Active Recordの特徴の一つであるMigrationsによって、
瞬時にデータベースのスキーマを展開する事ができます。

純粋なSQLスキーマに変更を加えるのではなく、
簡単なRuby DSLでテーブルに変更を加える事ができるのがMigrationsの特徴。

このガイドで、次の知識をえることができます。

・migrationsを作成する為のgenetators
・データベースを簡単に操作する為のActive Recordのmethodについて
・migrationsとスキーマを操作する為のrakeの動作
・migrationsとschema.rbの関係

1、Migrations概要
 Migrationsによって、便利で簡単に、矛盾なくデーターベーススキーマに変更を加えることができる。
方法としては、直接SQLを書かずにRuby DSLを使う。そしてRuby DSLはDBに依存しない。

migrationはまるで新しいデータベースのように思えるかもしれません。
migrationによって、何もない状態から、テーブルやカラム、エントリーをスキーマに作成したり、
スキーマから削除したりする。時系列にそって、最新の履歴からActive Record はスキーマに更新を
かけます。またDBの最新の状態と矛盾しないようdb/schema.rbを更新します。

migrationの一例です。

class CreateProducts < ActiveRecord::Migration
  def change
    create_table :products do |t|
      t.string :name
      t.text :description
 
      t.timestamps
    end
  end
end

このmigrationは型がstringのnameカラム、型がtextのdescriptionを持つproductsテーブル
を作成しています。
プライマリキーであるidは自動的に作成されます。(すべてのActive Record modelsにとってこのプライマリーキーはデフォルトです。)
timestampsのマクロは、created_atとupdated_atという2つのカラムを作成します。
この2つのカラムが存在する場合は、Active Recordによって、自動的に管理されます。

次にその時の変更を以前の状態に戻したい時の変更の定義についてお話しします。

migrationが実行される前は、テーブルは存在しません。実行された後には存在します。
逆に、ActiveRecordはこのmigrationの操作をなかったことにすることもできます。
migrationをrollbackすれば、テーブルは削除されます。

スキーマの変更文に対するトランザクションをサーポートするDBでは、migrationsは
トランザクションにラップされています。データベースがトランザクションをサポートしていない場合、
実行されたmigrationの一部に失敗し、rollbackはされません。
その時は、直接変更文を作成し、rollbackする必要があります。

Active Record では戻す事のできない処理をmigrationで行いたい場合は、reversibleを使う

 def change
    reversible do |dir|
      change_table :products do |t|
        dir.up   { t.change :price, :string }
        dir.down { t.change :price, :integer }
      end
    end
  end
end

またchangeの代わりにup and downも使える。

class ChangeProductsPrice < ActiveRecord::Migration
  def up
    change_table :products do |t|
      t.change :price, :string
    end
  end
 
  def down
    change_table :products do |t|
      t.change :price, :integer
    end
  end
end

2、Migrationの作成

2.1 スタンドアローンMigrationの作成
Migrationsはdb/migrateディレクトリーにmigration class毎に保存される。
ファイル名の命名規則はYYYYMMDDHHMMSS_create_products.tb。
UTC timestamp_migration名。ファイル名の後方はmigrationのクラス名と一致していなければ
ならない。
例えば20080906120000_create_products.rbのクラス名はCreateProducts、
20080906120001_add_details_to_products.rbはAddDetailsToProductsと
定義される必要がある。
Railsはどのmigrationを、どの順番で実行するかをこのtimestampで判断している。
なので、もし、自分で他のアプリケーションからmigrationをコピーし、ファイルを生成する場合、
順番に気をつけてもらいたい。

timestampを計算するのが楽しくない、なのでもちろんActive Recordにはtimestampを計算
するgeneratorがある

$ rails generate migration AddPartNumberToProducts

空ではあるが、きっちり命名されたmigrationが作成させる。

class AddPartNumberToProducts < ActiveRecord::Migration
  def change
  end
end

もしmigration nameが "AddXXXToYYY" or "RemoveXXXFromYYY"の形式で、
カラム名と型のリストが続いて書かれている場合、
add_column と remove_column を含んだ、migrationが作られる。

$ rails generate migration AddPartNumberToProducts part_number:string 

を実行すれば

class AddPartNumberToProducts < ActiveRecord::Migration
  def change
    add_column :products, :part_number, :string
  end
end

が生成される。

もし、新しいカラムにindexを作成したい場合は、このようにして下さい。

$ rails generate migration AddPartNumberToProducts part_number:string:index

を実行すれば

class AddPartNumberToProducts < ActiveRecord::Migration
  def change
    add_column :products, :part_number, :string
    add_index :products, :part_number
  end
end

ができる。

同じくコマンドラインからカラムを削除するmigrationを生成することができる

$ rails generate migration RemovePartNumberFromProducts part_number:string

を実行すれば、

class RemovePartNumberFromProducts < ActiveRecord::Migration
  def change
    remove_column :products, :part_number, :string
  end
end

が生成される。

一度に生成されるカラムに制限はありません。

例えば、

$ rails generate migration AddDetailsToProducts part_number:string price:decimal
<||

を実行すれば

>|ruby|
class AddDetailsToProducts < ActiveRecord::Migration
  def change
    add_column :products, :part_number, :string
    add_column :products, :price, :decimal
  end
end

が生成される。

migration名が"CreateXXX"でカラムと型名のリストが続く場合、migrationはリストにあるカラム
とセットのXXXテーブルが作成される。
例えば

$ rails generate migration CreateProducts name:string part_number:string

を実行すれば

class CreateProducts < ActiveRecord::Migration
  def change
    create_table :products do |t|
      t.string :name
      t.string :part_number
    end
  end
end

が生成される。

いつもどおり、最初の段階で生成されたものに、追加や削除の変更を加えたい場合、
db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rbファイルを編集すればよい。

また、話が変わるが、generatorはreferencesという型のカラムを作る事ができる。
例えば、

$ rails generate migration AddUserRefToProducts user:references

を実行すると

class AddUserRefToProducts < ActiveRecord::Migration
  def change
    add_reference :products, :user, index: true
  end
end

が生成される。

このmigrationはuser_idカラムと適切なインデックスを作成する。

もしJoinTableがmigration名の一部にあれば、結合したテーブルを生成するgeneratorとなる。

rails g migration CreateJoinTableCustomerProduct customer product

は下記のmigrationを生成する。

class CreateJoinTableCustomerProduct < ActiveRecord::Migration
  def change
    create_join_table :customers, :products do |t|
      # t.index [:customer_id, :product_id]
      # t.index [:product_id, :customer_id]
    end
  end
end

2.2 Model Generators

class CreateProducts < ActiveRecord::Migration
  def change
    create_table :products do |t|
      t.string :name
      t.text :description
 
      t.timestamps
    end
  end
end


2.2 Model Generator
model scaffold generator は 適切に新しいmodelを追加する為のmigrationを生成する。
このmigrationはmodelと関係するテーブルを作成する命令も含まれている。
どんなカラムが必要かRailsに指示すれば、それらのカラムを加える為の実行文が生成される。

例えば
$ rails generate model Product name:string description:text
を実行させると下記の様なmigrationが生成される。

class CreateProducts < ActiveRecord::Migration
  def change
    create_table :products do |t|
      t.string :name
      t.text :description
 
      t.timestamps
    end
  end
end

必要なカラムとその型を好きなだけ追加できます。

2.3 サポートされているタイプの拡張
フィールドタイプの後ろにカーリブランケットでいくつかのオプションの指定も可能だ。
以下の拡張が可能。

limit string/text/binary/integer フィールドの最大サイズの指定
precision decimal フィールドの精度の定義
scale decimal フィールドのscaleの定義
polymorphic 外部キー
null カラムへのnullを許可するかしないか

例えば

$ rails generate migration AddDetailsToProducts price:decimal{5,2} supplier:references{polymorphic}

を実行させると

class AddDetailsToProducts < ActiveRecord::Migration
  def change
    add_column :products, :price, :decimal, precision: 5, scale: 2
    add_reference :products, :supplier, polymorphic: true, index: true
  end
end

が生成される。

3 Migrationの書き方
generatorsでmigrationを作ったので、それを実行させよう。

3.1 Creating a Table
最も基本的だが、それっきりのmethodであるcreate_table methodは、
model scafforld generatorを使えば生成される。

典型的なものがこれである。

create_table :products do |t|
  t.string :name
end

nameカラムを持ったproductsテーブルが生成される。
(暗黙的にidカラムも生成される。)

デフォルトで、create_tableはidと呼ばれるプライマリキーが生成される。
primary_keyオプションを使用すれば、primary_keyの名前を変更する事が可能です。
(ただし関係するmodelを更新するのも忘れないで下さい。)
もしプライマリキーが不必要であれば、id: falseオプションを指定して下さい。
特別なオプションをデータベースに指定したい場合は:optionsオプションを使えばSQL
一部に置き換える事ができる。

例えば

create_table :products, options: "ENGINE=BLACKHOLE" do |t|
  t.string :name, null: false
end


テーブルを作成するのに使われるSQL分にENGINE=BLACKHOLEを加えます。
MySQLの場合defaultはENGINE=InnoDB

3.2 JoinTableを作成する。

create_join_table methodはHABTM join Tableを作成する。
典型的な使われ方です。
create_join_table :products, :categories
category_id と product_idと呼ばれる2つのカラムを持ったcategories_productsテーブル
を作成する。これらのカラムはdefaultでnullオプションにfalseが設定される。

テーブルnameをカスタマイズしたい時は:table_name を使えば可能だ。
create_join_table :products, :categories, table_name: :categorization
コレを実行すればcategorizationテーブルが生成されるでしょう。

デフォルトで、オプションのない2つのカラムを生成する。しかし、:column_options
オプションを使えばオプションを使用できる。例えば、
create_join_table :products, :categories, column_options: {null: true}

を実行すれば、:nullオプションがtrueのproduct_id と category_idが生成される。

create_join_tableはindexや追加カラムを加える為のブロックもかける。

create_join_table :products, :categories do |t|
  t.index :product_id
  t.index :category_id
end

3.3Changing Tables

create_tableに近いchange_tableは、既存のテーブルに変更を加える際に使用する。
create_tableにほぼ似ているが、ブロック内で使用されるオブジェクトはちょっとトッリキーだ。

change_table :products do |t|
  t.remove :description, :name
  t.string :part_number
  t.index :part_number
  t.rename :upccode, :upc_code
end

descriptionとnameカラムを削除し、part_numberカラムを追加しそれに、indexを加えている。
最後にはupccodeカラムをリネームしている。

3.4 When Helpers aren't Enough
Active Record のヘルパーでできない場合、
生のSQLを実行する為のexecute methodを使用する。

Products.connection.execute('UPDATE `products` SET `price`=`free` WHERE 1')

ここのmethodの詳細と例は、APIドキュメントをチェックしてください。
特に
ActiveRecord::ConnectionAdapters::SchemaStatements
( change, up and down methodsの利用方法)
ActiveRecord::ConnectionAdapters::TableDefinition
( create_tableによって生成されたオブジェクトの利用)
ActiveRecord::ConnectionAdapters::Table
( change_tableによって生成されたオブジェクトの利用)
のドキュメント


3.5 Using the change Method
変更する為のmethodは、基本ん的にwriting migrations.の方法です。
だいたいの場面で動作し、自動的にmigrationを戻す方法がActive Record にはある。
現在、変更methosは以下のmigrationの定義のみサポートする。

add_column

add_index
add_reference
add_timestamps
create_table
create_join_table
drop_table (must supply a block)
drop_join_table (must supply a block)
remove_timestamps
rename_column
rename_index
remove_reference
rename_table

change_table は change,change_default,removeを呼ぶ事のできない、
reversibleである。
もし、上記のmethod以外のものを使う必要がある場合は、changeテーブルの代わりにreversibleかup and down methos
を使用すべきである

3.6 Using reversible

複雑なmigrationsはActive Recordが置き換えられない処理を要求する場合がある。
migrationを実行する時にすべき事、migrationを変換させる時に他にする事、
を特記するのにreversibleを使用する。

例えば

class ExampleMigration < ActiveRecord::Migration
  def change
    create_table :products do |t|
      t.references :category
    end
 
    reversible do |dir|
      dir.up do
        #add a foreign key
        execute <<-SQL
          ALTER TABLE products
            ADD CONSTRAINT fk_products_categories
            FOREIGN KEY (category_id)
            REFERENCES categories(id)
        SQL
      end
      dir.down do
        execute <<-SQL
          ALTER TABLE products
            DROP FOREIGN KEY fk_products_categories
        SQL
      end
    end
 
    add_column :users, :home_page_url, :string
    rename_column :users, :email, :email_address
  end

reversibleを使えば、正しい順番で、指示が実行される。

このmigrationが戻される場合、home_page_urlが削除された後、productsテーブルが削除される直前にdownブロックが
実行される。

3.7 Using the up/down Methods
change mathodの変わりに、古いmigration styleのup down methodを使う事もできる。
up methosはスキーマに変更を加えたいとき、down methodは up methodによる変更を取り消したい
時に使う。一方でdown methodの変更を加えた後に、up methodを使用するような変更は、
データベーススキーマに加えられるべきではない。up methodでテーブルを作成したならば、
down methodでテーブルを削除するべきだ。
up methodでなされた変更の順番にそって、変更を取り消すのが賢明だ。
reversible methodnの例は以下と同等である。

class ExampleMigration < ActiveRecord::Migration
  def up
    create_table :products do |t|
      t.references :category
    end
 
    # add a foreign key
    execute <<-SQL
      ALTER TABLE products
        ADD CONSTRAINT fk_products_categories
        FOREIGN KEY (category_id)
        REFERENCES categories(id)
    SQL
 
    add_column :users, :home_page_url, :string
    rename_column :users, :email, :email_address
  end
 
  def down
    rename_column :users, :email_address, :email
    remove_column :users, :home_page_url
 
    execute <<-SQL
      ALTER TABLE products
        DROP FOREIGN KEY fk_products_categories
    SQL
 
    drop_table :products
  end
end


もしmigrationで逆行できない様な事があれば、down methodから ActiveRecord::IrreversibleMigration
を呼ぶべきです。誰かがそのmigrationを逆行しようとすれば、「それはできない」という
エラーメッセージをディスプレイに表示させる。

3.8 Reverting Previous Migrations
revert methosを使えば、migrationsをrollbackできるのも Active Record
の特徴だ。

require_relative '2012121212_example_migration'
 
class FixupExampleMigration < ActiveRecord::Migration
  def change
    revert ExampleMigration
 
    create_table(:apples) do |t|
      t.string :variety
    end
  end
end

またrevert methodでは逆行する為の命令文を、ブロックの中に書く事もできる。
以前のmigrationの一部を逆行する際に便利だ。ExampleMigrationがcommitされ、
後に、商品リストにするのが良いと決まった場合。一例としてこのようにかける。

class SerializeProductListMigration < ActiveRecord::Migration
  def change
    add_column :categories, :product_list
 
    reversible do |dir|
      dir.up do
        # transfer data from Products to Category#product_list
      end
      dir.down do
        # create Products from Category#product_list
      end
    end
 
    revert do
      # copy-pasted code from ExampleMigration
      create_table :products do |t|
        t.references :category
      end
 
      reversible do |dir|
        dir.up do
          #add a foreign key
          execute <<-SQL
            ALTER TABLE products
              ADD CONSTRAINT fk_products_categories
              FOREIGN KEY (category_id)
              REFERENCES categories(id)
          SQL
        end
        dir.down do
          execute <<-SQL
            ALTER TABLE products
              DROP FOREIGN KEY fk_products_categories
          SQL
        end
      end
 
      # The rest of the migration was ok
    end
  end
end


同様な動作をするmigrationを、revert methodを使わずに書く事はできるが、
2、3作業が増える。create_table、reversible methodの順番で逆行させ、
create_table method をdrop_table methodで置き換え、最後にdoen method
でup methodを置き換える。逆もまた然りである。
この作業ははrevert methodによっては必要ない。

4 Running Migrations
Railsには一連のmigrationsを実行する為の、Rakeタスクが存在する。
まだどのmigrationも実行されていない場合は、全てのmigrationでchange か up
methodが実行されるのが基本の動きである。すでに実行されたmigrationが存在する場合は、
migrationの日付を基準に実行される。

db:migrateタスクを実行すると、db/schema.rb fileを更新する為のdb:schema:dumpタスクも
実行される。これによりデータベースの構造との差異をなくす。

指定されたバージョンがあれば、 Active Recordはmigrationsが指定されたバージョンに
なるまで要求されたmigrationsを実行する。
バージョンはファイル名の数字の接頭辞である。例えば、version 20080906120000のmigrationを
実行する場合は
$ rake db:migrate VERSION=20080906120000
こうである。

もし20080906120000が現バージョンよりも新しければ、20080906120000を含むすべてのmigrationで
change or up methosを実行する。もし現バージョンよりも古ければ、何も実行しない。
もし、下方にmigrateしたい場合は、全てのmigrationでdownmethodが実行される。
それは20080906120000を含まない。

4.1 Rolling Back

よくあるのが、最後に実行したmigrationをrollbackすることだ。
実行したmigrationに間違いがあり、それを修正したい場合だ。
前に実行したmigrationと関係するバージョンの数字を追っていくよりも。

$ rake db:rollback
実行する事で実現する。

これで、最新のmigrationはrollbackされる。これでchange methodやdown methosで
後戻しする必要はない。もしいくつかのmigrationをなかったことにする必要がある場合は
STEPパラメーターを使用すれば良い。

$ rake db:rollback STEP=3

これを実行すれば、最新の3つのmigrationを後戻しすることができる。

db:migrate:redo taskを実行すれば、rollbackとmigrationの再実行を同時に行ってくれる。
db:rollbackと同様に、STEPパラメーターを使えば、1バージョン以上遡って実行することがかのうである。
例えば

$ rake db:migrate:redo STEP=3

db:migratを一度も実行していなこれば、これらのtaskは何も動作しない。
これらのmethodを使用すれば、migrateのバージョンを気にする必要もない。

4.2 Resetting the Database

4.3 Running Specific Migrations
特別なmigrationのup down methodを実行する必要がある時、
db:migrate:up, db:migrate:downタスクを実行させる。
適切なバージョンを指定し、それに一致するmigrationがあれば、
change,up,down methodが実行される。
例えば

$ rake db:migrate:up VERSION=20080906120000

を実行すれば
バージョン20080906120000のmigrationの、change or up methodが動作する。
このtaskは先ずmigrationが既に実行済みのものかをチェックし、それが既に実行されている
ものであれば、なんの動作も行わない。

4.4 Running Migrations in Different Environments
rake db:migrateはデフォルトではdevelopment環境で実行される。
別の環境に対して実行させたい場合は、RAILS_ENVという環境変数を使用すればよい。
例えばtest環境にたいしてmigratinを実行させたい場合、

$ rake db:migrate RAILS_ENV=test

を実行すればよい。

4.5 Changing the Output of Running Migrations
defaultのmigrationでは、何をして、それにどれくらいの時間がかかったを、教えてくれる。
テーブルを作成し。indexを作成すrmigrationは以下の様なものを出力する。

== CreateProducts: migrating =================================================

    • create_table(:products)

-> 0.0028s
== CreateProducts: migrated (0.0028s) ========================================

この出力を変更できるようにmigrationにはいくつかのmethodがある。

Method Purpose
suppress_messages Takes a block as an argument and suppresses any output generated by the block.
say Takes a message argument and outputs it as is. A second boolean argument can be passed to specify whether to indent or not.
say_with_time Outputs text along with how long it took to run its block. If the block returns an integer it assumes it is the number of rows affected.

例えば、このmigration。

class CreateProducts < ActiveRecord::Migration
  def change
    suppress_messages do
      create_table :products do |t|
        t.string :name
        t.text :description
        t.timestamps
      end
    end
 
    say "Created a table"
 
    suppress_messages {add_index :products, :name}
    say "and an index!", true
 
    say_with_time 'Waiting for a while' do
      sleep 10
      250
    end
  end
end

このような出力となる。

== CreateProducts: migrating =================================================

    • Created a table

-> and an index!

    • Waiting for a while

-> 10.0013s
-> 250 rows
== CreateProducts: migrated (10.0054s) =======================================

Active Recordを使って、何も出力してほしくない場合は、
rake db:migrate VERBOSE=false
を実行すればよい。


5 Changing Existing Migrations

migrationを書けば、時々ミスをする時もある。
既にそのmigrationを実行してしまっていれば、そのmigrationを編集する事も、
再度実行する事もできない。
Railsの設計上、すでにrake db:migrateされたmigrationに対しては
なにも行わない。
migrationをrollbackし、migrationを変更した後にrake db:migrate
を実行しなければならない。
普通、既に存在するmigrationを編集することはあまりおすすめしない。
自分や共同作業者に余分な仕事をさせてしまうだけでなく、既に存在している
migrationが本番で実行されてしまっている場合は、頭を悩ます大きな原因となる。
代わりに、変更を反映させる為の新しいmigrationを書くべきだ。
まだソース管理ツールにコミットされていないmigration(あなたのmachineにしかないmigration)
を編集することは、比較的危険性は低い。
以前のmigrationのすべて、または一部をなかったことにする為の、新しいmigration
書く際に,revert methodは役に立つ。

6 Using Models in Your Migrations

migrationでデータを作ったり、更新したりする時、しばしばModelを使う。
Modelを使用する事で、基本的なデータへのアクセスが簡単にできるようになる。
それはいいが、いくつか懸念点がある。
例えば、
問題はこういう場合に生じる。
今データベース上には存在しないが、
このmigrationか次のmigratioによって作られる様なカラムをModelで使用する際だ。

アリスとボブがProduct model含む、同じコードを共同作業度編集している場合を想定してほしい。

ボブは休暇に行くとする。

その間にアリスはproductsテーブルに新しいカラムを追加するmigrationを作り、それを実行する。
# db/migrate/20100513121110_add_flag_to_product.rb

class AddFlagToProduct < ActiveRecord::Migration
  def change
    add_column :products, :flag, :boolean
    reversible do |dir|
      dir.up { Product.update_all flag: false }
    end
  end
end

彼女はまた、新しいカラムに対するValidationをProduct Modelに追記する。
# app/models/product.rb

class Product < ActiveRecord::Base
  validates :flag, presence: true
end

アリスはproductsテーブルに他のカラムを追加する第二のmigrationを作成し、実行する。
# db/migrate/20100515121110_add_fuzz_to_product.rb

class AddFuzzToProduct < ActiveRecord::Migration
  def change
    add_column :products, :fuzz, :string
    reversible do |dir|
      dir.up { Product.update_all fuzz: 'fuzzy' }
    end
  end
end

彼女はまたProduct modelに新しカラムに対するvalidationを追記する。

# app/models/product.rb

class Product < ActiveRecord::Base
  validates :flag, :fuzz, presence: true
end

両方のmigrationは正常に動作する。

ボブは休みから帰ってくる、そして

・全てのソースを更新 - 両方のmigrationと最新のProduct Modelを含む
・rake db:migrateでmigrationを実行させる。それは、Product Modelを更新
するmigrationも含む。

migrationはクラッシュする。モデルが保存しようとする際、
最初のmigrationが実行された時にはデータベース上には
存在しない、2つ目のカラムに対してvalidation使用とするからだ。

rake aborted!
An error has occurred, this and all later migrations canceled:

undefined method `fuzz' for #

コレを解決するには、migration内にローカルなmodelを作成すればよい。
そうすれば、validationが実行されず、migrationの実行は完了する。

local modelを使用する際に、もう一つやっておいた方がいいことは、
Product.reset_column_informationを呼び出すことだ。
これにより、データベースを更新する前のProduct Modelに対するActive Recordのキャッシュ
を更新することができる。

もし代わりにアリスがやってくれていれば、なにも問題はない。

# db/migrate/20100513121110_add_flag_to_product.rb

class AddFlagToProduct < ActiveRecord::Migration
  class Product < ActiveRecord::Base
  end
 
  def change
    add_column :products, :flag, :boolean
    Product.reset_column_information
    reversible do |dir|
      dir.up { Product.update_all flag: false }
    end
  end
end

# db/migrate/20100515121110_add_fuzz_to_product.rb

class AddFuzzToProduct < ActiveRecord::Migration
  class Product < ActiveRecord::Base
  end
 
  def change
    add_column :products, :fuzz, :string
    Product.reset_column_information
    reversible do |dir|
      dir.up { Product.update_all fuzz: 'fuzzy' }
    end
  end	
end	

7 Schema Dumping and You

7.1 What are Schema Files for?

Migrationはデーターベーススキーマは主として操作する為のソースコードではない。
Migrationはデータベースの状態をしらべ、Active Recordが生成するdb/schema.rb
もしくは、SQLファイルを元に、データベースに落とし込む。
db/schema.rbやSQLファイルは編集されるように設計はされていない。
ただ現状のデータベースの状態を表しているだけである。
アプリの新しいインスタンスを展開するのに、migrationの履歴置き換える必要はない。
現在のスキーマの情報をデータベースにより単純で素早くロードする為の方法である。

例えば、testデータベースを作る方法の例を挙げる。
development用のデータベースがダンプされる。
そしてtest用データベースにロードされる。
db/schema.rbはActive Record object がどのような属性を持っているのかすぐに見るのに
便利である。modelのソースコードの情報もないし、いくつかのmigrationにそって展開されているが、
大変良く要約されている。

annotate_models gemを使えば、そのスキーマをまとめているモデルの最新の
コメントを要約し、更新することができる。

7.2 Types of Schema Dumps
スキーマをダンプするには2つの方法がある。
config/application.rbにconfig.active_record.schema_format setting,
を記述する。:ruby :sqlのステータスがある。
もし:rubyが選択されていれば、スキーマの情報はdb/schema.rbに蓄積されていく。
もし、

 ActiveRecord::Schema.define(version: 20080906171750) do
  create_table "authors", force: true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end
 
  create_table "products", force: true do |t|
    t.string   "name"
    t.text "description"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "part_number"
  end
end

このファイルはデータベースを検証し、create_table, add_indexなどで
構造を表現する事によって作られる。なぜなら、これらはデータベースに依存していないからだ。
Active Recordが対応しているどんなデータベースにでもロードされる。
どんな多様なデータベースを実行するアプリケーションを運用するのには非常に便利なことである。

しかしトレードオフとしてdb/schema.rb はデータベースに依存する、外部キーやトリガー
ストアドプロシージャようなものには対応する事ができない。
migrationの中で、カスタムされたSQLを実行することはできるが、このschemaの情報からは、
データベースに合うように命令文を再構築することができない。
もしこの特徴を使いたいのなら、:sqlオプションで、スキーマ情報を用意すべきだ。
Active Recordのスキーマ情報を使用する代わりに、データベースの構造はdb/structure.sql
にデータベースの情報を記載するツールを使う事で、データベースの構造を表現する事ができる。
例えばPostgreSQLではpg_dump utilityが使われる。MySQLでは、このファイルに
SHOW CREATE
TABLE for the various tables.
の出力が含まれている。

スキーマをロードする事は、単純な
定義によって、データベース構造の完全んあコピーを作る。:sql使うスキーマのフォーマットは、

7.3 Schema Dumps and Source Control
schema dumpsはデータベースのスキーマをコントロールする。
そのため、それらはソース管理することを進める。


8 Active Record and Referential Integrity

ロジックは、modelに書くべきで、データベースに書くものではないというのが、ActiveRecord
特徴だ。外部キーやトリガーのようなロジックをDBに集約してまうようは方法は多用しない。

validates :foreign_key, uniqueness:のようなバリデーションはデータが整合性を保つ為の方法である。
:dependentオプションは親のオブジェクトが消された時に自動的に子のオブジェクトを消す為のものだ。


9 Migrations and Seed Data
データベースにデータを作る際に

class AddInitialProducts < ActiveRecord::Migration
  def up
    5.times do |i|
      Product.create(name: "Product ##{i}", description: "A product.")
    end
  end
 
  def down
    Product.delete_all
  end
end
<||

こう書かれる時がある。
だが、Railsにはseedというものがる。初期データをデータベースに作る時に使われる。
これはほんとにシンプルなものだ。db/seeds.rb にRuby codeを書いて、rake db:seed:
を実行させるだけ。	

>|ruby|
5.times do |i|
  Product.create(name: "Product ##{i}", description: "A product.")
end
<||