表参道.rb #17 ~管理画面について語ろう~ - connpass の発表資料
自己紹介
- sue445
- ドリコム
- 高機能雑用(アプリからインフラまで)
- プリキュアの人
- 主に Shibuya.rb 辺りに参加
- 【宣伝1】 sue445 Advent Calendar 2016 - Qiita
- 【宣伝2】 ドリコム Advent Calendar 2016 - Adventar
最近の仕事
とある社内アプリのリプレイス
- Ruby 2.1.2 -> 2.3.3
- Rails 4.0.0 -> 5.0.0.1
- Resque -> Sidekiq
- capistrano 2系 -> 3系
- Bootstrap 2系 -> 3系
- Debian Lenny 1 -> CentOS 7
最近のトラブル
Agenda
- mountable engineとは?
- 2種類のやり方を紹介
-
- アプリ本体の管理画面をengineにする
-
- gemに管理画面を内包してアプリ本体から使う
-
mountable engineとは?
- Railsアプリに他のRailsアプリをgemとして組み込む仕組み
- 例) RailsAdmin
- MVC全部gemとして別アプリ提供することができる
- 長いので以下engineで表記
2種類のやり方を紹介
- 普通のRailsアプリの管理画面をengineに切り出す
- engine gemに管理画面のengineを内包してアプリ本体から使う
- engine in engine
Rails 5.0.0.1で確認
1. 普通のRailsアプリの管理画面をengineに切り出す
ユースケース
- ユーザからのリクエストを受ける本番アプリ(production-app)は複数サーバにデプロイしつつも、本番の管理画面(production-admin)は1サーバだけにデプロイしたい 2
- しかしproduction-appでは管理画面の機能は組み込みたくないので完全に切り離せるようにしたい
- 管理画面以外にもデバッグ系の機能をengineに切り出したいというケースもある
- 開発環境では使いたいがステージングや本番だと使いたくない
やり方
rails plugin new admin
トップディレクトリ( Gemfile
や Ralefile
がある場所)で下記を実行するとadminディレクトリが作られる
$ ./bin/rails plugin new admin --skip-bundle --mountable
create
create README.md
create Rakefile
create admin.gemspec
create MIT-LICENSE
create .gitignore
create Gemfile
create app
create app/controllers/admin/application_controller.rb
create app/helpers/admin/application_helper.rb
create app/jobs/admin/application_job.rb
create app/mailers/admin/application_mailer.rb
create app/models/admin/application_record.rb
create app/views/layouts/admin/application.html.erb
create app/assets/images/admin
create app/assets/images/admin/.keep
create config/routes.rb
create lib/admin.rb
create lib/tasks/admin_tasks.rake
create lib/admin/version.rb
create lib/admin/engine.rb
create app/assets/config/admin_manifest.js
create app/assets/stylesheets/admin/application.css
create app/assets/javascripts/admin/application.js
create bin/rails
create test/test_helper.rb
create test/admin_test.rb
append Rakefile
create test/integration/navigation_test.rb
vendor_app test/dummy
admin/admin.gemspec を編集
- gemspecにTODOが残ってると
bundle install
できないので適当に削る - https://github.com/sue445/admin_example/commit/4d14b4274f61fbcd2a49bf220c713cb160ceb35c
親アプリのGemfileに組み込む
本体側の Gemfile
に一行追加し、3
group :admin do
gem "admin", path: "admin"
end
routes.rb
でmountするだけ。
mount Admin::Engine => "/admin"
./bin/rails routes
してadminが見えれば成功
$ ./bin/rails routes
Prefix Verb URI Pattern Controller#Action
admin /admin Admin::Engine
Routes for Admin::Engine:
メリット
- 管理画面のみで必要な機能をアプリ本体に含める必要がなくなる
- admin gemを読まなければ管理系の機能は一切使えなくなるのでコード内でif文書くよりもシンプル
注意点
- 本体がAPI mode の時にengineに一部悪影響が出ることがある
- 管理画面エンジンのルーティングで、
resources
をonly
やexcept
句を使わずそのまま使っていた場合、new
とedit
のルーティングされない - API modeの時は
new
とedit
が不要なのだが、その影響をマウントしたengineも受けてしまう - https://github.com/rails/rails/blob/v5.0.0.1/actionpack/lib/action_dispatch/routing/mapper.rb#L1142-L1148
- アプリ本体をAPI modewp有効にする場合は
only
でactionを全部指定すれば回避できる
- 管理画面エンジンのルーティングで、
2. engine gemに管理画面のengineを内包してアプリ本体から使う
ユースケース
- 1つのgemで「エンドユーザにAPIとして提供するengine」と「管理画面のengine」を提供する
- 前者でmodelを提供しているとしたら、そのmodelのCRUDを管理画面で提供
やり方
さっきと同じようにengineを作る
rails plugin new engine_example --skip-bundle --mountable
gemspecからTODOを削る
- 同じくそのままだと
bundle install
でエラーになるので
engines/admin/ を作った後に下記ファイルを作成
module名はいい感じに変更すること
EngineExample::Admin::Engine.routes.draw do
end
module EngineExample
module Admin
class Engine < ::Rails::Engine
isolate_namespace EngineExample::Admin
end
end
end
require "engine_example/admin/engine"
module EngineExample
module Admin
class Railtie < ::Rails::Railtie
end
end
end
engine本体からadminのengineを読み込む
それぞれ下記を追加
s.require_paths = ["lib", "engines/admin/lib"]
require "engine_example/admin/railtie"
require "engine_example/railtie"
mount EngineExample::Admin::Engine => "/admin/engine_example"
動作確認
./bin/rails app:routes
してadminが見えれば成功 4
$ ./bin/rails app:routes
Prefix Verb URI Pattern Controller#Action
engine_example /engine_example EngineExample::Engine
engine_example_admin /admin/engine_example EngineExample::Admin::Engine
Routes for EngineExample::Engine:
Routes for EngineExample::Admin::Engine:
メリット
- gem化によって複数アプリでロジックが共通化されていると同時に、管理画面も共通化されていると運用コストが抑えられる
- 特に複数アプリ運用してる時にCSの学習コストが抑えられる
開発Tips
-
cd test/dummy/
でbundle exec rails s
すればdummyのRailsアプリが起動するのでviewの確認が捗る - 1つのgemに複数のengineを含めるか、それぞれgemを分けるかは適材適所
- 基本的には一緒の方が管理しやすいが、メインのengineが巨大だと分けた方がいい
Adminのみ特定の認証をかけたい時(例:devise)
Rails.application.routes.draw do
mount EngineExample::Engine => "/engine_example"
authenticate :admin_user do
mount EngineExample::Admin::Engine => "/admin/engine_example"
end
end
-
devise だと
authenticate
で囲むだけ - engineの外側に認証を持たせることで、engine側で認証機能を作る必要がなくなる
- 1つのengine内で複数の認証を持たせるのは大変なので、ユーザに提供するengineと管理画面engineとで分けてしまうのが楽
参考文献
- Getting Started with Engines — Ruby on Rails Guides
-
Mountable Engine だらけの Rails アプリ開発 - onk.ninja
- rspecを使いたい場合は 「Mountable Engine のテストの書き方」を参照
- TechRabbit: Rails 4 Engines
-
Rails3 Recipe Book Gaiden // Speaker Deck
- 124枚目 の「Engine gemにEngineをmountする」と https://github.com/amatsuda/hocus_pocus が参考になる
- Rails Engineを使ってAPIと管理画面を分離する - blog.daich.org
-
Lennyのサーバなんて古すぎて誰もサワレニー ↩
-
ここで書いている production-appやproduction-adminというのはRailsのenvironmentのことではなく、capistranoでデプロイする時のstageのようなものを指しています。 ↩
-
admin groupを付けているのは、本体とadminを別々に動かす時に本体のデプロイ時にadminを除外するため。(
capistrano-bundler
ならbundle_without
にadminを追加) ↩ -
test/dummy/ 配下のrake taskを呼びたい場合は task名の前に
app:
が付きます。(例:rails routes
->rails app:routes
) ↩