Refinery CMS - カスタム Engine でのマイグレーションの追加

Written by @dr_taka_n at 2011/06/26 23:05 [, , ]

作成したカスタム Engine でマイグレーションを追加する場合、Refinery CMS のお作法としてどうするのがよいか? 如何様にもできそうだが、カスタム Engine のメンテナンス性が保てるやり方がよい。そのメモ。

まず最初に product という簡単なサンプルカスタム Engine を作成する。 その後にマイグレーションを追加する方法を記載していく。作業の流れは以下の通り。

  1. Rails 標準の rails generate migration でマイグレーションファイルを作成する
  2. 作成されたマイグレーションファイルをカスタム Engine 配下(vendor/engines/my_engine/db/migrate)に配置する
  3. generator を使用して Rails プロジェクト本体に配置し直して、rake db:migrate

0. 新規のカスタム Engine を作成しておく

これから作業するための新規のカスタム Engine を作成しておく。product という簡易な engine になる。

$ rails g refinery_engine product title:string description:text image:image
      create  vendor/engines/products/app/controllers/admin/products_controller.rb
      create  vendor/engines/products/app/controllers/products_controller.rb
      create  vendor/engines/products/app/models/product.rb
...(snip)...
      create  vendor/engines/products/refinerycms-products.gemspec
      create  vendor/engines/products/spec/models/product_spec.rb
------------------------
Now run:
bundle install
rails generate refinerycms_products
rake db:migrate
------------------------

最後のメッセージの指示通り引き続きコマンドを叩き、カスタム Engine を動作可能な状態としておく。

$ bundle install
...(snip)...
$ rails g refinerycms_products
      create  db/migrate/20110615142942_create_products.rb
      create  db/seeds/products.rb
------------------------
Now run:
rake db:migrate
------------------------
$ rake db:migrate
==  CreateProducts: migrating =================================================
-- create_table(:products)
   -> 0.0014s
-- add_index(:products, :id)
   -> 0.0005s
==  CreateProducts: migrated (0.4472s) ========================================

以上で OK。サーバを起動して管理画面を確認してみると確かに product の管理画面が追加されている。

add-migrateion-to-engine

1. Rails 標準の rails generate migration でマイグレーションファイルを作成する

Product のモデルはすでに作成済みであるが、販売促進用のブローシャアをそれぞれの製品につけたくなったとする。製品に添付ファイルをつけられるようにする。

まずは、Rails 標準の rails generate migration を使用して普通に(Rails way で)マイグレーションファイルを作成する。

$ rails g migration AddBrochureToProducts
      invoke  active_record
      create    db/migrate/20110616161904_add_brochure_to_products.rb

マイグレーションファイルに必要な記載を行っておく。

db/migrate/20110615145336_add_brochure_to_product.rb:

class AddBrochureToProducts < ActiveRecord::Migration
  def self.up
    add_column :products, :brochure_id, :integer
  end

  def self.down
    remove_column :products, :brochure
  end
end

マイグレーションファイルへの追加は以上で終了となる。

上記のマイグレーションの記載に関して、products テーブルに brochure_id という外部キーのフィールドを追加し、関連付けまでさせてたのはわかるが、brochure 本体はどこ?となるだろう。

ここでは、Refinery CMS が標準で用意している Resources Engine を利用している。

resource(ファイル)を扱う為の仕組みは既に Refinery CMS には用意されており、Resource モデルも既に存在する。上記はその外部キーを products テーブルに作成しようとしている。

当然上記のスキーマの更新だけでは動作しないので、既に作成済みの Product モデルにも Resouce への関連を追加しておく。

vendor/engines/products/app/models/product.rb:

class Product < ActiveRecord::Base

  acts_as_indexed :fields => [:title, :description]

  validates :title, :presence => true, :uniqueness => true

  belongs_to :image

  belongs_to :brochure, :class_name => 'Resource' # add
end

2. 作成されたマイグレーションファイルをカスタム Engine 配下(vendor/engines/my_engine/db/migrate)に配置する

先ほどの作成されたマイグレーションファイルは Rails プロジェクト本体の db/migrate 配下に作成されている。 これを Engine の db/migrate 配下に移動する。

$ mv db/migrate/20110616161904_add_brochure_to_products.rb vendor/engines/products/db/migrate/

3. generator を使用して Rails プロジェクト本体に配置し直し rake db:migrate

Refinery CMS が用意している generator を使用して、Rails プロジェクト本体に配置し直す。

$ rails g refinerycms_products
      create  db/migrate/20110616163831_add_brochure_to_products.rb
You already have a migration called create_products
      create  db/seeds/products.rb
------------------------
Now run:
rake db:migrate
------------------------

products Engine 配下の db/migrate に移動したマイグレーションファイルが、Rails プロジェクト本体の db/migrate 配下にコピーされる。 これで migrate する。

$ rake db:migrate
==  AddBrochureToProducts: migrating ==========================================
-- add_column(:products, :brochure, :integer)
   -> 0.0014s
==  AddBrochureToProducts: migrated (0.0015s) =================================

仮に同じ名前のマイグレーションファイルがあった場合には、rails g refinerycms_products を実行しても Rails プロジェクト本体にはファイルはコピーされない。

$ rails g refinerycms_products
You already have a migration called add_brochure_to_products
You already have a migration called create_products
   identical  db/seeds/products.rb
------------------------
Now run:
rake db:migrate
------------------------

以上となる。

スキーマに変更を加える場合には 1 〜 3 の同様の作業を繰り返す。

まどろっこしいことをやっているようだが、この方法により、カスタム Engine ディレクトリ内に全て完結した構成がとれる。
独立性が保たれるので、他プロジェクトへの移植性も高くなる。

(蛇足) 管理画面にブローシャアの登録機能を追加

「カスタム Engine でのマイグレーションの追加」の作業は以上で終了だが、モデルには手を加えたものの、管理画面にブローシャアを登録する為の機能が存在しないので追加する。

既に管理画面用の共通部品として refinerycms-core で resource 用のPicker(/shared/admin/_resource_picker.html.erb)が用意されているので、これを利用する。

管理画面のフォームの view に以下の追加を行っておく。

vendor/engines/products/app/views/admin/products/_form.html.erb:

イメージのフィールドの下あたりで brochure 用のフィールドを追加しておく。

...(snip)
  <div class='field'>
    <%= f.label :image -%>
    <%= render :partial => "/shared/admin/image_picker", :locals => {
          :f => f,
          :field => :image_id,
          :image => @product.image,
          :toggle_image_display => false
        } %>
  </div>
  # add
  <div class='field'>
    <%= f.label :brochure -%>
    <%= render :partial => "/shared/admin/resource_picker", :locals => {
          :f => f,
          :field => :brochure_id,
          :resource => @product.brochure,
        } %>
  </div>

管理画面で確認すると確かにブローシャア用のフィールドが追加されておりちゃんと機能する。

add-migrateion-to-engine2

add-migrateion-to-engine3

同様にユーザに表示する画面にもブローシャアをダウンロードするための機能を追加しておく必要があるが、ここでは割愛する。

blog comments powered by Disqus