rails: to_query used on hash's don't keep order

There is an issue in the to_query function (defined in https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/to_query.rb) for Hash objects.

When to_query is used on an Array, the order of the input array is kept, while when used on a Hash, the order is changed to be lexographical, not what is input (the Hash.to_query is aliased to to_param, see https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/to_param.rb)

You can see this by the following:

1.9.3p392 :001 > require 'active_support/core_ext/object/to_query'
 => true 
1.9.3p392 :002 > ['c', 'a', 'b'].to_query('foo')
 => "foo%5B%5D=c&foo%5B%5D=a&foo%5B%5D=b" 
1.9.3p392 :003 > {'c' => 'b', 'a' => 'c', 'b' => 'a'}.to_query('foo')
 => "foo%5Ba%5D=c&foo%5Bb%5D=a&foo%5Bc%5D=b" 

Here, the [‘c’, ‘a’, ‘b’].to_query(‘foo’) goes to “foo[]=c&foo[]=a&foo[]=b” url escaped. Note that the order of 3 1 2 is kept. However, when the input is {‘c’ => 'b, ‘a’ => ‘c’, ‘b’ => ‘a’}.to_query(‘foo’), the output is “foo[a]=c&foo[b]=a&foo[c]=b” url escaped. Note that the order of the original hash keys was c a b, but the output has lexographical of a b c.

I would say this is a bug, as it is inconsistent with the Array to_query, and there is no standard saying that url param queries must be in lexographical order. Ruby hashes keep order, so why break it?

A fix for this is at https://gist.github.com/hughdavenport/5545117

Cheers,

Hugh

About this issue

  • Original URL
  • State: closed
  • Created 11 years ago
  • Comments: 19 (6 by maintainers)

Commits related to this issue

Most upvoted comments

Just for the files, you can solve it like this:

query = params.map{|k, v| CGI::escape(k.to_s) + '=' + CGI::escape(v)}.join('&')

The order of query parameters IS significant, even if you saw some software that ignores it during parsing. For example:

  • Equivalent URLs sometimes have a canonical/normalized form (e.g. to make visual comparisons easier)
  • When a human sees a URL, a certain order might be natural or more readable
  • Very long URLs often get truncated in certain contexts, so the most important query parameters (e.g. the database key) should come first so they aren’t the part that gets truncated

Sorting the keys might be useful, and it might even be a reasonable default, but it’s presumptuous for an API to completely remove the ability to specify the order! A bad decision was made here.

My response which was never answered remains…

Yup, I noticed that, this bug was merely as there is a difference between the Array and the Hash invocations. If it should be ordered lexographically, then why not both? and why does it need to be lexographical? as that is just extra work.

All this said, I am aware that the way I am using this is an unusual scenario, but yeh… I would say they should either both be sorted, or both not.

I would open a pull request, but I find that with this community that when I offer a suggestion to fix what myself and a whole lot of other users think is a bug, it gets shut down. The point of an open source community is to allow friendly collaboration. If you would like to discuss why you don’t find what I just wrote above is not a bug, could you? Otherwise why comment at all, just leave the bug tracker stale.

Hugh