oj 0.9.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of oj might be problematic. Click here for more details.

@@ -533,7 +533,7 @@ read_array(ParseInfo pi, int hint) {
533
533
  RSTRUCT_PTR(a)[cnt] = e;
534
534
  #endif
535
535
  } else {
536
- a = rb_ary_push(a, e);
536
+ rb_ary_push(a, e);
537
537
  }
538
538
  cnt++;
539
539
  }
@@ -1,5 +1,6 @@
1
1
  /* oj.c
2
- * Copyright (c) 2011, Peter Ohler
2
+ * Copyright (c) 2012, Peter Ohler
3
+ *
3
4
  * All rights reserved.
4
5
  *
5
6
  * Redistribution and use in source and binary forms, with or without
@@ -60,6 +61,8 @@ VALUE oj_bag_class;
60
61
  VALUE oj_struct_class;
61
62
  VALUE oj_time_class;
62
63
 
64
+ VALUE oj_slash_string;
65
+
63
66
  static VALUE auto_define_sym;
64
67
  static VALUE circular_sym;
65
68
  static VALUE compat_sym;
@@ -73,7 +76,7 @@ static VALUE strict_sym;
73
76
  Cache oj_class_cache = 0;
74
77
  Cache oj_attr_cache = 0;
75
78
 
76
- static struct _Options default_options = {
79
+ struct _Options oj_default_options = {
77
80
  { '\0' }, // encoding
78
81
  0, // indent
79
82
  No, // circular
@@ -94,13 +97,13 @@ static struct _Options default_options = {
94
97
  static VALUE
95
98
  get_def_opts(VALUE self) {
96
99
  VALUE opts = rb_hash_new();
97
- int elen = (int)strlen(default_options.encoding);
100
+ int elen = (int)strlen(oj_default_options.encoding);
98
101
 
99
- rb_hash_aset(opts, encoding_sym, (0 == elen) ? Qnil : rb_str_new(default_options.encoding, elen));
100
- rb_hash_aset(opts, indent_sym, INT2FIX(default_options.indent));
101
- rb_hash_aset(opts, circular_sym, (Yes == default_options.circular) ? Qtrue : ((No == default_options.circular) ? Qfalse : Qnil));
102
- rb_hash_aset(opts, auto_define_sym, (Yes == default_options.auto_define) ? Qtrue : ((No == default_options.auto_define) ? Qfalse : Qnil));
103
- switch (default_options.mode) {
102
+ rb_hash_aset(opts, encoding_sym, (0 == elen) ? Qnil : rb_str_new(oj_default_options.encoding, elen));
103
+ rb_hash_aset(opts, indent_sym, INT2FIX(oj_default_options.indent));
104
+ rb_hash_aset(opts, circular_sym, (Yes == oj_default_options.circular) ? Qtrue : ((No == oj_default_options.circular) ? Qfalse : Qnil));
105
+ rb_hash_aset(opts, auto_define_sym, (Yes == oj_default_options.auto_define) ? Qtrue : ((No == oj_default_options.auto_define) ? Qfalse : Qnil));
106
+ switch (oj_default_options.mode) {
104
107
  case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
105
108
  case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
106
109
  case NullMode: rb_hash_aset(opts, mode_sym, null_sym); break;
@@ -130,8 +133,8 @@ get_def_opts(VALUE self) {
130
133
  static VALUE
131
134
  set_def_opts(VALUE self, VALUE opts) {
132
135
  struct _YesNoOpt ynos[] = {
133
- { circular_sym, &default_options.circular },
134
- { auto_define_sym, &default_options.auto_define },
136
+ { circular_sym, &oj_default_options.circular },
137
+ { auto_define_sym, &oj_default_options.auto_define },
135
138
  { Qnil, 0 }
136
139
  };
137
140
  YesNoOpt o;
@@ -143,29 +146,29 @@ set_def_opts(VALUE self, VALUE opts) {
143
146
  if (Qundef == v) {
144
147
  // no change
145
148
  } else if (Qnil == v) {
146
- *default_options.encoding = '\0';
149
+ *oj_default_options.encoding = '\0';
147
150
  } else {
148
151
  Check_Type(v, T_STRING);
149
- strncpy(default_options.encoding, StringValuePtr(v), sizeof(default_options.encoding) - 1);
152
+ strncpy(oj_default_options.encoding, StringValuePtr(v), sizeof(oj_default_options.encoding) - 1);
150
153
  }
151
154
 
152
155
  v = rb_hash_aref(opts, indent_sym);
153
156
  if (Qnil != v) {
154
157
  Check_Type(v, T_FIXNUM);
155
- default_options.indent = FIX2INT(v);
158
+ oj_default_options.indent = FIX2INT(v);
156
159
  }
157
160
 
158
161
  v = rb_hash_lookup2(opts, mode_sym, Qundef);
159
162
  if (Qundef == v || Qnil == v) {
160
163
  // ignore
161
164
  } else if (object_sym == v) {
162
- default_options.mode = ObjectMode;
165
+ oj_default_options.mode = ObjectMode;
163
166
  } else if (strict_sym == v) {
164
- default_options.mode = StrictMode;
167
+ oj_default_options.mode = StrictMode;
165
168
  } else if (compat_sym == v) {
166
- default_options.mode = CompatMode;
169
+ oj_default_options.mode = CompatMode;
167
170
  } else if (null_sym == v) {
168
- default_options.mode = NullMode;
171
+ oj_default_options.mode = NullMode;
169
172
  } else {
170
173
  rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.\n");
171
174
  }
@@ -243,7 +246,7 @@ parse_options(VALUE ropts, Options copts) {
243
246
  static VALUE
244
247
  load(char *json, int argc, VALUE *argv, VALUE self) {
245
248
  VALUE obj;
246
- struct _Options options = default_options;
249
+ struct _Options options = oj_default_options;
247
250
 
248
251
  if (1 == argc) {
249
252
  parse_options(*argv, &options);
@@ -311,7 +314,7 @@ load_file(int argc, VALUE *argv, VALUE self) {
311
314
  static VALUE
312
315
  dump(int argc, VALUE *argv, VALUE self) {
313
316
  char *json;
314
- struct _Options copts = default_options;
317
+ struct _Options copts = oj_default_options;
315
318
  VALUE rstr;
316
319
 
317
320
  if (2 == argc) {
@@ -343,7 +346,7 @@ dump(int argc, VALUE *argv, VALUE self) {
343
346
  */
