Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Fix Hash#to_query edge case with html_safe string on 1.8 ruby #3049

merged 1 commit into from

3 participants


As follows from #1555 ActiveSupport::SafeBuffer#sub/gsub won't set method-local globals for block on 1.8 ruby.

Because of that Hash#to_query breaks when safe string is used as key (underneath CGI.escape invokes gsub with block on all items of hash):

  >> { '^^'.html_safe => ':(' }.to_query
  NoMethodError: You have a nil object when you didn't expect it!
  You might have expected an instance of Array.
  The error occurred while evaluating nil.size

The fix is simple use to_param on key instead of to_s since it is an alias of to_s for simple objects and alias of to_str for ActiveSupport::SafeBuffer.

I've provided test to show behavior.


This is not a proper fix, we should stop adding hacks to SafeBuffer. People should just call to_str over SafeBuffer stuff until we provide a better solution for it.
Can you show me a real world example that might make me change my mind?


Well, I'm not sure if it can be called a hack since I don't add anything specific to SafeBuffer. If anything commit adds consistency by calling to_param on both keys and values of hash (not only on values as previously).

By definition, to_param method is a common interface to represent objects in url and therefore it seems logical to use it inside to_query method not only for values, but for keys too (if anything, I think to_s calls in to_query method are not needed, but it's not my place to decide)

So this simple sticking to existing convention will allow us to support ActiveRecord and SafeBuffer, etc, objects as keys when we generate query from hash without any hacks.

So this change is not for sake of SafeBuffer but to use existing established interface for purposes it was created. And the positive effect this change has on SafeBuffer is just a consequence of a good interface.

Now back to the real world, I'm creating an interface where values supplied by user will be used as part of parameter names. You can see a similar interface at by clicking on "add header" link and submitting a form. I hope it's persuasive enough :)

Whatever decision you make, I'll accept it with good faith since your guys really rock and I'm very thankful for your constant work on rails. I'm just trying to help out as best as I can :)


@brainopia actually, you're right

@spastorino spastorino merged commit cb0dbe3 into rails:master

It's failing one test

Not sure if the test need a fix here


assert_equal 'custom2=param2-1&custom=param-1', {'custom') =>'param'),'custom2') =>'param2')}.to_param


assert_equal 'custom-1=param-1&custom2-1=param2-1', {'custom') =>'param'),'custom2') =>'param2')}.to_param

Do we need to change the test here?


Sorry, missed that test. But yes, I think it should be updated to reflect that key is called with to_param like you've shown.



@shwoodard shwoodard referenced this pull request from a commit in shwoodard/rails-1
@shwoodard shwoodard Merge branch '3-1-stable' of into 3-1-stable
* '3-1-stable' of (91 commits)
  Merge pull request #3049 from brainopia/fix_to_query_edge_case
  Bump AR-JDBC version. THis version is compatible with 3.1 and above
  Revert "Fixed Apache configuration for gzipped assets: FilesMatch and LocationMatch cannot be nested."
  Fixed Apache configuration for gzipped assets: FilesMatch and LocationMatch cannot be nested. (cherry picked from commit 5e2bf4d)
  Proper lines numbers for stack trace info
  Merge pull request #3040 from guilleiguaran/asset-host-should-not-be-nil-precompile
  cherry-pick #1318 into 3-1-stable
  Bumping up to 3.1.1.rc1
  Add some CHANGELOG missing stuff
  Allow asset tag helper methods to accept :digest => false option in order to completely avoid the digest generation.
  Add equivalent nginx configuration
  Fix typos and broken link on asset pipeline guide
  Merge pull request #3015 from guilleiguaran/clear-tmp-cache-on-precompile
  Merge pull request #3012 from guilleiguaran/3-1-1-changelogs
  Revert "Provide a way to access to assets without using the digest, useful for static files and emails"
  Merge pull request #3011 from guilleiguaran/disable-sprockets-server
  Provide a way to access to assets without using the digest, useful for static files and emails
  Merge pull request #2922 from wayneeseguin/master
  getting_started.textile: Fix typo and split up a sentence in section "Building a Multi-Model Form"
  getting_started.textile: Fix typos in section "Rendering a Partial Form"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
2  activesupport/lib/active_support/core_ext/object/to_query.rb
@@ -7,7 +7,7 @@ class Object
# Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
def to_query(key)
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
- "#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}"
+ "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
9 activesupport/test/core_ext/object/to_query_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
require 'active_support/ordered_hash'
require 'active_support/core_ext/object/to_query'
+require 'active_support/core_ext/string/output_safety.rb'
class ToQueryTest < Test::Unit::TestCase
def test_simple_conversion
@@ -11,6 +12,14 @@ def test_cgi_escaping
assert_query_equal 'a%3Ab=c+d', 'a:b' => 'c d'
+ def test_html_safe_parameter_key
+ assert_query_equal 'a%3Ab=c+d', 'a:b'.html_safe => 'c d'
+ end
+ def test_html_safe_parameter_value
+ assert_query_equal 'a=%5B10%5D', 'a' => '[10]'.html_safe
+ end
def test_nil_parameter_value
empty =
def empty.to_param; nil end
Something went wrong with that request. Please try again.