你想做的事情有点困难。如前所述,引擎路由是在应用程序路由之后加载的,覆盖此行为可能会出现问题。我能想到几件事,你可以试试。
在路由路径初始化器之后使用初始化器
在 rails 源代码中的 engine.rb 中有一个初始化程序,实现您所追求的一种方法是尝试挂钩它处理的功能。默认情况下,初始化程序如下所示:
initializer :add_routing_paths do |app|
paths.config.routes.to_a.each do |route|
app.routes_reloader.paths.unshift(route) if File.exists?(route)
end
end
本质上,这应该获取 Rails 知道的所有路由文件的路径,并尝试将它们添加到路由重新加载器(如果更改了路由文件,它会自动为您重新加载路由文件)。您可以定义另一个初始化程序在此初始化程序之后立即执行,然后您将检查存储在路由重新加载器中的路径,拉出属于您的引擎的路径,将其从路径数组中删除并将其重新插入,但在最后的路径数组。所以,在你的config/application.rb:
class Application < Rails::Application
initializer :munge_routing_paths, :after => :add_routing_paths do |app|
engine_routes_path = app.routes_reloader.paths.select{|path| path =~ /<regex that matches path to my engine>/}.first
app.routes_reloader.paths.delete(engine_routes_path)
app.routes_reloader.paths << engine_routes_path
end
end
这可能有效,也可能无效,无论哪种方式我都不太推荐它,它不是特别优雅(即丑陋的 hack 玩 Rails 的胆量)。
使用 Rails 3.1
这可能不是一个选项,但如果是,我可能会选择这个。在 Rails 3.1 中,您可以拥有 2 种不同类型的引擎,完整的和可安装的(这里是 an SO question talking about some of the differences)。但从本质上讲,您会将您的引擎更改为可挂载引擎,可挂载引擎中的路由是命名空间的,您可以将它们显式包含在主应用程序的路由文件中,例如:
Rails.application.routes.draw do
mount MyEngine::Engine => "/news"
end
您还可以确定安装的引擎路线并执行各种其他花哨的操作(更多信息here)。长话短说,如果您可以转到 3.1,那么这就是使用的方法。
将路由从您的引擎动态插入到您的主应用中
目前最知名的 Rails 引擎之一是 Devise。现在,devise 是一个引擎,它可能会为您的应用程序添加相当多的路由,但如果您查看 devise 源代码,您会发现它实际上根本没有 config/routes.rb 文件!这是因为设计动态地将其路由优势添加到您的主应用程序的routes.rb 文件中。
当您运行 devise 附带的模型生成器时,生成器会做的一件事是在您的 routes.rb 文件顶部添加一行,例如 devise_for :model,紧跟在 Rails.application.routes.draw do 行之后。因此,在您执行生成器以创建用户模型后,您的 route.rb 看起来与此类似:
Rails.application.routes.draw do
devise_for :users
...
end
现在,devise_for 是一种神奇的方法,它是 devise 的一部分(在 lib/devise/rails/routes.rb 中),但本质上它会根据您生成的模型创建一堆我们都知道的常规路线。
我们需要知道的是,如何设计将这一行插入到应用程序routes.rb 文件中,然后我们可以在我们的引擎中编写一个生成器,它将我们的任何路由插入主应用程序的顶部routes.rb文件。为此,我们查看lib/generators/devise/devise_generator.rb。在add_devise_routes 方法中,最后一行是route devise_route。 Route 是一个 Thor 操作,它将传递给它的字符串插入到主应用程序的 routes.rb 文件中。所以我们可以编写自己的生成器并做类似的事情,例如:
class MyCrazyGenerator < Rails::Generators::NamedBase
...
def add_my_crazy_routes
my_route = "match '/news', :to => 'bar_controller#foo_action'"
route my_route
end
end
当然,您需要确保所有生成器基础设施都到位,但这就是它的本质。 Devise 是由一些非常聪明的 Rails 家伙编写的,并且被很多人使用,模仿他们的工作可能是一个很好的方法。在我建议的三件事中,这件事是我处理你的问题的方式(考虑到迁移到 rails 3.1 可能不是一种选择)。