oj 3.7.4 → 3.13.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1352 -0
  3. data/README.md +29 -8
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +53 -72
  6. data/ext/oj/cache.c +326 -0
  7. data/ext/oj/cache.h +21 -0
  8. data/ext/oj/cache8.c +61 -64
  9. data/ext/oj/cache8.h +12 -39
  10. data/ext/oj/circarray.c +37 -43
  11. data/ext/oj/circarray.h +16 -17
  12. data/ext/oj/code.c +165 -179
  13. data/ext/oj/code.h +27 -29
  14. data/ext/oj/compat.c +174 -194
  15. data/ext/oj/custom.c +809 -866
  16. data/ext/oj/debug.c +132 -0
  17. data/ext/oj/dump.c +848 -863
  18. data/ext/oj/dump.h +81 -67
  19. data/ext/oj/dump_compat.c +85 -123
  20. data/ext/oj/dump_leaf.c +100 -188
  21. data/ext/oj/dump_object.c +527 -656
  22. data/ext/oj/dump_strict.c +315 -338
  23. data/ext/oj/encode.h +7 -34
  24. data/ext/oj/encoder.c +43 -0
  25. data/ext/oj/err.c +40 -29
  26. data/ext/oj/err.h +48 -48
  27. data/ext/oj/extconf.rb +17 -4
  28. data/ext/oj/fast.c +1070 -1087
  29. data/ext/oj/intern.c +301 -0
  30. data/ext/oj/intern.h +26 -0
  31. data/ext/oj/mimic_json.c +469 -436
  32. data/ext/oj/object.c +525 -593
  33. data/ext/oj/odd.c +154 -138
  34. data/ext/oj/odd.h +37 -38
  35. data/ext/oj/oj.c +1325 -986
  36. data/ext/oj/oj.h +333 -316
  37. data/ext/oj/parse.c +1002 -846
  38. data/ext/oj/parse.h +92 -87
  39. data/ext/oj/parser.c +1557 -0
  40. data/ext/oj/parser.h +91 -0
  41. data/ext/oj/rails.c +888 -878
  42. data/ext/oj/rails.h +11 -14
  43. data/ext/oj/reader.c +141 -147
  44. data/ext/oj/reader.h +73 -89
  45. data/ext/oj/resolve.c +41 -62
  46. data/ext/oj/resolve.h +7 -9
  47. data/ext/oj/rxclass.c +71 -75
  48. data/ext/oj/rxclass.h +18 -19
  49. data/ext/oj/saj.c +443 -486
  50. data/ext/oj/saj2.c +602 -0
  51. data/ext/oj/scp.c +88 -113
  52. data/ext/oj/sparse.c +787 -709
  53. data/ext/oj/stream_writer.c +133 -159
  54. data/ext/oj/strict.c +127 -118
  55. data/ext/oj/string_writer.c +230 -249
  56. data/ext/oj/trace.c +34 -41
  57. data/ext/oj/trace.h +19 -19
  58. data/ext/oj/usual.c +1254 -0
  59. data/ext/oj/util.c +136 -0
  60. data/ext/oj/util.h +20 -0
  61. data/ext/oj/val_stack.c +59 -67
  62. data/ext/oj/val_stack.h +91 -129
  63. data/ext/oj/validate.c +46 -0
  64. data/ext/oj/wab.c +342 -353
  65. data/lib/oj/bag.rb +1 -0
  66. data/lib/oj/easy_hash.rb +5 -4
  67. data/lib/oj/error.rb +1 -1
  68. data/lib/oj/json.rb +1 -1
  69. data/lib/oj/mimic.rb +48 -14
  70. data/lib/oj/saj.rb +20 -6
  71. data/lib/oj/state.rb +8 -7
  72. data/lib/oj/version.rb +2 -2
  73. data/lib/oj.rb +0 -8
  74. data/pages/Compatibility.md +1 -1
  75. data/pages/JsonGem.md +15 -0
  76. data/pages/Modes.md +53 -46
  77. data/pages/Options.md +72 -11
  78. data/pages/Parser.md +309 -0
  79. data/pages/Rails.md +73 -22
  80. data/pages/Security.md +1 -1
  81. data/test/activerecord/result_test.rb +7 -2
  82. data/test/activesupport5/abstract_unit.rb +45 -0
  83. data/test/activesupport5/decoding_test.rb +68 -60
  84. data/test/activesupport5/encoding_test.rb +111 -96
  85. data/test/activesupport5/encoding_test_cases.rb +33 -25
  86. data/test/activesupport5/test_helper.rb +43 -21
  87. data/test/activesupport5/time_zone_test_helpers.rb +18 -3
  88. data/test/activesupport6/abstract_unit.rb +44 -0
  89. data/test/activesupport6/decoding_test.rb +133 -0
  90. data/test/activesupport6/encoding_test.rb +507 -0
  91. data/test/activesupport6/encoding_test_cases.rb +98 -0
  92. data/test/activesupport6/test_common.rb +17 -0
  93. data/test/activesupport6/test_helper.rb +163 -0
  94. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  95. data/test/activesupport7/abstract_unit.rb +49 -0
  96. data/test/activesupport7/decoding_test.rb +125 -0
  97. data/test/activesupport7/encoding_test.rb +486 -0
  98. data/test/activesupport7/encoding_test_cases.rb +104 -0
  99. data/test/activesupport7/time_zone_test_helpers.rb +47 -0
  100. data/test/bar.rb +6 -12
  101. data/test/baz.rb +16 -0
  102. data/test/bug.rb +16 -0
  103. data/test/foo.rb +69 -75
  104. data/test/helper.rb +16 -0
  105. data/test/json_gem/json_common_interface_test.rb +8 -3
  106. data/test/json_gem/json_generator_test.rb +18 -4
  107. data/test/json_gem/json_parser_test.rb +9 -0
  108. data/test/json_gem/test_helper.rb +12 -0
  109. data/test/mem.rb +33 -0
  110. data/test/perf.rb +1 -1
  111. data/test/perf_dump.rb +50 -0
  112. data/test/perf_once.rb +58 -0
  113. data/test/perf_parser.rb +189 -0
  114. data/test/perf_scp.rb +11 -10
  115. data/test/perf_strict.rb +17 -23
  116. data/test/prec.rb +23 -0
  117. data/test/sample_json.rb +1 -1
  118. data/test/test_compat.rb +46 -10
  119. data/test/test_custom.rb +147 -8
  120. data/test/test_fast.rb +62 -2
  121. data/test/test_file.rb +25 -2
  122. data/test/test_gc.rb +13 -0
  123. data/test/test_generate.rb +21 -0
  124. data/test/test_hash.rb +11 -1
  125. data/test/test_integer_range.rb +7 -2
  126. data/test/test_object.rb +85 -9
  127. data/test/test_parser.rb +27 -0
  128. data/test/test_parser_saj.rb +335 -0
  129. data/test/test_parser_usual.rb +217 -0
  130. data/test/test_rails.rb +35 -0
  131. data/test/test_saj.rb +1 -1
  132. data/test/test_scp.rb +5 -5
  133. data/test/test_strict.rb +26 -1
  134. data/test/test_various.rb +87 -65
  135. data/test/test_wab.rb +2 -0
  136. data/test/test_writer.rb +19 -2
  137. data/test/tests.rb +1 -1
  138. data/test/zoo.rb +13 -0
  139. metadata +60 -110
  140. data/ext/oj/hash.c +0 -163
  141. data/ext/oj/hash.h +0 -46
  142. data/ext/oj/hash_test.c +0 -512
