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,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.dirname(__FILE__)
4
+ $: << File.dirname(File.dirname(__FILE__))
5
+
6
+ require 'helper'
7
+ require "rails/all"
8
+
9
+ Oj::Rails.set_encoder()
10
+ Oj::Rails.optimize()
11
+
12
+ Oj.default_options = { mode: :rails }
13
+
14
+ class ActiveRecordResultTest < Minitest::Test
15
+ def test_hash_rows
16
+
17
+ result = ActiveRecord::Result.new(["one", "two"],
18
+ [
19
+ ["row 1 col 1", "row 1 col 2"],
20
+ ["row 2 col 1", "row 2 col 2"],
21
+ ["row 3 col 1", "row 3 col 2"],
22
+ ])
23
+ #puts "*** result: #{Oj.dump(result, indent: 2)}"
24
+
25
+ assert_equal Oj.dump(result, mode: :rails), Oj.dump(result.to_hash)
26
+ end
27
+ end
@@ -0,0 +1,108 @@
1
+ # encoding: utf-8
2
+ require 'activesupport4/test_helper'
3
+ require 'active_support/json'
4
+ require 'active_support/time'
5
+
6
+ class TestJSONDecoding < ActiveSupport::TestCase
7
+ class Foo
8
+ def self.json_create(object)
9
+ "Foo"
10
+ end
11
+ end
12
+
13
+ TESTS = {
14
+ %q({"returnTo":{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}},
15
+ %q({"return\\"To\\":":{"\/categories":"\/"}}) => {"return\"To\":" => {"/categories" => "/"}},
16
+ %q({"returnTo":{"\/categories":1}}) => {"returnTo" => {"/categories" => 1}},
17
+ %({"returnTo":[1,"a"]}) => {"returnTo" => [1, "a"]},
18
+ %({"returnTo":[1,"\\"a\\",", "b"]}) => {"returnTo" => [1, "\"a\",", "b"]},
19
+ %({"a": "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"},
20
+ %({"a": "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"},
21
+ # multibyte
22
+ %({"matzue": "松江", "asakusa": "浅草"}) => {"matzue" => "松江", "asakusa" => "浅草"},
23
+ %({"a": "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)},
24
+ %({"a": "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)},
25
+ %(["2007-01-01 01:12:34 Z"]) => [Time.utc(2007, 1, 1, 1, 12, 34)],
26
+ %(["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)],
27
+ # no time zone
28
+ %({"a": "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"},
29
+ # invalid date
30
+ %({"a": "1089-10-40"}) => {'a' => "1089-10-40"},
31
+ # xmlschema date notation
32
+ %({"a": "2009-08-10T19:01:02Z"}) => {'a' => Time.utc(2009, 8, 10, 19, 1, 2)},
33
+ %({"a": "2009-08-10T19:01:02+02:00"}) => {'a' => Time.utc(2009, 8, 10, 17, 1, 2)},
34
+ %({"a": "2009-08-10T19:01:02-05:00"}) => {'a' => Time.utc(2009, 8, 11, 00, 1, 2)},
35
+ # needs to be *exact*
36
+ %({"a": " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "},
37
+ %({"a": "2007-01-01 : it's your birthday"}) => {'a' => "2007-01-01 : it's your birthday"},
38
+ %([]) => [],
39
+ %({}) => {},
40
+ %({"a":1}) => {"a" => 1},
41
+ %({"a": ""}) => {"a" => ""},
42
+ %({"a":"\\""}) => {"a" => "\""},
43
+ %({"a": null}) => {"a" => nil},
44
+ %({"a": true}) => {"a" => true},
45
+ %({"a": false}) => {"a" => false},
46
+ %q({"bad":"\\\\","trailing":""}) => {"bad" => "\\", "trailing" => ""},
47
+ %q({"a": "http:\/\/test.host\/posts\/1"}) => {"a" => "http://test.host/posts/1"},
48
+ %q({"a": "\u003cunicode\u0020escape\u003e"}) => {"a" => "<unicode escape>"},
49
+ %q({"a": "\\\\u0020skip double backslashes"}) => {"a" => "\\u0020skip double backslashes"},
50
+ %q({"a": "\u003cbr /\u003e"}) => {'a' => "<br />"},
51
+ %q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["<i>","<b>","<u>"]},
52
+ # test combination of dates and escaped or unicode encoded data in arrays
53
+ %q([{"d":"1970-01-01", "s":"\u0020escape"},{"d":"1970-01-01", "s":"\u0020escape"}]) =>
54
+ [{'d' => Date.new(1970, 1, 1), 's' => ' escape'},{'d' => Date.new(1970, 1, 1), 's' => ' escape'}],
55
+ %q([{"d":"1970-01-01","s":"http:\/\/example.com"},{"d":"1970-01-01","s":"http:\/\/example.com"}]) =>
56
+ [{'d' => Date.new(1970, 1, 1), 's' => 'http://example.com'},
57
+ {'d' => Date.new(1970, 1, 1), 's' => 'http://example.com'}],
58
+ # tests escaping of "\n" char with Yaml backend
59
+ %q({"a":"\n"}) => {"a"=>"\n"},
60
+ %q({"a":"\u000a"}) => {"a"=>"\n"},
61
+ %q({"a":"Line1\u000aLine2"}) => {"a"=>"Line1\nLine2"},
62
+ # prevent json unmarshalling
63
+ %q({"json_class":"TestJSONDecoding::Foo"}) => {"json_class"=>"TestJSONDecoding::Foo"},
64
+ # json "fragments" - these are invalid JSON, but ActionPack relies on this
65
+ %q("a string") => "a string",
66
+ %q(1.1) => 1.1,
67
+ %q(1) => 1,
68
+ %q(-1) => -1,
69
+ %q(true) => true,
70
+ %q(false) => false,
71
+ %q(null) => nil
72
+ }
73
+
74
+ TESTS.each_with_index do |(json, expected), index|
75
+ test "json decodes #{index}" do
76
+ prev = ActiveSupport.parse_json_times
77
+ ActiveSupport.parse_json_times = true
78
+ silence_warnings do
79
+ if expected.nil?
80
+ assert_nil ActiveSupport::JSON.decode(json), "JSON decoding failed for #{json}"
81
+ else
82
+ assert_equal expected, ActiveSupport::JSON.decode(json), "JSON decoding failed for #{json}"
83
+ end
84
+ end
85
+ ActiveSupport.parse_json_times = prev
86
+ end
87
+ end
88
+
89
+ test "json decodes time json with time parsing disabled" do
90
+ prev = ActiveSupport.parse_json_times
91
+ ActiveSupport.parse_json_times = false
92
+ expected = {"a" => "2007-01-01 01:12:34 Z"}
93
+ assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"}))
94
+ ActiveSupport.parse_json_times = prev
95
+ end
96
+
97
+ def test_failed_json_decoding
98
+ assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%(undefined)) }
99
+ assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({a: 1})) }
100
+ assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({: 1})) }
101
+ assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%()) }
102
+ end
103
+
104
+ def test_cannot_pass_unsupported_options
105
+ assert_raise(ArgumentError) { ActiveSupport::JSON.decode("", create_additions: true) }
106
+ end
107
+ end
108
+
@@ -0,0 +1,531 @@
1
+ # encoding: utf-8
2
+ require 'activesupport4/test_helper'
3
+ require 'securerandom'
4
+ require 'active_support/core_ext/string/inflections'
5
+ require 'active_support/json'
6
+ require 'active_support/time'
7
+
8
+
9
+ class TestJSONEncoding < ActiveSupport::TestCase
10
+ class Foo
11
+ def initialize(a, b)
12
+ @a, @b = a, b
13
+ end
14
+ end
15
+
16
+ class Hashlike
17
+ def to_hash
18
+ { :foo => "hello", :bar => "world" }
19
+ end
20
+ end
21
+
22
+ class Custom
23
+ def initialize(serialized)
24
+ @serialized = serialized
25
+ end
26
+
27
+ def as_json(options = nil)
28
+ @serialized
29
+ end
30
+ end
31
+
32
+ class CustomWithOptions
33
+ attr_accessor :foo, :bar
34
+
35
+ def as_json(options={})
36
+ options[:only] = %w(foo bar)
37
+ super(options)
38
+ end
39
+ end
40
+
41
+ class OptionsTest
42
+ def as_json(options = :default)
43
+ options
44
+ end
45
+ end
46
+
47
+ class HashWithAsJson < Hash
48
+ attr_accessor :as_json_called
49
+
50
+ def initialize(*)
51
+ super
52
+ end
53
+
54
+ def as_json(options={})
55
+ @as_json_called = true
56
+ super
57
+ end
58
+ end
59
+
60
+ TrueTests = [[ true, %(true) ]]
61
+ FalseTests = [[ false, %(false) ]]
62
+ NilTests = [[ nil, %(null) ]]
63
+ NumericTests = [[ 1, %(1) ],
64
+ [ 2.5, %(2.5) ],
65
+ [ 0.0/0.0, %(null) ],
66
+ [ 1.0/0.0, %(null) ],
67
+ [ -1.0/0.0, %(null) ],
68
+ [ BigDecimal('0.0')/BigDecimal('0.0'), %(null) ],
69
+ [ BigDecimal('2.5'), %("#{BigDecimal('2.5').to_s}") ]]
70
+
71
+ StringTests = [[ 'this is the <string>', %("this is the \\u003cstring\\u003e")],
72
+ [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ],
73
+ [ 'http://test.host/posts/1', %("http://test.host/posts/1")],
74
+ [ "Control characters: \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\u2028\u2029",
75
+ %("Control characters: \\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f\\u2028\\u2029") ]]
76
+
77
+ ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ],
78
+ [ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]]
79
+
80
+ RangeTests = [[ 1..2, %("1..2")],
81
+ [ 1...2, %("1...2")],
82
+ [ 1.5..2.5, %("1.5..2.5")]]
83
+
84
+ SymbolTests = [[ :a, %("a") ],
85
+ [ :this, %("this") ],
86
+ [ :"a b", %("a b") ]]
87
+
88
+ ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]]
89
+ HashlikeTests = [[ Hashlike.new, %({\"bar\":\"world\",\"foo\":\"hello\"}) ]]
90
+ CustomTests = [[ Custom.new("custom"), '"custom"' ],
91
+ [ Custom.new(nil), 'null' ],
92
+ [ Custom.new(:a), '"a"' ],
93
+ [ Custom.new([ :foo, "bar" ]), '["foo","bar"]' ],
94
+ [ Custom.new({ :foo => "hello", :bar => "world" }), '{"bar":"world","foo":"hello"}' ],
95
+ [ Custom.new(Hashlike.new), '{"bar":"world","foo":"hello"}' ],
96
+ [ Custom.new(Custom.new(Custom.new(:a))), '"a"' ]]
97
+
98
+ RegexpTests = [[ /^a/, '"(?-mix:^a)"' ], [/^\w{1,2}[a-z]+/ix, '"(?ix-m:^\\\\w{1,2}[a-z]+)"']]
99
+
100
+ DateTests = [[ Date.new(2005,2,1), %("2005/02/01") ]]
101
+ TimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
102
+ DateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
103
+
104
+ StandardDateTests = [[ Date.new(2005,2,1), %("2005-02-01") ]]
105
+ StandardTimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000Z") ]]
106
+ StandardDateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000+00:00") ]]
107
+ StandardStringTests = [[ 'this is the <string>', %("this is the <string>")]]
108
+
109
+ def sorted_json(json)
110
+ return json unless json =~ /^\{.*\}$/
111
+ '{' + json[1..-2].split(',').sort.join(',') + '}'
112
+ end
113
+
114
+ constants.grep(/Tests$/).each do |class_tests|
115
+ define_method("test_#{class_tests[0..-6].underscore}") do
116
+ begin
117
+ prev = ActiveSupport.use_standard_json_time_format
118
+
119
+ ActiveSupport.escape_html_entities_in_json = class_tests !~ /^Standard/
120
+ ActiveSupport.use_standard_json_time_format = class_tests =~ /^Standard/
121
+ self.class.const_get(class_tests).each do |pair|
122
+ assert_equal pair.last, sorted_json(ActiveSupport::JSON.encode(pair.first))
123
+ end
124
+ ensure
125
+ ActiveSupport.escape_html_entities_in_json = false
126
+ ActiveSupport.use_standard_json_time_format = prev
127
+ end
128
+ end
129
+ end
130
+
131
+ def test_process_status
132
+ # There doesn't seem to be a good way to get a handle on a Process::Status object without actually
133
+ # creating a child process, hence this to populate $?
134
+ system("not_a_real_program_#{SecureRandom.hex}")
135
+ assert_equal %({"exitstatus":#{$?.exitstatus},"pid":#{$?.pid}}), ActiveSupport::JSON.encode($?)
136
+ end
137
+
138
+ def test_hash_encoding
139
+ assert_equal %({\"a\":\"b\"}), ActiveSupport::JSON.encode(:a => :b)
140
+ assert_equal %({\"a\":1}), ActiveSupport::JSON.encode('a' => 1)
141
+ assert_equal %({\"a\":[1,2]}), ActiveSupport::JSON.encode('a' => [1,2])
142
+ assert_equal %({"1":2}), ActiveSupport::JSON.encode(1 => 2)
143
+
144
+ assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json(ActiveSupport::JSON.encode(:a => :b, :c => :d))
145
+ end
146
+
147
+ def test_hash_keys_encoding
148
+ ActiveSupport.escape_html_entities_in_json = true
149
+ assert_equal "{\"\\u003c\\u003e\":\"\\u003c\\u003e\"}", ActiveSupport::JSON.encode("<>" => "<>")
150
+ ensure
151
+ ActiveSupport.escape_html_entities_in_json = false
152
+ end
153
+
154
+ def test_utf8_string_encoded_properly
155
+ result = ActiveSupport::JSON.encode('€2.99')
156
+ assert_equal '"€2.99"', result
157
+ assert_equal(Encoding::UTF_8, result.encoding)
158
+
159
+ result = ActiveSupport::JSON.encode('✎☺')
160
+ assert_equal '"✎☺"', result
161
+ assert_equal(Encoding::UTF_8, result.encoding)
162
+ end
163
+
164
+ def test_non_utf8_string_transcodes
165
+ s = '二'.encode('Shift_JIS')
166
+ result = ActiveSupport::JSON.encode(s)
167
+ assert_equal '"二"', result
168
+ assert_equal Encoding::UTF_8, result.encoding
169
+ end
170
+
171
+ def test_wide_utf8_chars
172
+ w = '𠜎'
173
+ result = ActiveSupport::JSON.encode(w)
174
+ assert_equal '"𠜎"', result
175
+ end
176
+
177
+ def test_wide_utf8_roundtrip
178
+ hash = { string: "𐒑" }
179
+ json = ActiveSupport::JSON.encode(hash)
180
+ decoded_hash = ActiveSupport::JSON.decode(json)
181
+ assert_equal "𐒑", decoded_hash['string']
182
+ end
183
+
184
+ def test_reading_encode_big_decimal_as_string_option
185
+ assert_deprecated do
186
+ assert ActiveSupport.encode_big_decimal_as_string
187
+ end
188
+ end
189
+
190
+ def test_setting_deprecated_encode_big_decimal_as_string_option
191
+ assert_raise(NotImplementedError) do
192
+ ActiveSupport.encode_big_decimal_as_string = true
193
+ end
194
+
195
+ assert_raise(NotImplementedError) do
196
+ ActiveSupport.encode_big_decimal_as_string = false
197
+ end
198
+ end
199
+
200
+ def test_exception_raised_when_encoding_circular_reference_in_array
201
+ a = [1]
202
+ a << a
203
+ assert_deprecated do
204
+ assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) }
205
+ end
206
+ end
207
+
208
+ def test_exception_raised_when_encoding_circular_reference_in_hash
209
+ a = { :name => 'foo' }
210
+ a[:next] = a
211
+ assert_deprecated do
212
+ assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) }
213
+ end
214
+ end
215
+
216
+ def test_exception_raised_when_encoding_circular_reference_in_hash_inside_array
217
+ a = { :name => 'foo', :sub => [] }
218
+ a[:sub] << a
219
+ assert_deprecated do
220
+ assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) }
221
+ end
222
+ end
223
+
224
+ def test_hash_key_identifiers_are_always_quoted
225
+ values = {0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B"}
226
+ assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
227
+ end
228
+
229
+ def test_hash_should_allow_key_filtering_with_only
230
+ assert_equal %({"a":1}), ActiveSupport::JSON.encode({'a' => 1, :b => 2, :c => 3}, :only => 'a')
231
+ end
232
+
233
+ def test_hash_should_allow_key_filtering_with_except
234
+ assert_equal %({"b":2}), ActiveSupport::JSON.encode({'foo' => 'bar', :b => 2, :c => 3}, :except => ['foo', :c])
235
+ end
236
+
237
+ def test_time_to_json_includes_local_offset
238
+ with_standard_json_time_format(true) do
239
+ with_env_tz 'US/Eastern' do
240
+ assert_equal %("2005-02-01T15:15:10.000-05:00"), ActiveSupport::JSON.encode(Time.local(2005,2,1,15,15,10))
241
+ end
242
+ end
243
+ end
244
+
245
+ def test_hash_with_time_to_json
246
+ with_standard_json_time_format(false) do
247
+ assert_equal '{"time":"2009/01/01 00:00:00 +0000"}', { :time => Time.utc(2009) }.to_json
248
+ end
249
+ end
250
+
251
+ def test_nested_hash_with_float
252
+ assert_nothing_raised do
253
+ hash = {
254
+ "CHI" => {
255
+ :display_name => "chicago",
256
+ :latitude => 123.234
257
+ }
258
+ }
259
+ ActiveSupport::JSON.encode(hash)
260
+ end
261
+ end
262
+
263
+ def test_hash_like_with_options
264
+ h = Hashlike.new
265
+ json = h.to_json :only => [:foo]
266
+
267
+ assert_equal({"foo"=>"hello"}, JSON.parse(json))
268
+ end
269
+
270
+ def test_object_to_json_with_options
271
+ obj = Object.new
272
+ obj.instance_variable_set :@foo, "hello"
273
+ obj.instance_variable_set :@bar, "world"
274
+ json = obj.to_json :only => ["foo"]
275
+
276
+ assert_equal({"foo"=>"hello"}, JSON.parse(json))
277
+ end
278
+
279
+ def test_struct_to_json_with_options
280
+ struct = Struct.new(:foo, :bar).new
281
+ struct.foo = "hello"
282
+ struct.bar = "world"
283
+ json = struct.to_json :only => [:foo]
284
+
285
+ assert_equal({"foo"=>"hello"}, JSON.parse(json))
286
+ end
287
+
288
+ def test_hash_should_pass_encoding_options_to_children_in_as_json
289
+ person = {
290
+ :name => 'John',
291
+ :address => {
292
+ :city => 'London',
293
+ :country => 'UK'
294
+ }
295
+ }
296
+ json = person.as_json :only => [:address, :city]
297
+
298
+ assert_equal({ 'address' => { 'city' => 'London' }}, json)
299
+ end
300
+
301
+ def test_hash_should_pass_encoding_options_to_children_in_to_json
302
+ person = {
303
+ :name => 'John',
304
+ :address => {
305
+ :city => 'London',
306
+ :country => 'UK'
307
+ }
308
+ }
309
+ json = person.to_json :only => [:address, :city]
310
+
311
+ assert_equal(%({"address":{"city":"London"}}), json)
312
+ end
313
+
314
+ def test_array_should_pass_encoding_options_to_children_in_as_json
315
+ people = [
316
+ { :name => 'John', :address => { :city => 'London', :country => 'UK' }},
317
+ { :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }}
318
+ ]
319
+ json = people.as_json :only => [:address, :city]
320
+ expected = [
321
+ { 'address' => { 'city' => 'London' }},
322
+ { 'address' => { 'city' => 'Paris' }}
323
+ ]
324
+
325
+ assert_equal(expected, json)
326
+ end
327
+
328
+ def test_array_should_pass_encoding_options_to_children_in_to_json
329
+ people = [
330
+ { :name => 'John', :address => { :city => 'London', :country => 'UK' }},
331
+ { :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }}
332
+ ]
333
+ json = people.to_json :only => [:address, :city]
334
+
335
+ assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
336
+ end
337
+
338
+ def test_enumerable_should_pass_encoding_options_to_children_in_as_json
339
+ people = [
340
+ { :name => 'John', :address => { :city => 'London', :country => 'UK' }},
341
+ { :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }}
342
+ ]
343
+ json = people.each.as_json :only => [:address, :city]
344
+ expected = [
345
+ { 'address' => { 'city' => 'London' }},
346
+ { 'address' => { 'city' => 'Paris' }}
347
+ ]
348
+
349
+ assert_equal(expected, json)
350
+ end
351
+
352
+ def test_enumerable_should_pass_encoding_options_to_children_in_to_json
353
+ people = [
354
+ { :name => 'John', :address => { :city => 'London', :country => 'UK' }},
355
+ { :name => 'Jean', :address => { :city => 'Paris' , :country => 'France' }}
356
+ ]
357
+ json = people.each.to_json :only => [:address, :city]
358
+
359
+ assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
360
+ end
361
+
362
+ def test_hash_to_json_should_not_keep_options_around
363
+ f = CustomWithOptions.new
364
+ f.foo = "hello"
365
+ f.bar = "world"
366
+
367
+ hash = {"foo" => f, "other_hash" => {"foo" => "other_foo", "test" => "other_test"}}
368
+ assert_equal({"foo"=>{"foo"=>"hello","bar"=>"world"},
369
+ "other_hash" => {"foo"=>"other_foo","test"=>"other_test"}}, ActiveSupport::JSON.decode(hash.to_json))
370
+ end
371
+
372
+ def test_array_to_json_should_not_keep_options_around
373
+ f = CustomWithOptions.new
374
+ f.foo = "hello"
375
+ f.bar = "world"
376
+
377
+ array = [f, {"foo" => "other_foo", "test" => "other_test"}]
378
+ assert_equal([{"foo"=>"hello","bar"=>"world"},
379
+ {"foo"=>"other_foo","test"=>"other_test"}], ActiveSupport::JSON.decode(array.to_json))
380
+ end
381
+
382
+ def test_hash_as_json_without_options
383
+ json = { foo: OptionsTest.new }.as_json
384
+ assert_equal({"foo" => :default}, json)
385
+ end
386
+
387
+ def test_array_as_json_without_options
388
+ json = [ OptionsTest.new ].as_json
389
+ assert_equal([:default], json)
390
+ end
391
+
392
+ def test_struct_encoding
393
+ Struct.new('UserNameAndEmail', :name, :email)
394
+ Struct.new('UserNameAndDate', :name, :date)
395
+ Struct.new('Custom', :name, :sub)
396
+ user_email = Struct::UserNameAndEmail.new 'David', 'sample@example.com'
397
+ user_birthday = Struct::UserNameAndDate.new 'David', Date.new(2010, 01, 01)
398
+ custom = Struct::Custom.new 'David', user_birthday
399
+
400
+
401
+ json_strings = ""
402
+ json_string_and_date = ""
403
+ json_custom = ""
404
+
405
+ assert_nothing_raised do
406
+ json_strings = user_email.to_json
407
+ json_string_and_date = user_birthday.to_json
408
+ json_custom = custom.to_json
409
+ end
410
+
411
+ assert_equal({"name" => "David",
412
+ "sub" => {
413
+ "name" => "David",
414
+ "date" => "2010-01-01" }}, ActiveSupport::JSON.decode(json_custom))
415
+
416
+ assert_equal({"name" => "David", "email" => "sample@example.com"},
417
+ ActiveSupport::JSON.decode(json_strings))
418
+
419
+ assert_equal({"name" => "David", "date" => "2010-01-01"},
420
+ ActiveSupport::JSON.decode(json_string_and_date))
421
+ end
422
+
423
+ def test_nil_true_and_false_represented_as_themselves
424
+ assert_nil(nil.as_json)
425
+ assert_equal true, true.as_json
426
+ assert_equal false, false.as_json
427
+ end
428
+
429
+ def test_json_gem_dump_by_passing_active_support_encoder
430
+ h = HashWithAsJson.new
431
+ h[:foo] = "hello"
432
+ h[:bar] = "world"
433
+
434
+ assert_equal %({"foo":"hello","bar":"world"}), JSON.dump(h)
435
+ assert_nil h.as_json_called
436
+ end
437
+
438
+ def test_json_gem_generate_by_passing_active_support_encoder
439
+ h = HashWithAsJson.new
440
+ h[:foo] = "hello"
441
+ h[:bar] = "world"
442
+
443
+ assert_equal %({"foo":"hello","bar":"world"}), JSON.generate(h)
444
+ assert_nil h.as_json_called
445
+ end
446
+
447
+ def test_json_gem_pretty_generate_by_passing_active_support_encoder
448
+ h = HashWithAsJson.new
449
+ h[:foo] = "hello"
450
+ h[:bar] = "world"
451
+
452
+ assert_equal <<EXPECTED.chomp, JSON.pretty_generate(h)
453
+ {
454
+ "foo": "hello",
455
+ "bar": "world"
456
+ }
457
+ EXPECTED
458
+ assert_nil h.as_json_called
459
+ end
460
+
461
+ def test_twz_to_json_with_use_standard_json_time_format_config_set_to_false
462
+ with_standard_json_time_format(false) do
463
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
464
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
465
+ assert_equal "\"1999/12/31 19:00:00 -0500\"", ActiveSupport::JSON.encode(time)
466
+ end
467
+ end
468
+
469
+ def test_twz_to_json_with_use_standard_json_time_format_config_set_to_true
470
+ with_standard_json_time_format(true) do
471
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
472
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
473
+ assert_equal "\"1999-12-31T19:00:00.000-05:00\"", ActiveSupport::JSON.encode(time)
474
+ end
475
+ end
476
+
477
+ def test_twz_to_json_with_custom_time_precision
478
+ with_standard_json_time_format(true) do
479
+ ActiveSupport::JSON::Encoding.time_precision = 0
480
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
481
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
482
+ assert_equal "\"1999-12-31T19:00:00-05:00\"", ActiveSupport::JSON.encode(time)
483
+ end
484
+ ensure
485
+ ActiveSupport::JSON::Encoding.time_precision = 3
486
+ end
487
+
488
+ def test_time_to_json_with_custom_time_precision
489
+ with_standard_json_time_format(true) do
490
+ ActiveSupport::JSON::Encoding.time_precision = 0
491
+ assert_equal "\"2000-01-01T00:00:00Z\"", ActiveSupport::JSON.encode(Time.utc(2000))
492
+ end
493
+ ensure
494
+ ActiveSupport::JSON::Encoding.time_precision = 3
495
+ end
496
+
497
+ def test_datetime_to_json_with_custom_time_precision
498
+ with_standard_json_time_format(true) do
499
+ ActiveSupport::JSON::Encoding.time_precision = 0
500
+ assert_equal "\"2000-01-01T00:00:00+00:00\"", ActiveSupport::JSON.encode(DateTime.new(2000))
501
+ end
502
+ ensure
503
+ ActiveSupport::JSON::Encoding.time_precision = 3
504
+ end
505
+
506
+ def test_twz_to_json_when_wrapping_a_date_time
507
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
508
+ time = ActiveSupport::TimeWithZone.new(DateTime.new(2000), zone)
509
+ assert_equal '"1999-12-31T19:00:00.000-05:00"', ActiveSupport::JSON.encode(time)
510
+ end
511
+
512
+ protected
513
+
514
+ def object_keys(json_object)
515
+ json_object[1..-2].scan(/([^{}:,\s]+):/).flatten.sort
516
+ end
517
+
518
+ def with_env_tz(new_tz = 'US/Eastern')
519
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
520
+ yield
521
+ ensure
522
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
523
+ end
524
+
525
+ def with_standard_json_time_format(boolean = true)
526
+ old, ActiveSupport.use_standard_json_time_format = ActiveSupport.use_standard_json_time_format, boolean
527
+ yield
528
+ ensure
529
+ ActiveSupport.use_standard_json_time_format = old
530
+ end
531
+ end