如何在Rails中使用相同的URL格式路由到不同的控制器?

时间:2011-01-24 13:16:32

标签: ruby-on-rails url-routing

我有两种不同的模型可以显示在一个类别中。在我的 routes.rb 中,我希望有类似的内容:

ActionController::Routing::Routes.draw do |map|
  map.top_list ':category/:foo', :controller => 'foo', :action => 'show'
  map.top_list ':category/:bar', :controller => 'bar', :action => 'show'
end

当我加载像“/ some-category-name / some-foo-name”这样的URL时这很好用,其中“some-foo-name”可以由FooController加载,如下所示:

class FooController < ApplicationController
  def show
    @foo = Foo.find_by_url! params[:foo]
  end
end

但是当我尝试请求一个Bar,比如“/ some-category-name / some-bar-name”时,我在FooController#show中得到一个“ActiveRecord :: RecordNotFound”。我知道我可以通过要求所有Foo名称以“foo-”开头并且所有Bar名称以“bar-”开头,然后定义如下路线来解决此问题:

ActionController::Routing::Routes.draw do |map|
  map.top_list ':category/:foo', :controller => 'foo', :action => 'show', :requirements => { :name => /^foo-/ }
  map.top_list ':category/:bar', :controller => 'bar', :action => 'show', :requirements => { :name => /^bar-/ }
end

但强制​​对名称进行这种限制是非常不理想的。我找到了以下内容,看起来可能对我有用:Different routes but using the same controller for model subclasses in Rails。但是,我不太关注这个例子,所以我不知道这是否能解决我的问题。我的Foo和BarController必须从CategoryController继承它也不是很好。

我想到的一个想法是,我可以尝试查找Foo,然后如果失败则回到BarController。我可以很容易地在FooController中执行此操作并重定向到BarController,但这不是很好,因为所有对Bars的请求都将在Rails日志中记录为FooController #show。但是,如果我能够以某种方式配置路由来调用方法来确定基于URL的基本名称路由到什么,我可以得到我需要的行为; e.g。

ActionController::Routing::Routes.draw do |map|
  is_a_foo = Proc.new {|name| Foo.find_by_url! name && true }

  map.top_list ':category/:foo', :controller => 'foo', :action => 'show', :requirements => { :name => is_a_foo }
  map.top_list ':category/:bar', :controller => 'bar', :action => 'show'
end

在沼泽标准的Rails 2中有没有办法做到这一点?

2 个答案:

答案 0 :(得分:2)

我最后写了一个小Rails插件,允许我写这样的路线:

map.with_options :category => /[-A-Za-z0-9]+/ do |m|
  # Since both foos and bars can appear under category, we define a route for foo that only matches when
  # the foo can be found in the database, then fall back to the bar route
  m.foo ':category/:foo', :controller => 'foo', :action => 'show', :conditions => {
      :url => { :find_by_url => Foo }
  }
  m.bar ':category/:bar', :controller => 'bar', :action => 'show'
end

在我获得开源代码的许可之前,我必须将实现作为练习留给读者,但这是我如何到达那里:

  1. Monkey-patching Rails: Extending Routes #2
  2. Under the hood: route recognition in Rails
  3. The Complete Guide to Rails Plugins: Part II
  4. 我会用github链接更新这个答案。 :)

答案 1 :(得分:0)

这似乎是错误的,因为您使用相同的路由到两个控制器。只有你注意到,Rails才会选择一个。

使用这样的东西会更好:

map.top_list ':category/foo/:foo', :controller => 'foo', :action => 'show', :requirements => { :name => is_a_foo }
map.top_list ':category/bar/:bar', :controller => 'bar', :action => 'show'

因为在提供的路线中,每次都会给出两个参数。你用不同的名字命名并不重要。它只匹配两者。