data/test/perf_scp.rb CHANGED
@@ -14,16 +14,16 @@ require 'oj'
14
14
 
15
15
  $verbose = false
16
16
  $indent = 0
17
- $iter = 50000
17
+ $iter = 50_000
18
18
  $with_bignum = false
19
- $size = 0
19
+ $size = 1
20
20
 
21
21
  opts = OptionParser.new
22
22
  opts.on("-v", "verbose") { $verbose = true }
23
23
  opts.on("-c", "--count [Int]", Integer, "iterations") { |i| $iter = i }
24
24
  opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
25
- opts.on("-s", "--size [Int]", Integer, "size (~Kbytes)") { |i| $size = i }
26
- opts.on("-b", "with bignum") { $with_bignum = true }
25
+ opts.on("-s", "--size [Int]", Integer, "size (~Kbytes)") { |i| $size = i }
26
+ opts.on("-b", "with bignum") { $with_bignum = true }
27
27
  opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
28
28
  files = opts.parse(ARGV)
29
29
 
@@ -47,7 +47,7 @@ if 0 < $size
47
47
  end
48
48
  end
49
49
 
50
- Oj.default_options = { :indent => $indent, :mode => :compat }
50
+ Oj.default_options = { :indent => $indent, :mode => :strict, cache_keys: true, cache_str: 5 }
51
51
 
52
52
  $json = Oj.dump($obj)
53
53
  $failed = {} # key is same as String used in tests later
@@ -105,7 +105,7 @@ class AllHandler < Oj::ScHandler
105
105
 
106
106
  def hash_set(h, key, value)
107
107
  end
108
-
108
+
109
109
  def array_append(a, value)
110
110
  end
111
111
 
@@ -137,10 +137,11 @@ end
137
137
  puts '-' * 80
138
138
  puts "Parse Performance"
139
139
  perf = Perf.new()
