控制从发动机装载订单的路线

时间:2011-08-02 16:25:56

标签: ruby-on-rails-3

所以在Rails3引擎中有自己的模型/控制器/视图,当然还有路由。现在的问题是:如何确保在应用程序路由之前(或之后)以及所有其他引擎中加载引擎路由?

以下是我的Rails应用程序路径的示例:

match '*(path)', :to => 'foo_controller#bar_action'

我的引擎:

match '/news', :to => 'bar_controller#foo_action'

因此默认情况下,引擎路由将在应用程序之后加载。这意味着由于我的应用程序中的全部路径,引擎路由无法访问。如何强制引擎路由首先(或最后)加载?

3 个答案:

答案 0 :(得分:28)

你想要做的事情有点困难。如前所述,在应用程序路由之后加载引擎路由并且覆盖此行为可能会有问题。我可以想到你可以尝试的几件事。

在路由路径初始化程序后使用初始化程序

在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中,您可以拥有2种不同类型的引擎,完整且可安装(此处为an SO question talking about some of the differences)。但实质上你会将你的引擎改为可安装引擎,可安装引擎中的路由是命名空间的,你可以明确地将它们包含在主应用程序的routes文件中,例如:

Rails.application.routes.draw do
  mount MyEngine::Engine => "/news"
end

您还可以确定已安装的引擎路由的范围,并执行各种其他奇特的路由(更多信息here)。长话短说,如果你可以转到3.1那么这就是使用的方法。

将您的引擎中的路径动态插入主应用

目前最着名的Rails引擎之一是Devise。现在,设计是一个可能会为您的应用添加相当多路由的引擎,但如果您查看设计源,您会发现它实际上根本没有config/routes.rb文件!这是因为设计动态地将其路由优势添加到主应用程序的routes.rb文件中。

当您运行设计附带的模型生成器时,生成器将执行的一项操作是在{{1}之后的routes.rb文件的顶部添加一行devise_for :model。行。因此,在执行生成器以创建用户模型后,您的route.rb看起来与此类似:

Rails.application.routes.draw do

现在,devise_for是一个神奇的方法,作为设计的一部分(在Rails.application.routes.draw do devise_for :users ... end 中),但实际上它将根据您生成的模型创建一堆我们都知道的常规路径。 p>

我们需要知道的是,如何设计在应用lib/devise/rails/routes.rb文件中插入此行,然后我们可以在我们的引擎中编写一个生成器,它将在主要应用的顶部插入我们的任何路由{ {1}}文件。为此,我们查看routes.rb。在routes.rb方法中,最后一行是lib/generators/devise/devise_generator.rbadd_devise_routes是一个Thor动作,它将传递给它的字符串插入主应用程序的route devise_route文件中。因此,我们可以编写自己的生成器并执行类似的操作,例如:

Route

当然,你需要确保所有的发电机基础设施到位,但这是它的本质。 Devise是由一些非常聪明的铁杆家伙编写的,并且被很多人使用,仿效他们所做的可能是一个很好的方式。在我建议的3件事中,我会处理你的问题(考虑到转向rails 3.1可能不是一个选项)。

答案 1 :(得分:2)

现在有同样的问题。我提出的另一个不是特别优雅但安全的解决方案是在你的gem文件的末尾添加另一个引擎gem,只包含catch所有路由,没有别的。

编辑:实际上,违反直觉,路由宝石需要在之前列出所有其他引擎宝石,以便最后加载其路线。

答案 2 :(得分:0)

我不是专家,但是当我遇到他们的路由解决方案时,我昨天正在玩sferik/rails_admin。使用rails_admin:install rake任务,他们可以通过在文件开头添加config/routes.rb来直接修改mount RailsAdmin::Engine => '/admin', :as => 'rails_admin'文件,以确保其路由始终具有最高优先级。 mount方法引用自己的routes.rb:

RailsAdmin::Engine.routes.draw do
  # Prefix route urls with "admin" and route names with "rails_admin_"
  scope "history", :as => "history" do
    controller "history" do
      [...]
    end
  end
end

这可能是你的解决方案吗?