因此,在Rails3中,引擎带有其自己的模型/控制器/视图以及路线。现在的问题是:如何确保在应用程序路由和存在的所有其他引擎之前(或之后)加载引擎路由?
这是我的Rails应用路线的示例:
match '*(path)', :to => 'foo_controller#bar_action'
而我的引擎:
match '/news', :to => 'bar_controller#foo_action'
因此,默认情况下,引擎路由将在应用路由之后加载。这意味着由于我应用中的全部路由,无法访问引擎路由。如何强制首先(或最后)加载引擎路线?
最佳答案
您想做的事情有点困难。如前所述,引擎路由是在应用程序路由之后加载的,而覆盖此行为可能会出现问题。我可以想到您可以尝试的几件事。
路由路径初始化程序后使用初始化程序
在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知道的所有路由文件的路径,然后尝试将它们添加到路由重新加载器(如果更改,它会自动为您自动加载路由文件)。您可以在此之后定义另一个初始化程序,然后检查该路径重新加载器中存储的路径,拉出属于您的引擎的路径,将其从paths数组中删除,然后将其插入,但最后paths数组。因此,在您的
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
无论是哪种方式,无论我是否真的不推荐它,它都可能行得通,也可能行不通,因为它不是特别优雅(例如,玩弄铁轨的丑陋骇客)。
使用Rails 3.1
这可能不是一个选择,但是如果可以的话,我可能会选择这个。在Rails 3.1中,可以有2种不同类型的引擎,完整的和可安装的(这里是an SO question talking about some of the differences)。但从本质上讲,您可以将Engine更改为可安装的引擎,可安装的引擎中的路由具有名称空间,您可以将其明确包含在主应用程序的routes文件中,例如:
Rails.application.routes.draw do
mount MyEngine::Engine => "/news"
end
您还可以确定已挂载的引擎路线的范围,并进行各种其他繁琐的事情(更多信息here)。长话短说,如果您可以转到3.1,那么这就是使用的方法。
动态将引擎中的路线插入主应用程序
Devise是目前最著名的Rails引擎之一。现在,devise是一个引擎,可能会为您的应用程序添加很多路由,但是如果您查看devise源,您会发现它实际上根本没有
config/routes.rb
文件!这是因为devise将其路由优势动态添加到主应用程序的routes.rb
文件中。当运行devise附带的模型生成器时,生成器将要做的一件事就是在
devise_for :model
行之后,在route.rb文件顶部添加一行,例如Rails.application.routes.draw do
。因此,在执行生成器以创建用户模型后,您的route.rb看起来与此类似:Rails.application.routes.draw do
devise_for :users
...
end
现在,devise_for是devise的一部分(在
lib/devise/rails/routes.rb
中),是一种神奇的方法,但从本质上讲,它将基于您生成的模型创建一堆我们都知道的常规路线。我们需要知道的是,如何设计将此行插入apps
routes.rb
文件中,然后我们可以在引擎中编写一个生成器,以将我们的任何路由插入到主要apps 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可能不是一种选择)。
关于ruby-on-rails-3 - 控制引擎的路线加载顺序,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6915316/