140
- perf.add('Oj::Saj', 'all') { Oj.saj_parse(saj_handler, $json) }
141
- perf.add('Oj::Saj', 'none') { Oj.saj_parse(no_saj, $json) }
142
- perf.add('Oj::Scp', 'all') { Oj.sc_parse(sc_handler, $json) }
143
- perf.add('Oj::Scp', 'none') { Oj.sc_parse(no_handler, $json) }
140
+ perf.add('Oj::Saj.all', 'all') { Oj.saj_parse(saj_handler, $json) }
141
+ perf.add('Oj::Saj.none', 'none') { Oj.saj_parse(no_saj, $json) }
142
+ perf.add('Oj::Scp.all', 'all') { Oj.sc_parse(sc_handler, $json) }
143
+ perf.add('Oj::Scp.none', 'none') { Oj.sc_parse(no_handler, $json) }
144
+ perf.add('Oj::load', 'none') { Oj.wab_load($json) }
144
145
  perf.add('Yajl', 'parse') { Yajl::Parser.parse($json) } unless $failed.has_key?('Yajl')
145
146
  perf.add('JSON::Ext', 'parse') { JSON::Ext::Parser.new($json).parse } unless $failed.has_key?('JSON::Ext')
146
147
  perf.run($iter)
data/test/perf_strict.rb CHANGED
@@ -15,6 +15,8 @@ $iter = 20000
15
15
  $with_bignum = false
16
16
  $with_nums = true
17
17
  $size = 0
18
+ $symbolize = false
19
+ $cache_keys = true
18
20
 
19
21
  opts = OptionParser.new
20
22
  opts.on("-v", "verbose") { $verbose = true }
@@ -23,6 +25,8 @@ opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
23
25
  opts.on("-s", "--size [Int]", Integer, "size (~Kbytes)") { |i| $size = i }
24
26
  opts.on("-b", "with bignum") { $with_bignum = true }
25
27
  opts.on("-n", "without numbers") { $with_nums = false }
28
+ opts.on("-z", "--symbolize", "symbolize keys") { $symbolize = true }
29
+ opts.on("-k", "--no-cache", "turn off key caching") { $cache_keys = false }
26
30
  opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
27
31
  files = opts.parse(ARGV)
28
32
 
@@ -51,7 +55,7 @@ else
51
55
  }
52
56
  end
53
57
 
54
- Oj.default_options = { :indent => $indent, :mode => :strict }
58
+ Oj.default_options = { :indent => $indent, :mode => :strict, cache_keys: $cache_keys, cache_str: 5 }
55
59
 
56
60
  if 0 < $size
57
61
  o = $obj
@@ -62,9 +66,6 @@ if 0 < $size
62
66
  end
63
67
 
64
68
  $json = Oj.dump($obj)
65
- $obj_json = Oj.dump($obj, :mode => :object)
66
- #puts "*** size: #{$obj_json.size}"
67
- #puts "*** #{$obj_json}"
68
69
  $failed = {} # key is same as String used in tests later
69
70
 
70
71
  def capture_error(tag, orig, load_key, dump_key, &blk)
@@ -77,8 +78,13 @@ def capture_error(tag, orig, load_key, dump_key, &blk)
77
78
  end
78
79
 
79
80
  # Verify that all packages dump and load correctly and return the same Object as the original.
