在 Rails 引擎中的 Rails 路由上动态拦截和调用 Ruby
dynamically intercept and recall Ruby on Rails Routes in Rails Engine
我目前正在开发一个 Rails 引擎,它将在特定范围内复制主机应用程序的路由。因此,如果原始应用程序中存在路由 get '/posts', to: 'posts#index', as: 'posts'
,则该路由在引擎安装 get '/mount/posts'
下也应该可用,但应指向宿主应用程序中的相同 Contoller#Action。
但是,我无权访问最终应用程序,因此必须动态生成路由。
以下是我目前的方法,但也许换一种方法效果更好?
# lib/embed_me.rb
require "embed_me/engine"
module EmbedMe
# defines a scoped route under which the embedded content can be found
mattr_accessor :scope_name, default: :embed
end
# lib/embed_me/engine.rb
module EmbedMe
class Engine < ::Rails::Engine
isolate_namespace EmbedMe
initializer "embed_me", before: :load_config_initializers do |app|
Rails.application.routes.append do
mount EmbedMe::Engine, at: EmbedMe.scope_name
end
end
end
end
# config/routes.rb
EmbedMe::Engine.routes.draw do
match '/*path', to: 'application#index', via: :all
end
# app/controllers/embed_me/application_controller.rb
module EmbedMe
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
def index
# will validate existance of route, or raise ActionController::RoutingError
path = Rails.application.routes.recognize_path(params[:path])
controller = path[:controller]
action = path[:action]
# how to call original action?
end
end
end
我不能使用 redirect_to("/#{params[:path]}")
,因为它会将 URL 更改为无范围的 URL。
我不能使用render(action: action, controller: controller)
,因为只会渲染相应的视图,而会跳过controller中的逻辑。 (或者我做了一些根本性的错误?)
我不能使用 main_app.resource_path
因为路由不是静态的并且因应用程序而异。
我也看过 Link and Link,但也无法真正发挥作用
所以我或多或少让它起作用了。我没有找到解决这个确切问题的方法,所以我做了一些小的权衡。即,我决定主机应用程序的管理员必须对路由进行小的更改。
基本上,我现在使用如下示例中的范围。
scope path: "/#{EmbedMe.scope_name}", as: EmbedMe.scope_name, is_embedded: true
为了让引擎的用户尽可能简单,我集成了一个自定义路由功能,它处理路由的范围。
# lib/embed_me/rails/routes.rb
module ActionDispatch::Routing
class Mapper
def embeddable
# note that I call the yield function twice, see describtion below
yield
scope path: "/#{EmbedMe.scope_name}", as: EmbedMe.scope_name, is_embedded: true do
yield
end
end
end
end
# [Host Application]/config/routes.rb
Rails.application.routes.draw do
# not embeddable
get '/private', to: 'application#private'
# is embeddable
embeddable do
get '/embeddable', to: 'application#embeddable'
end
end
# output of $rails routes
...
private GET /private(.:format) application#private
embeddable GET /embeddable(.:format) application#embeddable
embed_embeddable GET /embed/embeddable(.:format) application#embeddable {:is_embedded=>true}
...
注意我调用了两次yield函数,一次正常获取路由,一次在作用域下。通过这种方式,我还获得了普通和嵌入式 link.
的多个路径助手
创建自定义路由方法的处理方式与 Devise 类似。 See Source
我目前正在开发一个 Rails 引擎,它将在特定范围内复制主机应用程序的路由。因此,如果原始应用程序中存在路由 get '/posts', to: 'posts#index', as: 'posts'
,则该路由在引擎安装 get '/mount/posts'
下也应该可用,但应指向宿主应用程序中的相同 Contoller#Action。
但是,我无权访问最终应用程序,因此必须动态生成路由。
以下是我目前的方法,但也许换一种方法效果更好?
# lib/embed_me.rb
require "embed_me/engine"
module EmbedMe
# defines a scoped route under which the embedded content can be found
mattr_accessor :scope_name, default: :embed
end
# lib/embed_me/engine.rb
module EmbedMe
class Engine < ::Rails::Engine
isolate_namespace EmbedMe
initializer "embed_me", before: :load_config_initializers do |app|
Rails.application.routes.append do
mount EmbedMe::Engine, at: EmbedMe.scope_name
end
end
end
end
# config/routes.rb
EmbedMe::Engine.routes.draw do
match '/*path', to: 'application#index', via: :all
end
# app/controllers/embed_me/application_controller.rb
module EmbedMe
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
def index
# will validate existance of route, or raise ActionController::RoutingError
path = Rails.application.routes.recognize_path(params[:path])
controller = path[:controller]
action = path[:action]
# how to call original action?
end
end
end
我不能使用 redirect_to("/#{params[:path]}")
,因为它会将 URL 更改为无范围的 URL。
我不能使用render(action: action, controller: controller)
,因为只会渲染相应的视图,而会跳过controller中的逻辑。 (或者我做了一些根本性的错误?)
我不能使用 main_app.resource_path
因为路由不是静态的并且因应用程序而异。
我也看过 Link and Link,但也无法真正发挥作用
所以我或多或少让它起作用了。我没有找到解决这个确切问题的方法,所以我做了一些小的权衡。即,我决定主机应用程序的管理员必须对路由进行小的更改。 基本上,我现在使用如下示例中的范围。
scope path: "/#{EmbedMe.scope_name}", as: EmbedMe.scope_name, is_embedded: true
为了让引擎的用户尽可能简单,我集成了一个自定义路由功能,它处理路由的范围。
# lib/embed_me/rails/routes.rb
module ActionDispatch::Routing
class Mapper
def embeddable
# note that I call the yield function twice, see describtion below
yield
scope path: "/#{EmbedMe.scope_name}", as: EmbedMe.scope_name, is_embedded: true do
yield
end
end
end
end
# [Host Application]/config/routes.rb
Rails.application.routes.draw do
# not embeddable
get '/private', to: 'application#private'
# is embeddable
embeddable do
get '/embeddable', to: 'application#embeddable'
end
end
# output of $rails routes
...
private GET /private(.:format) application#private
embeddable GET /embeddable(.:format) application#embeddable
embed_embeddable GET /embed/embeddable(.:format) application#embeddable {:is_embedded=>true}
...
注意我调用了两次yield函数,一次正常获取路由,一次在作用域下。通过这种方式,我还获得了普通和嵌入式 link.
的多个路径助手创建自定义路由方法的处理方式与 Devise 类似。 See Source