oj 2.0.0 → 3.0.0

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 (133) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +17 -23
  3. data/README.md +74 -425
  4. data/ext/oj/buf.h +103 -0
  5. data/ext/oj/cache8.c +4 -0
  6. data/ext/oj/circarray.c +68 -0
  7. data/ext/oj/circarray.h +23 -0
  8. data/ext/oj/code.c +227 -0
  9. data/ext/oj/code.h +40 -0
  10. data/ext/oj/compat.c +243 -0
  11. data/ext/oj/custom.c +1097 -0
  12. data/ext/oj/dump.c +766 -1534
  13. data/ext/oj/dump.h +92 -0
  14. data/ext/oj/dump_compat.c +937 -0
  15. data/ext/oj/dump_leaf.c +254 -0
  16. data/ext/oj/dump_object.c +810 -0
  17. data/ext/oj/dump_rails.c +329 -0
  18. data/ext/oj/dump_strict.c +416 -0
  19. data/ext/oj/encode.h +51 -0
  20. data/ext/oj/err.c +57 -0
  21. data/ext/oj/err.h +70 -0
  22. data/ext/oj/extconf.rb +17 -7
  23. data/ext/oj/fast.c +213 -180
  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 +817 -0
  28. data/ext/oj/mimic_rails.c +806 -0
  29. data/ext/oj/mimic_rails.h +17 -0
  30. data/ext/oj/object.c +752 -0
  31. data/ext/oj/odd.c +230 -0
  32. data/ext/oj/odd.h +44 -0
  33. data/ext/oj/oj.c +1288 -929
  34. data/ext/oj/oj.h +240 -69
  35. data/ext/oj/parse.c +1014 -0
  36. data/ext/oj/parse.h +92 -0
  37. data/ext/oj/reader.c +223 -0
  38. data/ext/oj/reader.h +151 -0
  39. data/ext/oj/resolve.c +127 -0
  40. data/ext/oj/{cache.h → resolve.h} +6 -13
  41. data/ext/oj/rxclass.c +133 -0
  42. data/ext/oj/rxclass.h +27 -0
  43. data/ext/oj/saj.c +77 -175
  44. data/ext/oj/scp.c +224 -0
  45. data/ext/oj/sparse.c +911 -0
  46. data/ext/oj/stream_writer.c +301 -0
  47. data/ext/oj/strict.c +162 -0
  48. data/ext/oj/string_writer.c +480 -0
  49. data/ext/oj/val_stack.c +98 -0
  50. data/ext/oj/val_stack.h +188 -0
  51. data/lib/oj/active_support_helper.rb +41 -0
  52. data/lib/oj/bag.rb +6 -10
  53. data/lib/oj/easy_hash.rb +52 -0
  54. data/lib/oj/json.rb +172 -0
  55. data/lib/oj/mimic.rb +260 -5
  56. data/lib/oj/saj.rb +13 -10
  57. data/lib/oj/schandler.rb +142 -0
  58. data/lib/oj/state.rb +131 -0
  59. data/lib/oj/version.rb +1 -1
  60. data/lib/oj.rb +11 -23
  61. data/pages/Advanced.md +22 -0
  62. data/pages/Compatibility.md +25 -0
  63. data/pages/Custom.md +23 -0
  64. data/pages/Encoding.md +65 -0
  65. data/pages/JsonGem.md +79 -0
  66. data/pages/Modes.md +140 -0
  67. data/pages/Options.md +250 -0
  68. data/pages/Rails.md +60 -0
  69. data/pages/Security.md +20 -0
  70. data/test/_test_active.rb +76 -0
  71. data/test/_test_active_mimic.rb +96 -0
  72. data/test/_test_mimic_rails.rb +126 -0
  73. data/test/activesupport4/decoding_test.rb +105 -0
  74. data/test/activesupport4/encoding_test.rb +531 -0
  75. data/test/activesupport4/test_helper.rb +41 -0
  76. data/test/activesupport5/decoding_test.rb +125 -0
  77. data/test/activesupport5/encoding_test.rb +483 -0
  78. data/test/activesupport5/encoding_test_cases.rb +90 -0
  79. data/test/activesupport5/test_helper.rb +50 -0
  80. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  81. data/test/helper.rb +27 -0
  82. data/test/isolated/shared.rb +310 -0
  83. data/test/isolated/test_mimic_after.rb +13 -0
  84. data/test/isolated/test_mimic_alone.rb +12 -0
  85. data/test/isolated/test_mimic_as_json.rb +45 -0
  86. data/test/isolated/test_mimic_before.rb +13 -0
  87. data/test/isolated/test_mimic_define.rb +28 -0
  88. data/test/isolated/test_mimic_rails_after.rb +22 -0
  89. data/test/isolated/test_mimic_rails_before.rb +21 -0
  90. data/test/isolated/test_mimic_redefine.rb +15 -0
  91. data/test/json_gem/json_addition_test.rb +216 -0
  92. data/test/json_gem/json_common_interface_test.rb +143 -0
  93. data/test/json_gem/json_encoding_test.rb +109 -0
  94. data/test/json_gem/json_ext_parser_test.rb +20 -0
  95. data/test/json_gem/json_fixtures_test.rb +35 -0
  96. data/test/json_gem/json_generator_test.rb +383 -0
  97. data/test/json_gem/json_generic_object_test.rb +90 -0
  98. data/test/json_gem/json_parser_test.rb +470 -0
  99. data/test/json_gem/json_string_matching_test.rb +42 -0
  100. data/test/json_gem/test_helper.rb +18 -0
  101. data/test/perf_compat.rb +130 -0
  102. data/test/perf_fast.rb +9 -9
  103. data/test/perf_file.rb +64 -0
  104. data/test/{perf_obj.rb → perf_object.rb} +24 -10
  105. data/test/perf_scp.rb +151 -0
  106. data/test/perf_strict.rb +32 -113
  107. data/test/sample.rb +2 -3
  108. data/test/test_compat.rb +474 -0
  109. data/test/test_custom.rb +355 -0
  110. data/test/test_debian.rb +53 -0
  111. data/test/test_fast.rb +66 -16
  112. data/test/test_file.rb +237 -0
  113. data/test/test_gc.rb +49 -0
  114. data/test/test_hash.rb +29 -0
  115. data/test/test_null.rb +376 -0
  116. data/test/test_object.rb +1010 -0
  117. data/test/test_saj.rb +16 -16
  118. data/test/test_scp.rb +417 -0
  119. data/test/test_strict.rb +410 -0
  120. data/test/test_various.rb +815 -0
  121. data/test/test_writer.rb +308 -0
  122. data/test/tests.rb +9 -902
  123. data/test/tests_mimic.rb +14 -0
  124. data/test/tests_mimic_addition.rb +7 -0
  125. metadata +253 -38
  126. data/ext/oj/cache.c +0 -148
  127. data/ext/oj/foo.rb +0 -6
  128. data/ext/oj/load.c +0 -1049
  129. data/test/a.rb +0 -38
  130. data/test/perf1.rb +0 -64
  131. data/test/perf2.rb +0 -76
  132. data/test/perf_obj_old.rb +0 -213
  133. data/test/test_mimic.rb +0 -208
