oj 3.2.1 → 3.3.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.
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '3.2.1'
4
+ VERSION = '3.3.0'
5
5
  end
@@ -70,6 +70,13 @@ mimic the json gem including methods called for encoding and inconsistencies
70
70
  between `JSON.dump()`, `JSON.generate()`, and `JSON()`. More details on the
71
71
  [{file:Custom.md}](Custom.md) page.
72
72
 
73
+ ## :wab Mode
74
+
75
+ WAB mode ignores all options except the indent option. Performance of this
76
+ mode is on slightly better than the :strict and :null modes. It is included to
77
+ support the [WABuR](https://github.com/ohler55/wabur) project. More details on
78
+ the [{file:WAB.md}](WAB.md) page.
79
+
73
80
  ## Options Matrix
74
81
 
75
82
  Not all options are available in all modes. The options matrix identifies the
@@ -77,46 +84,46 @@ options available in each mode. An `x` in the matrix indicates the option is
77
84
  supported in that mode. A number indicates the footnotes describe additional
78
85
  information.
79
86
 
80
- | Option | type | :null | :strict | :compat | :rails | :object | :custom |
81
- | ---------------------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- |
82
- | :allow_blank | Boolean | | | 1 | 1 | | x |
83
- | :allow_gc | Boolean | x | x | x | x | x | x |
84
- | :allow_invalid_unicode | Boolean | | | | | x | x |
85
- | :allow_nan | Boolean | | | x | | x | x |
86
- | :array_class | Class | | | x | x | | x |
87
- | :array_nl | String | | | | | | x |
88
- | :ascii_only | Boolean | x | x | 2 | 2 | x | x |
89
- | :auto_define | Boolean | | | | | x | x |
90
- | :bigdecimal_as_decimal | Boolean | | | | | x | x |
91
- | :bigdecimal_load | Boolean | | | | | | x |
92
- | :circular | Boolean | x | x | x | x | x | x |
93
- | :class_cache | Boolean | | | | | x | x |
94
- | :create_additions | Boolean | | | x | x | | x |
95
- | :create_id | String | | | x | x | | x |
96
- | :empty_string | Boolean | | | | | | x |
97
- | :escape_mode | Symbol | | | | | | x |
98
- | :float_precision | Fixnum | x | x | | | | x |
99
- | :hash_class | Class | | | x | x | | x |
100
- | :indent | Integer | x | x | 3 | 3 | x | x |
101
- | :indent_str | String | | | x | x | | x |
102
- | :match_string | Hash | | | x | x | | x |
103
- | :max_nesting | Fixnum | 4 | 4 | x | | 4 | 4 |
104
- | :mode | Symbol | - | - | - | - | - | - |
105
- | :nan | Symbol | | | | | | x |
106
- | :nilnil | Boolean | | | | | | x |
107
- | :object_class | Class | | | x | | | x |
108
- | :object_nl | String | | | x | x | | x |
109
- | :omit_nil | Boolean | x | x | x | x | x | x |
110
- | :quirks_mode | Boolean | | | 5 | | | x |
111
- | :second_precision | Fixnum | | | | | x | x |
112
- | :space | String | | | x | x | | x |
113
- | :space_before | String | | | x | x | | x |
114
- | :symbol_keys | Boolean | x | x | x | x | x | x |
115
- | :time_format | Symbol | | | | | x | x |
116
- | :use_as_json | Boolean | | | | | | x |
117
- | :use_to_hash | Boolean | | | | | | x |
118
- | :use_to_json | Boolean | | | | | | x |
119
- ----------------------------------------------------------------------------------------------
87
+ | Option | type | :null | :strict | :compat | :rails | :object | :custom | :wab |
88
+ | ---------------------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- |
89
+ | :allow_blank | Boolean | | | 1 | 1 | | x | |
90
+ | :allow_gc | Boolean | x | x | x | x | x | x | |
91
+ | :allow_invalid_unicode | Boolean | | | | | x | x | |
92
+ | :allow_nan | Boolean | | | x | | x | x | |
93
+ | :array_class | Class | | | x | x | | x | |
94
+ | :array_nl | String | | | | | | x | |
95
+ | :ascii_only | Boolean | x | x | 2 | 2 | x | x | |
96
+ | :auto_define | Boolean | | | | | x | x | |
97
+ | :bigdecimal_as_decimal | Boolean | | | | | x | x | |
98
+ | :bigdecimal_load | Boolean | | | | | | x | |
99
+ | :circular | Boolean | x | x | x | x | x | x | |
100
+ | :class_cache | Boolean | | | | | x | x | |
101
+ | :create_additions | Boolean | | | x | x | | x | |
102
+ | :create_id | String | | | x | x | | x | |
103
+ | :empty_string | Boolean | | | | | | x | |
104
+ | :escape_mode | Symbol | | | | | | x | |
105
+ | :float_precision | Fixnum | x | x | | | | x | |
106
+ | :hash_class | Class | | | x | x | | x | |
107
+ | :indent | Integer | x | x | 3 | 3 | x | x | x |
108
+ | :indent_str | String | | | x | x | | x | |
109
+ | :match_string | Hash | | | x | x | | x | |
110
+ | :max_nesting | Fixnum | 4 | 4 | x | | 4 | 4 | |
111
+ | :mode | Symbol | - | - | - | - | - | - | |
112
+ | :nan | Symbol | | | | | | x | |
113
+ | :nilnil | Boolean | | | | | | x | |
114
+ | :object_class | Class | | | x | | | x | |
115
+ | :object_nl | String | | | x | x | | x | |
116
+ | :omit_nil | Boolean | x | x | x | x | x | x | |
117
+ | :quirks_mode | Boolean | | | 5 | | | x | |
118
+ | :second_precision | Fixnum | | | | | x | x | |
119
+ | :space | String | | | x | x | | x | |
120
+ | :space_before | String | | | x | x | | x | |
121
+ | :symbol_keys | Boolean | x | x | x | x | x | x | |
122
+ | :time_format | Symbol | | | | | x | x | |
123
+ | :use_as_json | Boolean | | | | | | x | |
124
+ | :use_to_hash | Boolean | | | | | | x | |
125
+ | :use_to_json | Boolean | | | | | | x | |
126
+ --------------------------------------------------------------------------------------------------------
120
127
 
121
128
  1. :allow_blank an alias for :nilnil.
122
129
 
@@ -0,0 +1,13 @@
1
+ # WAB mode
2
+
3
+ The `:wab` mode ignores all options except the indent option. Performance of
4
+ this mode is slightly faster than the :strict and :null modes. It is included
5
+ to support the [WABuR](https://github.com/ohler55/wabur) project.
6
+
7
+ Options other than the indentation are not supported since the encoding and
8
+ formats are defined by the API that is used to encode data being passed from
9
+ one components in a WAB system and allowing an option that would break the
10
+ data exchange is best not supported.
11
+
12
+ The mode encodes like the strict mode except the URI, Time, WAB::UUID, and
13
+ BigDecimal are supported.
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env ruby
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 'perf'
10
+ require 'oj'
11
+
12
+ $verbose = false
13
+ $indent = 0
14
+ $iter = 20000
15
+ $with_bignum = false
16
+ $with_nums = true
17
+ $size = 0
18
+
19
+ opts = OptionParser.new
20
+ opts.on("-v", "verbose") { $verbose = true }
21
+ opts.on("-c", "--count [Int]", Integer, "iterations") { |i| $iter = i }
22
+ opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
23
+ opts.on("-s", "--size [Int]", Integer, "size (~Kbytes)") { |i| $size = i }
24
+ opts.on("-b", "with bignum") { $with_bignum = true }
25
+ opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
26
+ files = opts.parse(ARGV)
27
+
28
+ $obj = {
29
+ a: 'Alpha', # string
30
+ b: true, # boolean
31
+ c: 12345, # number
32
+ d: [ true, [false, [-123456789, nil], 3.9676, ['Something else.', false], nil]], # mix it up array
33
+ e: { zero: nil, one: 1, two: 2, three: [3], four: [0, 1, 2, 3, 4] }, # hash
34
+ f: nil, # nil
35
+ h: { a: { b: { c: { d: {e: { f: { g: nil }}}}}}}, # deep hash, not that deep
36
+ i: [[[[[[[nil]]]]]]] # deep array, again, not that deep
37
+ }
38
+ $obj[:g] = 12345678901234567890123456789 if $with_bignum
39
+
40
+ Oj.default_options = { :indent => $indent, :mode => :wab }
41
+
42
+ if 0 < $size
43
+ o = $obj
44
+ $obj = []
45
+ (4 * $size).times do
46
+ $obj << o
47
+ end
48
+ end
49
+
50
+ $json = Oj.dump($obj)
51
+ $obj_json = Oj.dump($obj, :mode => :object)
52
+ #puts "*** size: #{$obj_json.size}"
53
+ #puts "*** #{$obj_json}"
54
+ $failed = {} # key is same as String used in tests later
55
+
56
+ def capture_error(tag, orig, load_key, dump_key, &blk)
57
+ begin
58
+ obj = blk.call(orig)
59
+ raise "#{tag} #{dump_key} and #{load_key} did not return the same object as the original." unless orig == obj
60
+ rescue Exception => e
61
+ $failed[tag] = "#{e.class}: #{e.message}"
62
+ end
63
+ end
64
+
65
+ # Verify that all packages dump and load correctly and return the same Object as the original.
66
+ capture_error('Oj:wab', $obj, 'load', 'dump') { |o| Oj.wab_load(Oj.dump(o, :mode => :wab)) }
67
+ capture_error('Yajl', $obj, 'encode', 'parse') { |o| require 'yajl'; Yajl::Parser.parse(Yajl::Encoder.encode(o)) }
68
+ capture_error('JSON::Ext', $obj, 'generate', 'parse') { |o|
69
+ require 'json'
70
+ require 'json/ext'
71
+ JSON.generator = JSON::Ext::Generator
72
+ JSON.parser = JSON::Ext::Parser
73
+ JSON.parse(JSON.generate(o))
74
+ }
75
+ capture_error('JSON::Pure', $obj, 'generate', 'parse') { |o|
76
+ require 'json/pure'
77
+ JSON.generator = JSON::Pure::Generator
78
+ JSON.parser = JSON::Pure::Parser
79
+ JSON.parse(JSON.generate(o))
80
+ }
81
+
82
+ if $verbose
83
+ puts "json:\n#{$json}\n"
84
+ puts "object json:\n#{$obj_json}\n"
85
+ puts "Oj loaded object:\n#{Oj.wab_load($json)}\n"
86
+ puts "Yajl loaded object:\n#{Yajl::Parser.parse($json)}\n"
87
+ puts "JSON loaded object:\n#{JSON::Ext::Parser.new($json).parse}\n"
88
+ end
89
+
90
+ puts '-' * 80
91
+ puts "Wab Parse Performance"
92
+ perf = Perf.new()
93
+ unless $failed.has_key?('JSON::Ext')
94
+ perf.add('JSON::Ext', 'parse') { JSON.parse($json) }
95
+ perf.before('JSON::Ext') { JSON.parser = JSON::Ext::Parser }
96
+ end
97
+ unless $failed.has_key?('JSON::Pure')
98
+ perf.add('JSON::Pure', 'parse') { JSON.parse($json) }
99
+ perf.before('JSON::Pure') { JSON.parser = JSON::Pure::Parser }
100
+ end
101
+ unless $failed.has_key?('Oj:wab')
102
+ perf.add('Oj:wab', 'wab_load') { Oj.wab_load($json) }
103
+ end
104
+ perf.add('Yajl', 'parse') { Yajl::Parser.parse($json) } unless $failed.has_key?('Yajl')
105
+ perf.run($iter)
106
+
107
+ puts '-' * 80
108
+ puts "Wab Dump Performance"
109
+ perf = Perf.new()
110
+ unless $failed.has_key?('JSON::Ext')
111
+ perf.add('JSON::Ext', 'dump') { JSON.generate($obj) }
112
+ perf.before('JSON::Ext') { JSON.generator = JSON::Ext::Generator }
113
+ end
114
+ unless $failed.has_key?('JSON::Pure')
115
+ perf.add('JSON::Pure', 'generate') { JSON.generate($obj) }
116
+ perf.before('JSON::Pure') { JSON.generator = JSON::Pure::Generator }
117
+ end
118
+ unless $failed.has_key?('Oj:wab')
119
+ perf.add('Oj:wab', 'dump') { Oj.dump($obj, :mode => :wab) }
120
+ end
121
+ perf.add('Yajl', 'encode') { Yajl::Encoder.encode($obj) } unless $failed.has_key?('Yajl')
122
+ perf.run($iter)
123
+
124
+ puts
125
+ puts '-' * 80
126
+ puts
127
+
128
+ unless $failed.empty?
129
+ puts "The following packages were not included for the reason listed"
130
+ $failed.each { |tag,msg| puts "***** #{tag}: #{msg}" }
131
+ end
@@ -0,0 +1,307 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ $: << File.dirname(__FILE__)
5
+ $oj_dir = File.dirname(File.expand_path(File.dirname(__FILE__)))
6
+ %w(lib ext).each do |dir|
7
+ $: << File.join($oj_dir, dir)
8
+ end
9
+
10
+ require 'minitest'
11
+ require 'minitest/autorun'
12
+ require 'stringio'
13
+ require 'date'
14
+ require 'bigdecimal'
15
+ require 'uri'
16
+ require 'oj'
17
+
18
+ # Simple version of WAB::UUID for testing.
19
+ module WAB
20
+ class UUID
21
+ attr_reader :id
22
+ def initialize(id)
23
+ @id = id.downcase
24
+ raise Exception.new("Invalid UUID format.") if /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.match(@id).nil?
25
+ end
26
+ def to_s
27
+ @id
28
+ end
29
+ def ==(other)
30
+ other.is_a?(self.class) && @id == other.id
31
+ end
32
+ end # UUID
33
+ end # WAB
34
+
35
+
36
+ class WabJuice < Minitest::Test
37
+
38
+ module TestModule
39
+ end
40
+
41
+ def test_nil
42
+ dump_and_load(nil, false)
43
+ end
44
+
45
+ def test_true
46
+ dump_and_load(true, false)
47
+ end
48
+
49
+ def test_false
50
+ dump_and_load(false, false)
51
+ end
52
+
53
+ def test_fixnum
54
+ dump_and_load(0, false)
55
+ dump_and_load(12345, false)
56
+ dump_and_load(-54321, false)
57
+ dump_and_load(1, false)
58
+ end
59
+
60
+ def test_float
61
+ dump_and_load(0.0, false)
62
+ dump_and_load(12345.6789, false)
63
+ dump_and_load(70.35, false)
64
+ dump_and_load(-54321.012, false)
65
+ dump_and_load(1.7775, false)
66
+ dump_and_load(2.5024, false)
67
+ dump_and_load(2.48e16, false)
68
+ dump_and_load(2.48e100 * 1.0e10, false)
69
+ dump_and_load(-2.48e100 * 1.0e10, false)
70
+ end
71
+
72
+ def test_nan_dump
73
+ assert_raises() { Oj.dump(0/0.0, mode: :wab) }
74
+ end
75
+
76
+ def test_infinity_dump
77
+ assert_raises() { Oj.dump(1/0.0, mode: :wab) }
78
+ end
79
+
80
+ def test_neg_infinity_dump
81
+ assert_raises() { Oj.dump(-1/0.0, mode: :wab) }
82
+ end
83
+
84
+ def test_string
85
+ dump_and_load('', false)
86
+ dump_and_load('abc', false)
87
+ dump_and_load("abc\ndef", false)
88
+ dump_and_load("a\u0041", false)
89
+ end
90
+
91
+ def test_encode
92
+ dump_and_load("ぴーたー", false)
93
+ end
94
+
95
+ def test_array
96
+ dump_and_load([], false)
97
+ dump_and_load([true, false], false)
98
+ dump_and_load(['a', 1, nil], false)
99
+ dump_and_load([[nil]], false)
100
+ dump_and_load([[nil], 58], false)
101
+ end
102
+
103
+ def test_array_deep
104
+ dump_and_load([1,[2,[3,[4,[5,[6,[7,[8,[9,[10,[11,[12,[13,[14,[15,[16,[17,[18,[19,[20]]]]]]]]]]]]]]]]]]]], false)
105
+ end
106
+
107
+ def test_deep_nest
108
+ begin
109
+ n = 10000
110
+ Oj.wab_load('[' * n + ']' * n)
111
+ rescue Exception => e
112
+ assert(false, e.message)
113
+ end
114
+ end
115
+
116
+ # Hash
117
+ def test_hash
118
+ dump_and_load({}, false)
119
+ dump_and_load({ true: true, false: false}, false)
120
+ dump_and_load({ true: true, array: [], hash: { }}, false)
121
+ end
122
+
123
+ def test_hash_non_sym_keys
124
+ assert_raises() { Oj.dump({ 'true' => true}, mode: :wab) }
125
+ end
126
+
127
+ def test_hash_deep
128
+ dump_and_load({x1: {
129
+ x2: {
130
+ x3: {
131
+ x4: {
132
+ x5: {
133
+ x6: {
134
+ x7: {
135
+ x8: {
136
+ x9: {
137
+ x10: {
138
+ x11: {
139
+ x12: {
140
+ x13: {
141
+ x14: {
142
+ x15: {
143
+ x16: {
144
+ x17: {
145
+ x18: {
146
+ x19: {
147
+ x20: {}}}}}}}}}}}}}}}}}}}}}, false)
148
+ end
149
+
150
+ def test_non_str_hash
151
+ assert_raises() { Oj.dump({ 1 => true, 0 => false }, mode: :wab) }
152
+ end
153
+
154
+ def test_bignum_object
155
+ dump_and_load(7 ** 55, false)
156
+ end
157
+
158
+ # BigDecimal
159
+ def test_bigdecimal_wab
160
+ dump_and_load(BigDecimal.new('3.14159265358979323846'), false)
161
+ end
162
+
163
+ def test_bigdecimal_load
164
+ orig = BigDecimal.new('80.51')
165
+ json = Oj.dump(orig, mode: :wab)
166
+ bg = Oj.load(json, :mode => :wab, :bigdecimal_load => true)
167
+ assert_equal(BigDecimal, bg.class)
168
+ assert_equal(orig, bg)
169
+ end
170
+
171
+ def test_range
172
+ assert_raises() { Oj.dump(1..7, mode: :wab) }
173
+ end
174
+
175
+ # Stream IO
176
+ def test_io_string
177
+ json = %{{
178
+ "x":true,
179
+ "y":58,
180
+ "z": [1,2,3]
181
+ }
182
+ }
183
+ input = StringIO.new(json)
184
+ obj = Oj.wab_load(input)
185
+ assert_equal({ x: true, y: 58, z: [1, 2, 3]}, obj)
186
+ end
187
+
188
+ def test_io_file
189
+ filename = File.join(File.dirname(__FILE__), 'open_file_test.json')
190
+ File.open(filename, 'w') { |f| f.write(%{{
191
+ "x":true,
192
+ "y":58,
193
+ "z": [1,2,3]
194
+ }
195
+ }) }
196
+ f = File.new(filename)
197
+ obj = Oj.wab_load(f)
198
+ f.close()
199
+ assert_equal({ x: true, y: 58, z: [1, 2, 3]}, obj)
200
+ end
201
+
202
+ def test_symbol
203
+ json = Oj.dump(:abc, mode: :wab)
204
+ assert_equal('"abc"', json)
205
+ end
206
+
207
+ def test_time
208
+ t = Time.gm(2017, 1, 5, 23, 58, 7, 123456.789)
209
+ json = Oj.dump(t, mode: :wab)
210
+ assert_equal('"2017-01-05T23:58:07.123456789Z"', json)
211
+ # must load and convert to json as the Time#== does not match identical
212
+ # times due to the way it tracks fractional seconds.
213
+ loaded = Oj.wab_load(json);
214
+ assert_equal(json, Oj.dump(loaded, mode: :wab), "json mismatch after load")
215
+ end
216
+
217
+ def test_uuid
218
+ u = ::WAB::UUID.new('123e4567-e89b-12d3-a456-426655440000')
219
+ json = Oj.dump(u, mode: :wab)
220
+ assert_equal('"123e4567-e89b-12d3-a456-426655440000"', json)
221
+ dump_and_load(u, false)
222
+ end
223
+
224
+ def test_uri
225
+ u = URI('http://opo.technology/sample')
226
+ json = Oj.dump(u, mode: :wab)
227
+ assert_equal('"http://opo.technology/sample"', json)
228
+ dump_and_load(u, false)
229
+ end
230
+
231
+ def test_class
232
+ assert_raises() { Oj.dump(WabJuice, mode: :wab) }
233
+ end
234
+
235
+ def test_module
236
+ assert_raises() { Oj.dump(TestModule, mode: :wab) }
237
+ end
238
+
239
+ # symbol_keys option
240
+ def test_symbol_keys
241
+ json = %{{
242
+ "x":true,
243
+ "y":58,
244
+ "z": [1,2,3]
245
+ }
246
+ }
247
+ obj = Oj.wab_load(json, :symbol_keys => true)
248
+ assert_equal({ x: true, y: 58, z: [1, 2, 3]}, obj)
249
+ end
250
+
251
+ # comments
252
+ def test_comment_slash
253
+ json = %{{
254
+ "x":true,//three
255
+ "y":58,
256
+ "z": [1,2,
257
+ 3 // six
258
+ ]}
259
+ }
260
+ obj = Oj.wab_load(json)
261
+ assert_equal({ x: true, y: 58, z: [1, 2, 3]}, obj)
262
+ end
263
+
264
+ def test_comment_c
265
+ json = %{{
266
+ "x"/*one*/:/*two*/true,
267
+ "y":58,
268
+ "z": [1,2,3]}
269
+ }
270
+ obj = Oj.wab_load(json)
271
+ assert_equal({ x: true, y: 58, z: [1, 2, 3]}, obj)
272
+ end
273
+
274
+ def test_comment
275
+ json = %{{
276
+ "x"/*one*/:/*two*/true,//three
277
+ "y":58/*four*/,
278
+ "z": [1,2/*five*/,
279
+ 3 // six
280
+ ]
281
+ }
282
+ }
283
+ obj = Oj.wab_load(json)
284
+ assert_equal({ x: true, y: 58, z: [1, 2, 3]}, obj)
285
+ end
286
+
287
+ def test_double
288
+ json = %{{ "x": 1}{ "y": 2}}
289
+ results = []
290
+ Oj.load(json, :mode => :wab) { |x| results << x }
291
+
292
+ assert_equal([{ x: 1 }, { y: 2 }], results)
293
+ end
294
+
295
+ def dump_and_load(obj, trace=false)
296
+ json = Oj.dump(obj, mode: :wab, indent: 2)
297
+ puts json if trace
298
+ loaded = Oj.wab_load(json);
299
+ if obj.nil?
300
+ assert_nil(loaded)
301
+ else
302
+ assert_equal(obj, loaded)
303
+ end
304
+ loaded
305
+ end
306
+
307
+ end