Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

`#as_json` isolates options when encoding a hash. Closes #8182 #8185

Merged
merged 1 commit into from

4 participants

@senny
Owner

I modified the Encoder so that duplicates of the original options hash are passed around. I'm not sure if there are cases where we actually wan't the side effects but all the tests passed.

This is a fix #8182

@senny
Owner

@rafaelfranca @carlosantoniodasilva can you take a look?

activesupport/lib/active_support/json/encoding.rb
@@ -66,7 +66,7 @@ def options_for(value)
options.merge(:encoder => self)
else
options
- end
+ end.dup

I think you can dup only in the else clause, since options.merge already creates a "duped" object.

@senny Owner
senny added a note

good catch. updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@carlosantoniodasilva

Yeah, in this case I don't think we want the options to cause this side effect, since options within the object are affecting the other external hash. Thanks!

@carlosantoniodasilva

Looks good, I'll have to ask you to expand the commit message with a description of the problem, and maybe some example code, otherwise @drogus will kill us :smile:

@senny
Owner

I added a more detailed description. I did not add an example though. Example code would look exactly like the test case and I think the diff is very short to understand the situation.

@carlosantoniodasilva is that ok?

@carlosantoniodasilva

I think so, looks fine, just found a typo in the commit message: in the has. :) Thanks

@senny senny `#as_json` isolates options when encoding a hash. Closes #8182
Setting options in a custom `#as_json` method had side effects.
Modifications of the `options` hash leaked outside and influenced
the conversion of other objects contained in the hash.
78dca35
@carlosantoniodasilva carlosantoniodasilva merged commit d5c4370 into rails:master
@carlosantoniodasilva

Thanks!

@senny senny referenced this pull request from a commit in senny/rails
@senny senny backport #8185, `#as_json` isolates options when encoding a hash.
Setting options in a custom `#as_json` method had side effects.
Modifications of the `options` hash leaked outside and influenced
the conversion of other objects contained in the hash.

Conflicts:

	activesupport/CHANGELOG.md
be79632
@nikitug nikitug referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@slbug

@senny, test fails on rails master. But fails only sometimes. I've run tests about 10 times and got 3 failures. Using ruby 1.9.3p327 (resulting json is the same, just options order messed up)

test_to_json_should_not_keep_options_around(TestJSONEncoding) [rails/activesupport/test/json/encoding_test.rb:279]:
--- expected
+++ actual
@@ -1 +1 @@
-"{\"foo\":{\"foo\":\"hello\",\"bar\":\"world\"},\"other_hash\":{\"foo\":\"other_foo\",\"test\":\"other_test\"}}"
+"{\"foo\":{\"bar\":\"world\",\"foo\":\"hello\"},\"other_hash\":{\"foo\":\"other_foo\",\"test\":\"other_test\"}}"

bundle list

  * actionpack (4.0.0.beta)
  * activemodel (4.0.0.beta)
  * activerecord (4.0.0.beta)
  * activerecord-deprecated_finders (0.0.1 2125c7b)
  * activesupport (4.0.0.beta)
  * arel (3.0.2.20120819075748 38d0a22)
  * atomic (1.0.1)
  * bcrypt-ruby (3.0.1)
  * benchmark-ips (1.2.0)
  * builder (3.1.4)
  * bundler (1.2.2)
  * coffee-rails (4.0.0.beta 052634e)
  * coffee-script (2.2.0)
  * coffee-script-source (1.4.0)
  * columnize (0.3.6)
  * dalli (2.6.0)
  * debugger (1.2.2)
  * debugger-linecache (1.1.2)
  * debugger-ruby_core_source (1.1.5)
  * erubis (2.7.0)
  * execjs (1.4.0)
  * hike (1.2.1)
  * i18n (0.6.1)
  * jquery-rails (2.1.4 cf47e71)
  * json (1.7.5)
  * kindlerb (0.1.1)
  * mail (2.5.3)
  * metaclass (0.0.1)
  * mime-types (1.19)
  * minitest (4.3.3)
  * mocha (0.13.1)
  * multi_json (1.5.0)
  * mustache (0.99.4)
  * mysql (2.9.0)
  * mysql2 (0.3.11)
  * nokogiri (1.5.6)
  * pg (0.14.1)
  * polyglot (0.3.3)
  * racc (1.4.9)
  * rack (1.4.1)
  * rack-cache (1.2)
  * rack-test (0.6.2 1b1e730)
  * rails (4.0.0.beta 99bcb42)
  * railties (4.0.0.beta)
  * rake (10.0.3)
  * rdoc (3.12)
  * redcarpet (2.2.2)
  * ruby-prof (0.11.2)
  * sdoc (0.3.20 87ad100)
  * sprockets (2.8.2)
  * sprockets-rails (2.0.0.rc1 0228520)
  * sqlite3 (1.3.6)
  * thor (0.16.0)
  * thread_safe (0.1.0)
  * tilt (1.3.3)
  * treetop (1.4.12)
  * turbolinks (0.6.1)
  * tzinfo (0.3.35)
  * uglifier (1.3.0)
  * w3c_validators (1.2)
  * yajl-ruby (1.1.0)
