Nested Hash Params with Named Routes in Ruby on Rails 3
I've never (as of rev 5522) been able to pass a nested hash as parameters to url_for and have it flatten it properly on the generated URL. I encountered the problem again the other day and, having had just about enough, decided to try and nip it in the bud.
The problem I was having occurred when tried to make an HTTP request to a named route such as:
people_url(:person => { :name => 'Bob', :profession => 'Developer' }) When I've encountered this problem in the past, I could never find any information from someone who's actually solved the problem. There are a few patches submitted as tickets to the Rails devs, but one seems to be a duplicate of the other, and neither seems to fix this problem on Edge, at least not for my particular use case.
The root of this particular problem lies in a method in the action_controller/routing.rb file within actionpack/actioncontroller, in the options_as_params of the RoutingSet class. The method, without comments, looks like this:
1 2 3 4 5 6 7 | |
value is a Hash. In that case, the to_param method simply converts the Hash instance into its String representation, which is just a pile of keys and values mashed together, as in my people_url example.
To solve this problem, I changed the method to look like this:
def options_as_params(options)
options_as_params = options[:controller] ? { :action => "index" } : {}
options.parameterize do |param_key, param_value|
options_as_params[param_key] = param_value.to_param
end
options_as_params
end parameterize method is an extension I made to the Hash class that essentially flattens a hash like:
{
:person => {
:name => 'Bob',
:profession => 'Developer'
}
} {
:'person[name]' => 'Bob',
:'person[profession]' => 'Developer'
} class Hash
def flatten(superkey)
flattened_hash = {}
self.each do |key, value|
flattened_hash["#{superkey}[#{key}]".to_sym] = value
end
flattened_hash
end
def parameterize
self.each do |k, value|
if value.is_a?(Hash)
value.flatten(k).each { |fk, fv| yield fk, fv }
else
yield k, value
end
end
end
end Trackbacks
Use the following link to trackback from your own site:
http://kinderman.net/trackbacks?article_id=nested-hash-params-with-named-routes-in-ruby-on-rails&day=16&month=11&year=2006


I did the same thing (1.2.3 still shows this problem). Instead mine was to do something like this
def flatteninteriorhashes_to_singles(params) out = {} for key, possiblecombinedpoorly_value in params value = params[key] if value.class.ancestors.include? Hash for keyinner, valueinner in value out["#{key}[#{keyinner}]"] = valueinner # they had better not be 3dd or we're stuck again. Fix when necessary end else out[key] = value end end out end
To avoid having to rewrite ruby code shared among developers. Cheers!
linkto 'whatever', flatteninteriorhashesto_singles(params).merge({:page => 3})
Note that this doesn't convert them to :symbols (so it's deficient in that way).
oops I meant
def flatten_interior_hashes_to_singles(params) out = {} for key, possible_combined_poorly_value in params value = params[key] if value.class.ancestors.include? Hash for key_inner, value_inner in value out["#{key}[#{key_inner}]"] = value_inner # they had better not be 3dd or we're stuck again. Fix when necessary end else out[key] = value end end out endused like
linkto 'something', flatteninteriorhashestosingles(params).merge({:newparam => new_setting})or what have you.I think you should be able to achieve the same result by just overriding Hash#to_param