从引擎控制路线加载顺序 [英] Controlling routes loading order from Engines

查看:49
本文介绍了从引擎控制路线加载顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

So in Rails3 Engines come with their own models/controllers/views and of course routes. Now the question is: How do you ensure that Engine routes will be loaded before (or after) application routes and all other Engines that are present?

以下是我的 Rails 应用路由示例:

Here's an example of my Rails app routes:

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

还有我的引擎:

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

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

So by default Engines routes will be loaded after the application ones. This means that Engine routes are inaccessible due to that catch-all route in my app. How can force Engine routes to be loaded first (or last)?

推荐答案

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

What you're trying to do is kinda difficult. As has been mentioned engine routes are loaded after the app routes and overriding this behaviour may be problematic. I can think of several things that you can try.

在路由路径初始化器之后使用初始化器

在 Rails 源代码中的 engine.rb 中有一个初始化程序,实现您所追求的目标的一种方法是尝试挂钩它处理的功能.默认情况下,初始值设定项如下所示:

There is an initializer in engine.rb inside the rails source, one way to accomplish what you're after is to try to hook into the functionality that it deals with. The initializer looks like this by default:

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 中:

Essentially, this should take the paths to all the routes files that Rails knows about and try and add them to the routes reloader (the thing that reloades your routes file automagically for you if it is changed). You can define another initializer to be executed right after this one, you will then inspect the paths stored in the routes reloader, pull out the path that belongs to your engine, remove it from the paths array and insert it back, but at the end of the paths array. So, in your 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).

This may or may not work, either way I don't really recommend it, it's not particularly elegant (i.e. ugly hack playing with the guts of rails).

使用 Rails 3.1

这可能不是一种选择,但如果是,我可能会选择这个.在 Rails 3.1 中,您可以拥有 2 种不同类型的引擎,完整的和可安装的(这里是 一个关于某些差异的问题).但本质上,您可以将引擎更改为可挂载引擎,可挂载引擎中的路由是命名空间的,您可以将它们显式包含在主应用程序的路由文件中,例如:

This may not be an option, but if it is, I'd probably go with this one. In Rails 3.1 you can have 2 different types of Engines, full and mountable (here is an SO question talking about some of the differences). But in essence you would alter your Engine to be a mountable engine, the routes in a mountable engine are namespaced and you can explicitly include them in the routes file of your main app e.g.:

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

您还可以确定已安装的引擎路由的范围并执行各种其他花哨的路由操作(更多信息 此处).长话短说,如果你能到 3.1,那么这就是使用的方法.

You can also scope your mounted engine routes and do all sorts of other fancy routy things (more info here). Long story short, if you can go to 3.1 then this is the approach to use.

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

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

One of the most well known Rails engines around at the moment is Devise. Now, devise is an engine that will potentially add quite a number of routes to your app, but if you'll look at the devise source you'll see that it doesn't actually have a config/routes.rb file at all! This is because devise dynamically adds its routing goodness to your main app's routes.rb file.

当您运行 devise 附带的模型生成器时,生成器会做的一件事是在 routes.rb 文件的顶部添加诸如 devise_for :model 之类的行,紧接着Rails.application.routes.draw do 行.因此,在执行生成器以创建用户模型后,您的 route.rb 看起来与此类似:

When you run the model generator that comes with devise, one of the things the generator will do is add a line such as devise_for :model at the top of your routes.rb file, right after the Rails.application.routes.draw do line. So your route.rb looks similar to this after you execute a generator to create a User model:

Rails.application.routes.draw do
  devise_for :users
  ...
end

现在,devise_for 是一个神奇的方法,它是 devise 的一部分(在 lib/devise/rails/routes.rb 中),但本质上它会创建一堆我们所有人的常规路线根据您生成的模型了解.

Now, devise_for is a magical method that comes as part of devise (in lib/devise/rails/routes.rb), but in essence it will create a bunch of regular routes that we all know based on the model that you generated.

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

The thing we need to know, is how devise insert this line in the apps routes.rb file, then we can write a generator in our engine that will insert any of our routes at the top of the main apps routes.rb file. For this we look at lib/generators/devise/devise_generator.rb. In the add_devise_routes method the last line is route devise_route. Route is a Thor action which inserts the string passed to it into the main app's routes.rb file. So we can write a generator of our own and do something similar e.g.:

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 人编写的,并被很多人使用,模仿他们的工作可能是一个很好的方法.在我建议的 3 件事中,这将是我处理您的问题的方式(考虑到转移到 rails 3.1 可能不是一种选择).

Of course you would need to make sure all the generator infrastructure is in place but that's the essence of it. Devise is written by some very smart rails dudes and used by quite a lot of people, emulating what they do is likely a pretty good way to go. Out of the 3 things that I suggested this one would be the way I would handle your issue (given that moving to rails 3.1 is probably not an option).

这篇关于从引擎控制路线加载顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