80
- capture_error('Oj:strict', $obj, 'load', 'dump') { |o| Oj.strict_load(Oj.dump(o, :mode => :strict)) }
81
- capture_error('Yajl', $obj, 'encode', 'parse') { |o| require 'yajl'; Yajl::Parser.parse(Yajl::Encoder.encode(o)) }
81
+ capture_error('Oj:strict', $obj, 'load', 'dump') { |o|
82
+ Oj.strict_load(Oj.dump(o))
83
+ }
84
+ capture_error('Yajl', $obj, 'encode', 'parse') { |o|
85
+ require 'yajl'
86
+ Yajl::Parser.parse(Yajl::Encoder.encode(o))
87
+ }
82
88
  capture_error('JSON::Ext', $obj, 'generate', 'parse') { |o|
83
89
  require 'json'
84
90
  require 'json/ext'
@@ -86,16 +92,11 @@ capture_error('JSON::Ext', $obj, 'generate', 'parse') { |o|
86
92
  JSON.parser = JSON::Ext::Parser
87
93
  JSON.parse(JSON.generate(o))
88
94
  }
89
- capture_error('JSON::Pure', $obj, 'generate', 'parse') { |o|
90
- require 'json/pure'
91
- JSON.generator = JSON::Pure::Generator
92
- JSON.parser = JSON::Pure::Parser
93
- JSON.parse(JSON.generate(o))
94
- }
95
+
96
+ Oj.default_options = { symbol_keys: $symbolize }
95
97
 
96
98
  if $verbose
97
99
  puts "json:\n#{$json}\n"
98
- puts "object json:\n#{$obj_json}\n"
99
100
  puts "Oj loaded object:\n#{Oj.strict_load($json)}\n"
100
101
  puts "Yajl loaded object:\n#{Yajl::Parser.parse($json)}\n"
101
102
  puts "JSON loaded object:\n#{JSON::Ext::Parser.new($json).parse}\n"
@@ -105,15 +106,12 @@ puts '-' * 80
105
106
  puts "Strict Parse Performance"
106
107
  perf = Perf.new()
107
108
  unless $failed.has_key?('JSON::Ext')
108
- perf.add('JSON::Ext', 'parse') { JSON.parse($json) }
109
+ perf.add('JSON::Ext', 'parse') { JSON.parse($json, symbolize_names: $symbolize) }
109
110
  perf.before('JSON::Ext') { JSON.parser = JSON::Ext::Parser }
110
111
  end
111
- unless $failed.has_key?('JSON::Pure')
112
- perf.add('JSON::Pure', 'parse') { JSON.parse($json) }
113
- perf.before('JSON::Pure') { JSON.parser = JSON::Pure::Parser }
114
- end
115
112
  unless $failed.has_key?('Oj:strict')
116
113
  perf.add('Oj:strict', 'strict_load') { Oj.strict_load($json) }
114
+ perf.add('Oj:wab', 'wab_load') { Oj.wab_load($json) }
117
115
  end
118
116
  perf.add('Yajl', 'parse') { Yajl::Parser.parse($json) } unless $failed.has_key?('Yajl')
119
117
  perf.run($iter)
@@ -125,12 +123,8 @@ unless $failed.has_key?('JSON::Ext')
125
123
  perf.add('JSON::Ext', 'dump') { JSON.generate($obj) }
126
124
  perf.before('JSON::Ext') { JSON.generator = JSON::Ext::Generator }
127
125
  end
128
- unless $failed.has_key?('JSON::Pure')
129
- perf.add('JSON::Pure', 'generate') { JSON.generate($obj) }
130
- perf.before('JSON::Pure') { JSON.generator = JSON::Pure::Generator }
131
- end
132
126
  unless $failed.has_key?('Oj:strict')
133
- perf.add('Oj:strict', 'dump') { Oj.dump($obj, :mode => :strict) }
127
+ perf.add('Oj:strict', 'dump') { Oj.dump($obj) }
134
128
  end
135
129
  perf.add('Yajl', 'encode') { Yajl::Encoder.encode($obj) } unless $failed.has_key?('Yajl')
136
130
  perf.run($iter)
data/test/prec.rb ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'oj'
4
+
5
+ extras = {"locationLng" => -97.14690769100295}
6
+
7
+ Oj.default_options = {float_precision: 17}
8
+
9
+ encoded = Oj.dump(extras)
10
+ puts encoded
11
+ puts Oj.load(encoded)
12
+
13
+ require "active_record"
14
+
15
+ Oj::Rails.set_encoder()
16
+ Oj::Rails.set_decoder()
17
+
18
+ Oj.default_options = {float_precision: 17}
19
+ # Using Oj rails encoder, gets the correct value: {"locationLng":-97.14690769100295}
20
+ encoded = ActiveSupport::JSON.encode(extras)
21
+ puts encoded
22
+ puts ActiveSupport::JSON.decode(encoded)
23
+ puts Oj.load(encoded)
data/test/sample_json.rb CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby -wW2
1
+ #!/usr/bin/env ruby
2
2
 
3
3
  if $0 == __FILE__
4
4
  $: << '.'
data/test/test_compat.rb CHANGED
@@ -147,16 +147,9 @@ class CompatJuice < Minitest::Test
147
147
 
148
148
  def test_encode
149
149
  opts = Oj.default_options
150
- Oj.default_options = { :ascii_only => false }
151
- unless 'jruby' == $ruby
152
- dump_and_load("ぴーたー", false)
153
- end
154
150
  Oj.default_options = { :ascii_only => true }
155
151
  json = Oj.dump("ぴーたー")
156
152
  assert_equal(%{"\\u3074\\u30fc\\u305f\\u30fc"}, json)
157
- unless 'jruby' == $ruby
158
- dump_and_load("ぴーたー", false)
159
- end
160
153
  Oj.default_options = opts
161
154
  end
162
155
 
@@ -185,7 +178,7 @@ class CompatJuice < Minitest::Test
185
178
  assert_equal('"abc"', json)
186
179
  end
187
180
 
188
- def test_time
181
+ def test_time_xml_schema
189
182
  t = Time.xmlschema("2012-01-05T23:58:07.123456000+09:00")
190
183
  #t = Time.local(2012, 1, 5, 23, 58, 7, 123456)
191
184
  json = Oj.dump(t, :mode => :compat)
@@ -243,6 +236,17 @@ class CompatJuice < Minitest::Test
243
236
  assert_equal({"a\nb" => true, "c\td" => false}, obj)
244
237
  end
245
238
 
239
+ def test_invalid_escapes_handled
240
+ json = '{"subtext":"\"404er\” \w \k \3 \a"}'
241
+ obj = Oj.compat_load(json)
242
+ assert_equal({"subtext" => "\"404er” w k 3 a"}, obj)
243
+ end
244
+
245
+ def test_hash_escaping
246
+ json = Oj.to_json({'<>' => '<>'}, mode: :compat)
247
+ assert_equal(json, '{"<>":"<>"}')
248
+ end
249
+
246
250
  def test_bignum_object
247
251
  dump_and_load(7 ** 55, false)
248
252
  end
@@ -273,12 +277,19 @@ class CompatJuice < Minitest::Test
273
277
  # BigDecimal
274
278
  def test_bigdecimal
275
279
  # BigDecimals are dumped as strings and can not be restored to the
276
- # original value.
280
+ # original value without using an undocumented feature of the JSON gem.
277
281
  json = Oj.dump(BigDecimal('3.14159265358979323846'))
278
282
  # 2.4.0 changes the exponent to lowercase
279
283
  assert_equal('"0.314159265358979323846e1"', json.downcase)
280
284
  end
281
285
 
286
+ def test_decimal_class
287
+ big = BigDecimal('3.14159265358979323846')
288
+ # :decimal_class is the undocumented feature.
289
+ json = Oj.load('3.14159265358979323846', mode: :compat, decimal_class: BigDecimal)
290
+ assert_equal(big, json)
291
+ end
292
+
282
293
  def test_infinity
283
294
  assert_raises(Oj::ParseError) { Oj.load('Infinity', :mode => :strict) }
284
295
  x = Oj.load('Infinity', :mode => :compat)
@@ -286,7 +297,7 @@ class CompatJuice < Minitest::Test
286
297
  end
287
298
 
288
299
  # Time
289
- def test_time
300
+ def test_time_from_time_object
290
301
  t = Time.new(2015, 1, 5, 21, 37, 7.123456, -8 * 3600)
291
302
  expect = '"' + t.to_s + '"'
292
303
  json = Oj.dump(t)
@@ -477,6 +488,31 @@ class CompatJuice < Minitest::Test
477
488
  assert_equal([1,2], Oj.load(s, :mode => :compat))
478
489
  end
479
490
 
491
+ def test_parse_large_string
492
+ error = assert_raises() { Oj.load(%|{"a":"aaaaaaaaaa\0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}|) }
493
+ assert(error.message.include?('NULL byte in string'))
494
+
495
+ error = assert_raises() { Oj.load(%|{"a":"aaaaaaaaaaaaaaaaaaaa }|) }
496
+ assert(error.message.include?('quoted string not terminated'))
497
+
498
+ json =<<~JSON
499
+ {
500
+ "a": "\\u3074\\u30fc\\u305f\\u30fc",
501
+ "b": "aaaaaaaaaaaaaaaaaaaaaaaaaaaa"
502
+ }
503
+ JSON
504
+ assert_equal("ぴーたー", Oj.load(json)['a'])
505
+ end
506
+
507
+ def test_parse_large_escaped_string
508
+ invalid_json = %|{"a":\"aaaa\\nbbbb\\rcccc\\tddd\\feee\\bf\/\\\\\\u3074\\u30fc\\u305f\\u30fc }|
509
+ error = assert_raises() { Oj.load(invalid_json) }
510
+ assert(error.message.include?('quoted string not terminated'))
511
+
512
+ json = "\"aaaa\\nbbbb\\rcccc\\tddd\\feee\\bf\/\\\\\\u3074\\u30fc\\u305f\\u30fc \""
513
+ assert_equal("aaaa\nbbbb\rcccc\tddd\feee\bf/\\ぴーたー ", Oj.load(json))
514
+ end
515
+
480
516
  def dump_and_load(obj, trace=false)
481
517
  json = Oj.dump(obj)
482
518
  puts json if trace
data/test/test_custom.rb CHANGED
@@ -20,11 +20,12 @@ class CustomJuice < Minitest::Test
20
20
  end
21
21
 
22
22
  class Jeez
23
- attr_accessor :x, :y
23
+ attr_accessor :x, :y, :_z
24
24
 
25
25
  def initialize(x, y)
26
26
  @x = x
27
27
  @y = y
28
+ @_z = x.to_s
28
29
  end
29
30
  def ==(o)
30
31
  self.class == o.class && @x == o.x && @y = o.y
@@ -32,6 +33,9 @@ class CustomJuice < Minitest::Test
32
33
  def to_json(*args)
33
34
  %|{"xx":#{@x},"yy":#{y}}|
34
35
  end
36
+ def raw_json(depth, indent)
37
+ %|{"xxx":#{@x},"yyy":#{y}}|
38
+ end
35
39
  def as_json(*args)
36
40
  {'a' => @x, :b => @y }
37
41
  end
@@ -40,6 +44,40 @@ class CustomJuice < Minitest::Test
40
44
  end
41
45
  end
42
46
 
47
+ class AsJson
48
+ attr_accessor :x, :y
49
+
50
+ def initialize(x, y)
51
+ @x = x
52
+ @y = y
53
+ end
54
+ def ==(o)
55
+ self.class == o.class && @x == o.x && @y = o.y
56
+ end
57
+ def as_json(*args)
58
+ {'a' => @x, :b => @y }
59
+ end
60
+ end
61
+
62
+ class AsRails
63
+ attr_accessor :x, :y
64
+
65
+ def initialize(x, y)
66
+ @x = x
67
+ @y = y
68
+ end
69
+ def ==(o)
70
+ self.class == o.class && @x == o.x && @y = o.y
71
+ end
72
+ def as_json(*args)
73
+ a = @x
74
+ a = a.as_json if a.respond_to?('as_json')
75
+ b = @y
76
+ b = b.as_json if b.respond_to?('as_json')
77
+ {'a' => a, :b => b }
78
+ end
79
+ end
80
+
43
81
  def setup
44
82
  @default_options = Oj.default_options
45
83
  Oj.default_options = { :mode => :custom }
@@ -80,6 +118,17 @@ class CustomJuice < Minitest::Test
80
118
  dump_and_load(-2.48e100 * 1.0e10, false)
81
119
  end
82
120
 
121
+ def test_float_parse
122
+ f = Oj.load("12.123456789012345678", mode: :custom, bigdecimal_load: :float);
123
+ assert_equal(Float, f.class)
124
+ end
125
+
126
+ def test_float_parse_fast
127
+ f = Oj.load("12.123456789012345678", mode: :custom, bigdecimal_load: :fast);
128
+ assert_equal(Float, f.class)
129
+ assert(12.12345678901234 <= f && f < 12.12345678901236)
130
+ end
131
+
83
132
  def test_nan_dump
84
133
  assert_equal('null', Oj.dump(0/0.0, :nan => :null))
85
134
  assert_equal('3.3e14159265358979323846', Oj.dump(0/0.0, :nan => :huge))
@@ -92,7 +141,7 @@ class CustomJuice < Minitest::Test
92
141
  end
93
142
  assert(false, "*** expected an exception")
94
143
  end
95
-
144
+
96
145
  def test_infinity_dump
97
146
  assert_equal('null', Oj.dump(1/0.0, :nan => :null))
98
147
  assert_equal('3.0e14159265358979323846', Oj.dump(1/0.0, :nan => :huge))
@@ -151,6 +200,8 @@ class CustomJuice < Minitest::Test
151
200
  end
152
201
 
153
202
  def test_deep_nest
203
+ skip 'TruffleRuby causes SEGV' if RUBY_ENGINE == 'truffleruby'
204
+
154
205
  begin
155
206
  n = 10000
156
207
  Oj.strict_load('[' * n + ']' * n)
@@ -187,7 +238,7 @@ class CustomJuice < Minitest::Test
187
238
  '19' => {
188
239
  '20' => {}}}}}}}}}}}}}}}}}}}}}, false)