@@ -0,0 +1,1010 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ $: << File.dirname(__FILE__)
5
+
6
+ require 'helper'
7
+
8
+ class ObjectJuice < Minitest::Test
9
+ class Jeez
10
+ attr_accessor :x, :y
11
+
12
+ def initialize(x, y)
13
+ @x = x
14
+ @y = y
15
+ end
16
+
17
+ def eql?(o)
18
+ self.class == o.class && @x == o.x && @y == o.y
19
+ end
20
+ alias == eql?
21
+
22
+ def to_json(*a)
23
+ %{{"json_class":"#{self.class}","x":#{@x},"y":#{@y}}}
24
+ end
25
+
26
+ def self.json_create(h)
27
+ self.new(h['x'], h['y'])
28
+ end
29
+ end # Jeez
30
+
31
+ class Jam
32
+ attr_accessor :x, :y
33
+
34
+ def initialize(x, y)
35
+ @x = x
36
+ @y = y
37
+ end
38
+
39
+ def eql?(o)
40
+ self.class == o.class && @x == o.x && @y == o.y
41
+ end
42
+ alias == eql?
43
+
44
+ end # Jam
45
+
46
+ class Jazz < Jam
47
+ def initialize(x, y)
48
+ super
49
+ end
50
+ def to_hash()
51
+ { 'json_class' => self.class.to_s, 'x' => @x, 'y' => @y }
52
+ end
53
+ def self.json_create(h)
54
+ self.new(h['x'], h['y'])
55
+ end
56
+ end # Jazz
57
+
58
+ class Orange < Jam
59
+ def initialize(x, y)
60
+ super
61
+ end
62
+
63
+ def as_json()
64
+ { :json_class => self.class,
65
+ :x => @x,
66
+ :y => @y }
67
+ end
68
+
69
+ def self.json_create(h)
70
+ self.new(h['x'], h['y'])
71
+ end
72
+ end
73
+
74
+ module One
75
+ module Two
76
+ module Three
77
+ class Deep
78
+
79
+ def initialize()
80
+ end
81
+
82
+ def eql?(o)
83
+ self.class == o.class
84
+ end
85
+ alias == eql?
86
+
87
+ def to_hash()
88
+ {'json_class' => "#{self.class.name}"}
89
+ end
90
+
91
+ def to_json(*a)
92
+ %{{"json_class":"#{self.class.name}"}}
93
+ end
94
+
95
+ def self.json_create(h)
96
+ self.new()
97
+ end
98
+ end # Deep
99
+ end # Three
100
+ end # Two
101
+
102
+ class Stuck2 < Struct.new(:a, :b)
103
+ end
104
+
105
+ end # One
106
+
107
+ class Stuck < Struct.new(:a, :b)
108
+ end
109
+
110
+ class Strung < String
111
+
112
+ def initialize(str, safe)
113
+ super(str)
114
+ @safe = safe
115
+ end
116
+
117
+ def safe?()
118
+ @safe
119
+ end
120
+
121
+ def self.create(str, safe)
122
+ new(str, safe)
123
+ end
124
+
125
+ def eql?(o)
126
+ super && self.class == o.class && @safe == o.safe?
127
+ end
128
+ alias == eql?
129
+
130
+ def inspect()
131
+ return super + '(' + @safe + ')'
132
+ end
133
+ end
134
+
135
+ class AutoStrung < String
136
+ attr_accessor :safe
137
+
138
+ def initialize(str, safe)
139
+ super(str)
140
+ @safe = safe
141
+ end
142
+
143
+ def eql?(o)
144
+ self.class == o.class && super(o) && @safe == o.safe
145
+ end
146
+ alias == eql?
147
+ end
148
+
149
+ class AutoArray < Array
150
+ attr_accessor :safe
151
+
152
+ def initialize(a, safe)
153
+ super(a)
154
+ @safe = safe
155
+ end
156
+
157
+ def eql?(o)
158
+ self.class == o.class && super(o) && @safe == o.safe
159
+ end
160
+ alias == eql?
161
+ end
162
+
163
+ class AutoHash < Hash
164
+ attr_accessor :safe
165
+
166
+ def initialize(h, safe)
167
+ super(h)
168
+ @safe = safe
169
+ end
170
+
171
+ def eql?(o)
172
+ self.class == o.class && super(o) && @safe == o.safe
173
+ end
174
+ alias == eql?
175
+ end
176
+
177
+ class Raw
178
+ attr_accessor :json
179
+
180
+ def initialize(j)
181
+ @json = j
182
+ end
183
+
184
+ def to_json(*a)
185
+ @json
186
+ end
187
+
188
+ def self.create(h)
189
+ h
190
+ end
191
+ end # Raw
192
+
193
+ module Ichi
194
+ module Ni
195
+ def self.direct(h)
196
+ h
197
+ end
198
+
199
+ module San
200
+ class Shi
201
+
202
+ attr_accessor :hash
203
+
204
+ def initialize(h)
205
+ @hash = h
206
+ end
207
+
208
+ def dump()
209
+ @hash
210
+ end
211
+
212
+ end # Shi
213
+ end # San
214
+ end # Ni
215
+ end # Ichi
216
+
217
+ def setup
218
+ @default_options = Oj.default_options
219
+ end
220
+
221
+ def teardown
222
+ Oj.default_options = @default_options
223
+ end
224
+
225
+ def test_nil
226
+ dump_and_load(nil, false)
227
+ end
228
+
229
+ def test_true
230
+ dump_and_load(true, false)
231
+ end
232
+
233
+ def test_false
234
+ dump_and_load(false, false)
235
+ end
236
+
237
+ def test_fixnum
238
+ dump_and_load(0, false)
239
+ dump_and_load(12345, false)
240
+ dump_and_load(-54321, false)
241
+ dump_and_load(1, false)
242
+ end
243
+
244
+ def test_float
245
+ dump_and_load(0.0, false)
246
+ dump_and_load(12345.6789, false)
247
+ dump_and_load(70.35, false)
248
+ dump_and_load(-54321.012, false)
249
+ dump_and_load(1.7775, false)
250
+ dump_and_load(2.5024, false)
251
+ dump_and_load(2.48e16, false)
252
+ dump_and_load(2.48e100 * 1.0e10, false)
253
+ dump_and_load(-2.48e100 * 1.0e10, false)
254
+ dump_and_load(1/0.0, false)
255
+ # NaN does not always == NaN
256
+ json = Oj.dump(0/0.0, :mode => :object)
257
+ assert_equal('3.3e14159265358979323846', json)
258
+ loaded = Oj.load(json);
259
+ assert_equal(true, loaded.nan?)
260
+ end
261
+
262
+ def test_string
263
+ dump_and_load('', false)
264
+ dump_and_load('abc', false)
265
+ dump_and_load("abc\ndef", false)
266
+ dump_and_load("a\u0041", false)
267
+ end
268
+
269
+ def test_symbol
270
+ dump_and_load(:abc, false)
271
+ dump_and_load(":abc", false)
272
+ dump_and_load(':xyz'.to_sym, false)
273
+ end
274
+
275
+ def test_encode
276
+ opts = Oj.default_options
277
+ Oj.default_options = { :ascii_only => false }
278
+ dump_and_load("ぴーたー", false)
279
+ Oj.default_options = { :ascii_only => true }
280
+ json = Oj.dump("ぴーたー")
281
+ assert_equal(%{"\\u3074\\u30fc\\u305f\\u30fc"}, json)
282
+ dump_and_load("ぴーたー", false)
283
+ Oj.default_options = opts
284
+ end
285
+
286
+ def test_unicode
287
+ # hits the 3 normal ranges and one extended surrogate pair
288
+ json = %{"\\u019f\\u05e9\\u3074\\ud834\\udd1e"}
289
+ obj = Oj.load(json)
290
+ json2 = Oj.dump(obj, :ascii_only => true)
291
+ assert_equal(json, json2)
292
+ end
293
+
294
+ def test_array
295
+ dump_and_load([], false)
296
+ dump_and_load([true, false], false)
297
+ dump_and_load(['a', 1, nil], false)
298
+ dump_and_load([[nil]], false)
299
+ dump_and_load([[nil], 58], false)
300
+ end
301
+
302
+ def test_array_deep
303
+ dump_and_load([1,[2,[3,[4,[5,[6,[7,[8,[9,[10,[11,[12,[13,[14,[15,[16,[17,[18,[19,[20]]]]]]]]]]]]]]]]]]]], false)
304
+ end
305
+
306
+ # Hash
307
+ def test_hash
308
+ dump_and_load({}, false)
309
+ dump_and_load({ 'true' => true, 'false' => false}, false)
310
+ dump_and_load({ 'true' => true, 'array' => [], 'hash' => { }}, false)
311
+ end
312
+
313
+ def test_hash_deep
314
+ dump_and_load({'1' => {
315
+ '2' => {
316
+ '3' => {
317
+ '4' => {
318
+ '5' => {
319
+ '6' => {
320
+ '7' => {
321
+ '8' => {
322
+ '9' => {
323
+ '10' => {
324
+ '11' => {
325
+ '12' => {
326
+ '13' => {
327
+ '14' => {
328
+ '15' => {
329
+ '16' => {
330
+ '17' => {
331
+ '18' => {
332
+ '19' => {
333
+ '20' => {}}}}}}}}}}}}}}}}}}}}}, false)
334
+ end
335
+
336
+ def test_hash_escaped_key
337
+ json = %{{"a\nb":true,"c\td":false}}
338
+ obj = Oj.object_load(json)
339
+ assert_equal({"a\nb" => true, "c\td" => false}, obj)
340
+ end
341
+
342
+ def test_bignum_object
343
+ dump_and_load(7 ** 55, false)
344
+ end
345
+
346
+ # BigDecimal
347
+ def test_bigdecimal_object
348
+ dump_and_load(BigDecimal.new('3.14159265358979323846'), false)
349
+ end
350
+
351
+ def test_bigdecimal_load
352
+ orig = BigDecimal.new('80.51')
353
+ json = Oj.dump(orig, :mode => :object, :bigdecimal_as_decimal => true)
354
+ bg = Oj.load(json, :mode => :object, :bigdecimal_load => true)
355
+ assert_equal(BigDecimal, bg.class)
356
+ assert_equal(orig, bg)
357
+ # Infinity is the same for Float and BigDecimal
358
+ json = Oj.dump(BigDecimal.new('Infinity'), :mode => :object)
359
+ assert_equal('Infinity', json)
360
+ json = Oj.dump(BigDecimal.new('-Infinity'), :mode => :object)
361
+ assert_equal('-Infinity', json)
362
+ end
363
+
364
+ # Stream IO
365
+ def test_io_string
366
+ json = %{{
367
+ "x":true,
368
+ "y":58,
369
+ "z": [1,2,3]
370
+ }
371
+ }
372
+ input = StringIO.new(json)
373
+ obj = Oj.object_load(input)
374
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
375
+ end
376
+
377
+ def test_io_file
378
+ filename = File.join(File.dirname(__FILE__), 'open_file_test.json')
379
+ File.open(filename, 'w') { |f| f.write(%{{
380
+ "x":true,
381
+ "y":58,
382
+ "z": [1,2,3]
383
+ }
384
+ }) }
385
+ f = File.new(filename)
386
+ obj = Oj.object_load(f)
387
+ f.close()
388
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
389
+ end
390
+
391
+ # symbol_keys option
392
+ def test_symbol_keys
393
+ json = %{{
394
+ "x":true,
395
+ "y":58,
396
+ "z": [1,2,3]
397
+ }
398
+ }
399
+ obj = Oj.object_load(json, :symbol_keys => true)
400
+ assert_equal({ :x => true, :y => 58, :z => [1, 2, 3]}, obj)
401
+ end
402
+
403
+ def test_class_object
404
+ dump_and_load(ObjectJuice, false)
405
+ end
406
+
407
+ def test_module_object
408
+ dump_and_load(One, false)
409
+ end
410
+
411
+ def test_non_str_hash_object
412
+ json = Oj.dump({ 1 => true, :sim => nil }, :mode => :object)
413
+ h = Oj.load(json, :mode => :strict)
414
+ assert_equal({"^#1" => [1, true], ":sim" => nil}, h)
415
+ h = Oj.load(json, :mode => :object)
416
+ assert_equal({ 1 => true, :sim => nil }, h)
417
+ end
418
+
419
+ # comments
420
+ def test_comment_slash
421
+ json = %{{
422
+ "x":true,//three
423
+ "y":58,
424
+ "z": [1,2,
425
+ 3 // six
426
+ ]}
427
+ }
428
+ obj = Oj.object_load(json)
429
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
430
+ end
431
+
432
+ def test_comment_c
433
+ json = %{{
434
+ "x"/*one*/:/*two*/true,
435
+ "y":58,
436
+ "z": [1,2,3]}
437
+ }
438
+ obj = Oj.object_load(json)
439
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
440
+ end
441
+
442
+ def test_comment
443
+ json = %{{
444
+ "x"/*one*/:/*two*/true,//three
445
+ "y":58/*four*/,
446
+ "z": [1,2/*five*/,
447
+ 3 // six
448
+ ]
449
+ }
450
+ }
451
+ obj = Oj.object_load(json)
452
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
453
+ end
454
+
455
+ def test_json_module_object
456
+ obj = One::Two::Three::Deep.new()
457
+ dump_and_load(obj, false)
458
+ end
459
+
460
+ def test_xml_time
461
+ if RUBY_VERSION.start_with?('1.8')
462
+ t = Time.parse('2015-01-05T21:37:07.123456789-08:00')
463
+ else
464
+ t = Time.new(2015, 1, 5, 21, 37, 7.123456789, -8 * 3600)
465
+ end
466
+ # The fractional seconds are not always recreated exactly which causes a
467
+ # mismatch so instead the seconds, nsecs, and gmt_offset are checked
468
+ # separately along with utc.
469
+ json = Oj.dump(t, :mode => :object, :time_format => :xmlschema)
470
+ #puts "*** json for test_xml_time '#{json}'"
471
+ loaded = Oj.object_load(json);
472
+ assert_equal(t.tv_sec, loaded.tv_sec)
473
+ if t.respond_to?(:tv_nsec)
474
+ assert_equal(t.tv_nsec, loaded.tv_nsec)
475
+ else
476
+ assert_equal(t.tv_usec, loaded.tv_usec)
477
+ end
478
+ assert_equal(t.utc?, loaded.utc?)
479
+ assert_equal(t.utc_offset, loaded.utc_offset)
480
+ end
481
+
482
+ def test_xml_time_utc
483
+ if RUBY_VERSION.start_with?('1.8')
484
+ t = Time.parse('2015-01-05T21:37:07.123456789Z')
485
+ else
486
+ t = Time.utc(2015, 1, 5, 21, 37, 7.123456789)
487
+ end
488
+ # The fractional seconds are not always recreated exactly which causes a
489
+ # mismatch so instead the seconds, nsecs, and gmt_offset are checked
490
+ # separately along with utc.
491
+ json = Oj.dump(t, :mode => :object, :time_format => :xmlschema)
492
+ loaded = Oj.object_load(json);
493
+ assert_equal(t.tv_sec, loaded.tv_sec)
494
+ if t.respond_to?(:tv_nsec)
495
+ assert_equal(t.tv_nsec, loaded.tv_nsec)
496
+ else
497
+ assert_equal(t.tv_usec, loaded.tv_usec)
498
+ end
499
+ assert_equal(t.utc?, loaded.utc?)
500
+ assert_equal(t.utc_offset, loaded.utc_offset)
501
+ end
502
+
503
+ def test_ruby_time
504
+ if RUBY_VERSION.start_with?('1.8')
505
+ t = Time.parse('2015-01-05T21:37:07.123456789-08:00')
506
+ else
507
+ t = Time.new(2015, 1, 5, 21, 37, 7.123456789, -8 * 3600)
508
+ end
509
+ # The fractional seconds are not always recreated exactly which causes a
510
+ # mismatch so instead the seconds, nsecs, and gmt_offset are checked
511
+ # separately along with utc.
512
+ json = Oj.dump(t, :mode => :object, :time_format => :ruby)
513
+ #puts "*** json for test_ruby_time '#{json}'"
514
+ loaded = Oj.object_load(json);
515
+ assert_equal(t.tv_sec, loaded.tv_sec)
516
+ if t.respond_to?(:tv_nsec)
517
+ assert_equal(t.tv_nsec, loaded.tv_nsec)
518
+ else
519
+ assert_equal(t.tv_usec, loaded.tv_usec)
520
+ end
521
+ assert_equal(t.utc?, loaded.utc?)
522
+ assert_equal(t.utc_offset, loaded.utc_offset)
523
+ end
524
+
525
+ def test_ruby_time_12345
526
+ if RUBY_VERSION.start_with?('1.8')
527
+ t = Time.parse('2015-01-05T21:37:07.123456789+03:25')
528
+ else
529
+ t = Time.new(2015, 1, 5, 21, 37, 7.123456789, 12345/60*60)
530
+ end
531
+ # The fractional seconds are not always recreated exactly which causes a
532
+ # mismatch so instead the seconds, nsecs, and gmt_offset are checked
533
+ # separately along with utc.
534
+ json = Oj.dump(t, :mode => :object, :time_format => :ruby)
535
+ #puts "*** json for test_ruby_time '#{json}'"
536
+ loaded = Oj.object_load(json);
537
+ #puts "*** loaded: #{loaded}"
538
+ assert_equal(t.tv_sec, loaded.tv_sec)
539
+ if t.respond_to?(:tv_nsec)
540
+ assert_equal(t.tv_nsec, loaded.tv_nsec)
541
+ else
542
+ assert_equal(t.tv_usec, loaded.tv_usec)
543
+ end
544
+ assert_equal(t.utc?, loaded.utc?)
545
+ assert_equal(t.utc_offset, loaded.utc_offset)
546
+ end
547
+
548
+ def test_ruby_time_utc
549
+ if RUBY_VERSION.start_with?('1.8')
550
+ t = Time.parse('2015-01-05T21:37:07.123456789Z')
551
+ else
552
+ t = Time.utc(2015, 1, 5, 21, 37, 7.123456789)
553
+ end
554
+ # The fractional seconds are not always recreated exactly which causes a
555
+ # mismatch so instead the seconds, nsecs, and gmt_offset are checked
556
+ # separately along with utc.
557
+ json = Oj.dump(t, :mode => :object, :time_format => :ruby)
558
+ #puts json
559
+ loaded = Oj.object_load(json);
560
+ assert_equal(t.tv_sec, loaded.tv_sec)
561
+ if t.respond_to?(:tv_nsec)
562
+ assert_equal(t.tv_nsec, loaded.tv_nsec)
563
+ else
564
+ assert_equal(t.tv_usec, loaded.tv_usec)
565
+ end
566
+ assert_equal(t.utc?, loaded.utc?)
567
+ assert_equal(t.utc_offset, loaded.utc_offset)
568
+ end
569
+
570
+ def test_time_early
571
+ if RUBY_VERSION.start_with?('1.8')
572
+ t = Time.parse('1954-01-05T21:37:07.123456789-08:00')
573
+ else
574
+ t = Time.new(1954, 1, 5, 21, 37, 7.123456789, -8 * 3600)
575
+ end
576
+ # The fractional seconds are not always recreated exactly which causes a
577
+ # mismatch so instead the seconds, nsecs, and gmt_offset are checked
578
+ # separately along with utc.
579
+ json = Oj.dump(t, :mode => :object, :time_format => :unix_zone)
580
+ #puts json
581
+ loaded = Oj.object_load(json);
582
+ assert_equal(t.tv_sec, loaded.tv_sec)
583
+ if t.respond_to?(:tv_nsec)
584
+ assert_equal(t.tv_nsec, loaded.tv_nsec)
585
+ else
586
+ assert_equal(t.tv_usec, loaded.tv_usec)
587
+ end
588
+ assert_equal(t.utc?, loaded.utc?)
589
+ assert_equal(t.utc_offset, loaded.utc_offset)
590
+ end
591
+
592
+ def test_time_unix_zone
593
+ if RUBY_VERSION.start_with?('1.8')
594
+ t = Time.parse('2015-01-05T21:37:07.123456789-08:00')
595
+ else
596
+ t = Time.new(2015, 1, 5, 21, 37, 7.123456789, -8 * 3600)
597
+ end
598
+ # The fractional seconds are not always recreated exactly which causes a
599
+ # mismatch so instead the seconds, nsecs, and gmt_offset are checked
600
+ # separately along with utc.
601
+ json = Oj.dump(t, :mode => :object, :time_format => :unix_zone)
602
+ #puts json
603
+ loaded = Oj.object_load(json);
604
+ assert_equal(t.tv_sec, loaded.tv_sec)
605
+ if t.respond_to?(:tv_nsec)
606
+ assert_equal(t.tv_nsec, loaded.tv_nsec)
607
+ else
608
+ assert_equal(t.tv_usec, loaded.tv_usec)
609
+ end
610
+ assert_equal(t.utc?, loaded.utc?)
611
+ assert_equal(t.utc_offset, loaded.utc_offset)
612
+ end
613
+
614
+ unless RUBY_VERSION.start_with?('1.8')
615
+ def test_time_unix_zone_12345
616
+ t = Time.new(2015, 1, 5, 21, 37, 7.123456789, 12345)
617
+ # The fractional seconds are not always recreated exactly which causes a
618
+ # mismatch so instead the seconds, nsecs, and gmt_offset are checked
619
+ # separately along with utc.
620
+ json = Oj.dump(t, :mode => :object, :time_format => :unix_zone)
621
+ #puts json
622
+ loaded = Oj.object_load(json);
623
+ assert_equal(t.tv_sec, loaded.tv_sec)
624
+ if t.respond_to?(:tv_nsec)
625
+ assert_equal(t.tv_nsec, loaded.tv_nsec)
626
+ else
627
+ assert_equal(t.tv_usec, loaded.tv_usec)
628
+ end
629
+ assert_equal(t.utc?, loaded.utc?)
630
+ assert_equal(t.utc_offset, loaded.utc_offset)
631
+ end
632
+ end
633
+
634
+ def test_time_unix_zone_utc
635
+ if RUBY_VERSION.start_with?('1.8')
636
+ t = Time.parse('2015-01-05T21:37:07.123456789Z')
637
+ else
638
+ t = Time.utc(2015, 1, 5, 21, 37, 7.123456789)
639
+ end
640
+ # The fractional seconds are not always recreated exactly which causes a
641
+ # mismatch so instead the seconds, nsecs, and gmt_offset are checked
642
+ # separately along with utc.
643
+ json = Oj.dump(t, :mode => :object, :time_format => :unix_zone)
644
+ #puts json
645
+ loaded = Oj.object_load(json);
646
+ assert_equal(t.tv_sec, loaded.tv_sec)
647
+ if t.respond_to?(:tv_nsec)
648
+ assert_equal(t.tv_nsec, loaded.tv_nsec)
649
+ else
650
+ assert_equal(t.tv_usec, loaded.tv_usec)
651
+ end
652
+ assert_equal(t.utc?, loaded.utc?)
653
+ assert_equal(t.utc_offset, loaded.utc_offset)
654
+ end
655
+
656
+ def test_json_object
657
+ obj = Jeez.new(true, 58)
658
+ dump_and_load(obj, false)
659
+ end
660
+
661
+ def test_json_object_create_deep
662
+ obj = One::Two::Three::Deep.new()
663
+ dump_and_load(obj, false)
664
+ end
665
+
666
+ def test_json_object_bad
667
+ json = %{{"^o":"Junk","x":true}}
668
+ begin
669
+ Oj.object_load(json)
670
+ rescue Exception => e
671
+ assert_equal("ArgumentError", e.class().name)
672
+ return
673
+ end
674
+ assert(false, "*** expected an exception")
675
+ end
676
+
677
+ def test_json_object_not_hat_hash
678
+ json = %{{"^#x":[1,2]}}
679
+ h = Oj.object_load(json)
680
+ assert_equal({1 => 2}, h);
681
+
682
+ json = %{{"~#x":[1,2]}}
683
+ h = Oj.object_load(json)
684
+ assert_equal({'~#x' => [1,2]}, h);
685
+ end
686
+
687
+ def test_json_struct
688
+ obj = Stuck.new(false, 7)
689
+ dump_and_load(obj, false)
690
+ end
691
+
692
+ def test_json_struct2
693
+ obj = One::Stuck2.new(false, 7)
694
+ dump_and_load(obj, false)
695
+ end
696
+
697
+ def test_json_anonymous_struct
698
+ s = Struct.new(:x, :y)
699
+ obj = s.new(1, 2)
700
+ json = Oj.dump(obj, :indent => 2, :mode => :object)
701
+ #puts json
702
+ loaded = Oj.object_load(json);
703
+ assert_equal(obj.members, loaded.members)
704
+ assert_equal(obj.values, loaded.values)
705
+ end
706
+
707
+ def test_json_non_str_hash
708
+ obj = { 59 => "young", false => true }
709
+ dump_and_load(obj, false)
710
+ end
711
+
712
+ def test_mixed_hash_object
713
+ Oj.default_options = { :mode => :object }
714
+ json = Oj.dump({ 1 => true, 'nil' => nil, :sim => 4 })
715
+ h = Oj.object_load(json)
716
+ assert_equal({ 1 => true, 'nil' => nil, :sim => 4 }, h)
717
+ end
718
+
719
+ def test_json_object_object
720
+ obj = Jeez.new(true, 58)
721
+ json = Oj.dump(obj, :mode => :object, :indent => 2)
722
+ assert(%{{
723
+ "^o":"ObjectJuice::Jeez",
724
+ "x":true,
725
+ "y":58
726
+ }
727
+ } == json ||
728
+ %{{
729
+ "^o":"ObjectJuice::Jeez",
730
+ "y":58,
731
+ "x":true
732
+ }
733
+ } == json)
734
+ obj2 = Oj.load(json, :mode => :object)
735
+ assert_equal(obj, obj2)
736
+ end
737
+
738
+ def test_to_hash_object_object
739
+ obj = Jazz.new(true, 58)
740
+ json = Oj.dump(obj, :mode => :object, :indent => 2)
741
+ assert(%{{
742
+ "^o":"ObjectJuice::Jazz",
743
+ "x":true,
744
+ "y":58
745
+ }
746
+ } == json ||
747
+ %{{
748
+ "^o":"ObjectJuice::Jazz",
749
+ "y":58,
750
+ "x":true
751
+ }
752
+ } == json)
753
+ obj2 = Oj.load(json, :mode => :object)
754
+ assert_equal(obj, obj2)
755
+ end
756
+
757
+ def test_as_json_object_object
758
+ obj = Orange.new(true, 58)
759
+ json = Oj.dump(obj, :mode => :object, :indent => 2)
760
+ assert(%{{
761
+ "^o":"ObjectJuice::Orange",
762
+ "x":true,
763
+ "y":58
764
+ }
765
+ } == json ||
766
+ %{{
767
+ "^o":"ObjectJuice::Orange",
768
+ "y":58,
769
+ "x":true
770
+ }
771
+ } == json)
772
+ obj2 = Oj.load(json, :mode => :object)
773
+ assert_equal(obj, obj2)
774
+ end
775
+
776
+ def test_object_object_no_cache
777
+ obj = Jam.new(true, 58)
778
+ json = Oj.dump(obj, :mode => :object, :indent => 2)
779
+ assert(%{{
780
+ "^o":"ObjectJuice::Jam",
781
+ "x":true,
782
+ "y":58
783
+ }
784
+ } == json ||
785
+ %{{
786
+ "^o":"ObjectJuice::Jam",
787
+ "y":58,
788
+ "x":true
789
+ }
790
+ } == json)
791
+ obj2 = Oj.load(json, :mode => :object, :class_cache => false)
792
+ assert_equal(obj, obj2)
793
+ end
794
+
795
+ def test_exception
796
+ err = nil
797
+ begin
798
+ raise StandardError.new('A Message')
799
+ rescue Exception => e
800
+ err = e
801
+ end
802
+ json = Oj.dump(err, :mode => :object, :indent => 2)
803
+ #puts "*** #{json}"
804
+ e2 = Oj.load(json, :mode => :strict)
805
+ assert_equal(err.class.to_s, e2['^o'])
806
+ assert_equal(err.message, e2['~mesg'])
807
+ assert_equal(err.backtrace, e2['~bt'])
808
+ e2 = Oj.load(json, :mode => :object)
809
+ if RUBY_VERSION.start_with?('1.8') || 'rubinius' == $ruby
810
+ assert_equal(e.class, e2.class);
811
+ assert_equal(e.message, e2.message);
812
+ assert_equal(e.backtrace, e2.backtrace);
813
+ else
814
+ assert_equal(e, e2);
815
+ end
816
+ end
817
+
818
+ def test_range_object
819
+ unless RUBY_VERSION.start_with?('1.8')
820
+ Oj.default_options = { :mode => :object }
821
+ json = Oj.dump(1..7, :mode => :object, :indent => 0)
822
+ if 'rubinius' == $ruby
823
+ assert(%{{"^O":"Range","begin":1,"end":7,"exclude_end?":false}} == json)
824
+ else
825
+ assert_equal(%{{"^u":["Range",1,7,false]}}, json)
826
+ end
827
+ dump_and_load(1..7, false)
828
+ dump_and_load(1..1, false)
829
+ dump_and_load(1...7, false)
830
+ end
831
+ end
832
+
833
+ def test_circular_hash
834
+ h = { 'a' => 7 }
835
+ h['b'] = h
836
+ json = Oj.dump(h, :mode => :object, :indent => 2, :circular => true)
837
+ h2 = Oj.object_load(json, :circular => true)
838
+ assert_equal(h2['b'].__id__, h2.__id__)
839
+ end
840
+
841
+ def test_circular_array
842
+ a = [7]
843
+ a << a
844
+ json = Oj.dump(a, :mode => :object, :indent => 2, :circular => true)
845
+ a2 = Oj.object_load(json, :circular => true)
846
+ assert_equal(a2[1].__id__, a2.__id__)
847
+ end
848
+
849
+ def test_circular_array2
850
+ a = [7]
851
+ a << a
852
+ json = Oj.dump(a, :mode => :object, :indent => 2, :circular => true)
853
+ assert_equal(%{[
854
+ "^i1",
855
+ 7,
856
+ "^r1"
857
+ ]
858
+ }, json)
859
+ a2 = Oj.load(json, :mode => :object, :circular => true)
860
+ assert_equal(a2[1].__id__, a2.__id__)
861
+ end
862
+
863
+ def test_circular_hash2
864
+ h = { 'a' => 7 }
865
+ h['b'] = h
866
+ json = Oj.dump(h, :mode => :object, :indent => 2, :circular => true)
867
+ ha = Oj.load(json, :mode => :strict)
868
+ assert_equal({'^i' => 1, 'a' => 7, 'b' => '^r1'}, ha)
869
+ Oj.load(json, :mode => :object, :circular => true)
870
+ assert_equal(h['b'].__id__, h.__id__)
871
+ end
872
+
873
+ def test_circular_object
874
+ obj = Jeez.new(nil, 58)
875
+ obj.x = obj
876
+ json = Oj.dump(obj, :mode => :object, :indent => 2, :circular => true)
877
+ obj2 = Oj.object_load(json, :circular => true)
878
+ assert_equal(obj2.x.__id__, obj2.__id__)
879
+ end
880
+
881
+ def test_circular_object2
882
+ obj = Jam.new(nil, 58)
883
+ obj.x = obj
884
+ json = Oj.dump(obj, :mode => :object, :indent => 2, :circular => true)
885
+ assert(%{{
886
+ "^o":"ObjectJuice::Jam",
887
+ "^i":1,
888
+ "x":"^r1",
889
+ "y":58
890
+ }
891
+ } == json ||
892
+ %{{
893
+ "^o":"ObjectJuice::Jam",
894
+ "^i":1,
895
+ "y":58,
896
+ "x":"^r1"
897
+ }
898
+ } == json)
899
+ obj2 = Oj.load(json, :mode => :object, :circular => true)
900
+ assert_equal(obj2.x.__id__, obj2.__id__)
901
+ end
902
+
903
+ def test_circular
904
+ h = { 'a' => 7 }
905
+ obj = Jeez.new(h, 58)
906
+ obj.x['b'] = obj
907
+ json = Oj.dump(obj, :mode => :object, :indent => 2, :circular => true)
908
+ Oj.object_load(json, :circular => true)
909
+ assert_equal(obj.x.__id__, h.__id__)
910
+ assert_equal(h['b'].__id__, obj.__id__)
911
+ end
912
+
913
+ def test_circular2
914
+ h = { 'a' => 7 }
915
+ obj = Jam.new(h, 58)
916
+ obj.x['b'] = obj
917
+ json = Oj.dump(obj, :mode => :object, :indent => 2, :circular => true)
918
+ ha = Oj.load(json, :mode => :strict)
919
+ assert_equal({'^o' => 'ObjectJuice::Jam', '^i' => 1, 'x' => { '^i' => 2, 'a' => 7, 'b' => '^r1' }, 'y' => 58 }, ha)
920
+ Oj.load(json, :mode => :object, :circular => true)
921
+ assert_equal(obj.x.__id__, h.__id__)
922
+ assert_equal(h['b'].__id__, obj.__id__)
923
+ end
924
+
925
+ def test_omit_nil
926
+ jam = Jam.new({'a' => 1, 'b' => nil }, nil)
927
+
928
+ json = Oj.dump(jam, :omit_nil => true, :mode => :object)
929
+ assert_equal(%|{"^o":"ObjectJuice::Jam","x":{"a":1}}|, json)
930
+ end
931
+
932
+ def test_odd_date
933
+ dump_and_load(Date.new(2012, 6, 19), false)
934
+ end
935
+
936
+ def test_odd_datetime
937
+ dump_and_load(DateTime.new(2012, 6, 19, 13, 5, Rational(4, 3)), false)
938
+ dump_and_load(DateTime.new(2012, 6, 19, 13, 5, Rational(7123456789, 1000000000)), false)
939
+ end
940
+
941
+ def test_bag
942
+ json = %{{
943
+ "^o":"ObjectJuice::Jem",
944
+ "x":true,
945
+ "y":58 }}
946
+ obj = Oj.load(json, :mode => :object, :auto_define => true)
947
+ assert_equal('ObjectJuice::Jem', obj.class.name)
948
+ assert_equal(true, obj.x)
949
+ assert_equal(58, obj.y)
950
+ end
951
+
952
+ def test_odd_string
953
+ Oj.register_odd(Strung, Strung, :create, :to_s, 'safe?')
954
+ s = Strung.new("Pete", true)
955
+ dump_and_load(s, false)
956
+ end
957
+
958
+ def test_odd_date_replaced
959
+ Oj.register_odd(Date, Date, :jd, :jd)
960
+ json = Oj.dump(Date.new(2015, 3, 7), :mode => :object)
961
+ assert_equal(%|{"^O":"Date","jd":2457089}|, json)
962
+ dump_and_load(Date.new(2012, 6, 19), false)
963
+ end
964
+
965
+ def test_odd_raw
966
+ Oj.register_odd_raw(Raw, Raw, :create, :to_json)
967
+ json = Oj.dump(Raw.new(%|{"a":1}|), :mode => :object)
968
+ assert_equal(%|{"^O":"ObjectJuice::Raw","to_json":{"a":1}}|, json)
969
+ h = Oj.load(json, :mode => :object)
970
+ assert_equal({'a' => 1}, h)
971
+ end
972
+
973
+ def test_odd_mod
974
+ Oj.register_odd(Ichi::Ni, Ichi::Ni, :direct, :dump)
975
+ json = Oj.dump(Ichi::Ni::San::Shi.new({'a' => 1}), :mode => :object)
976
+ assert_equal(%|{"^O":"ObjectJuice::Ichi::Ni::San::Shi","dump":{"a":1}}|, json)
977
+ h = Oj.load(json, :mode => :object)
978
+ assert_equal({'a' => 1}, h)
979
+ end
980
+
981
+ def test_auto_string
982
+ s = AutoStrung.new("Pete", true)
983
+ dump_and_load(s, false)
984
+ end
985
+
986
+ def test_auto_array
987
+ a = AutoArray.new([1, 'abc', nil], true)
988
+ dump_and_load(a, false)
989
+ end
990
+
991
+ def test_auto_hash
992
+ h = AutoHash.new(nil, true)
993
+ h['a'] = 1
994
+ h['b'] = 2
995
+ dump_and_load(h, false)
996
+ end
997
+
998
+ def dump_and_load(obj, trace=false)
999
+ json = Oj.dump(obj, :indent => 2, :mode => :object)
1000
+ puts json if trace
1001
+ loaded = Oj.object_load(json);
1002
+ if obj.nil?
1003
+ assert_nil(loaded)
1004
+ else
1005
+ assert_equal(obj, loaded)
1006
+ end
1007
+ loaded
1008
+ end
1009
+
1010
+ end