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.
- data/README.md +95 -8
- data/ext/oj/fast.c +1540 -0
- data/ext/oj/load.c +1 -1
- data/ext/oj/oj.c +28 -21
- data/ext/oj/oj.h +6 -0
- data/lib/oj.rb +2 -1
- data/lib/oj/version.rb +1 -1
- data/test/perf_fast.rb +119 -0
- data/test/perf_strict.rb +13 -8
- data/test/test_fast.rb +331 -0
- data/test/where.rb +54 -0
- metadata +6 -2
data/ext/oj/load.c
CHANGED
data/ext/oj/oj.c
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
/* oj.c
|
2
|
-
* Copyright (c)
|
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
|
-
|
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(
|
100
|
+
int elen = (int)strlen(oj_default_options.encoding);
|
98
101
|
|
99
|
-
rb_hash_aset(opts, encoding_sym, (0 == elen) ? Qnil : rb_str_new(
|
100
|
-
rb_hash_aset(opts, indent_sym, INT2FIX(
|
101
|
-
rb_hash_aset(opts, circular_sym, (Yes ==
|
102
|
-
rb_hash_aset(opts, auto_define_sym, (Yes ==
|
103
|
-
switch (
|
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, &
|
134
|
-
{ auto_define_sym, &
|
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
|
-
*
|
149
|
+
*oj_default_options.encoding = '\0';
|
147
150
|
} else {
|
148
151
|
Check_Type(v, T_STRING);
|
149
|
-
strncpy(
|
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
|
-
|
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
|
-
|
165
|
+
oj_default_options.mode = ObjectMode;
|
163
166
|
} else if (strict_sym == v) {
|
164
|
-
|
167
|
+
oj_default_options.mode = StrictMode;
|
165
168
|
} else if (compat_sym == v) {
|
166
|
-
|
169
|
+
oj_default_options.mode = CompatMode;
|
167
170
|
} else if (null_sym == v) {
|
168
|
-
|
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 =
|
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 =
|
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 =
|
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
|
-
|
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
|
data/ext/oj/oj.h
CHANGED
@@ -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
data/lib/oj/version.rb
CHANGED
data/test/perf_fast.rb
ADDED
@@ -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
|
data/test/perf_strict.rb
CHANGED
@@ -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['
|
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['
|
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 }
|
data/test/test_fast.rb
ADDED
@@ -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
|