189
240
  end
190
-
241
+
191
242
  def test_hash_escaped_key
192
243
  json = %{{"a\nb":true,"c\td":false}}
193
244
  obj = Oj.load(json)
@@ -209,8 +260,13 @@ class CustomJuice < Minitest::Test
209
260
  end
210
261
 
211
262
  def test_object
263
+ skip 'TruffleRuby fails this spec with `RuntimeError: rb_ivar_foreach not implemented`' if RUBY_ENGINE == 'truffleruby'
264
+
212
265
  obj = Jeez.new(true, 58)
213
- Oj.dump(obj, :create_id => "^o", :use_to_json => false, :use_as_json => false, :use_to_hash => false)
266
+ json = Oj.dump(obj, create_id: "^o", use_to_json: false, use_as_json: false, use_to_hash: false)
267
+ assert_equal(%|{"x":true,"y":58,"_z":"true"}|, json)
268
+ json = Oj.dump(obj, create_id: "^o", use_to_json: false, use_as_json: false, use_to_hash: false, ignore_under: true)
269
+ assert_equal(%|{"x":true,"y":58}|, json)
214
270
  dump_and_load(obj, false, :create_id => "^o", :create_additions => true)
215
271
  end
216
272
 
@@ -232,6 +288,66 @@ class CustomJuice < Minitest::Test
232
288
  assert_equal(%|{"b":true,"n":58}|, json)
