Darwinweb

Nested Hashes in url_for Parameters

May 18, 2007     

You might be surprised that in Rails 1.2.3 the following blows up:

params = {:controller => 'search', :filter => {:featured => true, :tag => 'fun'}}

Any nested hash in url_for parameters will be harshly flattened using to_s, instead of being converted to the form you might expect—a url that generates the given params hash.

Well that problem is fixed in edge Rails, but it’s a pretty small reason to switch your project to trunk. The patch that fixes this is not directly portable to 1.2.3 because it’s mixed up with other changes in edge.

I absolutely needed this working, so I created a brutish solution by adding a Hash method called flatten_for_url that solves my problem. Stick it in your environment.rb or somewhere in lib/ if you like to keep things organized.

class Hash
  def flatten_for_url
    nh = HashWithIndifferentAccess.new #Important for use with Rails params
    recursive_flatten_for_url(self,nh)
    nh
  end
  
  private
  def recursive_flatten_for_url(hash,hash_out,keys=[])
    hash.each do |k,v|
      keys1 = keys.dup
      keys1 << k
      if v.is_a? Hash
        recursive_flatten_for_url(v,hash_out,keys1)
      else
        keys2 = keys1.dup
        key = keys2.shift
        key += keys2.collect{ |k2| "[#{k2}]" }.join
        hash_out[key] = v
      end
    end
  end
end

My use case is a generic paging helper using Bruce Williams’ excellent paginator gem. All I do is add one method call and things just work.

url_for(params.flatten_for_url.merge(:page => n))