WEBサービス創造記

WEBサービスを作ったり保守したりしてる人のメモブログです。

Rails エンジン入門 その1 〜ローカルでエンジンを生成してホストアプリケーションからマウントするまでの手順〜

   

Rails エンジン入門 | Rails ガイドを読んで、Railsエンジン(以下単にエンジンと記載)の仕様について学習したときの備忘録です。

エンジンの概要

エンジンはアプリケーションのミニチュアのようなもので、ホストアプリケーションに対してエンジンが保有している機能を提供します。

エンジンを採用している著名なGemとしては、deviserails_adminがありますので、これらを実際に利用した人はイメージがつきやすいと思います。
例えば、deviseのエンジンをマウントしたアプリケーションは、deviseによって提供される認証機能を利用することができるようになります。

アプリケーションとの違い

Rails::ApplicationクラスはRails::Engineから多くの振る舞いを継承しています。

従って、エンジンとアプリケーションは、細かな違いを除けばほぼ同じものであると考えていただいてよいでしょう。

Rails エンジン入門 | Rails ガイド

上記引用の通り、アプリケーションで可能な実装(例えばMVC各レイヤーの実装やルーティング)を同様にエンジンでも行うことができるため、アプリケーション≒エンジンということになります。

違いとしてはエンジンはホストアプリケーションに組み込んで利用するものなのでそれ単体では起動できないことなどがあげられます。

エンジンの生成

Railsはエンジンを一種の「完全なプラグイン」とみなしています。エンジン・プラグインどちらもlibディレクトリ構造を有していて、プラグインジェネレータの実行によって生成されます。

したがって、エンジンを生成するときはプラグインジェネレータを実行しますが、マウント可能なエンジンとして生成する場合は–mountableオプションを渡す必要があります。
例えば、ホストアプリケーションに記事の投稿機能を提供するblogというエンジンを生成する場合は以下の通りコマンドを実行します。

$ rails plugin new blog --mountable

オプションについて

–full

以下の構造を提供

  • appディレクトリツリー
  • config/routes.rb
  • lib/エンジン名/engine.rb(アプリケーション用途時ののconfig/application.rb に相当)
–mountable

–fullオプションで提供されるものに加えて以下を追加

  • アセットマニフェストファイル (application.jsおよびapplication.css)
  • 名前空間化されたApplicationControllerスタブ
  • 名前空間化されたApplicationHelperスタブ
  • エンジンで使用するレイアウトビューテンプレート
  • config/routes.rbでの名前空間分離
  • lib/エンジン名/engine.rbでの名前空間分離

さらに、–mountableオプションはダミーのテスト用アプリケーションを test/dummyに配置します。
このテスト用のアプリケーションのルーティングファイルには以下のように、デフォルトでエンジンが組み込まれています。

Rails.application.routes.draw do

  mount Blog::Engine => "/blog"
end

これによって、テスト用のアプリケーションを利用してエンジンの動作を確認することができます。

$ cd test/dummy
$ rails s    エンジンを組み込んだテストアプリが起動
http://localhost:3000/blog/ などにアクセスして動作確認が可能

エンジンを作ってホストアプリケーションからマウントしてみる

エンジンを作ってホストアプリケーションからマウントするところまでやってみたいと思います。
※エンジンの組み込みの挙動の確認を目的としているのでエンジンに特定の機能の実装などは行いません。

エンジンの生成

まずホストアプリケーションに組み込むblogというエンジンを生成します。

$ cd /path/to/work_dir
$ rails plugin new blog --mountable

このエンジンにダミーのページを用意します。

$ cd blog/
$ rails g controller top index

上記ページをトップページとして利用するようにroutes/config.rbを設定します。

Blog::Engine.routes.draw do
  root to: 'top#index'
end

この段階でテスト用のアプリケーションで上記ページにアクセスできるかを確認します。

$ cd test/dummy
$ rails s
http://localhost:3000/blog/にアクセスしてtop#indexが表示されるかを確認

ホストアプリケーションの生成

上記ではエンジンを生成した際に一緒に作られるテストアプリケーションでエンジンの組み込みを確認しましたが、こんどはホストアプリケーションに組み込んでみます。
下記コマンドでホストアプリケーションを作成します。

$ cd /path/to/work_dir
$ rails new foo -T -d mysql

ホストアプリケーションでのエンジンのマウント

ホストアプリケーションのGemfileに以下を追記します。

gem 'blog', path:"../blog"

pathは、ローカルの場合はエンジンのルートディレクトリになります。

続いてconfig/routesでエンジンをマウントします。

mount Blog::Engine => "/blog"

これでWebrickを起動したら、さっきエンジンのテストアプリケーションで確認したように http://localhost:3000/blog にアクセスできるようになっています。

このようにマウントすれば、blogという名前空間でホストアプリケーションとエンジンとが仕切られ、ホストアプリケーションがarticles_pathのようなルーティングヘルパーによってパスを提供できるとすると、そのアプリケーションのエンジンも同じくarticles_pathというヘルパーによってパスを提供でき、しかも両者が衝突しないということになります。

マウント位置をルートにすることもできます。

mount Blog::Engine => "/"

ただし、この場合ホストアプリケーションとエンジンとでルーティングが衝突していた場合は、ホストアプリケーションでの定義が優先されます。

アプリケーションは いかなる場合も エンジンよりも優先されます。ある環境において、最終的な決定権を持つのはアプリケーション自身です。エンジンはアプリケーションの動作を大幅に変更するものではなく、アプリケーションを単に拡張するものです。

Rails エンジン入門 | Rails ガイド

以上、ローカル作ったエンジンを、同じくローカルにあるアプリケーションからマウントすることができました。

しかし、実践でエンジンを利用するには以下のような疑問が残っています。

  • ホストアプリケーションとエンジン間のDBスキーマの共有はどうするのか
  • エンジンをローカルではなくリモートのリポジトリに置く場合はどうマウントすればいいのか

これらについては別記事で学習していきます。

 - Ruby on Rails , ,