使いやすくなった Rails 3.1 の Engine
Rails エンジン (Rails Engine) は、ひとことで言うと、Rails アプリケーションの再利用を容易にする仕組みです。Rails エンジンに関することは、https://github.com/rails/rails/blob/master/railties/lib/rails/engine.rb に全て書いてあるのですが、自分用のメモとして Rails エンジンの {良いところ,作り方,使い方} を簡単にまとめてみます。
この記事は、執筆時点の最新版 (Rails 3.1.0.rc5) をもとにしているため、8/22 リリースとアナウンスされている 3.1.0 では参考にならないかもしれません。ご注意を。
Rails エンジンとは
他の Rails アプリケーションを自分の Rails アプリケーションから利用したい、という場合、どのようにすればよいでしょうか。Rails アプリケーションは Web アプリケーションなので、2 つのアプリケーションを立ち上げて、ハイパーリンクでつないであげるだけで連携できます。
しかし、Rails プロセスを 2 つ立ち上げておくのは効率も格好も悪いです。そこで、Rails エンジンの登場です。Rails アプリケーションをエンジンとして取り込んでしまえば、Rails プロセスを 2 つ立ち上げる必要が無くなります。
このように、Rails エンジンを使うと、アプリケーションの再利用が容易になります。
以下、Rails エンジンが組み込まれるほうの Rails アプリケーション(図の上のほうの 「Rails アプリ」)を便宜上「親アプリケーション」と呼ぶことにします。
Rails エンジンの良いところ
ここまで読んでいて、Rails のプラグインを作ったことのある人なら、「それ、プラグインでできるよ」と思ったに違いありません。私も最初そう思いました。しかし、エンジンにはプラグインには無い良さがあります。たくさんあるので一部を列挙すると:
- app ディレクトリの models/controllers/helpers を自動的にロード
- エンジンが独自の config/routes.rb のルーティング設定、config/locales/* のロケール、lib/tasks/* のタスクをもつことができ、しかも、自動的にロード
- エンジン独自の Rack ミドルウェア
- (などなど)
があります。
Rails エンジンの作り方
「でも、Rails エンジンって作るの難しいんでしょう?」
いえいえ、rails new ではなく rails plugin new するだけで、あとは、Rails アプリケーションと同じです。ただし、rails plugin new に --mountable オプションをつけるのをお忘れなく。
$ rails plugin new engine_name --mountable
例として、blog エンジンを作成するつもりになって、rails plugins コマンドを実行してみます。
$ rails plugin new blog --mountable
すると、blog ディレクトリにエンジンの雛形ができます。あとは、雛形を参考に、普通の Rails アプリケーションと同じように開発するだけです。
Rails エンジンのディレクトリ構成
ちょっと寄り道をして blog ディレクトリをのぞいてみましょう。
blog ディレクトリを上から見ていくと、Rails アプリケーションでも毎度おなじみ app フォルダの下に blog.gemspec ファイルが見つかります。名前のとおり、gemspec の雛形になっています。gem にしておくと、アプリケーションへのエンジン組み込みが簡単になるので、エンジンを作るときには gem にするのがお勧めです。
config ディレクトリを見ると、中に routes.rb が見えます。この config/routes.rb にエンジン独自のルートを記述します。もう、親アプリケーションの config/routes.rb にルーティング設定をコピーするプラグイン時代は過去のものになりました。その次に見える lib フォルダの話は後回しにして、Rails アプリケーションでおなじみの db ディレクトリが見つかりません。これは、Rails エンジンが親アプリケーションと同じデータベースを利用するためです。
しかし、データベース無しでは各種テストを実行できません。そこで、test/dummy にテスト用のダミー Rails アプリケーションが用意されています。エンジンをテストするときには、この test/dummy アプリケーションにエンジンを組み込んでテストすることになります。
最後に、後回しにした lib ディレクトリについて説明します。この lib ディレクトリには、このアプリケーションがエンジンであることを証明する大切なコードが含まれています。それが、lib/blog/engine.rb です。
# lib/blog/engine.rb module Blog class Engine < Rails::Engine isolate_namespace Blog end end
isolate_namespace() は普通の Rails アプリケーションでは見ないメソッドです。このメソッドは、ヘルパーメソッドと名前つきルートについて、エンジンと親アプリケーションの名前空間を分離するものです。
エンジンが独自のルーティング設定 (config/routes.rb) をもつことを前に述べました。たとえば、エンジンに独自の root ルートを設定し、親アプリケーションにも root ルートが設定されていたとしましょう。この場合、エンジンのコントローラから root_path を参照するとどうなるのでしょう? エンジンも親アプリケーションの名前空間を共有するので困ったことになってしまいます。
ここで isolate_namespace() の登場です。isolate_namespace() で指定されたエンジンのモジュールからは、親アプリケーションのヘルパーメソッドと名前つきルートにアクセスできなくなります。isolate_namespace() した場合、エンジンのコントローラで root_path を呼び出すと、エンジンの root_path を参照することになります。
もしエンジンから親アプリケーションの root_path にアクセスしたくなったときでも大丈夫です。main_app をつけて、
module Blog class CommentController < ActionController::Base def index main_app.root_path end end end
などとすれば、アクセスできます。
さて、話を lib/blog/engine.rb に戻します。このファイルには Blog::Engine クラスが含まれているのですが、この Blog::Engine クラスを親アプリケーションがロードすることによって、親アプリケーションがエンジンを組み込みます。lib ディレクトリのもう一つのファイル、lib/blog.rb を見てみると、
# lib/blog.rb require "blog/engine" module Blog end
となっているので、親アプリケーションでは、
require 'blog'
するだけで、blog エンジンを組み込めます。
エンジンの使い方
最後に、親アプリケーションからエンジンを利用する方法についてです。親アプリケーションからエンジンを利用するには、
- エンジンのロード
- エンジンのマウント
- マイグレーションのコピー
を行ないます。
(1) エンジンのロード
Rails エンジンを親アプリケーションから使うには、エンジンの MyEngine::Engine クラスを親アプリケーションの config/application.rb か、Gemfile でロードします。例で挙げた blog エンジンであれば、Blog::Engine クラスをロードします。blog エンジンが gem になっていれば、親アプリケーションの Gemfile で
gem 'blog', :git => 'https://example.com/path/to/blog.git'
とするだけで、コードのコピーも Blog::Engine クラスのロードもできるので簡単です。もし、gem になっていなくても、config/application.rb で
require 'blog'
すればエンジンを組み込めます。$: にエンジンの lib を追加するのをお忘れなく。
(2) エンジンのマウント
親アプリケーションの config/routes.rb でエンジンをマウントします。次の例では、MyApplication が親アプリケーションです:
# my_application/config/routes.rb MyApplication::Application.routes.draw do mount Blog::Engine => '/blog' end
おわりに
Rails エンジンの {良いところ,作り方,使い方} を簡単に説明してきたのですが、まだまだ、エンジン名の話やら、ヘルパーの話やら、endpoint の話やら、Rails エンジンについて話していないことがたくさんあります。実際に Rails エンジンを作るとなると、この記事の情報だけでは不十分なので、この記事を読んで Rails エンジンに興味をもったら、ぜひ、https://github.com/rails/rails/blob/master/railties/lib/rails/engine.rb を読んでみてください。Rails エンジンは、ここで書いた以上に便利でかつパワフルです。
一部の情報については古くなっているのですが、http://piotrsarnacki.com/2010/09/14/mountable-engines/ も有用です(Migration と Asset に関する記述は参考にならないかも)などは参考になります。
Rails 3.0 以前からエンジンの仕組み自体は存在しており、RailsAdmin や Tolk といったエンジンが既にあります。Rails 3.1 からは、独自の routes.rb をもてたり、マウントできたりするようになり、さらに使いやすくなりました。Rails 3.1 のエンジンとして公開されるアプリケーションが増え、アプリケーション単位の再利用で Rails 開発がもっと楽しくなることを願っています。