344
347
  static VALUE
345
348
  to_file(int argc, VALUE *argv, VALUE self) {
346
- struct _Options copts = default_options;
349
+ struct _Options copts = oj_default_options;
347
350
 
348
351
  if (3 == argc) {
349
352
  parse_options(argv[2], &copts);
@@ -393,10 +396,14 @@ void Init_oj() {
393
396
  object_sym = ID2SYM(rb_intern("object")); rb_ary_push(keep, object_sym);
394
397
  strict_sym = ID2SYM(rb_intern("strict")); rb_ary_push(keep, strict_sym);
395
398
 
396
- default_options.mode = ObjectMode;
399
+ oj_slash_string = rb_str_new2("/"); rb_ary_push(keep, oj_slash_string);
400
+
401
+ oj_default_options.mode = ObjectMode;
397
402
 
398
403
  oj_cache_new(&oj_class_cache);
399
404
  oj_cache_new(&oj_attr_cache);
405
+
406
+ oj_init_doc();
400
407
  }
401
408
 
402
409
  void
@@ -91,12 +91,18 @@ extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
91
91
 
92
92
  extern void _oj_raise_error(const char *msg, const char *xml, const char *current, const char* file, int line);
93
93
 
94
+ extern void oj_init_doc(void);
95
+
94
96
  extern VALUE Oj;
97
+ extern struct _Options oj_default_options;
95
98
 
96
99
  extern VALUE oj_bag_class;
100
+ extern VALUE oj_doc_class;
97
101
  extern VALUE oj_struct_class;
98
102
  extern VALUE oj_time_class;
99
103
 
104
+ extern VALUE oj_slash_string;
105
+
100
106
  extern ID oj_as_json_id;
101
107
  extern ID oj_at_id;
102
108
  extern ID oj_instance_variables_id;
data/lib/oj.rb CHANGED
@@ -1,4 +1,5 @@
1
- # Copyright (c) 2011, Peter Ohler<br>
1
+ # Copyright (c) 2011, Peter Ohler
2
+ #
2
3
  # All rights reserved.
3
4
  #
4
5
  # Redistribution and use in source and binary forms, with or without
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '0.9.0'
4
+ VERSION = '1.0.0'
5
5
  end
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env ruby -wW1
2
+ # encoding: UTF-8
3
+
4
+ $: << '.'
5
+ $: << File.join(File.dirname(__FILE__), "../lib")
6
+ $: << File.join(File.dirname(__FILE__), "../ext")
7
+
8
+ require 'optparse'
9
+ require 'yajl'
10
+ require 'perf'
11
+ require 'json'
12
+ require 'json/ext'
13
+ require 'oj'
14
+
15
+ $verbose = false
16
+ $indent = 0
17
+ $iter = 100000
18
+ $gets = 0
19
+ $fetch = false
20
+
21
+ opts = OptionParser.new
22
+ opts.on("-v", "verbose") { $verbose = true }
23
+ opts.on("-c", "--count [Int]", Integer, "iterations") { |i| $iter = i }
24
+ opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
25
+ opts.on("-g", "--gets [Int]", Integer, "number of gets") { |i| $gets = i }
26
+ opts.on("-f", "fetch") { $fetch = true }
27
+ opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
28
+ files = opts.parse(ARGV)
29
+
30
+ # This just navigates to each leaf of the JSON structure.
31
+ def dig(obj, &blk)
32
+ case obj
33
+ when Array
34
+ obj.each { |e| dig(e, &blk) }
35
+ when Hash
36
+ obj.values.each { |e| dig(e, &blk) }
37
+ else
38
+ blk.yield(obj)
39
+ end
40
+ end
41
+
42
+ $obj = {
43
+ 'a' => 'Alpha', # string
44
+ 'b' => true, # boolean
45
+ 'c' => 12345, # number
46
+ 'd' => [ true, [false, {'12345' => 12345, 'nil' => nil}, 3.967, { 'x' => 'something', 'y' => false, 'z' => true}, nil]], # mix it up array
47
+ 'e' => { 'one' => 1, 'two' => 2 }, # hash
48
+ 'f' => nil, # nil
49
+ 'g' => 12345678901234567890123456789, # big number
50
+ 'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
51
+ 'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
52
+ }
53
+
54
+ Oj.default_options = { :indent => $indent, :mode => :compat }
55
+
56
+ $json = Oj.dump($obj)
57
+ $failed = {} # key is same as String used in tests later
58
+
59
+ def capture_error(tag, orig, load_key, dump_key, &blk)
60
+ begin
61
+ obj = blk.call(orig)
62
+ raise "#{tag} #{dump_key} and #{load_key} did not return the same object as the original." unless orig == obj
63
+ rescue Exception => e
64
+ $failed[tag] = "#{e.class}: #{e.message}"
65
+ end
66
+ end
67
+
68
+ # Verify that all packages dump and load correctly and return the same Object as the original.
69
+ capture_error('Oj::Doc', $obj, 'load', 'dump') { |o| Oj::Doc.open(Oj.dump(o, :mode => :strict)) { |f| f.fetch() } }
70
+ capture_error('Yajl', $obj, 'encode', 'parse') { |o| Yajl::Parser.parse(Yajl::Encoder.encode(o)) }
71
+ capture_error('JSON::Ext', $obj, 'generate', 'parse') { |o| JSON.generator = JSON::Ext::Generator; JSON::Ext::Parser.new(JSON.generate(o)).parse }
72
+
73
+ if $verbose
74
+ puts "json:\n#{$json}\n"
75
+ end
76
+
77
+ puts '-' * 80
78
+ puts "Parse Performance"
79
+ perf = Perf.new()
80
+ perf.add('Oj::Doc', 'parse') { Oj::Doc.open($json) {|f| } } unless $failed.has_key?('Oj::Doc')
81
+ perf.add('Yajl', 'parse') { Yajl::Parser.parse($json) } unless $failed.has_key?('Yajl')
82
+ perf.add('JSON::Ext', 'parse') { JSON::Ext::Parser.new($json).parse } unless $failed.has_key?('JSON::Ext')
83
+ perf.run($iter)
84
+
85
+ if 0 < $gets
86
+ puts '-' * 80
87
+ puts "Parse and get all values Performance"
88
+ perf = Perf.new()
89
+ perf.add('Oj::Doc', 'parse') { Oj::Doc.open($json) {|f| $gets.times { f.each_value() {} } } } unless $failed.has_key?('Oj::Doc')
90
+ perf.add('Yajl', 'parse') { $gets.times { dig(Yajl::Parser.parse($json)) {} } } unless $failed.has_key?('Yajl')
91
+ perf.add('JSON::Ext', 'parse') { $gets.times { dig(JSON::Ext::Parser.new($json).parse) {} } } unless $failed.has_key?('JSON::Ext')
92
+ perf.run($iter)
93
+ end
94
+
95
+ if $fetch
96
+ puts '-' * 80
97
+ puts "fetch nested Performance"
98
+ json_hash = Oj.load($json, :mode => :strict)
99
+ Oj::Doc.open($json) do |fast|
100
+ #puts "*** C fetch: #{fast.fetch('/d/2/4/y')}"
101
+ #puts "*** Ruby fetch: #{json_hash.fetch('d', []).fetch(1, []).fetch(3, []).fetch('x', nil)}"
102
+ perf = Perf.new()
103
+ perf.add('Oj::Doc', 'fetch') { fast.fetch('/d/2/4/x'); fast.fetch('/h/a/b/c/d/e/f/g'); fast.fetch('/i/1/1/1/1/1/1/1') }
104
+ # version that fails gracefully
105
+ perf.add('Ruby', 'fetch') do
106
+ json_hash.fetch('d', []).fetch(1, []).fetch(3, []).fetch('x', nil)
107
+ json_hash.fetch('h', {}).fetch('a', {}).fetch('b', {}).fetch('c', {}).fetch('d', {}).fetch('e', {}).fetch('f', {}).fetch('g', {})
108
+ json_hash.fetch('i', []).fetch(0, []).fetch(0, []).fetch(0, []).fetch(0, []).fetch(0, []).fetch(0, []).fetch(0, nil)
109
+ end
110
+ # version that raises if the path is incorrect
111
+ # perf.add('Ruby', 'fetch') { $fetch.times { json_hash['d'][1][3][1] } }
112
+ perf.run($iter)
113
+ end
114
+ end
115
+
116
+ unless $failed.empty?
117
+ puts "The following packages were not included for the reason listed"
118
+ $failed.each { |tag,msg| puts "***** #{tag}: #{msg}" }
119
+ end
@@ -82,14 +82,17 @@ files = opts.parse(ARGV)
82
82
 
83
83
  if $with_nums
84
84
  $obj = {
85
- 'a' => 'Alpha',
86
- 'b' => true,
87
- 'c' => 12345,
88
- 'd' => [ true, [false, [12345, nil], 3.967, ['something', false], nil]],
89
- 'e' => { 'one' => 1, 'two' => 2 },
90
- 'f' => nil,
85
+ 'a' => 'Alpha', # string
86
+ 'b' => true, # boolean
87
+ 'c' => 12345, # number
88
+ 'd' => [ true, [false, [12345, nil], 3.967, ['something', false], nil]], # mix it up array
89
+ 'e' => { 'one' => 1, 'two' => 2 }, # hash
90
+ 'f' => nil, # nil
91
+ #'g' => 12345678901234567890123456789, # big number
92
+ 'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
93
+ 'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
91
94
  }
92
- $obj['h'] = 12345678901234567890123456789 if $with_bignum
95
+ $obj['g'] = 12345678901234567890123456789 if $with_bignum
93
96
  else
94
97
  $obj = {
95
98
  'a' => 'Alpha',
@@ -98,9 +101,11 @@ else
98
101
  'd' => [ true, [false, ['12345', nil], '3.967', ['something', false], nil]],
99
102
  'e' => { 'one' => '1', 'two' => '2' },
100
103
  'f' => nil,
104
+ 'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
105
+ 'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
101
106
  }
102
107
  end
103
- $obj['g'] = Jazz.new() if $with_object
108
+ $obj['j'] = Jazz.new() if $with_object
104
109
 
105
110
  Oj.default_options = { :indent => $indent, :mode => :compat }
106
111
  Ox.default_options = { :indent => $indent, :mode => :object }
@@ -0,0 +1,331 @@
1
+ #!/usr/bin/env ruby -wW1
2
+ # encoding: UTF-8
3
+
4
+ $: << File.join(File.dirname(__FILE__), "../lib")
5
+ $: << File.join(File.dirname(__FILE__), "../ext")
6
+
7
+ require 'test/unit'
8
+ require 'oj'
9
+
10
+ $json1 = %{{
11
+ "array": [
12
+ {
13
+ "num" : 3,
14
+ "string": "message",
15
+ "hash" : {
16
+ "h2" : {
17
+ "a" : [ 1, 2, 3 ]
18
+ }
19
+ }
20
+ }
21
+ ],
22
+ "boolean" : true
23
+ }}
24
+
25
+ class DocTest < ::Test::Unit::TestCase
26
+ def test_nil
27
+ json = %{null}
28
+ Oj::Doc.open(json) do |doc|
29
+ assert_equal(NilClass, doc.type)
30
+ assert_equal(nil, doc.fetch())
31
+ end
32
+ end
33
+
34
+ def test_true
35
+ json = %{true}
36
+ Oj::Doc.open(json) do |doc|
37
+ assert_equal(TrueClass, doc.type)
38
+ assert_equal(true, doc.fetch())
39
+ end
40
+ end
41
+
42
+ def test_false
43
+ json = %{false}
44
+ Oj::Doc.open(json) do |doc|
45
+ assert_equal(FalseClass, doc.type)
46
+ assert_equal(false, doc.fetch())
47
+ end
48
+ end
49
+
50
+ def test_string
51
+ json = %{"a string"}
52
+ Oj::Doc.open(json) do |doc|
53
+ assert_equal(String, doc.type)
54
+ assert_equal('a string', doc.fetch())
55
+ end
56
+ end
57
+
58
+ def test_fixnum
59
+ json = %{12345}
60
+ Oj::Doc.open(json) do |doc|
61
+ assert_equal(Fixnum, doc.type)
62
+ assert_equal(12345, doc.fetch())
63
+ end
64
+ end
65
+
66
+ def test_float
67
+ json = %{12345.6789}
68
+ Oj::Doc.open(json) do |doc|
69
+ assert_equal(Float, doc.type)
70
+ assert_equal(12345.6789, doc.fetch())
71
+ end
72
+ end
73
+
74
+ def test_float_exp
75
+ json = %{12345.6789e7}
76
+ Oj::Doc.open(json) do |doc|
77
+ assert_equal(Float, doc.type)
78
+ assert_equal(12345.6789e7, doc.fetch())
79
+ end
80
+ end
81
+
82
+ def test_array_empty
83
+ json = %{[]}
84
+ Oj::Doc.open(json) do |doc|
85
+ assert_equal(Array, doc.type)
86
+ assert_equal([], doc.fetch())
87
+ end
88
+ end
89
+
90
+ def test_array
91
+ json = %{[true,false]}
92
+ Oj::Doc.open(json) do |doc|
93
+ assert_equal(Array, doc.type)
94
+ assert_equal([true, false], doc.fetch())
95
+ end
96
+ end
97
+
98
+ def test_hash_empty
99
+ json = %{{}}
100
+ Oj::Doc.open(json) do |doc|
101
+ assert_equal(Hash, doc.type)
102
+ assert_equal({}, doc.fetch())
103
+ end
104
+ end
105
+
106
+ def test_hash
107
+ json = %{{"one":true,"two":false}}
108
+ Oj::Doc.open(json) do |doc|
109
+ assert_equal(Hash, doc.type)
110
+ assert_equal({'one' => true, 'two' => false}, doc.fetch())
111
+ end
112
+ end
113
+
114
+ # move() and where?()
115
+ def test_move_hash
116
+ json = %{{"one":{"two":false}}}
117
+ Oj::Doc.open(json) do |doc|
118
+ doc.move('/one')
119
+ assert_equal('/one', doc.where?)
120
+ doc.move('/one/two')
121
+ assert_equal('/one/two', doc.where?)
122
+ end
123
+ end
124
+
125
+ def test_move_array
126
+ json = %{[1,[2,true]]}
127
+ Oj::Doc.open(json) do |doc|
128
+ doc.move('/1')
129
+ assert_equal('/1', doc.where?)
130
+ doc.move('/2/1')
131
+ assert_equal('/2/1', doc.where?)
132
+ end
133
+ end
134
+
135
+ def test_move
136
+ Oj::Doc.open($json1) do |doc|
137
+ [ '/',
138
+ '/array',
139
+ '/boolean',
140
+ '/array/1/hash/h2/a/3',
141
+ ].each do |p|
142
+ doc.move(p)
143
+ assert_equal(p, doc.where?)
144
+ end
145
+ begin
146
+ doc.move('/array/x')
147
+ rescue Exception => e
148
+ assert_equal('/', doc.where?)
149
+ assert(true)
150
+ end
151
+ end
152
+ end
153
+
154
+ def test_move_relative
155
+ Oj::Doc.open($json1) do |doc|
156
+ [['/', 'array', '/array'],
157
+ ['/array', '1/num', '/array/1/num'],
158
+ ['/array/1/hash', 'h2/a', '/array/1/hash/h2/a'],
159
+ ['/array/1', 'hash/h2/a/2', '/array/1/hash/h2/a/2'],
160
+ ['/array/1/hash', '../string', '/array/1/string'],
161
+ ['/array/1/hash', '..', '/array/1'],
162
+ ].each do |start,path,where|
163
+ doc.move(start)
164
+ doc.move(path)
165
+ assert_equal(where, doc.where?)
166
+ end
167
+ end
168
+ end
169
+
170
+ def test_type
171
+ Oj::Doc.open($json1) do |doc|
172
+ [['/', Hash],
173
+ ['/array', Array],
174
+ ['/array/1', Hash],
175
+ ['/array/1/num', Fixnum],
176
+ ['/array/1/string', String],
177
+ ['/array/1/hash/h2/a', Array],
178
+ ['/array/1/hash/../num', Fixnum],
179
+ ['/array/1/hash/../..', Array],
180
+ ].each do |path,type|
181
+ assert_equal(type, doc.type(path))
182
+ end
183
+ end
184
+ end
185
+
186
+ def test_local_key
187
+ Oj::Doc.open($json1) do |doc|
188
+ [['/', nil],
189
+ ['/array', 'array'],
190
+ ['/array/1', 1],
191
+ ['/array/1/num', 'num'],
192
+ ['/array/1/string', 'string'],
193
+ ['/array/1/hash/h2/a', 'a'],
194
+ ['/array/1/hash/../num', 'num'],
195
+ ['/array/1/hash/..', 1],
196
+ ['/array/1/hash/../..', 'array'],
197
+ ].each do |path,key|
198
+ doc.move(path)
199
+ assert_equal(key, doc.local_key())
200
+ end
201
+ end
202
+ end
203
+
204
+ def test_fetch_move
205
+ Oj::Doc.open($json1) do |doc|
206
+ [['/array/1/num', 3],
207
+ ['/array/1/string', 'message'],
208
+ ['/array/1/hash/h2/a', [1, 2, 3]],
209
+ ['/array/1/hash/../num', 3],
210
+ ['/array/1/hash/..', {'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}],
211
+ ['/array/1/hash/../..', [{'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}]],
212
+ ['/array/1', {'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}],
213
+ ['/array', [{'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}]],
214
+ ['/', {'array' => [{'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}], 'boolean' => true}],
215
+ ].each do |path,val|
216
+ doc.move(path)
217
+ assert_equal(val, doc.fetch())
218
+ end
219
+ end
220
+ end
221
+
222
+ def test_fetch_path
223
+ Oj::Doc.open($json1) do |doc|
224
+ [['/array/1/num', 3],
225
+ ['/array/1/string', 'message'],
226
+ ['/array/1/hash/h2/a', [1, 2, 3]],
227
+ ['/array/1/hash/../num', 3],
228
+ ['/array/1/hash/..', {'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}],
229
+ ['/array/1/hash/../..', [{'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}]],
230
+ ['/array/1', {'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}],
231
+ ['/array', [{'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}]],
232
+ ['/', {'array' => [{'num' => 3, 'string' => 'message', 'hash' => {'h2' => {'a' => [1, 2, 3]}}}], 'boolean' => true}],
233
+ ].each do |path,val|
234
+ assert_equal(val, doc.fetch(path))
235
+ end
236
+ end
237
+ end
238
+
239
+ def test_home
240
+ Oj::Doc.open($json1) do |doc|
241
+ doc.move('/array/1/num')
242
+ doc.home()
243
+ assert_equal('/', doc.where?)
244
+ end
245
+ end
246
+
247
+ def test_each_value_root
248
+ Oj::Doc.open($json1) do |doc|
249
+ values = []
250
+ doc.each_value() { |v| values << v.to_s }
251
+ assert_equal(['1', '2', '3', '3', 'message', 'true'], values.sort)
252
+ end
253
+ end
254
+
255
+ def test_each_value_move
256
+ Oj::Doc.open($json1) do |doc|
257
+ doc.move('/array/1/hash')
258
+ values = []
259
+ doc.each_value() { |v| values << v.to_s }
260
+ assert_equal(['1', '2', '3'], values.sort)
261
+ end
262
+ end
263
+
264
+ def test_each_value_path
265
+ Oj::Doc.open($json1) do |doc|
266
+ values = []
267
+ doc.each_value('/array/1/hash') { |v| values << v.to_s }
268
+ assert_equal(['1', '2', '3'], values.sort)
269
+ end
270
+ end
271
+
272
+ def test_each_child_move
273
+ Oj::Doc.open($json1) do |doc|
274
+ locations = []
275
+ doc.move('/array/1/hash/h2/a')
276
+ doc.each_child() { |d| locations << d.where? }
277
+ assert_equal(['/array/1/hash/h2/a/1', '/array/1/hash/h2/a/2', '/array/1/hash/h2/a/3'], locations)
278
+ locations = []
279
+ doc.move('/array/1')
280
+ doc.each_child() { |d| locations << d.where? }
281
+ assert_equal(['/array/1/num', '/array/1/string', '/array/1/hash'], locations)
282
+ end
283
+ end
284
+
285
+ def test_each_child_path
286
+ Oj::Doc.open($json1) do |doc|
287
+ locations = []
288
+ doc.each_child('/array/1/hash/h2/a') { |d| locations << d.where? }
289
+ assert_equal(['/array/1/hash/h2/a/1', '/array/1/hash/h2/a/2', '/array/1/hash/h2/a/3'], locations)
290
+ locations = []
291
+ doc.each_child('/array/1') { |d| locations << d.where? }
292
+ assert_equal(['/array/1/num', '/array/1/string', '/array/1/hash'], locations)
293
+ end
294
+ end
295
+
296
+ def test_size
297
+ Oj::Doc.open('[1,2,3]') do |doc|
298
+ assert_equal(4, doc.size)
299
+ end
300
+ Oj::Doc.open('{"a":[1,2,3]}') do |doc|
301
+ assert_equal(5, doc.size)
302
+ end
303
+ end
304
+
305
+ def test_open_file
306
+ filename = 'open_file_test.json'
307
+ File.open(filename, 'w') { |f| f.write('{"a":[1,2,3]}') }
308
+ Oj::Doc.open_file(filename) do |doc|
309
+ assert_equal(5, doc.size)
310
+ end
311
+ end
312
+
313
+ def test_dump
314
+ Oj::Doc.open('[1,[2,3]]') do |doc|
315
+ assert_equal('[1,[2,3]]', doc.dump())
316
+ end
317
+ Oj::Doc.open('[1,[2,3]]') do |doc|
318
+ assert_equal('[2,3]', doc.dump('/2'))
319
+ end
320
+ end
321
+
322
+ def test_each_leaf
323
+ results = Oj::Doc.open('[1,[2,3]]') do |doc|
324
+ h = {}
325
+ doc.each_leaf() { |d| h[d.where?] = d.fetch() }
326
+ h
327
+ end
328
+ assert_equal({'/1' => 1, '/2/1' => 2, '/2/2' => 3}, results)
329
+ end
330
+
331
+ end # DocTest