233
289
  end
234
290
 
291
+ def test_object_raw_json
292
+ obj = Jeez.new(true, 58)
293
+ json = Oj.dump(obj, :use_to_json => true, :use_as_json => false, :use_raw_json => true, :use_to_hash => false)
294
+ assert_equal(%|{"xxx":true,"yyy":58}|, json)
295
+ end
296
+
297
+ def test_raw_json_stringwriter
298
+ obj = Oj::StringWriter.new(:indent => 0)
299
+ obj.push_array()
300
+ obj.pop()
301
+ json = Oj.dump(obj, :use_raw_json => true)
302
+ assert_equal(%|[]|, json)
303
+ end
304
+
305
+ def test_as_raw_json_stringwriter
306
+ obj = Oj::StringWriter.new(:indent => 0)
307
+ obj.push_array()
308
+ obj.push_value(3)
309
+ obj.pop()
310
+ j = AsJson.new(1, obj)
311
+
312
+ json = Oj.dump(j, use_raw_json: true, use_as_json: true, indent: 2)
313
+ assert_equal(%|{
314
+ "a":1,
315
+ "b":[3]
316
+ }
317
+ |, json)
318
+
319
+ json = Oj.dump(j, use_raw_json: false, use_as_json: true, indent: 2)
320
+ assert_equal(%|{
321
+ "a":1,
322
+ "b":{}
323
+ }
324
+ |, json)
325
+ end
326
+
327
+ def test_rails_as_raw_json_stringwriter
328
+ obj = Oj::StringWriter.new(:indent => 0)
329
+ obj.push_array()
330
+ obj.push_value(3)
331
+ obj.pop()
332
+ j = AsRails.new(1, obj)
333
+ json = Oj.dump(j, mode: :rails, use_raw_json: true, indent: 2)
334
+ assert_equal(%|{
335
+ "a":1,
336
+ "b":{}
337
+ }
338
+ |, json)
339
+
340
+ Oj::Rails.optimize
341
+ json = Oj.dump(j, mode: :rails, use_raw_json: true, indent: 2)
342
+ Oj::Rails.deoptimize
343
+ assert_equal(%|{
344
+ "a":1,
345
+ "b":[3]
346
+ }
347
+ |, json)
348
+
349
+ end
350
+
235
351
  def test_symbol
