Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: control ordering of query parameters in url_for?

I'd like to generate a URL where the "p=1" query param appears at the end of the URL, like:

/path?foo=X&bar=Y&p=1

Is it possible to control the ordering of query parameters when generating URLs via:

url_for(params.merge({ p: page_num }))

?

Update:

I tried ChuckE's suggestion below. It turns out that in Ruby 1.9 Hashes are already ordered, so the code in ActiveSupport::OrderedHash is effectively no-op'd. You can verify with Ruby 1.9 that order is preserved:

>> h = {one: 1, two: 2, three: 3 }
{:one=>1, :two=>2, :three=>3}
>> f = h.except(:one)
{:two=>2, :three=>3}
>> f[:one] = 1
1
>> f
{:two=>2, :three=>3, :one=>1}

However, url_for still puts the "p" param first. It seems that any potential solution will need to address how url_for iterates the hash.

like image 382
George Armhold Avatar asked Jul 26 '12 10:07

George Armhold


2 Answers

After further digging, I see that what's happening is that url_for is actually sorting the parameters by key lexicographically, independent of their insertion order in the hash. Apparently this is being done to aid caching, since URL params are often used for page cache keys.

In short, you can't do it without patching Hash, specifically, you need to override activesupport/core_ext/object/to_param.rb so that Hash#to_param does not call .sort on the return value.

Related question: How to generate custom sorted query string URL in Rails link_to?.

like image 62
George Armhold Avatar answered Nov 17 '22 03:11

George Armhold


First question is: why would you need something like that? The order which the parameters appear in the url in doesn't influence the way they are fetched by the server, since they are basic key/value associations. So, no matter where the parameter appears, it will always be recognized by the server.

Nonetheless, to answer your question, yes, it is possible. You just have to use ordered hashes. They are available through active support.

opts = OrderedHash.new
opts[:foo] = 'X'
opts[:bar] = 'Y'
opts[:p] = 1
your_helper_url(opts)

Should do the trick for you.

like image 1
ChuckE Avatar answered Nov 17 '22 02:11

ChuckE