@senny
Owner

@slbug It seems something weird is going on with the ordering of the hash. I'll take a look and rewrite the tests if necessary.

@jeremy
Owner

Because Object#as_json uses #instance_values which uses #instance_variables which isn't guaranteed to be in sorted order

@senny senny referenced this pull request from a commit in senny/rails
@senny senny rewrite order dependent test case. #8185
As reported (rails#8185 (comment))
this test relied on the order a hash was serialized. Comparing the parsed
hash makes the test no longer order dependent.
e68505a
@senny
Owner

@slbug I rewrote the testcase so that it's no longer order dependent. You can rebase against rails/master and your tests should be fine now.

@carlosantoniodasilva

Just as a side note, these tests are only failing when applying falcon patch (at least to me). With 1.9.3-p327, everything is green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 12, 2012
  1. @senny

    `#as_json` isolates options when encoding a hash. Closes #8182

    senny authored
    Setting options in a custom `#as_json` method had side effects.
    Modifications of the `options` hash leaked outside and influenced
    the conversion of other objects contained in the hash.
This page is out of date. Refresh to see the latest.
View
5 activesupport/CHANGELOG.md
@@ -1,5 +1,10 @@
## Rails 4.0.0 (unreleased) ##
+* `#as_json` isolates options when encoding a hash.
+ Fix #8182
+
+ *Yves Senn*
+
* Deprecate Hash#diff in favor of MiniTest's #diff. *Steve Klabnik*
* Kernel#capture can catch output from subprocesses *Dmitry Vorotilin*
View
2  activesupport/lib/active_support/json/encoding.rb
@@ -65,7 +65,7 @@ def options_for(value)
# they can detect circular references.
options.merge(:encoder => self)
else
- options
+ options.dup
end
end
View
18 activesupport/test/json/encoding_test.rb
@@ -22,6 +22,15 @@ def as_json(options)
end
end
+ class CustomWithOptions
+ attr_accessor :foo, :bar
+
+ def as_json(options={})
+ options[:only] = %w(foo bar)
+ super(options)
+ end
+ end
+
TrueTests = [[ true, %(true) ]]
FalseTests = [[ false, %(false) ]]
NilTests = [[ nil, %(null) ]]
@@ -248,6 +257,15 @@ def test_enumerable_should_pass_encoding_options_to_children_in_to_json
assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
end
+ def test_to_json_should_not_keep_options_around
+ f = CustomWithOptions.new
+ f.foo = "hello"
+ f.bar = "world"
+
+ hash = {"foo" => f, "other_hash" => {"foo" => "other_foo", "test" => "other_test"}}
+ assert_equal(%({"foo":{"foo":"hello","bar":"world"},"other_hash":{"foo":"other_foo","test":"other_test"}}), hash.to_json)
+ end
+
def test_struct_encoding
Struct.new('UserNameAndEmail', :name, :email)
Struct.new('UserNameAndDate', :name, :date)
Something went wrong with that request. Please try again.