236
352
  json = Oj.dump(:abc)
237
353
  assert_equal('"abc"', json)
@@ -291,11 +407,15 @@ class CustomJuice < Minitest::Test
291
407
  end
292
408
 
293
409
  def test_range
410
+ skip 'TruffleRuby fails this spec' if RUBY_ENGINE == 'truffleruby'
411
+
294
412
  obj = 3..8
295
413
  dump_and_load(obj, false, :create_id => "^o", :create_additions => true)
296
414
  end
297
415
 
298
416
  def test_date
417
+ skip 'TruffleRuby causes SEGV' if RUBY_ENGINE == 'truffleruby'
418
+
299
419
  obj = Date.new(2017, 1, 5)
300
420
  dump_and_load(obj, false, :create_id => "^o", :create_additions => true)
301
421
  end
@@ -325,6 +445,8 @@ class CustomJuice < Minitest::Test
325
445
  end
326
446
 
327
447
  def test_datetime
448
+ skip 'TruffleRuby causes SEGV' if RUBY_ENGINE == 'truffleruby'
449
+
328
450
  obj = DateTime.new(2017, 1, 5, 10, 20, 30)
329
451
  dump_and_load(obj, false, :create_id => "^o", :create_additions => true)
330
452
  end
@@ -368,11 +490,13 @@ class CustomJuice < Minitest::Test
368
490
  end
369
491
 
370
492
  def test_time
493
+ skip 'TruffleRuby fails this spec' if RUBY_ENGINE == 'truffleruby'
494
+
371
495
  obj = Time.now()
372
- dump_and_load(obj, false, :time_format => :unix, :create_id => "^o", :create_additions => true)
373
- dump_and_load_inspect(obj, false, :time_format => :unix_zone, :create_id => "^o", :create_additions => true)
374
- dump_and_load_inspect(obj, false, :time_format => :xmlschema, :create_id => "^o", :create_additions => true)
375
- dump_and_load_inspect(obj, false, :time_format => :ruby, :create_id => "^o", :create_additions => true)
496
+ dump_load_dump(obj, false, :time_format => :unix, :create_id => "^o", :create_additions => true)
497
+ dump_load_dump(obj, false, :time_format => :unix_zone, :create_id => "^o", :create_additions => true)
498
+ dump_load_dump(obj, false, :time_format => :xmlschema, :create_id => "^o", :create_additions => true)
499
+ dump_load_dump(obj, false, :time_format => :ruby, :create_id => "^o", :create_additions => true)
376
500
  end
