standalone-migrations | Rails 以外のプロジェクトでマイグレーションを使う


Rails のマイグレーションは、DB のスキーマを管理してくれる大変便利な機能です。`standalone-migrations` は、Rails プロジェクトではないんだけど、DB のスキーマを Rails のマイグレーションで管理したいの！っていうときに使える便利な gem です。

## リソース

- [GitHub](https://github.com/thuss/standalone-migrations)
- [RubyGems.org](https://rubygems.org/gems/standalone_migrations/versions/4.0.2)
- [The Ruby Toolbox](https://www.ruby-toolbox.com/projects/standalone_migrations)

## 初期設定

今回は DB に MySQL を使うため、`mysql2` の gem を合わせてインストールしておきます。

```
$ gem install standalone_migrations
$ gem install mysql2
```

以下の内容で、`Rakefile` を作成します。

```ruby:Rakefile
require 'standalone_migrations'
StandaloneMigrations::Tasks.load_tasks
```

Rails 同様、DB の設定ファイルを用意します。自分の環境に合わせて記述しましょう。

```yaml:db/config.yml
default: &default
  adapter: mysql2
  encoding: utf8
  reconnect: false
  pool: 5
  username: root
  password:

development:
  <<: *default
  database: sample_development

test: &test
  <<: *default
  database: sample_test

production:
  <<: *default
  database: sample_production
```

## 利用方法

利用可能なコマンドの一覧を表示してみましょう。

```
$ rake -T
rake about                           # List versions of all Rails frameworks and the environment
rake db:create                       # Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config)
rake db:drop                         # Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config)
rake db:fixtures:load                # Load fixtures into the current environment's database
rake db:migrate                      # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
rake db:migrate:status               # Display status of migrations
rake db:new_migration[name,options]  # Creates a new migration file with the specified name
rake db:rollback                     # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rake db:schema:cache:clear           # Clear a db/schema_cache.dump file
rake db:schema:cache:dump            # Create a db/schema_cache.dump file
rake db:schema:dump                  # Create a db/schema.rb file that is portable against any DB supported by AR
rake db:schema:load                  # Load a schema.rb file into the database
rake db:seed                         # Load the seed data from db/seeds.rb
rake db:setup                        # Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the database first)
rake db:structure:dump               # Dump the database structure to db/structure.sql
rake db:structure:load               # Recreate the databases from the structure.sql file
rake db:version                      # Retrieves the current schema version number
rake doc:app                         # Generate docs for the app -- also available doc:rails, doc:guides (options: TEMPLATE=/rdoc-template.rb, TITLE="Custom Title")
rake log:clear                       # Truncates all *.log files in log/ to zero bytes (specify which logs with LOGS=test,development)
rake middleware                      # Prints out your Rack middleware stack
rake notes                           # Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)
rake notes:custom                    # Enumerate a custom annotation, specify with ANNOTATION=CUSTOM
rake rails:template                  # Applies the template supplied by LOCATION=(/path/to/template) or URL
rake rails:update                    # Update configs and some other initially generated files (or use just update:configs or update:bin)
rake routes                          # Print out all defined routes in match order, with names
rake secret                          # Generate a cryptographically secure secret key (this is typically used to generate a secret for cookie sessions)
rake stats                           # Report code statistics (KLOCs, etc) from the application or engine
rake time:zones:all                  # Displays all time zones, also available: time:zones:us, time:zones:local -- filter with OFFSET parameter, e.g., OFFSET=-6
rake tmp:clear                       # Clear session, cache, and socket files from tmp/ (narrow w/ tmp:sessions:clear, tmp:cache:clear, tmp:sockets:clear)
rake tmp:create                      # Creates tmp directories for sessions, cache, sockets, and pids
```

`string` 型の `title` と `text` 型の `body` というカラムを持つ `blogs` というテーブルを新規作成してみましょう。若干、Rails のマイグレーションとコマンドの書き方に違いがありますが、以下のコマンドを実行してみましょう。

```
$ rake db:new_migration name=create_blogs options='title body:text'
      create  db/migrate/20150513130801_create_blogs.rb
```

Rails のマイグレーションでお馴染みのファイルが自動生成されます。`created_at` や `updated_at` を生成したい場合は、`t.timestamp` を追記する必要があります。

```ruby:db/migrate/20150513130801_create_blogs.rb
class CreateBlogs < ActiveRecord::Migration
  def change
    create_table :blogs do |t|
      t.string :title
      t.text :body
    end
  end
end
```

まだ、データベースを作成していなかったので、以下のコマンドでデータベースを作成します。

```
$ rake db:create
```

続いて、以下のコマンドでマイグレーションを実行して、`blogs` テーブルを作成します。

```
$ rake db:migrate
== 20150513130801 CreateBlogs: migrating ======================================
-- create_table(:blogs)
   -> 0.0172s
== 20150513130801 CreateBlogs: migrated (0.0173s) =============================
```

DB に `blogs` テーブルが作成されて、以下のようなスキーマファイルも出来上がります。うん、Rails ですね。

```ruby:db/scheme.rb
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20150513130801) do

  create_table "blogs", force: :cascade do |t|
    t.string "title", limit: 255
    t.text   "body",  limit: 65535
  end

end
```

マイグレーションファイルは、Rails と同じように記述することが出来るため、例えば、以下のように書くことが出来ます。

```ruby:db/migrate/20150513130801_create_blogs.rb
class CreateBlogs < ActiveRecord::Migration
  def change
    create_table :blogs, id: false do |t|
      t.column :id, 'BIGINT PRIMARY KEY AUTO_INCREMENT'
      t.integer :user_id, limit: 8, null: false
      t.boolean :type, null: false
      t.decimal :score, precision: 6, scale: 3, null: true
      t.date :date, null: false
      t.string :title, null: false
      t.text :body, null: false

      t.timestamps null: false
    end
  end
end
```

マイグレーションを実行すると、以下のようなスキーマファイルとなります。

```ruby:db/scheme.rb
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20150513130801) do

  create_table "blogs", force: :cascade do |t|
    t.integer  "user_id",    limit: 8,                             null: false
    t.boolean  "type",       limit: 1,                             null: false
    t.decimal  "score",                    precision: 6, scale: 3
    t.date     "date",                                             null: false
    t.string   "title",      limit: 255,                           null: false
    t.text     "body",       limit: 65535,                         null: false
    t.datetime "created_at",                                       null: false
    t.datetime "updated_at",                                       null: false
  end

end
```

## サンプルプロジェクト

今回、使用したプロジェクトはサンプルとして、以下のリポジトリに置いてあります。

- [fakestarbaby/standalone-migrations](https://github.com/fakestarbaby/standalone-migrations)

## 合わせて読みたい

- [Rails以外でもDBのスキーマ管理にMigrationが使えるStandaloneMigration | Oreradio.memo](http://www.oreradio.com/2012/02/08.php)
- [Railsじゃなくてもマイグレーションを使えるStandaloneMigration | ひげろぐ](http://higelog.brassworks.jp/?p=1718)