Rails の Mountable Engine 側の _path や _url をメインアプリから呼び出す
1. 前提
まず config/routes.rb
に
# config/routes.rb Rails.application.routes.draw do mount Admin::Engine, at: "/admin" end
は書かれていることと思う。
今回はその Admin::Engine の配下にある /admin/users
を呼び出したいとする。
つまり、 admin/config/routes.rb
には以下が定義済みとする。
# admin/config/routes.rb Admin::Engine.routes.draw do resources :users end
2. controller から呼び出すぶんにはカンタン
/admin
は admin_path
で呼び出せるし、
/admin/users
は admin.users_path
で呼び出せる。
_url
側は後述する設定をおこなわないとまだダメかもしれない。
3. as 設定を入れておく
この設定は必要ではないのだけれど、
Admin app からメインアプリを呼び出すときの routing が
main_app.mypage_path
のようなものなので、そこと対称性を持つためと、
あとで文字列でのファイル検索がしやすいように、 admin_app
と名付ける。
# config/routes.rb Rails.application.routes.draw do mount Admin::Engine, at: "/admin", as: :admin_app end
この変更により、
admin_path
→admin_app_path
admin_url
→admin_app_url
admin.users_path
→admin_app.users_path
が正しい path になるので、 as 設定を入れる場合は、現在の記載を変更するようお忘れなく。
4. admin_app.xxx_url を使えるようにする
admin_app.users_path
は使えるが、
admin_app.users_url
を使おうとすると以下のようにエラーが出るはず。
ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true from /bundle/ruby/2.7.0/gems/actionpack-6.0.3.4/lib/action_dispatch/http/url.rb:63:in `full_url_for'
というわけで、警告通り、
admin_app.users_url(host: "localhost:3000")
のようにオプション付きで呼び出すか、
default_url_options の設定をする必要がある。
今回は、メインアプリ側の config/environments
で環境ごとの
default_url_options
を指定してあるので、それを呼び出すものとする。
# config/environments/development.rb 内の記載 Rails.application.routes.default_url_options[:host] = "localhost:3000" # config/environments/production.rb 内の記載 Rails.application.routes.default_url_options[:host] = "prod.example.com"
上のような設定が各ファイルに書かれているとして、
以下のように admin/config/routes.rb
には書いてあげて Rails.application.routes.default_url_options[:host]
を再利用する。
# admin/config/routes.rb Admin::Engine.routes.draw do default_url_options host: Rails.application.routes.default_url_options[:host] resources :users end
これで admin_app.users_url
のような呼び出しも可能になったはずだ。
5. Job から Mountable Engine 側の path や url を呼び出す
Controller はカンタンだけど、では Job や Model から呼ぶにはどうするか。
特に Slack 通知なんかで管理画面側の URL 表記をしたい場合があるだろう。
また、 Rails console から routing を確認したいときもあるだろう。
メインアプリ側であれば include Rails.application.routes.url_helpers の記載があれば
mypage_path
などを呼び出せた。
Engine 側はこれだけでは呼び出せない。
NameError: undefined local variable or method `admin_app' for main:Object Did you mean? admin_app_url
などと言われてしまうことだろう。
どうするか。 include Rails.application.routes.mounted_helpers を呼び出せばいい。
というわけで、Job は例えばこうなる。
class MountedEngineTestSlackNotificationJob include Rails.application.routes.url_helpers include Rails.application.routes.mounted_helpers include Sidekiq::Worker sidekiq_options queue: :slack, retry: 3 def perform blocks = [] blocks.push({ type: :section, text: { type: :mrkdwn, text: "リンク → <#{admin_app.users_url}|ユーザー一覧管理画面>", }, }) notifier = Slack::Notifier.new("https://hooks.slack.com/services/ABCDEF/GHIJKL/MNOPQRSTUVWXYZ") notifier.post(attachments: { blocks: blocks, color: "#2eb886" }) end end
gem入れてること前提のコードなので、上のはあくまで参考程度に。
Mountable Engine 周りの話はネットに情報少なめなので、情報探すの苦労しました。
最終的には Rails 本体のテスト周りのコードを読むことで解決しました。
やはり https://github.com/rails/rails は神……。