Help us understand the problem. What is going on with this article?

mountable engineで管理画面を作る

More than 3 years have passed since last update.

mountable engineで管理画面を作る

by sue445
1 / 26

表参道.rb #17 ~管理画面について語ろう~ - connpass の発表資料


自己紹介


最近の仕事

とある社内アプリのリプレイス

  • 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

最近のトラブル

image


image


Agenda

  • mountable engineとは?
  • 2種類のやり方を紹介
    • 1. アプリ本体の管理画面をengineにする
    • 2. gemに管理画面を内包してアプリ本体から使う

mountable engineとは?

  • Railsアプリに他のRailsアプリをgemとして組み込む仕組み
  • MVC全部gemとして別アプリ提供することができる
  • 長いので以下engineで表記

2種類のやり方を紹介

  1. 普通のRailsアプリの管理画面をengineに切り出す
  2. 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

トップディレクトリ( GemfileRalefileがある場所)で下記を実行すると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

https://github.com/sue445/admin_example/commit/c18a92890b36495a8a796c3af234badd503d572a


admin/admin.gemspec を編集


親アプリのGemfileに組み込む

本体側の Gemfile に一行追加し、3

Gemfile
group :admin do
  gem "admin", path: "admin"
end

routes.rb でmountするだけ。

config/routes.rb
mount Admin::Engine => "/admin"

https://github.com/sue445/admin_example/commit/79b817b34b70a2a5301c238a2a1f6e5f3ab76d9f

./bin/rails routes してadminが見えれば成功

$ ./bin/rails routes
Prefix Verb URI Pattern Controller#Action
 admin      /admin      Admin::Engine

Routes for Admin::Engine:

メリット :ok_woman:

  • 管理画面のみで必要な機能をアプリ本体に含める必要がなくなる
    • admin gemを読まなければ管理系の機能は一切使えなくなるのでコード内でif文書くよりもシンプル

注意点

  • 本体がAPI mode の時にengineに一部悪影響が出ることがある

2. engine gemに管理画面のengineを内包してアプリ本体から使う

ユースケース

  • 1つのgemで「エンドユーザにAPIとして提供するengine」と「管理画面のengine」を提供する
    • 前者でmodelを提供しているとしたら、そのmodelのCRUDを管理画面で提供

やり方

さっきと同じようにengineを作る

rails plugin new engine_example --skip-bundle --mountable

https://github.com/sue445/engine_example/commit/045311540dd0d9561dbe96e86707aea2887f5ba5


gemspecからTODOを削る

  • 同じくそのままだと bundle install でエラーになるので

https://github.com/sue445/engine_example/commit/f3dd906b11937e21465fc35b19c255c53e5f98fa


engines/admin/ を作った後に下記ファイルを作成

module名はいい感じに変更すること

engines/admin/config/routes.rb
EngineExample::Admin::Engine.routes.draw do
end
engines/admin/lib/engine_example/admin/engine.rb
module EngineExample
  module Admin
    class Engine < ::Rails::Engine
      isolate_namespace EngineExample::Admin
    end
  end
end
engines/admin/lib/engine_example/admin/railtie.rb
require "engine_example/admin/engine"

module EngineExample
  module Admin
    class Railtie < ::Rails::Railtie
    end
  end
end

https://github.com/sue445/engine_example/commit/e2a136186473025f510de72b7ee2f401157ac743


engine本体からadminのengineを読み込む

それぞれ下記を追加

engine_example.gemspec
s.require_paths = ["lib", "engines/admin/lib"]
lib/engine_example/railtie.rb
require "engine_example/admin/railtie"
lib/engine_example.rb
require "engine_example/railtie"
test/dummy/config/routes.rb
mount EngineExample::Admin::Engine => "/admin/engine_example"

https://github.com/sue445/engine_example/commit/598eb2ec90e19853a5490a0ffbf888e0ed54125b


動作確認

./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:

メリット :ok_woman:

  • gem化によって複数アプリでロジックが共通化されていると同時に、管理画面も共通化されていると運用コストが抑えられる
    • 特に複数アプリ運用してる時にCSの学習コストが抑えられる

開発Tips

  • cd test/dummy/bundle exec rails s すればdummyのRailsアプリが起動するのでviewの確認が捗る
  • 1つのgemに複数のengineを含めるか、それぞれgemを分けるかは適材適所
    • 基本的には一緒の方が管理しやすいが、メインのengineが巨大だと分けた方がいい

Adminのみ特定の認証をかけたい時(例:devise)

confg/routes.rb
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とで分けてしまうのが楽

参考文献


  1. Lennyのサーバなんて古すぎて誰もサワレニー 

  2. ここで書いている production-appやproduction-adminというのはRailsのenvironmentのことではなく、capistranoでデプロイする時のstageのようなものを指しています。 

  3. admin groupを付けているのは、本体とadminを別々に動かす時に本体のデプロイ時にadminを除外するため。( capistrano-bundler なら bundle_without にadminを追加) 

  4. test/dummy/ 配下のrake taskを呼びたい場合は task名の前に app: が付きます。(例:rails routes -> rails app:routes) 

sue445
https://twitter.com/sue445
http://sue445.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away