param with map.resources

6 views
Skip to first unread message

I. E. Smith-Heisters

unread,
Sep 30, 2008, 3:54:09 PM9/30/08
to Ruby on Rails: Talk
Hi all,

With ye olde routes, you could do stuff like:

map.connect '/foo/:id', :controller => 'bar', :type => 'foo'

but you can't pass arbitrary params into #resources:

map.resources :foo, :controller => 'bar', :type => 'foo' # bonk!

the :controller key works fine, but :type is just ignored. This would
be very handy for an STI-backed controller I have, to change a url
like "/bar/new?type=foo" to "/foo/new" but still handle it with
BarController.

I've seen a few people ask this question, eg.

http://groups.google.com/group/rubyonrails-talk/browse_frm/thread/ab38af850896baeb/384c7c3ef2a9f9cb?lnk=gst&q=params+resources#384c7c3ef2a9f9cb

but no answers. I've tried a bunch of stuff
with :requirements, :as, :path_prefix, and nesting, but all to no
avail. If anyone has any advice, I'd be most grateful.

Thanks,
Ian

gdevore

unread,
Oct 1, 2008, 9:19:13 AM10/1/08
to Ruby on Rails: Talk
Ian-
It might be helpful if you gave a few more examples of what you want
to accomplish. Are you trying to have the following routes all point
to the "new" method on the same controller?

/foo/new
/bar/new
/somethingelse/new


Greg DeVore

Ian Smith-Heisters

unread,
Oct 1, 2008, 6:12:47 PM10/1/08
to rubyonra...@googlegroups.com
Sure. You have it basically right, but let me elaborate.

I have models like so:

class Papa < ActiveRecord::Base; abstract_class = true; end
class Daughter < Papa; end
class Son < Papa; end

I have this controller:

class PapaController < ApplicationController
def new; @model = new_model; end
def new_model
params[:type].classify.constantize.new # there's more safety logic
here in reality, but this is the gist
end
end

I started with the default route:

map.resources :papa

So, then, I can create a new son by doing GET /papa/new?type=son,
which is fine, but not ideal. Ideally, I would do GET /son/new, but it
would still use the Papa controller, and set params[:type] to "son".
This is what I have right now:

map.resources :papa, :path_prefix => '/:type'

which allows me to GET /son/papa/new, and create links like
papa_path(@model.type.to_s.downcase, @model).

So the question is how to make these specs pass:

params_from(:get, "/son/new").should == {:controller => 'papa',
:action => 'new', :type => 'son'}
params_from(:get, "/daughter/new").should == {:controller => 'papa',
:action => 'new', :type => 'daughter'}

Is that clearer?

Thanks,
Ian

Greg DeVore

unread,
Oct 1, 2008, 11:35:32 PM10/1/08
to rubyonra...@googlegroups.com
Ian-
I don't know how you would do that in the way you are describing. One
possibility though would be to do this:

map.resources :sons, :controller => "papas"
map.resources :daughters, :controller => "papas"

This will point both routes to the papas controller. You won't see
"sons" or "daughters" come through as params. But you can query
request.env['REQUEST_URI'] and you would get back something like '/
sons/new/'.

So:
1. request.env['REQUEST_URI'].split("/") will give you an array
["","sons","new"]
2. access [1] of the array and you will have your parameter.

A little convoluted but I think that it would do what you want.

Greg

Dave S

unread,
Oct 2, 2008, 12:18:22 PM10/2/08
to Ruby on Rails: Talk
It sounds like you want to be using nested resources. Try something
like this

map.resources :papas, :has_many => [:sons, :daughters]

For more info read...

http://api.rubyonrails.org/classes/ActionController/Resources.html

Ian Smith-Heisters

unread,
Oct 2, 2008, 2:44:51 PM10/2/08
to rubyonra...@googlegroups.com
Wouldn't that give me "/papas/1/sons/new"? Not exactly what I'm
looking for if I understand it correctly, but thanks.

Ian Smith-Heisters

unread,
Oct 2, 2008, 2:46:39 PM10/2/08
to rubyonra...@googlegroups.com
Sigh, yeah, there's always that way. So unrailsy... but probably what
I'll end up doing. Or I could patch Resources to make it push any
unclaimed options into params. But parsing the URI by hand is probably
easier.

Ian Smith-Heisters

unread,
Oct 3, 2008, 12:05:59 AM10/3/08
to rubyonra...@googlegroups.com
Here's what I cooked, up. Wasn't that hard, and appears to work so far...


$ cat lib/param_resources.rb
module ParamResources
def self.included base
super base
base.alias_method_chain :action_options_for, :params
end

def action_options_for_with_params action, resource, method=nil
action_options_for_without_params(action, resource, method).
merge(resource.options[:params] || {})
end
end
$ cat config/routes.rb
ActionController::Routing::RouteSet::Mapper.send :include, ParamResources
ActionController::Routing::Routes.draw do |map|

map.resources :papa
map.resources :son, :controller => :papa, :params => {:type => "son"}
map.resources :daughter, :controller => :papa, :params => {:type =>
"daughter"}
<snip>

I'd be interested in peoples' takes on it.

Thanks,
Ian

Reply all
Reply to author
Forward
0 new messages