[Ruby on Rails]Rails Engine – (1)サブプロジェクトを作成する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

ある程度のアプリケーションを開発していると、特定の機能をサブプロジェクトとして切り出して開発したくなることがあるかと思います。例えばBtoCサイトにおける管理機能をサブプロジェクト化してセキュリティを考慮するとか、肥大化してきたアプリケーションを機能単位に分割する等です。Ruby on RailsではRails Engineを使って、これを実現することができます。今回は先のリンクのチュートリアルに従い、Rails Engineを使ってみたので手順等を纏めてみたいと思います。

Rails Engineについて

Rails Engineについては公式サイトの日本語版の説明が一番分かり易いと思います。

エンジン (engine) とは、アプリケーションのミニチュアのようなものであり、ホストアプリケーションに機能を提供します。(中略)Rails::ApplicationクラスはRails::Engineから多くの振る舞いを継承しています。

Rails エンジン入門より

今回作成するサンプルの概要

先にも書いた通り、Rails Engineを使うとサブプロジェクトを作成することができます。今回行ったチュートリアルでは「blorgh」という名のサブプロジェクトを作成し(チュートリアル内ではEngineと呼んでいる)、後からメインのプロジェクトに統合しています。私はメインのプロジェクトを「blorgh-parent」という名前で作成し、サブプロジェクトと同階層に作成しました。図にすると以下の様な構成です。

.
├── blorgh          ・・・サブプロジェクト。メインのプロジェクトに組み込まれる。
└── blorgh-parent   ・・・メインのプロジェクト。

サブプロジェクトの作成〜メインプロジェクトへの統合手順

ではサブプロジェクトを作成し、メインのプロジェクトに組み込むまでの手順についてです。一部チュートリアルと異なる手順で行った箇所もあります。また足りない箇所などあれば、参考サイト欄のチュートリアルを参考にしてください。

1.サブプロジェクトの作成

以下のコマンドを実行してサブプロジェクトを作成します。

$ mkdir blorgh
$ cd blorgh
$ vim Gemfile
source "https://rubygems.org"
gem "rails"
$ bundle install --path=vendor/bundle
$ bundle exec rails plugin new . --mountable

開発マシン自体にRailsをインストールしたくないため(このプロジェクトにのみRailsをインストールしたい)、「--path=vendor/bundle」を指定したbundle installしています。ポイントとなるのは最後の行で「rails plugin new . --mountable」とすることでEngineのプロジェクトを作成しています。この時「-T」オプションを付けると、サブプロジェクトを単体で起動できなくなるので注意してください。理由は「-T」を付けることによりRailsのデフォルトのテストは作成されなくなりますが、サブプロジェクトを単体で動かすためのRailsは「test/dummy」フォルダにできるので、結果的にフォルダ毎作成されなくなるためです。

2.Scaffoldによる記事投稿機能を作成

Scaffoldなどは通常のRailsアプリケーションと変わりません。チュートリアルと同様、Blogの記事を投稿する機能を作成します。

$ bundle exec rails g scaffold article title:string text:text
$ bundle exec rake db:migrate

config/routes.rbに以下を追記します。

root to: "articles#index"

以下のコマンドでサブプロジェクト単体で起動し、動作を確認します。

$ cd test/dummy
$ rails s

先にも書いた通り単体で動かすためのRailsは「test/dummy」フォルダに存在することに注意してください。余談ですがルーティングをみるための「rake routes」も「test/dummy」フォルダでしか実行できません。

3.コメントの投稿機能を作成

記事に対するコメントを投稿する機能を作成します。

先ずはModelを作成します。

$ bundle exec rails generate model Comment article_id:integer text:text
$ bundle exec rake db:migrate

app/views/blorgh/articles/show.html.erbを編集し、Editリンクの直前に以下を追記します。

<h3>Comments</h3>
<%= render @article.comments %>
<%= render "blorgh/comments/form" %>

ArticleモデルとCommentモデルがhas_manyの関係であると分かるよう、app/models/blorgh/article.rbに以下を追記します。

has_many :comments

Controllerを作成します。

$ bundle exec rails g controller comments

app/controllers/blorgh/comments_controller.rbに以下を追記します。

    def create
      @article = Article.find(params[:article_id])
      @comment = @article.comments.create(comment_params)
      flash[:notice] = "Comment has been created!"
      redirect_to articles_path
    end

    private
    
    def comment_params
      params.require(:comment).permit(:text)
    end

app/views/blorgh/comments/_comment.html.erbを作成し、以下を追加します。

<%= comment_counter + 1 %>. <%= comment.text %>

config/routes.rbを修正します。

resources :articles do
  resources :comments
end

rails s でRailsを起動し、以下のURLでアクセスできるかと思います。

http://localhost:3000/blorgh/articles

ここまでは全てサププロジェクトでの作業でした。

4.メインプロジェクトへの統合

最後にメインプロジェクトを作成し、サブプロジェクトを統合します。

サププロジェクト「blorgh」と同階層に「blorgh-parent」プロジェクトを作成します。今度は普通のRailsアプリケーションとしてください。

作成したメインプロジェクトのGemfileに以下を記述し、サププロジェクトを取り込みます。

gem 'blorgh', path: '../blorgh'

Gemfileに記述するように、Engineで作成したサブプロジェクトはGemの一種のようです。bundle installを実行して、サブプロジェクトを取り込みます。

config/routes.rbに以下を追記します。

mount Blorgh::Engine, at: "/blog"

以下を実行して、サブプロジェクト側で実行したマイグレーションをコピーします。

$ bundle exec rake blorgh:install:migrations
$ bundle exec rake db:migrate SCOPE=blorgh

今度はメインプロジェクト側でRailsを起動し、以下のURLでアクセスできるかと思います。

http://localhost:3000/blog/articles

まとめ

サブプロジェクトを作成し、後からメインとなるプロジェクトへ統合することが、今回やってみたRails Engineの特徴だと私は思います。実際の開発や保守などでも、このような需要は多々あるのではないかと思います。何かの時に役に立てば幸いです。

参考サイト

Getting Started with Engines
Rails エンジン入門(日本語)