oj 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +104 -0
- data/ext/oj/buf.h +103 -0
- data/ext/oj/cache8.c +107 -0
- data/ext/oj/cache8.h +48 -0
- data/ext/oj/circarray.c +68 -0
- data/ext/oj/circarray.h +23 -0
- data/ext/oj/code.c +235 -0
- data/ext/oj/code.h +42 -0
- data/ext/oj/compat.c +299 -0
- data/ext/oj/custom.c +1191 -0
- data/ext/oj/dump.c +1252 -0
- data/ext/oj/dump.h +96 -0
- data/ext/oj/dump_compat.c +977 -0
- data/ext/oj/dump_leaf.c +252 -0
- data/ext/oj/dump_object.c +837 -0
- data/ext/oj/dump_strict.c +433 -0
- data/ext/oj/encode.h +45 -0
- data/ext/oj/err.c +57 -0
- data/ext/oj/err.h +70 -0
- data/ext/oj/extconf.rb +47 -0
- data/ext/oj/fast.c +1771 -0
- data/ext/oj/hash.c +163 -0
- data/ext/oj/hash.h +46 -0
- data/ext/oj/hash_test.c +512 -0
- data/ext/oj/mimic_json.c +878 -0
- data/ext/oj/object.c +771 -0
- data/ext/oj/odd.c +231 -0
- data/ext/oj/odd.h +44 -0
- data/ext/oj/oj.c +1704 -0
- data/ext/oj/oj.h +385 -0
- data/ext/oj/parse.c +1086 -0
- data/ext/oj/parse.h +111 -0
- data/ext/oj/rails.c +1493 -0
- data/ext/oj/rails.h +21 -0
- data/ext/oj/reader.c +231 -0
- data/ext/oj/reader.h +151 -0
- data/ext/oj/resolve.c +102 -0
- data/ext/oj/resolve.h +14 -0
- data/ext/oj/rxclass.c +147 -0
- data/ext/oj/rxclass.h +27 -0
- data/ext/oj/saj.c +714 -0
- data/ext/oj/scp.c +224 -0
- data/ext/oj/sparse.c +910 -0
- data/ext/oj/stream_writer.c +363 -0
- data/ext/oj/strict.c +212 -0
- data/ext/oj/string_writer.c +534 -0
- data/ext/oj/trace.c +79 -0
- data/ext/oj/trace.h +28 -0
- data/ext/oj/util.c +136 -0
- data/ext/oj/util.h +19 -0
- data/ext/oj/val_stack.c +118 -0
- data/ext/oj/val_stack.h +185 -0
- data/ext/oj/wab.c +631 -0
- data/lib/oj.rb +21 -0
- data/lib/oj/active_support_helper.rb +41 -0
- data/lib/oj/bag.rb +88 -0
- data/lib/oj/easy_hash.rb +52 -0
- data/lib/oj/error.rb +22 -0
- data/lib/oj/json.rb +176 -0
- data/lib/oj/mimic.rb +267 -0
- data/lib/oj/saj.rb +66 -0
- data/lib/oj/schandler.rb +142 -0
- data/lib/oj/state.rb +131 -0
- data/lib/oj/version.rb +5 -0
- data/pages/Advanced.md +22 -0
- data/pages/Compatibility.md +25 -0
- data/pages/Custom.md +23 -0
- data/pages/Encoding.md +65 -0
- data/pages/JsonGem.md +79 -0
- data/pages/Modes.md +155 -0
- data/pages/Options.md +283 -0
- data/pages/Rails.md +116 -0
- data/pages/Security.md +20 -0
- data/pages/WAB.md +13 -0
- data/test/_test_active.rb +76 -0
- data/test/_test_active_mimic.rb +96 -0
- data/test/_test_mimic_rails.rb +126 -0
- data/test/activerecord/result_test.rb +27 -0
- data/test/activesupport4/decoding_test.rb +108 -0
- data/test/activesupport4/encoding_test.rb +531 -0
- data/test/activesupport4/test_helper.rb +41 -0
- data/test/activesupport5/decoding_test.rb +125 -0
- data/test/activesupport5/encoding_test.rb +485 -0
- data/test/activesupport5/encoding_test_cases.rb +90 -0
- data/test/activesupport5/test_helper.rb +50 -0
- data/test/activesupport5/time_zone_test_helpers.rb +24 -0
- data/test/bar.rb +25 -0
- data/test/files.rb +29 -0
- data/test/foo.rb +167 -0
- data/test/helper.rb +26 -0
- data/test/isolated/shared.rb +308 -0
- data/test/isolated/test_mimic_after.rb +13 -0
- data/test/isolated/test_mimic_alone.rb +12 -0
- data/test/isolated/test_mimic_as_json.rb +45 -0
- data/test/isolated/test_mimic_before.rb +13 -0
- data/test/isolated/test_mimic_define.rb +28 -0
- data/test/isolated/test_mimic_rails_after.rb +22 -0
- data/test/isolated/test_mimic_rails_before.rb +21 -0
- data/test/isolated/test_mimic_redefine.rb +15 -0
- data/test/json_gem/json_addition_test.rb +216 -0
- data/test/json_gem/json_common_interface_test.rb +148 -0
- data/test/json_gem/json_encoding_test.rb +107 -0
- data/test/json_gem/json_ext_parser_test.rb +20 -0
- data/test/json_gem/json_fixtures_test.rb +35 -0
- data/test/json_gem/json_generator_test.rb +383 -0
- data/test/json_gem/json_generic_object_test.rb +90 -0
- data/test/json_gem/json_parser_test.rb +470 -0
- data/test/json_gem/json_string_matching_test.rb +42 -0
- data/test/json_gem/test_helper.rb +18 -0
- data/test/perf.rb +107 -0
- data/test/perf_compat.rb +130 -0
- data/test/perf_fast.rb +164 -0
- data/test/perf_file.rb +64 -0
- data/test/perf_object.rb +138 -0
- data/test/perf_saj.rb +109 -0
- data/test/perf_scp.rb +151 -0
- data/test/perf_simple.rb +287 -0
- data/test/perf_strict.rb +145 -0
- data/test/perf_wab.rb +131 -0
- data/test/sample.rb +54 -0
- data/test/sample/change.rb +14 -0
- data/test/sample/dir.rb +19 -0
- data/test/sample/doc.rb +36 -0
- data/test/sample/file.rb +48 -0
- data/test/sample/group.rb +16 -0
- data/test/sample/hasprops.rb +16 -0
- data/test/sample/layer.rb +12 -0
- data/test/sample/line.rb +20 -0
- data/test/sample/oval.rb +10 -0
- data/test/sample/rect.rb +10 -0
- data/test/sample/shape.rb +35 -0
- data/test/sample/text.rb +20 -0
- data/test/sample_json.rb +37 -0
- data/test/test_compat.rb +509 -0
- data/test/test_custom.rb +503 -0
- data/test/test_debian.rb +53 -0
- data/test/test_fast.rb +470 -0
- data/test/test_file.rb +239 -0
- data/test/test_gc.rb +49 -0
- data/test/test_hash.rb +29 -0
- data/test/test_integer_range.rb +73 -0
- data/test/test_null.rb +376 -0
- data/test/test_object.rb +1018 -0
- data/test/test_saj.rb +186 -0
- data/test/test_scp.rb +433 -0
- data/test/test_strict.rb +410 -0
- data/test/test_various.rb +741 -0
- data/test/test_wab.rb +307 -0
- data/test/test_writer.rb +380 -0
- data/test/tests.rb +24 -0
- data/test/tests_mimic.rb +14 -0
- data/test/tests_mimic_addition.rb +7 -0
- data/test/zoo.rb +13 -0
- metadata +359 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'active_support/testing/assertions'
|
2
|
+
require 'active_support/testing/deprecation'
|
3
|
+
require 'active_support/testing/declarative'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
module ActiveSupport
|
7
|
+
class TestCase < ::Minitest::Test
|
8
|
+
Assertion = Minitest::Assertion
|
9
|
+
|
10
|
+
alias_method :method_name, :name
|
11
|
+
|
12
|
+
include ActiveSupport::Testing::Assertions
|
13
|
+
include ActiveSupport::Testing::Deprecation
|
14
|
+
extend ActiveSupport::Testing::Declarative
|
15
|
+
|
16
|
+
# test/unit backwards compatibility methods
|
17
|
+
alias :assert_raise :assert_raises
|
18
|
+
alias :assert_not_empty :refute_empty
|
19
|
+
alias :assert_not_equal :refute_equal
|
20
|
+
alias :assert_not_in_delta :refute_in_delta
|
21
|
+
alias :assert_not_in_epsilon :refute_in_epsilon
|
22
|
+
alias :assert_not_includes :refute_includes
|
23
|
+
alias :assert_not_instance_of :refute_instance_of
|
24
|
+
alias :assert_not_kind_of :refute_kind_of
|
25
|
+
alias :assert_no_match :refute_match
|
26
|
+
alias :assert_not_nil :refute_nil
|
27
|
+
alias :assert_not_operator :refute_operator
|
28
|
+
alias :assert_not_predicate :refute_predicate
|
29
|
+
alias :assert_not_respond_to :refute_respond_to
|
30
|
+
alias :assert_not_same :refute_same
|
31
|
+
|
32
|
+
# Fails if the block raises an exception.
|
33
|
+
#
|
34
|
+
# assert_nothing_raised do
|
35
|
+
# ...
|
36
|
+
# end
|
37
|
+
def assert_nothing_raised(*args)
|
38
|
+
yield
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'activesupport5/test_helper'
|
2
|
+
require 'active_support/json'
|
3
|
+
require 'active_support/time'
|
4
|
+
require 'activesupport5/time_zone_test_helpers'
|
5
|
+
|
6
|
+
require 'oj'
|
7
|
+
|
8
|
+
Oj::Rails.set_decoder()
|
9
|
+
|
10
|
+
class TestJSONDecoding < ActiveSupport::TestCase
|
11
|
+
include TimeZoneTestHelpers
|
12
|
+
|
13
|
+
class Foo
|
14
|
+
def self.json_create(object)
|
15
|
+
"Foo"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
TESTS = {
|
20
|
+
%q({"returnTo":{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}},
|
21
|
+
%q({"return\\"To\\":":{"\/categories":"\/"}}) => {"return\"To\":" => {"/categories" => "/"}},
|
22
|
+
%q({"returnTo":{"\/categories":1}}) => {"returnTo" => {"/categories" => 1}},
|
23
|
+
%({"returnTo":[1,"a"]}) => {"returnTo" => [1, "a"]},
|
24
|
+
%({"returnTo":[1,"\\"a\\",", "b"]}) => {"returnTo" => [1, "\"a\",", "b"]},
|
25
|
+
%({"a": "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"},
|
26
|
+
%({"a": "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"},
|
27
|
+
# multibyte
|
28
|
+
%({"matzue": "松江", "asakusa": "浅草"}) => {"matzue" => "松江", "asakusa" => "浅草"},
|
29
|
+
%({"a": "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)},
|
30
|
+
%({"a": "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)},
|
31
|
+
%(["2007-01-01 01:12:34 Z"]) => [Time.utc(2007, 1, 1, 1, 12, 34)],
|
32
|
+
%(["2007-01-01 01:12:34 Z", "2007-01-01 01:12:35 Z"]) => [Time.utc(2007, 1, 1, 1, 12, 34), Time.utc(2007, 1, 1, 1, 12, 35)],
|
33
|
+
# no time zone
|
34
|
+
%({"a": "2007-01-01 01:12:34"}) => {'a' => Time.new(2007, 1, 1, 1, 12, 34, "-05:00")},
|
35
|
+
# invalid date
|
36
|
+
%({"a": "1089-10-40"}) => {'a' => "1089-10-40"},
|
37
|
+
# xmlschema date notation
|
38
|
+
%({"a": "2009-08-10T19:01:02"}) => {'a' => Time.new(2009, 8, 10, 19, 1, 2, "-04:00")},
|
39
|
+
%({"a": "2009-08-10T19:01:02Z"}) => {'a' => Time.utc(2009, 8, 10, 19, 1, 2)},
|
40
|
+
%({"a": "2009-08-10T19:01:02+02:00"}) => {'a' => Time.utc(2009, 8, 10, 17, 1, 2)},
|
41
|
+
%({"a": "2009-08-10T19:01:02-05:00"}) => {'a' => Time.utc(2009, 8, 11, 00, 1, 2)},
|
42
|
+
# needs to be *exact*
|
43
|
+
%({"a": " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "},
|
44
|
+
%({"a": "2007-01-01 : it's your birthday"}) => {'a' => "2007-01-01 : it's your birthday"},
|
45
|
+
%([]) => [],
|
46
|
+
%({}) => {},
|
47
|
+
%({"a":1}) => {"a" => 1},
|
48
|
+
%({"a": ""}) => {"a" => ""},
|
49
|
+
%({"a":"\\""}) => {"a" => "\""},
|
50
|
+
%({"a": null}) => {"a" => nil},
|
51
|
+
%({"a": true}) => {"a" => true},
|
52
|
+
%({"a": false}) => {"a" => false},
|
53
|
+
%q({"bad":"\\\\","trailing":""}) => {"bad" => "\\", "trailing" => ""},
|
54
|
+
%q({"a": "http:\/\/test.host\/posts\/1"}) => {"a" => "http://test.host/posts/1"},
|
55
|
+
%q({"a": "\u003cunicode\u0020escape\u003e"}) => {"a" => "<unicode escape>"},
|
56
|
+
%q({"a": "\\\\u0020skip double backslashes"}) => {"a" => "\\u0020skip double backslashes"},
|
57
|
+
%q({"a": "\u003cbr /\u003e"}) => {'a' => "<br />"},
|
58
|
+
%q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["<i>","<b>","<u>"]},
|
59
|
+
# test combination of dates and escaped or unicode encoded data in arrays
|
60
|
+
%q([{"d":"1970-01-01", "s":"\u0020escape"},{"d":"1970-01-01", "s":"\u0020escape"}]) =>
|
61
|
+
[{'d' => Date.new(1970, 1, 1), 's' => ' escape'},{'d' => Date.new(1970, 1, 1), 's' => ' escape'}],
|
62
|
+
%q([{"d":"1970-01-01","s":"http:\/\/example.com"},{"d":"1970-01-01","s":"http:\/\/example.com"}]) =>
|
63
|
+
[{'d' => Date.new(1970, 1, 1), 's' => 'http://example.com'},
|
64
|
+
{'d' => Date.new(1970, 1, 1), 's' => 'http://example.com'}],
|
65
|
+
# tests escaping of "\n" char with Yaml backend
|
66
|
+
%q({"a":"\n"}) => {"a"=>"\n"},
|
67
|
+
%q({"a":"\u000a"}) => {"a"=>"\n"},
|
68
|
+
%q({"a":"Line1\u000aLine2"}) => {"a"=>"Line1\nLine2"},
|
69
|
+
# prevent json unmarshalling
|
70
|
+
%q({"json_class":"TestJSONDecoding::Foo"}) => {"json_class"=>"TestJSONDecoding::Foo"},
|
71
|
+
# json "fragments" - these are invalid JSON, but ActionPack relies on this
|
72
|
+
%q("a string") => "a string",
|
73
|
+
%q(1.1) => 1.1,
|
74
|
+
%q(1) => 1,
|
75
|
+
%q(-1) => -1,
|
76
|
+
%q(true) => true,
|
77
|
+
%q(false) => false,
|
78
|
+
%q(null) => nil
|
79
|
+
}
|
80
|
+
|
81
|
+
TESTS.each_with_index do |(json, expected), index|
|
82
|
+
test "json decodes #{index}" do
|
83
|
+
with_tz_default 'Eastern Time (US & Canada)' do
|
84
|
+
with_parse_json_times(true) do
|
85
|
+
silence_warnings do
|
86
|
+
if expected.nil?
|
87
|
+
assert_nil(ActiveSupport::JSON.decode(json), "JSON failed for #{json}")
|
88
|
+
else
|
89
|
+
assert_equal(expected, ActiveSupport::JSON.decode(json), "JSON failed for #{json}")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
test "json decodes time json with time parsing disabled" do
|
98
|
+
with_parse_json_times(false) do
|
99
|
+
expected = {"a" => "2007-01-01 01:12:34 Z"}
|
100
|
+
assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"}))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_failed_json_decoding
|
105
|
+
assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%(undefined)) }
|
106
|
+
assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({a: 1})) }
|
107
|
+
assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({: 1})) }
|
108
|
+
assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%()) }
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_cannot_pass_unsupported_options
|
112
|
+
assert_raise(ArgumentError) { ActiveSupport::JSON.decode("", create_additions: true) }
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def with_parse_json_times(value)
|
118
|
+
old_value = ActiveSupport.parse_json_times
|
119
|
+
ActiveSupport.parse_json_times = value
|
120
|
+
yield
|
121
|
+
ensure
|
122
|
+
ActiveSupport.parse_json_times = old_value
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
@@ -0,0 +1,485 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'activesupport5/test_helper'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'active_support/core_ext/string/inflections'
|
5
|
+
require 'active_support/json'
|
6
|
+
require 'active_support/time'
|
7
|
+
require 'activesupport5/time_zone_test_helpers'
|
8
|
+
require 'activesupport5/encoding_test_cases'
|
9
|
+
|
10
|
+
require 'oj'
|
11
|
+
|
12
|
+
# Sets the ActiveSupport encoder to be Oj and also wraps the setting of
|
13
|
+
# globals.
|
14
|
+
Oj::Rails.set_encoder()
|
15
|
+
#Oj::Rails.optimize(Hash, Array, BigDecimal, Time, Range, Regexp, ActiveSupport::TimeWithZone)
|
16
|
+
Oj::Rails.optimize()
|
17
|
+
|
18
|
+
class TestJSONEncoding < ActiveSupport::TestCase
|
19
|
+
include TimeZoneTestHelpers
|
20
|
+
|
21
|
+
def sorted_json(json)
|
22
|
+
return json unless json =~ /^\{.*\}$/
|
23
|
+
'{' + json[1..-2].split(',').sort.join(',') + '}'
|
24
|
+
end
|
25
|
+
|
26
|
+
JSONTest::EncodingTestCases.constants.each do |class_tests|
|
27
|
+
define_method("test_#{class_tests[0..-6].underscore}") do
|
28
|
+
begin
|
29
|
+
prev = ActiveSupport.use_standard_json_time_format
|
30
|
+
|
31
|
+
ActiveSupport.escape_html_entities_in_json = class_tests !~ /^Standard/
|
32
|
+
ActiveSupport.use_standard_json_time_format = class_tests =~ /^Standard/
|
33
|
+
JSONTest::EncodingTestCases.const_get(class_tests).each do |pair|
|
34
|
+
assert_equal pair.last, sorted_json(ActiveSupport::JSON.encode(pair.first))
|
35
|
+
end
|
36
|
+
ensure
|
37
|
+
ActiveSupport.escape_html_entities_in_json = false
|
38
|
+
ActiveSupport.use_standard_json_time_format = prev
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_process_status
|
44
|
+
rubinius_skip "https://github.com/rubinius/rubinius/issues/3334"
|
45
|
+
|
46
|
+
# There doesn't seem to be a good way to get a handle on a Process::Status object without actually
|
47
|
+
# creating a child process, hence this to populate $?
|
48
|
+
system("not_a_real_program_#{SecureRandom.hex}")
|
49
|
+
assert_equal %({"exitstatus":#{$?.exitstatus},"pid":#{$?.pid}}), ActiveSupport::JSON.encode($?)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_hash_encoding
|
53
|
+
assert_equal %({\"a\":\"b\"}), ActiveSupport::JSON.encode(:a => :b)
|
54
|
+
assert_equal %({\"a\":1}), ActiveSupport::JSON.encode('a' => 1)
|
55
|
+
assert_equal %({\"a\":[1,2]}), ActiveSupport::JSON.encode('a' => [1,2])
|
56
|
+
assert_equal %({"1":2}), ActiveSupport::JSON.encode(1 => 2)
|
57
|
+
|
58
|
+
assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json(ActiveSupport::JSON.encode(:a => :b, :c => :d))
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_hash_keys_encoding
|
62
|
+
ActiveSupport.escape_html_entities_in_json = true
|
63
|
+
assert_equal "{\"\\u003c\\u003e\":\"\\u003c\\u003e\"}", ActiveSupport::JSON.encode("<>" => "<>")
|
64
|
+
ensure
|
65
|
+
ActiveSupport.escape_html_entities_in_json = false
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_utf8_string_encoded_properly
|
69
|
+
# The original test seems to expect that
|
70
|
+
# ActiveSupport.escape_html_entities_in_json reverts to true even after
|
71
|
+
# being set to false. I haven't been able to figure that out so the value
|
72
|
+
# is set to true, the default, before running the test. This might be
|
73
|
+
# wrong but for now it will have to do.
|
74
|
+
ActiveSupport.escape_html_entities_in_json = true
|
75
|
+
result = ActiveSupport::JSON.encode('€2.99')
|
76
|
+
assert_equal '"€2.99"', result
|
77
|
+
assert_equal(Encoding::UTF_8, result.encoding)
|
78
|
+
|
79
|
+
result = ActiveSupport::JSON.encode('✎☺')
|
80
|
+
assert_equal '"✎☺"', result
|
81
|
+
assert_equal(Encoding::UTF_8, result.encoding)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_non_utf8_string_transcodes
|
85
|
+
s = '二'.encode('Shift_JIS')
|
86
|
+
result = ActiveSupport::JSON.encode(s)
|
87
|
+
assert_equal '"二"', result
|
88
|
+
assert_equal Encoding::UTF_8, result.encoding
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_wide_utf8_chars
|
92
|
+
w = '𠜎'
|
93
|
+
result = ActiveSupport::JSON.encode(w)
|
94
|
+
assert_equal '"𠜎"', result
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_wide_utf8_roundtrip
|
98
|
+
hash = { string: "𐒑" }
|
99
|
+
json = ActiveSupport::JSON.encode(hash)
|
100
|
+
decoded_hash = ActiveSupport::JSON.decode(json)
|
101
|
+
assert_equal "𐒑", decoded_hash['string']
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_hash_key_identifiers_are_always_quoted
|
105
|
+
values = {0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B"}
|
106
|
+
assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_hash_should_allow_key_filtering_with_only
|
110
|
+
assert_equal %({"a":1}), ActiveSupport::JSON.encode({'a' => 1, :b => 2, :c => 3}, :only => 'a')
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_hash_should_allow_key_filtering_with_except
|
114
|
+
assert_equal %({"b":2}), ActiveSupport::JSON.encode({'foo' => 'bar', :b => 2, :c => 3}, :except => ['foo', :c])
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_time_to_json_includes_local_offset
|
118
|
+
with_standard_json_time_format(true) do
|
119
|
+
with_env_tz 'US/Eastern' do
|
120
|
+
assert_equal %("2005-02-01T15:15:10.000-05:00"), ActiveSupport::JSON.encode(Time.local(2005,2,1,15,15,10))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_hash_with_time_to_json
|
126
|
+
with_standard_json_time_format(false) do
|
127
|
+
assert_equal '{"time":"2009/01/01 00:00:00 +0000"}', { :time => Time.utc(2009) }.to_json
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_nested_hash_with_float
|
132
|
+
assert_nothing_raised do
|
133
|
+
hash = {
|
134
|
+
"CHI" => {
|
135
|
+
:display_name => "chicago",
|
136
|
+
:latitude => 123.234
|
137
|
+
}
|
138
|
+
}
|
139
|
+
ActiveSupport::JSON.encode(hash)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_hash_like_with_options
|
144
|
+
h = JSONTest::Hashlike.new
|
145
|
+
json = h.to_json :only => [:foo]
|
146
|
+
|
147
|
+
assert_equal({"foo"=>"hello"}, JSON.parse(json))
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_object_to_json_with_options
|
151
|
+
obj = Object.new
|
152
|
+
obj.instance_variable_set :@foo, "hello"
|
153
|
+
obj.instance_variable_set :@bar, "world"
|
154
|
+
json = obj.to_json :only => ["foo"]
|
155
|
+
|
156
|
+
assert_equal({"foo"=>"hello"}, JSON.parse(json))
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_struct_to_json_with_options
|
160
|
+
struct = Struct.new(:foo, :bar).new
|
161
|
+
struct.foo = "hello"
|
162
|
+
struct.bar = "world"
|
163
|
+
json = struct.to_json :only => [:foo]
|
164
|
+
|
165
|
+
assert_equal({"foo"=>"hello"}, JSON.parse(json))
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_hash_should_pass_encoding_options_to_children_in_as_json
|
169
|
+
person = {
|
170
|
+
:name => 'John',
|
171
|
+
:address => {
|
172
|
+
:city => 'London',
|
173
|
+
:country => 'UK'
|
174
|
+
}
|
175
|
+
}
|
176
|
+
json = person.as_json :only => [:address, :city]
|
177
|
+
|
178
|
+
assert_equal({ 'address' => { 'city' => 'London' }}, json)
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_hash_should_pass_encoding_options_to_children_in_to_json
|
182
|
+
person = {
|
183
|
+
:name => 'John',
|
184
|
+
:address => {
|
185
|
+
:city => 'London',
|
186
|
+
:country => 'UK'
|
187
|
+
}
|
188
|
+
}
|
189
|
+
json = person.to_json :only => [:address, :city]
|
190
|
+
|
191
|
+
assert_equal(%({"address":{"city":"London"}}), json)
|
192
|
+
end
|
193
|
+
|
194
|
+
def test_array_should_pass_encoding_options_to_children_in_as_json
|
195
|
+
people = [
|
196
|
+
{ :name => 'John', :address => { :city => 'London', :country => 'UK' }},
|
197
|
+
{ :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }}
|
198
|
+
]
|
199
|
+
json = people.as_json :only => [:address, :city]
|
200
|
+
expected = [
|
201
|
+
{ 'address' => { 'city' => 'London' }},
|
202
|
+
{ 'address' => { 'city' => 'Paris' }}
|
203
|
+
]
|
204
|
+
|
205
|
+
assert_equal(expected, json)
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_array_should_pass_encoding_options_to_children_in_to_json
|
209
|
+
people = [
|
210
|
+
{ :name => 'John', :address => { :city => 'London', :country => 'UK' }},
|
211
|
+
{ :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }}
|
212
|
+
]
|
213
|
+
json = people.to_json :only => [:address, :city]
|
214
|
+
|
215
|
+
assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
|
216
|
+
end
|
217
|
+
|
218
|
+
People = Class.new(BasicObject) do
|
219
|
+
include Enumerable
|
220
|
+
def initialize()
|
221
|
+
@people = [
|
222
|
+
{ :name => 'John', :address => { :city => 'London', :country => 'UK' }},
|
223
|
+
{ :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }}
|
224
|
+
]
|
225
|
+
end
|
226
|
+
def each(*, &blk)
|
227
|
+
@people.each do |p|
|
228
|
+
yield p if blk
|
229
|
+
p
|
230
|
+
end.each
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_enumerable_should_generate_json_with_as_json
|
235
|
+
json = People.new.as_json :only => [:address, :city]
|
236
|
+
expected = [
|
237
|
+
{ 'address' => { 'city' => 'London' }},
|
238
|
+
{ 'address' => { 'city' => 'Paris' }}
|
239
|
+
]
|
240
|
+
|
241
|
+
assert_equal(expected, json)
|
242
|
+
end
|
243
|
+
|
244
|
+
def test_enumerable_should_generate_json_with_to_json
|
245
|
+
json = People.new.to_json :only => [:address, :city]
|
246
|
+
assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
|
247
|
+
end
|
248
|
+
|
249
|
+
def test_enumerable_should_pass_encoding_options_to_children_in_as_json
|
250
|
+
json = People.new.each.as_json :only => [:address, :city]
|
251
|
+
expected = [
|
252
|
+
{ 'address' => { 'city' => 'London' }},
|
253
|
+
{ 'address' => { 'city' => 'Paris' }}
|
254
|
+
]
|
255
|
+
|
256
|
+
assert_equal(expected, json)
|
257
|
+
end
|
258
|
+
|
259
|
+
def test_enumerable_should_pass_encoding_options_to_children_in_to_json
|
260
|
+
json = People.new.each.to_json :only => [:address, :city]
|
261
|
+
|
262
|
+
assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
|
263
|
+
end
|
264
|
+
|
265
|
+
class CustomWithOptions
|
266
|
+
attr_accessor :foo, :bar
|
267
|
+
|
268
|
+
def as_json(options={})
|
269
|
+
options[:only] = %w(foo bar)
|
270
|
+
super(options)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def test_hash_to_json_should_not_keep_options_around
|
275
|
+
f = CustomWithOptions.new
|
276
|
+
f.foo = "hello"
|
277
|
+
f.bar = "world"
|
278
|
+
|
279
|
+
hash = {"foo" => f, "other_hash" => {"foo" => "other_foo", "test" => "other_test"}}
|
280
|
+
assert_equal({"foo"=>{"foo"=>"hello","bar"=>"world"},
|
281
|
+
"other_hash" => {"foo"=>"other_foo","test"=>"other_test"}}, ActiveSupport::JSON.decode(hash.to_json))
|
282
|
+
end
|
283
|
+
|
284
|
+
def test_array_to_json_should_not_keep_options_around
|
285
|
+
f = CustomWithOptions.new
|
286
|
+
f.foo = "hello"
|
287
|
+
f.bar = "world"
|
288
|
+
|
289
|
+
array = [f, {"foo" => "other_foo", "test" => "other_test"}]
|
290
|
+
assert_equal([{"foo"=>"hello","bar"=>"world"},
|
291
|
+
{"foo"=>"other_foo","test"=>"other_test"}], ActiveSupport::JSON.decode(array.to_json))
|
292
|
+
end
|
293
|
+
|
294
|
+
class OptionsTest
|
295
|
+
def as_json(options = :default)
|
296
|
+
options
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def test_hash_as_json_without_options
|
301
|
+
json = { foo: OptionsTest.new }.as_json
|
302
|
+
assert_equal({"foo" => :default}, json)
|
303
|
+
end
|
304
|
+
|
305
|
+
def test_array_as_json_without_options
|
306
|
+
json = [ OptionsTest.new ].as_json
|
307
|
+
assert_equal([:default], json)
|
308
|
+
end
|
309
|
+
|
310
|
+
def test_struct_encoding
|
311
|
+
Struct.new('UserNameAndEmail', :name, :email)
|
312
|
+
Struct.new('UserNameAndDate', :name, :date)
|
313
|
+
Struct.new('Custom', :name, :sub)
|
314
|
+
user_email = Struct::UserNameAndEmail.new 'David', 'sample@example.com'
|
315
|
+
user_birthday = Struct::UserNameAndDate.new 'David', Date.new(2010, 01, 01)
|
316
|
+
custom = Struct::Custom.new 'David', user_birthday
|
317
|
+
|
318
|
+
|
319
|
+
json_strings = ""
|
320
|
+
json_string_and_date = ""
|
321
|
+
json_custom = ""
|
322
|
+
|
323
|
+
assert_nothing_raised do
|
324
|
+
json_strings = user_email.to_json
|
325
|
+
json_string_and_date = user_birthday.to_json
|
326
|
+
json_custom = custom.to_json
|
327
|
+
end
|
328
|
+
|
329
|
+
assert_equal({"name" => "David",
|
330
|
+
"sub" => {
|
331
|
+
"name" => "David",
|
332
|
+
"date" => "2010-01-01" }}, ActiveSupport::JSON.decode(json_custom))
|
333
|
+
|
334
|
+
assert_equal({"name" => "David", "email" => "sample@example.com"},
|
335
|
+
ActiveSupport::JSON.decode(json_strings))
|
336
|
+
|
337
|
+
assert_equal({"name" => "David", "date" => "2010-01-01"},
|
338
|
+
ActiveSupport::JSON.decode(json_string_and_date))
|
339
|
+
end
|
340
|
+
|
341
|
+
def test_nil_true_and_false_represented_as_themselves
|
342
|
+
assert_nil nil.as_json
|
343
|
+
assert_equal true, true.as_json
|
344
|
+
assert_equal false, false.as_json
|
345
|
+
end
|
346
|
+
|
347
|
+
class HashWithAsJson < Hash
|
348
|
+
attr_accessor :as_json_called
|
349
|
+
|
350
|
+
def initialize(*)
|
351
|
+
super
|
352
|
+
end
|
353
|
+
|
354
|
+
def as_json(options={})
|
355
|
+
@as_json_called = true
|
356
|
+
super
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def test_json_gem_dump_by_passing_active_support_encoder
|
361
|
+
h = HashWithAsJson.new
|
362
|
+
h[:foo] = "hello"
|
363
|
+
h[:bar] = "world"
|
364
|
+
|
365
|
+
assert_equal %({"foo":"hello","bar":"world"}), JSON.dump(h)
|
366
|
+
assert_nil h.as_json_called
|
367
|
+
end
|
368
|
+
|
369
|
+
def test_json_gem_generate_by_passing_active_support_encoder
|
370
|
+
h = HashWithAsJson.new
|
371
|
+
h[:foo] = "hello"
|
372
|
+
h[:bar] = "world"
|
373
|
+
|
374
|
+
assert_equal %({"foo":"hello","bar":"world"}), JSON.generate(h)
|
375
|
+
assert_nil h.as_json_called
|
376
|
+
end
|
377
|
+
|
378
|
+
def test_json_gem_pretty_generate_by_passing_active_support_encoder
|
379
|
+
h = HashWithAsJson.new
|
380
|
+
h[:foo] = "hello"
|
381
|
+
h[:bar] = "world"
|
382
|
+
|
383
|
+
assert_equal <<EXPECTED.chomp, JSON.pretty_generate(h)
|
384
|
+
{
|
385
|
+
"foo": "hello",
|
386
|
+
"bar": "world"
|
387
|
+
}
|
388
|
+
EXPECTED
|
389
|
+
assert_nil h.as_json_called
|
390
|
+
end
|
391
|
+
|
392
|
+
def test_twz_to_json_with_use_standard_json_time_format_config_set_to_false
|
393
|
+
with_standard_json_time_format(false) do
|
394
|
+
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
|
395
|
+
time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
|
396
|
+
assert_equal "\"1999/12/31 19:00:00 -0500\"", ActiveSupport::JSON.encode(time)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
def test_twz_to_json_with_use_standard_json_time_format_config_set_to_true
|
401
|
+
with_standard_json_time_format(true) do
|
402
|
+
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
|
403
|
+
time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
|
404
|
+
assert_equal "\"1999-12-31T19:00:00.000-05:00\"", ActiveSupport::JSON.encode(time)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
def test_twz_to_json_with_custom_time_precision
|
409
|
+
with_standard_json_time_format(true) do
|
410
|
+
with_time_precision(0) do
|
411
|
+
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
|
412
|
+
time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
|
413
|
+
assert_equal "\"1999-12-31T19:00:00-05:00\"", ActiveSupport::JSON.encode(time)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
def test_time_to_json_with_custom_time_precision
|
419
|
+
with_standard_json_time_format(true) do
|
420
|
+
with_time_precision(0) do
|
421
|
+
assert_equal "\"2000-01-01T00:00:00Z\"", ActiveSupport::JSON.encode(Time.utc(2000))
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
def test_datetime_to_json_with_custom_time_precision
|
427
|
+
with_standard_json_time_format(true) do
|
428
|
+
with_time_precision(0) do
|
429
|
+
assert_equal "\"2000-01-01T00:00:00+00:00\"", ActiveSupport::JSON.encode(DateTime.new(2000))
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
def test_twz_to_json_when_wrapping_a_date_time
|
435
|
+
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
|
436
|
+
time = ActiveSupport::TimeWithZone.new(DateTime.new(2000), zone)
|
437
|
+
assert_equal '"1999-12-31T19:00:00.000-05:00"', ActiveSupport::JSON.encode(time)
|
438
|
+
end
|
439
|
+
|
440
|
+
def test_exception_to_json
|
441
|
+
exception = Exception.new("foo")
|
442
|
+
assert_equal '"foo"', ActiveSupport::JSON.encode(exception)
|
443
|
+
end
|
444
|
+
|
445
|
+
class InfiniteNumber
|
446
|
+
def as_json(options = nil)
|
447
|
+
{ "number" => Float::INFINITY }
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
def test_to_json_works_when_as_json_returns_infinite_number
|
452
|
+
assert_equal '{"number":null}', InfiniteNumber.new.to_json
|
453
|
+
end
|
454
|
+
|
455
|
+
class NaNNumber
|
456
|
+
def as_json(options = nil)
|
457
|
+
{ "number" => Float::INFINITY }
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
def test_to_json_works_when_as_json_returns_NaN_number
|
462
|
+
assert_equal '{"number":null}', NaNNumber.new.to_json
|
463
|
+
end
|
464
|
+
|
465
|
+
protected
|
466
|
+
|
467
|
+
def object_keys(json_object)
|
468
|
+
json_object[1..-2].scan(/([^{}:,\s]+):/).flatten.sort
|
469
|
+
end
|
470
|
+
|
471
|
+
def with_standard_json_time_format(boolean = true)
|
472
|
+
old, ActiveSupport.use_standard_json_time_format = ActiveSupport.use_standard_json_time_format, boolean
|
473
|
+
yield
|
474
|
+
ensure
|
475
|
+
ActiveSupport.use_standard_json_time_format = old
|
476
|
+
end
|
477
|
+
|
478
|
+
def with_time_precision(value)
|
479
|
+
old_value = ActiveSupport::JSON::Encoding.time_precision
|
480
|
+
ActiveSupport::JSON::Encoding.time_precision = value
|
481
|
+
yield
|
482
|
+
ensure
|
483
|
+
ActiveSupport::JSON::Encoding.time_precision = old_value
|
484
|
+
end
|
485
|
+
end
|