以下是我的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知道的所有路由文件的路径,并尝试将其添加到路由重新加载程序(如果更改则为您自动重新加载路由文件).您可以在此之后定义要执行的另一个初始化程序,然后将检查存储在路由重新加载程序中的路径,拉出属于引擎的路径,将其从路径数组中删除并将其插入,但最后的路径数组.所以在你的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中,您可以拥有两种不同类型的引擎,可以完全安装(这里是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是一个引擎,可能会添加相当多的路由到你的应用程序,但如果你看看设计源码,你会看到它实际上没有一个config / routes.rb文件!这是因为设计动态地将其路由优点添加到您的主应用程序的routes.rb文件中.
当您运行与devise一起使用的模型生成器时,生成器将会做的事情之一是在rails.rb文件的顶部添加一行,如devise_for:model,紧跟在Rails.application.routes.draw之后.因此,在执行生成器以创建用户模型后,您的route.rb与此类似:
Rails.application.routes.draw do devise_for :users ... end
现在,devise_for是一个神奇的方法,作为设计的一部分(在lib / devise / rails / routes.rb中),但实质上它将创建一堆基于您生成的模型的常规路由.
我们需要知道的是,设计如何将这条线插入应用程序routes.rb文件中,然后我们可以在引擎中编写一个生成器,它将在主应用程序routes.rb文件的顶部插入任何路由.为此,我们来看看lib / generators / devise / devise_generator.rb.在add_devise_routes方法中,最后一行是路由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是由一些非常聪明的导轨,由很多人使用,模仿他们做的可能是一个很好的方式去.在我建议的3件事情中,我会处理你的问题(考虑到移动到rails 3.1可能不是一个选择).