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.
Files changed (156) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +104 -0
  4. data/ext/oj/buf.h +103 -0
  5. data/ext/oj/cache8.c +107 -0
  6. data/ext/oj/cache8.h +48 -0
  7. data/ext/oj/circarray.c +68 -0
  8. data/ext/oj/circarray.h +23 -0
  9. data/ext/oj/code.c +235 -0
  10. data/ext/oj/code.h +42 -0
  11. data/ext/oj/compat.c +299 -0
  12. data/ext/oj/custom.c +1191 -0
  13. data/ext/oj/dump.c +1252 -0
  14. data/ext/oj/dump.h +96 -0
  15. data/ext/oj/dump_compat.c +977 -0
  16. data/ext/oj/dump_leaf.c +252 -0
  17. data/ext/oj/dump_object.c +837 -0
  18. data/ext/oj/dump_strict.c +433 -0
  19. data/ext/oj/encode.h +45 -0
  20. data/ext/oj/err.c +57 -0
  21. data/ext/oj/err.h +70 -0
  22. data/ext/oj/extconf.rb +47 -0
  23. data/ext/oj/fast.c +1771 -0
  24. data/ext/oj/hash.c +163 -0
  25. data/ext/oj/hash.h +46 -0
  26. data/ext/oj/hash_test.c +512 -0
  27. data/ext/oj/mimic_json.c +878 -0
  28. data/ext/oj/object.c +771 -0
  29. data/ext/oj/odd.c +231 -0
  30. data/ext/oj/odd.h +44 -0
  31. data/ext/oj/oj.c +1704 -0
  32. data/ext/oj/oj.h +385 -0
  33. data/ext/oj/parse.c +1086 -0
  34. data/ext/oj/parse.h +111 -0
  35. data/ext/oj/rails.c +1493 -0
  36. data/ext/oj/rails.h +21 -0
  37. data/ext/oj/reader.c +231 -0
  38. data/ext/oj/reader.h +151 -0
  39. data/ext/oj/resolve.c +102 -0
  40. data/ext/oj/resolve.h +14 -0
  41. data/ext/oj/rxclass.c +147 -0
  42. data/ext/oj/rxclass.h +27 -0
  43. data/ext/oj/saj.c +714 -0
  44. data/ext/oj/scp.c +224 -0
  45. data/ext/oj/sparse.c +910 -0
  46. data/ext/oj/stream_writer.c +363 -0
  47. data/ext/oj/strict.c +212 -0
  48. data/ext/oj/string_writer.c +534 -0
  49. data/ext/oj/trace.c +79 -0
  50. data/ext/oj/trace.h +28 -0
  51. data/ext/oj/util.c +136 -0
  52. data/ext/oj/util.h +19 -0
  53. data/ext/oj/val_stack.c +118 -0
  54. data/ext/oj/val_stack.h +185 -0
  55. data/ext/oj/wab.c +631 -0
  56. data/lib/oj.rb +21 -0
  57. data/lib/oj/active_support_helper.rb +41 -0
  58. data/lib/oj/bag.rb +88 -0
  59. data/lib/oj/easy_hash.rb +52 -0
  60. data/lib/oj/error.rb +22 -0
  61. data/lib/oj/json.rb +176 -0
  62. data/lib/oj/mimic.rb +267 -0
  63. data/lib/oj/saj.rb +66 -0
  64. data/lib/oj/schandler.rb +142 -0
  65. data/lib/oj/state.rb +131 -0
  66. data/lib/oj/version.rb +5 -0
  67. data/pages/Advanced.md +22 -0
  68. data/pages/Compatibility.md +25 -0
  69. data/pages/Custom.md +23 -0
  70. data/pages/Encoding.md +65 -0
  71. data/pages/JsonGem.md +79 -0
  72. data/pages/Modes.md +155 -0
  73. data/pages/Options.md +283 -0
  74. data/pages/Rails.md +116 -0
  75. data/pages/Security.md +20 -0
  76. data/pages/WAB.md +13 -0
  77. data/test/_test_active.rb +76 -0
  78. data/test/_test_active_mimic.rb +96 -0
  79. data/test/_test_mimic_rails.rb +126 -0
  80. data/test/activerecord/result_test.rb +27 -0
  81. data/test/activesupport4/decoding_test.rb +108 -0
  82. data/test/activesupport4/encoding_test.rb +531 -0
  83. data/test/activesupport4/test_helper.rb +41 -0
  84. data/test/activesupport5/decoding_test.rb +125 -0
  85. data/test/activesupport5/encoding_test.rb +485 -0
  86. data/test/activesupport5/encoding_test_cases.rb +90 -0
  87. data/test/activesupport5/test_helper.rb +50 -0
  88. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  89. data/test/bar.rb +25 -0
  90. data/test/files.rb +29 -0
  91. data/test/foo.rb +167 -0
  92. data/test/helper.rb +26 -0
  93. data/test/isolated/shared.rb +308 -0
  94. data/test/isolated/test_mimic_after.rb +13 -0
  95. data/test/isolated/test_mimic_alone.rb +12 -0
  96. data/test/isolated/test_mimic_as_json.rb +45 -0
  97. data/test/isolated/test_mimic_before.rb +13 -0
  98. data/test/isolated/test_mimic_define.rb +28 -0
  99. data/test/isolated/test_mimic_rails_after.rb +22 -0
  100. data/test/isolated/test_mimic_rails_before.rb +21 -0
  101. data/test/isolated/test_mimic_redefine.rb +15 -0
  102. data/test/json_gem/json_addition_test.rb +216 -0
  103. data/test/json_gem/json_common_interface_test.rb +148 -0
  104. data/test/json_gem/json_encoding_test.rb +107 -0
  105. data/test/json_gem/json_ext_parser_test.rb +20 -0
  106. data/test/json_gem/json_fixtures_test.rb +35 -0
  107. data/test/json_gem/json_generator_test.rb +383 -0
  108. data/test/json_gem/json_generic_object_test.rb +90 -0
  109. data/test/json_gem/json_parser_test.rb +470 -0
  110. data/test/json_gem/json_string_matching_test.rb +42 -0
  111. data/test/json_gem/test_helper.rb +18 -0
  112. data/test/perf.rb +107 -0
  113. data/test/perf_compat.rb +130 -0
  114. data/test/perf_fast.rb +164 -0
  115. data/test/perf_file.rb +64 -0
  116. data/test/perf_object.rb +138 -0
  117. data/test/perf_saj.rb +109 -0
  118. data/test/perf_scp.rb +151 -0
  119. data/test/perf_simple.rb +287 -0
  120. data/test/perf_strict.rb +145 -0
  121. data/test/perf_wab.rb +131 -0
  122. data/test/sample.rb +54 -0
  123. data/test/sample/change.rb +14 -0
  124. data/test/sample/dir.rb +19 -0
  125. data/test/sample/doc.rb +36 -0
  126. data/test/sample/file.rb +48 -0
  127. data/test/sample/group.rb +16 -0
  128. data/test/sample/hasprops.rb +16 -0
  129. data/test/sample/layer.rb +12 -0
  130. data/test/sample/line.rb +20 -0
  131. data/test/sample/oval.rb +10 -0
  132. data/test/sample/rect.rb +10 -0
  133. data/test/sample/shape.rb +35 -0
  134. data/test/sample/text.rb +20 -0
  135. data/test/sample_json.rb +37 -0
  136. data/test/test_compat.rb +509 -0
  137. data/test/test_custom.rb +503 -0
  138. data/test/test_debian.rb +53 -0
  139. data/test/test_fast.rb +470 -0
  140. data/test/test_file.rb +239 -0
  141. data/test/test_gc.rb +49 -0
  142. data/test/test_hash.rb +29 -0
  143. data/test/test_integer_range.rb +73 -0
  144. data/test/test_null.rb +376 -0
  145. data/test/test_object.rb +1018 -0
  146. data/test/test_saj.rb +186 -0
  147. data/test/test_scp.rb +433 -0
  148. data/test/test_strict.rb +410 -0
  149. data/test/test_various.rb +741 -0
  150. data/test/test_wab.rb +307 -0
  151. data/test/test_writer.rb +380 -0
  152. data/test/tests.rb +24 -0
  153. data/test/tests_mimic.rb +14 -0
  154. data/test/tests_mimic_addition.rb +7 -0
  155. data/test/zoo.rb +13 -0
  156. 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