377
501
 
378
502
  def dump_and_load(obj, trace=false, options={})
@@ -403,4 +527,19 @@ class CustomJuice < Minitest::Test
403
527
  loaded
404
528
  end
405
529
 
530
+ def dump_load_dump(obj, trace=false, options={})
531
+ options = options.merge(:indent => 2, :mode => :custom)
532
+ json = Oj.dump(obj, options)
533
+ puts json if trace
534
+
535
+ loaded = Oj.load(json, options);
536
+ if obj.nil?
537
+ assert_nil(loaded)
538
+ else
539
+ json2 = Oj.dump(loaded, options)
540
+ assert_equal(json, json2)
541
+ end
542
+ loaded
543
+ end
544
+
406
545
  end
data/test/test_fast.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- # encoding: UTF-8
2
+ # coding: utf-8
3
+ # frozen_string_literal: true
3
4
 
4
5
  $: << File.dirname(__FILE__)
5
6
 
@@ -36,6 +37,17 @@ class DocTest < Minitest::Test
36
37
  end
37
38
  end
38
39
 
40
+ def test_leaf_of_existing_path
41
+ json = %{{"foo": 1, "fizz": true}}
42
+ Oj::Doc.open(json) do |doc|
43
+ %w(/foo/bar /fizz/bar).each do |path|
44
+ assert_nil(doc.fetch(path))
45
+ assert_equal(:default, doc.fetch(path, :default))
46
+ refute(doc.exists?(path))
47
+ end
48
+ end
49
+ end
50
+
39
51
  def test_true
40
52
  json = %{true}
41
53
  Oj::Doc.open(json) do |doc|
@@ -279,10 +291,26 @@ class DocTest < Minitest::Test
279
291
  ['/array/1', {'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}],
280
292
  ['/array', [{'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}]],
281
293
  ['/', {'array' => [{'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}], 'boolean' => true}],
294
+ ['/nothing', nil],
295
+ ['/array/10', nil],
282
296
  ].each do |path,val|
283
- assert_equal(val, doc.fetch(path))
297
+ if val.nil?
298
+ assert_nil(doc.fetch(path))
299
+ else
300
+ assert_equal(val, doc.fetch(path))
301
+ end
284
302
  end
285
303
  end
304
+ # verify empty hash and arrays return nil when a member is requested
305
+ Oj::Doc.open('{}') do |doc|
306
+ assert_nil(doc.fetch('/x'))
307
+ assert_nil(doc.fetch('/0'))
308
+ end
309
+ Oj::Doc.open('[]') do |doc|
310
+ assert_nil(doc.fetch('/x'))
311
+ assert_nil(doc.fetch('/0'))
312
+ end
313
+
286
314
  end
287
315
 
288
316
  def test_move_fetch_path
@@ -297,6 +325,20 @@ class DocTest < Minitest::Test
297
325
  end
298
326
  end
299
327
 
328
+ def test_exists
329
+ Oj::Doc.open(@json1) do |doc|
330
+ [['/array/1', true],
331
+ ['/array/1', true],
332
+ ['/array/1/hash', true],
333
+ ['/array/1/dash', false],
334
+ ['/array/3', false],
335
+ ['/nothing', false],
336
+ ].each do |path,val|
337
+ assert_equal(val, doc.exists?(path), "failed for #{path.inspect}")
338
+ end
339
+ end
340
+ end
341
+
300
342
  def test_home
301
343
  Oj::Doc.open(@json1) do |doc|
302
344
  doc.move('/array/1/num')
@@ -354,6 +396,19 @@ class DocTest < Minitest::Test
354
396
  end
355
397
  end
356
398
 
399
+ def test_nested_each_child
400
+ h = {}
401
+ Oj::Doc.open('{"a":1,"c":[2],"d":3}') do |doc|
402
+ doc.each_child('/') do |child|
403
+ h[child.path] = child.fetch
404
+ child.each_child do |grandchild|
405
+ h[grandchild.path] = grandchild.fetch
406
+ end
407
+ end
408
+ end
409
+ assert_equal({"/a"=>1, "/c"=>[2], "/c/1"=>2, "/d"=>3}, h)
410
+ end
411
+
357
412
  def test_size
358
413
  Oj::Doc.open('[1,2,3]') do |doc|
359
414
  assert_equal(4, doc.size)
@@ -450,6 +505,11 @@ class DocTest < Minitest::Test
450
505
  assert_equal({'/a/x' => 2, '/b/y' => 4}, results)
451
506
  end
452
507
 
508
+ def test_doc_empty
509
+ result = Oj::Doc.open("") { |doc| doc.each_child {} }
510
+ assert_nil(result)
511
+ end
512
+
453
513
  def test_comment
454
514
  json = %{{
455
515
  "x"/*one*/:/*two*/true,//three