mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #12862 from chancancode/json_gem_compat
JSON gem compatibility
This commit is contained in:
commit
bed324c6fe
3 changed files with 80 additions and 3 deletions
|
@ -1,3 +1,17 @@
|
|||
* Improved compatibility with the stdlib JSON gem.
|
||||
|
||||
Previously, calling `::JSON.{generate,dump}` sometimes causes unexpected
|
||||
failures such as intridea/multi_json#86.
|
||||
|
||||
`::JSON.{generate,dump}` now bypasses the ActiveSupport JSON encoder
|
||||
completely and yields the same result with or without ActiveSupport. This
|
||||
means that it will **not** call `as_json` and will ignore any options that
|
||||
the JSON gem does not natively understand. To invoke ActiveSupport's JSON
|
||||
encoder instead, use `obj.to_json(options)` or
|
||||
`ActiveSupport::JSON.encode(obj, options)`.
|
||||
|
||||
*Godfrey Chan*
|
||||
|
||||
* Fix Active Support `Time#to_json` and `DateTime#to_json` to return 3 decimal
|
||||
places worth of fractional seconds, similar to `TimeWithZone`.
|
||||
|
||||
|
|
|
@ -9,18 +9,36 @@ require 'time'
|
|||
require 'active_support/core_ext/time/conversions'
|
||||
require 'active_support/core_ext/date_time/conversions'
|
||||
require 'active_support/core_ext/date/conversions'
|
||||
require 'active_support/core_ext/module/aliasing'
|
||||
|
||||
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
|
||||
# their default behavior. That said, we need to define the basic to_json method in all of them,
|
||||
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
|
||||
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
|
||||
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
|
||||
#
|
||||
# On the other hand, we should avoid conflict with ::JSON.{generate,dump}(obj). Unfortunately, the
|
||||
# JSON gem's encoder relies on its own to_json implementation to encode objects. Since it always
|
||||
# passes a ::JSON::State object as the only argument to to_json, we can detect that and forward the
|
||||
# calls to the original to_json method.
|
||||
#
|
||||
# It should be noted that when using ::JSON.{generate,dump} directly, ActiveSupport's encoder is
|
||||
# bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
|
||||
# ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
|
||||
# should give exactly the same results with or without active support.
|
||||
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
|
||||
klass.class_eval do
|
||||
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
|
||||
def to_json(options = nil)
|
||||
ActiveSupport::JSON.encode(self, options)
|
||||
def to_json_with_active_support_encoder(options = nil)
|
||||
if options.is_a?(::JSON::State)
|
||||
# Called from JSON.{generate,dump}, forward it to JSON gem's to_json
|
||||
self.to_json_without_active_support_encoder(options)
|
||||
else
|
||||
# to_json is being invoked directly, use ActiveSupport's encoder
|
||||
ActiveSupport::JSON.encode(self, options)
|
||||
end
|
||||
end
|
||||
|
||||
alias_method_chain :to_json, :active_support_encoder
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -32,6 +32,19 @@ class TestJSONEncoding < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
class HashWithAsJson < Hash
|
||||
attr_accessor :as_json_called
|
||||
|
||||
def initialize(*)
|
||||
super
|
||||
end
|
||||
|
||||
def as_json(options={})
|
||||
@as_json_called = true
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
TrueTests = [[ true, %(true) ]]
|
||||
FalseTests = [[ false, %(false) ]]
|
||||
NilTests = [[ nil, %(null) ]]
|
||||
|
@ -367,6 +380,38 @@ class TestJSONEncoding < ActiveSupport::TestCase
|
|||
assert_equal false, false.as_json
|
||||
end
|
||||
|
||||
def test_json_gem_dump_by_passing_active_support_encoder
|
||||
h = HashWithAsJson.new
|
||||
h[:foo] = "hello"
|
||||
h[:bar] = "world"
|
||||
|
||||
assert_equal %({"foo":"hello","bar":"world"}), JSON.dump(h)
|
||||
assert_nil h.as_json_called
|
||||
end
|
||||
|
||||
def test_json_gem_generate_by_passing_active_support_encoder
|
||||
h = HashWithAsJson.new
|
||||
h[:foo] = "hello"
|
||||
h[:bar] = "world"
|
||||
|
||||
assert_equal %({"foo":"hello","bar":"world"}), JSON.generate(h)
|
||||
assert_nil h.as_json_called
|
||||
end
|
||||
|
||||
def test_json_gem_pretty_generate_by_passing_active_support_encoder
|
||||
h = HashWithAsJson.new
|
||||
h[:foo] = "hello"
|
||||
h[:bar] = "world"
|
||||
|
||||
assert_equal <<EXPECTED.chomp, JSON.pretty_generate(h)
|
||||
{
|
||||
"foo": "hello",
|
||||
"bar": "world"
|
||||
}
|
||||
EXPECTED
|
||||
assert_nil h.as_json_called
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def object_keys(json_object)
|
||||
|
|
Loading…
Reference in a new issue