はっさんブログ

フォローするとお得なブログ

機能が増えたRailsアプリケーションをRails Engineで分割する【vol.2】

参考になったらシェアいただけると幸いです!

f:id:usgitan:20180211120428j:plain

背景

以下の続きになります。

vol.1では導入に至った背景から、Engine側へのルーティングの疎通確認まで行いました。

www.hassansan.me

Engine側にファイルを移行していく前に

一度Engine側のルートで適当にControllerの生成をしてみてください。

その際、Engine側でも$ bundle install --path vendor/bundleが必要になります。

$ pwd
=> /User/hassan/engine-sample/main_app
~/rails-apps/engine-sample/main_app » rails g controller hoges index
      create  app/controllers/main_app/hoges_controller.rb
       route  get 'hoges/index'
      invoke  erb
      create    app/views/main_app/hoges
      create    app/views/main_app/hoges/index.html.erb
      invoke  test_unit
      create    test/controllers/main_app/hoges_controller_test.rb
      invoke  helper
      create    app/helpers/main_app/hoges_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    js
      create      app/assets/javascripts/main_app/hoges.js
      invoke    css
      create      app/assets/stylesheets/main_app/hoges.css

Engine内にファイルを配置する場合、例えばControllerであれば app/controllers/[engine名]/hoge_controller.rbとengine名ディレクトリの中にファイルを置く必要があります。

設定より規約で必要になります。

Engine側にControllerを移行する

レールに乗るために適切なディレクトリ配下にファイルを置けばOKです。

# main_app/app/controllers/main_app/hoges_controller.rb

# MainApp Engine側のapplication_controllerを読み込む
require_dependency "main_app/application_controller"

module MainApp
  class HogesController < ApplicationController
      def index
        @hoges = Model.all
      end
  end
end

デフォルトでRails本体側のModelが見えています。

Engine側でViewを作成する

Controller同様に適切なディレクトリ配下にファイルを置いてください。

データが表示されることを確認してみましょう。

# main_app/app/views/main_app/hoges/index.html.erb

<% @hoges.each do |hoge| %>
  <%= hoge.attribute %>
<% end %>

#=> データが表示されていることを確認

layoutsの場合

main_app/app/views/layouts/main_app/application.html.erb

stylesheets / javascripts群

main_app/app/assets/stylesheets/main_app/application.scss

main_app/app/assets/javascripts/main_app/application.js

本体側に記述された内容をそのまま写すのみで変更点はありません。

後述するgemをEngine側のGemfileに書く場合、requireで読み込むパスを変更する必要があります。

Engine側でModelを拡張する

# main_app/lib/main_app/engine.rb

module MainApp
  class Engine < ::Rails::Engine
    isolate_namespace MainApp

    ...
    config.to_prepare do
      Dir.glob(Rails.root + 'main_app/app/models/main_app/**/*.rb').each do |c|
        require_dependency(c)
      end
    end
  
  end
end
# main_app/app/models/main_app/model.rb

Model.class_eval do
  # MainAppのみで扱うModel内に書きたい処理
end

Engine側でinitializersを作成する

Engine作成時にはinitializersファイルが存在しないため、新規に作成してautoload_paths に追加します。

# main_app/lib/main_app/engine.rb
module MainApp
  class Engine < ::Rails::Engine
    isolate_namespace MainApp

    config.autoload_paths << File.expand_path('../../config/initializers', __FILE__)
  end
end

initializersに置くファイル例:

# main_app/config/initializers/assets.rb
Rails.application.config.assets.precompile += %w( main_app/defaults/user.png )

Gemfileとgem

group :main_app do
  gem 'main_app', path: 'main_app'

  # main_appで扱うgemを追記していく
  gem 'carrierwave'
  gem 'fog-aws'
  ...
end

Rails Engineを使ってAPIと管理画面を分離する - blog.daich.org のTipsの一番上にあるようにEngineごとにGemfileを分けても良かったのですが、色々と面倒なことになりそうだったので本体の方でgroup分けをします。

まとめ

Railsアプリが大きくなってきて、アプリケーションを分割したいときにEngineを用いる例を紹介しました。

本来はモデルをplugin化するところかもしれませんが、分割したい要件は満たせてると思います。

一例として参考になれば幸いです。

次回は管理者機能Engineである administrate をRails Engineに乗せてみたことを書こうと思います。