jsi 0.0.4 → 0.4.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.
- checksums.yaml +4 -4
- data/.simplecov +3 -1
- data/CHANGELOG.md +48 -0
- data/LICENSE.md +613 -0
- data/README.md +84 -45
- data/jsi.gemspec +11 -14
- data/lib/jsi.rb +31 -12
- data/lib/jsi/base.rb +310 -344
- data/lib/jsi/base/to_rb.rb +2 -0
- data/lib/jsi/jsi_coder.rb +91 -0
- data/lib/jsi/json-schema-fragments.rb +3 -135
- data/lib/jsi/json.rb +3 -0
- data/lib/jsi/json/node.rb +72 -197
- data/lib/jsi/json/pointer.rb +419 -0
- data/lib/jsi/metaschema.rb +7 -0
- data/lib/jsi/metaschema_node.rb +218 -0
- data/lib/jsi/pathed_node.rb +118 -0
- data/lib/jsi/schema.rb +168 -223
- data/lib/jsi/schema_classes.rb +158 -0
- data/lib/jsi/simple_wrap.rb +12 -0
- data/lib/jsi/typelike_modules.rb +71 -45
- data/lib/jsi/util.rb +47 -57
- data/lib/jsi/version.rb +1 -1
- data/lib/schemas/json-schema.org/draft-04/schema.rb +7 -0
- data/lib/schemas/json-schema.org/draft-06/schema.rb +7 -0
- data/resources/icons/AGPL-3.0.png +0 -0
- data/test/base_array_test.rb +210 -84
- data/test/base_hash_test.rb +201 -58
- data/test/base_test.rb +212 -121
- data/test/jsi_coder_test.rb +85 -0
- data/test/jsi_json_arraynode_test.rb +26 -25
- data/test/jsi_json_hashnode_test.rb +40 -39
- data/test/jsi_json_node_test.rb +95 -126
- data/test/jsi_json_pointer_test.rb +102 -0
- data/test/jsi_typelike_as_json_test.rb +53 -0
- data/test/metaschema_node_test.rb +19 -0
- data/test/schema_module_test.rb +21 -0
- data/test/schema_test.rb +109 -97
- data/test/spreedly_openapi_test.rb +8 -0
- data/test/test_helper.rb +42 -8
- data/test/util_test.rb +14 -14
- metadata +54 -25
- data/LICENSE.txt +0 -21
- data/lib/jsi/schema_instance_json_coder.rb +0 -83
- data/lib/jsi/struct_json_coder.rb +0 -30
- data/test/schema_instance_json_coder_test.rb +0 -121
- data/test/struct_json_coder_test.rb +0 -130
@@ -0,0 +1,85 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
describe JSI::JSICoder do
|
4
|
+
let(:schema_content) do
|
5
|
+
{properties: {foo: {}, bar: {}}}
|
6
|
+
end
|
7
|
+
let(:schema) { JSI::Schema.new(schema_content) }
|
8
|
+
let(:options) { {} }
|
9
|
+
let(:schema_instance_json_coder) { JSI::JSICoder.new(schema, options) }
|
10
|
+
describe 'json' do
|
11
|
+
describe 'load' do
|
12
|
+
it 'loads nil' do
|
13
|
+
assert_nil(schema_instance_json_coder.load(nil))
|
14
|
+
end
|
15
|
+
it 'loads a hash' do
|
16
|
+
assert_equal(schema.new_jsi('foo' => 'bar'), schema_instance_json_coder.load({"foo" => "bar"}))
|
17
|
+
end
|
18
|
+
it 'loads something else' do
|
19
|
+
assert_equal(schema.new_jsi([[]]), schema_instance_json_coder.load([[]]))
|
20
|
+
end
|
21
|
+
describe 'array' do
|
22
|
+
let(:options) { {array: true} }
|
23
|
+
it 'loads an array of hashes' do
|
24
|
+
data = [{"foo" => "bar"}, {"foo" => "baz"}]
|
25
|
+
assert_equal([schema.new_jsi('foo' => 'bar'), schema.new_jsi('foo' => 'baz')], schema_instance_json_coder.load(data))
|
26
|
+
end
|
27
|
+
it 'loads an empty array' do
|
28
|
+
assert_equal([], schema_instance_json_coder.load([]))
|
29
|
+
end
|
30
|
+
it 'loads a not an array' do
|
31
|
+
assert_raises(TypeError) do
|
32
|
+
schema_instance_json_coder.load(Object.new)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
describe 'array schema' do
|
37
|
+
let(:schema_content) { {items: {properties: {foo: {}, bar: {}}}} }
|
38
|
+
it 'loads an array of hashes' do
|
39
|
+
data = [{"foo" => "bar"}, {"foo" => "baz"}]
|
40
|
+
assert_equal(schema.new_jsi([{'foo' => 'bar'}, {'foo' => 'baz'}]), schema_instance_json_coder.load(data))
|
41
|
+
end
|
42
|
+
it 'loads an empty array' do
|
43
|
+
assert_equal(schema.new_jsi([]), schema_instance_json_coder.load([]))
|
44
|
+
end
|
45
|
+
it 'loads a not an array' do
|
46
|
+
instance = Object.new
|
47
|
+
assert_equal(schema.new_jsi(instance), schema_instance_json_coder.load(instance))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
describe 'dump' do
|
52
|
+
it 'dumps nil' do
|
53
|
+
assert_nil(schema_instance_json_coder.dump(nil))
|
54
|
+
end
|
55
|
+
it 'dumps a schema_instance_class' do
|
56
|
+
assert_equal({"foo" => "x", "bar" => "y"}, schema_instance_json_coder.dump(schema.new_jsi(foo: 'x', bar: 'y')))
|
57
|
+
end
|
58
|
+
it 'dumps something else' do
|
59
|
+
assert_raises(TypeError) do
|
60
|
+
schema_instance_json_coder.dump(Object.new)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
it 'dumps some of the keys of a schema_instance_class after loading in a partial one' do
|
64
|
+
schema_instance_class = schema_instance_json_coder.load({'foo' => 'who'})
|
65
|
+
assert_equal({'foo' => 'who'}, schema_instance_json_coder.dump(schema_instance_class))
|
66
|
+
schema_instance_class.bar = 'whar'
|
67
|
+
assert_equal({'foo' => 'who', 'bar' => 'whar'}, schema_instance_json_coder.dump(schema_instance_class))
|
68
|
+
end
|
69
|
+
describe 'array' do
|
70
|
+
let(:options) { {array: true} }
|
71
|
+
it 'dumps an array of schema_instances' do
|
72
|
+
schema_instances = [schema.new_jsi(foo: 'x', bar: 'y'), schema.new_jsi(foo: 'z', bar: 'q')]
|
73
|
+
assert_equal([{"foo" => "x", "bar" => "y"}, {"foo" => "z", "bar" => "q"}], schema_instance_json_coder.dump(schema_instances))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
describe 'array schema' do
|
77
|
+
let(:schema_content) { {items: {properties: {foo: {}, bar: {}}}} }
|
78
|
+
it 'dumps a schema_instance array' do
|
79
|
+
schema_instances = schema.new_jsi([{foo: 'x', bar: 'y'}, {foo: 'z', bar: 'q'}])
|
80
|
+
assert_equal([{"foo" => "x", "bar" => "y"}, {"foo" => "z", "bar" => "q"}], schema_instance_json_coder.dump(schema_instances))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -3,23 +3,24 @@ require_relative 'test_helper'
|
|
3
3
|
document_types = [
|
4
4
|
{
|
5
5
|
make_document: -> (d) { d },
|
6
|
-
|
6
|
+
node_document: ['a', ['b', 'q'], {'c' => {'d' => 'e'}}],
|
7
7
|
type_desc: 'Array',
|
8
8
|
},
|
9
9
|
{
|
10
10
|
make_document: -> (d) { SortOfArray.new(d) },
|
11
|
-
|
11
|
+
node_document: SortOfArray.new(['a', SortOfArray.new(['b', 'q']), SortOfHash.new({'c' => SortOfHash.new({'d' => 'e'})})]),
|
12
12
|
type_desc: 'sort of Array-like',
|
13
13
|
},
|
14
14
|
]
|
15
15
|
document_types.each do |document_type|
|
16
16
|
describe "JSI::JSON::ArrayNode with #{document_type[:type_desc]}" do
|
17
|
-
#
|
18
|
-
let(:
|
17
|
+
# node_document of the node being tested
|
18
|
+
let(:node_document) { document_type[:node_document] }
|
19
19
|
# by default the node is the whole document
|
20
20
|
let(:path) { [] }
|
21
|
+
let(:node_ptr) { JSI::JSON::Pointer.new(path) }
|
21
22
|
# the node being tested
|
22
|
-
let(:node) { JSI::JSON::Node.new_by_type(
|
23
|
+
let(:node) { JSI::JSON::Node.new_by_type(node_document, node_ptr) }
|
23
24
|
|
24
25
|
describe '#[] bad index' do
|
25
26
|
it 'improves TypeError for Array subsript' do
|
@@ -55,7 +56,7 @@ document_types.each do |document_type|
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
describe '#as_json' do
|
58
|
-
let(:
|
59
|
+
let(:node_document) { document_type[:make_document].call(['a', 'b']) }
|
59
60
|
it '#as_json' do
|
60
61
|
assert_equal(['a', 'b'], node.as_json)
|
61
62
|
assert_equal(['a', 'b'], node.as_json(some_option: false))
|
@@ -107,35 +108,35 @@ document_types.each do |document_type|
|
|
107
108
|
# for this reason, rassoc is NOT defined on Arraylike. it's here with as_json.
|
108
109
|
#
|
109
110
|
# I've never even seen anybody use rassoc. of all the methods to put into the standard library ...
|
110
|
-
it('#rassoc')
|
111
|
+
it('#rassoc') { assert_equal(['b', 'q'], node.as_json.rassoc('q')) }
|
111
112
|
it('#repeated_combination') { assert_equal([[]], node.repeated_combination(0).to_a) }
|
112
113
|
it('#repeated_permutation') { assert_equal([[]], node.repeated_permutation(0).to_a) }
|
113
|
-
it('#reverse')
|
114
|
-
it('#reverse_each')
|
115
|
-
it('#rindex')
|
116
|
-
it('#rotate')
|
117
|
-
it('#sample')
|
118
|
-
it('#shelljoin')
|
119
|
-
it('#shuffle')
|
120
|
-
it('#slice')
|
121
|
-
it('#sort')
|
122
|
-
it('#take')
|
123
|
-
it('#take_while')
|
124
|
-
it('#transpose')
|
125
|
-
it('#uniq')
|
126
|
-
it('#values_at')
|
127
|
-
it('#zip')
|
114
|
+
it('#reverse') { assert_equal([node[2], node[1], 'a'], node.reverse) }
|
115
|
+
it('#reverse_each') { assert_equal([node[2], node[1], 'a'], node.reverse_each.to_a) }
|
116
|
+
it('#rindex') { assert_equal(0, node.rindex('a')) }
|
117
|
+
it('#rotate') { assert_equal([node[1], node[2], 'a'], node.rotate) }
|
118
|
+
it('#sample') { assert_equal('a', JSI::JSON::Node.new_doc(['a']).sample) }
|
119
|
+
it('#shelljoin') { assert_equal('a', JSI::JSON::Node.new_doc(['a']).shelljoin) } if [].respond_to?(:shelljoin)
|
120
|
+
it('#shuffle') { assert_equal(3, node.shuffle.size) }
|
121
|
+
it('#slice') { assert_equal(['a'], node.slice(0, 1)) }
|
122
|
+
it('#sort') { assert_equal(['a'], JSI::JSON::Node.new_doc(['a']).sort) }
|
123
|
+
it('#take') { assert_equal(['a'], node.take(1)) }
|
124
|
+
it('#take_while') { assert_equal([], node.take_while { false }) }
|
125
|
+
it('#transpose') { assert_equal([], JSI::JSON::Node.new_doc([]).transpose) }
|
126
|
+
it('#uniq') { assert_equal(node.to_a, node.uniq) }
|
127
|
+
it('#values_at') { assert_equal(['a'], node.values_at(0)) }
|
128
|
+
it('#zip') { assert_equal([['a', 'a'], [node[1], node[1]], [node[2], node[2]]], node.zip(node)) }
|
128
129
|
end
|
129
130
|
describe 'modified copy methods' do
|
130
131
|
it('#reject') { assert_equal(JSI::JSON::Node.new_doc(['a']), node.reject { |e| e != 'a' }) }
|
131
132
|
it('#select') { assert_equal(JSI::JSON::Node.new_doc(['a']), node.select { |e| e == 'a' }) }
|
132
|
-
it('#compact') { assert_equal(JSI::JSON::Node.new_doc(node.
|
133
|
+
it('#compact') { assert_equal(JSI::JSON::Node.new_doc(node.node_content.to_ary), node.compact) }
|
133
134
|
describe 'at a depth' do
|
134
|
-
let(:
|
135
|
+
let(:node_document) { document_type[:make_document].call([['b', 'q'], {'c' => ['d', 'e']}]) }
|
135
136
|
let(:path) { ['1', 'c'] }
|
136
137
|
it('#select') do
|
137
138
|
selected = node.select { |e| e == 'd' }
|
138
|
-
equivalent = JSI::JSON::Node.new_by_type([['b', 'q'], {'c' => ['d']}],
|
139
|
+
equivalent = JSI::JSON::Node.new_by_type([['b', 'q'], {'c' => ['d']}], node_ptr)
|
139
140
|
assert_equal(equivalent, selected)
|
140
141
|
end
|
141
142
|
end
|
@@ -3,23 +3,24 @@ require_relative 'test_helper'
|
|
3
3
|
document_types = [
|
4
4
|
{
|
5
5
|
make_document: -> (d) { d },
|
6
|
-
|
6
|
+
node_document: {'a' => 'b', 'c' => {'d' => 'e'}},
|
7
7
|
type_desc: 'Hash',
|
8
8
|
},
|
9
9
|
{
|
10
10
|
make_document: -> (d) { SortOfHash.new(d) },
|
11
|
-
|
11
|
+
node_document: SortOfHash.new({'a' => 'b', 'c' => SortOfHash.new({'d' => 'e'})}),
|
12
12
|
type_desc: 'sort of Hash-like',
|
13
13
|
},
|
14
14
|
]
|
15
15
|
document_types.each do |document_type|
|
16
16
|
describe "JSI::JSON::HashNode with #{document_type[:type_desc]}" do
|
17
|
-
#
|
18
|
-
let(:
|
17
|
+
# node_document of the node being tested
|
18
|
+
let(:node_document) { document_type[:node_document] }
|
19
19
|
# by default the node is the whole document
|
20
20
|
let(:path) { [] }
|
21
|
+
let(:node_ptr) { JSI::JSON::Pointer.new(path) }
|
21
22
|
# the node being tested
|
22
|
-
let(:node) { JSI::JSON::Node.new_by_type(
|
23
|
+
let(:node) { JSI::JSON::Node.new_by_type(node_document, node_ptr) }
|
23
24
|
|
24
25
|
describe '#each' do
|
25
26
|
it 'iterates, one argument' do
|
@@ -54,21 +55,21 @@ document_types.each do |document_type|
|
|
54
55
|
end
|
55
56
|
end
|
56
57
|
describe '#merge' do
|
57
|
-
let(:
|
58
|
+
let(:node_document) { document_type[:make_document].call({'a' => {'b' => 0}, 'c' => {'d' => 'e'}}) }
|
58
59
|
# testing the node at 'c' here, merging a hash at a path within a document.
|
59
60
|
let(:path) { ['c'] }
|
60
61
|
it 'merges' do
|
61
62
|
merged = node.merge('x' => 'y')
|
62
|
-
# check the
|
63
|
-
assert_equal({'a' => {'b' => 0}, 'c' => {'d' => 'e', 'x' => 'y'}}, merged.
|
63
|
+
# check the node_content at 'c' was merged with the remainder of the document intact (at 'a')
|
64
|
+
assert_equal({'a' => {'b' => 0}, 'c' => {'d' => 'e', 'x' => 'y'}}, merged.node_document)
|
64
65
|
# check the original node retains its original document
|
65
|
-
assert_equal(document_type[:make_document].call({'a' => {'b' => 0}, 'c' => {'d' => 'e'}}), node.
|
66
|
+
assert_equal(document_type[:make_document].call({'a' => {'b' => 0}, 'c' => {'d' => 'e'}}), node.node_document)
|
66
67
|
# check that unnecessary copies of unaffected parts of the document were not made
|
67
|
-
assert_equal(node.
|
68
|
+
assert_equal(node.node_document.to_hash['a'].object_id, merged.node_document['a'].object_id)
|
68
69
|
end
|
69
70
|
end
|
70
71
|
describe '#as_json' do
|
71
|
-
let(:
|
72
|
+
let(:node_document) { document_type[:make_document].call({'a' => 'b'}) }
|
72
73
|
it '#as_json' do
|
73
74
|
assert_equal({'a' => 'b'}, node.as_json)
|
74
75
|
assert_equal({'a' => 'b'}, node.as_json(this_option: 'what?'))
|
@@ -80,46 +81,46 @@ document_types.each do |document_type|
|
|
80
81
|
it('#empty?') { assert_equal(false, node.empty?) }
|
81
82
|
it('#has_key?') { assert_equal(true, node.has_key?('a')) }
|
82
83
|
it('#include?') { assert_equal(false, node.include?('q')) }
|
83
|
-
it('#key?')
|
84
|
-
it('#keys')
|
85
|
-
it('#length')
|
86
|
-
it('#member?')
|
87
|
-
it('#size')
|
84
|
+
it('#key?') { assert_equal(true, node.key?('c')) }
|
85
|
+
it('#keys') { assert_equal(['a', 'c'], node.keys) }
|
86
|
+
it('#length') { assert_equal(2, node.length) }
|
87
|
+
it('#member?') { assert_equal(false, node.member?(0)) }
|
88
|
+
it('#size') { assert_equal(2, node.size) }
|
88
89
|
end
|
89
90
|
describe 'key + value methods' do
|
90
|
-
it('#<')
|
91
|
-
it('#<=')
|
92
|
-
it('#>')
|
93
|
-
it('#>=')
|
94
|
-
it('#any?')
|
95
|
-
it('#assoc')
|
96
|
-
it('#dig')
|
97
|
-
it('#each_pair')
|
98
|
-
it('#each_value')
|
99
|
-
it('#fetch')
|
91
|
+
it('#<') { assert_equal(true, node < {'a' => 'b', 'c' => node['c'], 'x' => 'y'}) } if {}.respond_to?(:<)
|
92
|
+
it('#<=') { assert_equal(true, node <= node) } if {}.respond_to?(:<=)
|
93
|
+
it('#>') { assert_equal(true, node > {}) } if {}.respond_to?(:>)
|
94
|
+
it('#>=') { assert_equal(false, node >= {'foo' => 'bar'}) } if {}.respond_to?(:>=)
|
95
|
+
it('#any?') { assert_equal(false, node.any? { |k, v| v == 3 }) }
|
96
|
+
it('#assoc') { assert_equal(['a', 'b'], node.assoc('a')) }
|
97
|
+
it('#dig') { assert_equal('e', node.dig('c', 'd')) } if {}.respond_to?(:dig)
|
98
|
+
it('#each_pair') { assert_equal([['a', 'b'], ['c', node['c']]], node.each_pair.to_a) }
|
99
|
+
it('#each_value') { assert_equal(['b', node['c']], node.each_value.to_a) }
|
100
|
+
it('#fetch') { assert_equal('b', node.fetch('a')) }
|
100
101
|
it('#fetch_values') { assert_equal(['b'], node.fetch_values('a')) } if {}.respond_to?(:fetch_values)
|
101
|
-
it('#has_value?')
|
102
|
-
it('#invert')
|
103
|
-
it('#key')
|
104
|
-
it('#rassoc')
|
105
|
-
it('#to_h')
|
106
|
-
it('#to_proc')
|
102
|
+
it('#has_value?') { assert_equal(true, node.has_value?('b')) }
|
103
|
+
it('#invert') { assert_equal({'b' => 'a', node['c'] => 'c'}, node.invert) }
|
104
|
+
it('#key') { assert_equal('a', node.key('b')) }
|
105
|
+
it('#rassoc') { assert_equal(['a', 'b'], node.rassoc('b')) }
|
106
|
+
it('#to_h') { assert_equal({'a' => 'b', 'c' => node['c']}, node.to_h) }
|
107
|
+
it('#to_proc') { assert_equal('b', node.to_proc.call('a')) } if {}.respond_to?(:to_proc)
|
107
108
|
if {}.respond_to?(:transform_values)
|
108
109
|
it('#transform_values') { assert_equal({'a' => nil, 'c' => nil}, node.transform_values { |_| nil }) }
|
109
110
|
end
|
110
|
-
it('#value?')
|
111
|
-
it('#values')
|
112
|
-
it('#values_at')
|
111
|
+
it('#value?') { assert_equal(false, node.value?('0')) }
|
112
|
+
it('#values') { assert_equal(['b', node['c']], node.values) }
|
113
|
+
it('#values_at') { assert_equal(['b'], node.values_at('a')) }
|
113
114
|
end
|
114
115
|
describe 'modified copy methods' do
|
115
116
|
# I'm going to rely on the #merge test above to test the modified copy functionality and just do basic
|
116
117
|
# tests of all the modified copy methods here
|
117
|
-
it('#merge')
|
118
|
-
it('#reject')
|
119
|
-
it('#select')
|
118
|
+
it('#merge') { assert_equal(JSI::JSON::Node.new_doc(node.node_content), node.merge({})) }
|
119
|
+
it('#reject') { assert_equal(JSI::JSON::Node.new_doc({}), node.reject { true }) }
|
120
|
+
it('#select') { assert_equal(JSI::JSON::Node.new_doc({}), node.select { false }) }
|
120
121
|
# Hash#compact only available as of ruby 2.5.0
|
121
122
|
if {}.respond_to?(:compact)
|
122
|
-
it('#compact') { assert_equal(JSI::JSON::Node.new_doc({"a" => "b", "c" => node.
|
123
|
+
it('#compact') { assert_equal(JSI::JSON::Node.new_doc({"a" => "b", "c" => node.node_content.to_hash["c"]}), node.compact) }
|
123
124
|
end
|
124
125
|
end
|
125
126
|
JSI::Hashlike::DESTRUCTIVE_METHODS.each do |destructive_method_name|
|
data/test/jsi_json_node_test.rb
CHANGED
@@ -2,61 +2,70 @@ require_relative 'test_helper'
|
|
2
2
|
|
3
3
|
describe JSI::JSON::Node do
|
4
4
|
let(:path) { [] }
|
5
|
-
let(:
|
5
|
+
let(:node_ptr) { JSI::JSON::Pointer.new(path) }
|
6
|
+
let(:node) { JSI::JSON::Node.new(node_document, node_ptr) }
|
6
7
|
|
7
8
|
describe 'initialization' do
|
8
9
|
it 'initializes' do
|
9
|
-
node = JSI::JSON::Node.new({'a' => 'b'},
|
10
|
-
assert_equal({'a' => 'b'}, node.
|
11
|
-
assert_equal([], node.
|
10
|
+
node = JSI::JSON::Node.new({'a' => 'b'}, node_ptr)
|
11
|
+
assert_equal({'a' => 'b'}, node.node_document)
|
12
|
+
assert_equal(JSI::JSON::Pointer.new([]), node.node_ptr)
|
13
|
+
end
|
14
|
+
it 'initializes, node_ptr is not node_ptr' do
|
15
|
+
err = assert_raises(TypeError) { JSI::JSON::Node.new({'a' => 'b'}, []) }
|
16
|
+
assert_equal('node_ptr must be a JSI::JSON::Pointer. got: [] (Array)', err.message)
|
17
|
+
end
|
18
|
+
it 'initializes, node_document is another Node' do
|
19
|
+
err = assert_raises(TypeError) { JSI::JSON::Node.new(JSI::JSON::Node.new({'a' => 'b'}, node_ptr), node_ptr) }
|
20
|
+
assert_equal("node_document of a Node should not be another JSI::JSON::Node: #<JSI::JSON::Node # {\"a\"=>\"b\"}>", err.message)
|
12
21
|
end
|
13
22
|
end
|
14
23
|
describe 'initialization by .new_by_type' do
|
15
24
|
it 'initializes HashNode' do
|
16
25
|
node = JSI::JSON::Node.new_doc({'a' => 'b'})
|
17
26
|
assert_instance_of(JSI::JSON::HashNode, node)
|
18
|
-
assert_equal({'a' => 'b'}, node.
|
27
|
+
assert_equal({'a' => 'b'}, node.node_document)
|
19
28
|
end
|
20
29
|
it 'initializes ArrayNode' do
|
21
30
|
node = JSI::JSON::Node.new_doc(['a', 'b'])
|
22
31
|
assert_instance_of(JSI::JSON::ArrayNode, node)
|
23
|
-
assert_equal(['a', 'b'], node.
|
32
|
+
assert_equal(['a', 'b'], node.node_document)
|
24
33
|
end
|
25
34
|
it 'initializes Node' do
|
26
35
|
object = Object.new
|
27
36
|
node = JSI::JSON::Node.new_doc(object)
|
28
37
|
assert_instance_of(JSI::JSON::Node, node)
|
29
|
-
assert_equal(object, node.
|
38
|
+
assert_equal(object, node.node_document)
|
30
39
|
end
|
31
40
|
end
|
32
|
-
describe '#
|
33
|
-
it 'is a ::JSON::
|
34
|
-
assert_instance_of(::JSON::
|
41
|
+
describe '#node_ptr' do
|
42
|
+
it 'is a JSI::JSON::Pointer' do
|
43
|
+
assert_instance_of(JSI::JSON::Pointer, JSI::JSON::Node.new({}, node_ptr).node_ptr)
|
35
44
|
end
|
36
45
|
end
|
37
|
-
describe '#
|
38
|
-
it 'returns the
|
39
|
-
assert_equal({'a' => 'b'}, JSI::JSON::Node.new({'a' => 'b'},
|
46
|
+
describe '#node_content' do
|
47
|
+
it 'returns the node_content at the root' do
|
48
|
+
assert_equal({'a' => 'b'}, JSI::JSON::Node.new({'a' => 'b'}, node_ptr).node_content)
|
40
49
|
end
|
41
|
-
it 'returns the
|
42
|
-
assert_equal('b', JSI::JSON::Node.new([0, {'x' => [{'a' => ['b']}]}], [1, 'x', 0, 'a', 0]).
|
50
|
+
it 'returns the node_content from the deep' do
|
51
|
+
assert_equal('b', JSI::JSON::Node.new([0, {'x' => [{'a' => ['b']}]}], JSI::JSON::Pointer.new([1, 'x', 0, 'a', 0])).node_content)
|
43
52
|
end
|
44
53
|
end
|
45
54
|
describe '#deref' do
|
46
|
-
let(:
|
55
|
+
let(:node_document) do
|
47
56
|
{
|
48
57
|
'foo' => {'bar' => ['baz']},
|
49
58
|
'a' => {'$ref' => '#/foo'},
|
50
59
|
}
|
51
60
|
end
|
52
61
|
it 'follows a $ref' do
|
53
|
-
assert_equal({'bar' => ['baz']}, node['a'].deref.
|
62
|
+
assert_equal({'bar' => ['baz']}, node['a'].deref.node_content)
|
54
63
|
end
|
55
64
|
it 'returns the node when there is no $ref to follow' do
|
56
|
-
assert_equal({'bar' => ['baz']}, node['foo'].deref.
|
65
|
+
assert_equal({'bar' => ['baz']}, node['foo'].deref.node_content)
|
57
66
|
end
|
58
67
|
describe "dealing with google's invalid $refs" do
|
59
|
-
let(:
|
68
|
+
let(:node_document) do
|
60
69
|
{
|
61
70
|
'schemas' => {'bar' => {'description' => ['baz']}},
|
62
71
|
'a' => {'$ref' => 'bar', 'foo' => 'bar'},
|
@@ -64,163 +73,122 @@ describe JSI::JSON::Node do
|
|
64
73
|
end
|
65
74
|
it 'subscripts a node consisting of a $ref WITHOUT following' do
|
66
75
|
subscripted = node['a']
|
67
|
-
assert_equal({'$ref' => 'bar', 'foo' => 'bar'}, subscripted.
|
68
|
-
assert_equal(['a'], subscripted.
|
76
|
+
assert_equal({'$ref' => 'bar', 'foo' => 'bar'}, subscripted.node_content)
|
77
|
+
assert_equal(JSI::JSON::Pointer.new(['a']), subscripted.node_ptr)
|
69
78
|
end
|
70
79
|
it 'looks for a node in #/schemas with the name of the $ref' do
|
71
|
-
assert_equal({'description' => ['baz']}, node['a'].deref.
|
72
|
-
end
|
73
|
-
it 'follows a $ref when subscripting past it' do
|
74
|
-
subscripted = node['a']['description']
|
75
|
-
assert_equal(['baz'], subscripted.content)
|
76
|
-
assert_equal(['schemas', 'bar', 'description'], subscripted.path)
|
77
|
-
end
|
78
|
-
it 'does not follow a $ref when subscripting a key that is present' do
|
79
|
-
subscripted = node['a']['foo']
|
80
|
-
assert_equal('bar', subscripted)
|
80
|
+
assert_equal({'description' => ['baz']}, node['a'].deref.node_content)
|
81
81
|
end
|
82
82
|
end
|
83
83
|
describe "dealing with whatever this is" do
|
84
84
|
# I think google uses this style in some cases maybe. I don't remember.
|
85
|
-
let(:
|
85
|
+
let(:node_document) do
|
86
86
|
{
|
87
87
|
'schemas' => {'bar' => {'id' => 'BarID', 'description' => 'baz'}},
|
88
88
|
'a' => {'$ref' => 'BarID'},
|
89
89
|
}
|
90
90
|
end
|
91
91
|
it 'looks for a node in #/schemas with the name of the $ref' do
|
92
|
-
assert_equal({'id' => 'BarID', 'description' => 'baz'}, node['a'].deref.
|
92
|
+
assert_equal({'id' => 'BarID', 'description' => 'baz'}, node['a'].deref.node_content)
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
96
96
|
describe '#[]' do
|
97
97
|
describe 'without dereferencing' do
|
98
|
-
let(:
|
98
|
+
let(:node_document) { [0, {'x' => [{'a' => ['b']}]}] }
|
99
99
|
it 'subscripts arrays and hashes' do
|
100
100
|
assert_equal('b', node[1]['x'][0]['a'][0])
|
101
101
|
end
|
102
102
|
it 'returns ArrayNode for an array' do
|
103
103
|
subscripted = node[1]['x']
|
104
104
|
assert_instance_of(JSI::JSON::ArrayNode, subscripted)
|
105
|
-
assert_equal([{'a' => ['b']}], subscripted.
|
106
|
-
assert_equal([1, 'x'], subscripted.
|
105
|
+
assert_equal([{'a' => ['b']}], subscripted.node_content)
|
106
|
+
assert_equal(JSI::JSON::Pointer.new([1, 'x']), subscripted.node_ptr)
|
107
107
|
end
|
108
108
|
it 'returns HashNode for a Hash' do
|
109
109
|
subscripted = node[1]
|
110
110
|
assert_instance_of(JSI::JSON::HashNode, subscripted)
|
111
|
-
assert_equal({'x' => [{'a' => ['b']}]}, subscripted.
|
112
|
-
assert_equal([1], subscripted.
|
111
|
+
assert_equal({'x' => [{'a' => ['b']}]}, subscripted.node_content)
|
112
|
+
assert_equal(JSI::JSON::Pointer.new([1]), subscripted.node_ptr)
|
113
|
+
end
|
114
|
+
describe 'node_content does not respond to []' do
|
115
|
+
let(:node_document) { Object.new }
|
116
|
+
it 'cannot subscript' do
|
117
|
+
err = assert_raises(NoMethodError) { node['x'] }
|
118
|
+
assert_equal("undefined method `[]`\nsubscripting with \"x\" (String) from Object. content is: #{node_document.pretty_inspect.chomp}", err.message)
|
119
|
+
end
|
113
120
|
end
|
114
121
|
end
|
115
122
|
describe 'with dereferencing' do
|
116
|
-
let(:
|
123
|
+
let(:node_document) do
|
117
124
|
{
|
118
125
|
'foo' => {'bar' => ['baz']},
|
119
126
|
'a' => {'$ref' => '#/foo', 'description' => 'hi'}, # not sure a description is actually allowed here, whatever
|
120
127
|
}
|
121
128
|
end
|
122
|
-
it 'subscripts a node consisting of a $ref
|
129
|
+
it 'subscripts a node consisting of a $ref without following' do
|
123
130
|
subscripted = node['a']
|
124
|
-
assert_equal({'$ref' => '#/foo', 'description' => 'hi'}, subscripted.
|
125
|
-
assert_equal(['a'], subscripted.
|
126
|
-
end
|
127
|
-
it 'follows a $ref when subscripting past it' do
|
128
|
-
subscripted = node['a']['bar']
|
129
|
-
assert_equal(['baz'], subscripted.content)
|
130
|
-
assert_equal(['foo', 'bar'], subscripted.path)
|
131
|
-
end
|
132
|
-
it 'does not follow a $ref when subscripting a key that is present' do
|
133
|
-
subscripted = node['a']['description']
|
134
|
-
assert_equal('hi', subscripted)
|
131
|
+
assert_equal({'$ref' => '#/foo', 'description' => 'hi'}, subscripted.node_content)
|
132
|
+
assert_equal(JSI::JSON::Pointer.new(['a']), subscripted.node_ptr)
|
135
133
|
end
|
136
134
|
end
|
137
135
|
end
|
138
136
|
describe '#[]=' do
|
139
|
-
let(:
|
137
|
+
let(:node_document) { [0, {'x' => [{'a' => ['b']}]}] }
|
140
138
|
it 'assigns' do
|
141
139
|
node[0] = 'abcdefg'
|
142
|
-
assert_equal(['abcdefg', {'x' => [{'a' => ['b']}]}],
|
143
|
-
string_node = JSI::JSON::Node.new(
|
140
|
+
assert_equal(['abcdefg', {'x' => [{'a' => ['b']}]}], node_document)
|
141
|
+
string_node = JSI::JSON::Node.new(node_document, JSI::JSON::Pointer.new([0]))
|
144
142
|
string_node[0..2] = '0'
|
145
|
-
assert_equal(['0defg', {'x' => [{'a' => ['b']}]}],
|
143
|
+
assert_equal(['0defg', {'x' => [{'a' => ['b']}]}], node_document)
|
146
144
|
node[0] = node[1]
|
147
|
-
assert_equal([{'x' => [{'a' => ['b']}]}, {'x' => [{'a' => ['b']}]}],
|
145
|
+
assert_equal([{'x' => [{'a' => ['b']}]}, {'x' => [{'a' => ['b']}]}], node_document)
|
148
146
|
end
|
149
147
|
it 'assigns, deeper' do
|
150
148
|
node[1]['y'] = node[1]['x'][0]
|
151
|
-
assert_equal([0, {'x' => [{'a' => ['b']}], 'y' => {'a' => ['b']}}],
|
149
|
+
assert_equal([0, {'x' => [{'a' => ['b']}], 'y' => {'a' => ['b']}}], node_document)
|
152
150
|
end
|
153
151
|
end
|
154
|
-
describe '#
|
155
|
-
let(:
|
156
|
-
it 'has
|
157
|
-
assert_equal({'a' => {'b' => 3}}, node['a'].
|
152
|
+
describe '#document_root_node' do
|
153
|
+
let(:node_document) { {'a' => {'b' => 3}} }
|
154
|
+
it 'has node_content that is the node_document' do
|
155
|
+
assert_equal({'a' => {'b' => 3}}, node['a'].document_root_node.node_content)
|
158
156
|
end
|
159
157
|
end
|
160
158
|
describe '#parent_node' do
|
161
|
-
let(:
|
159
|
+
let(:node_document) { {'a' => {'b' => []}} }
|
162
160
|
it 'finds a parent' do
|
163
161
|
sub = node['a']['b']
|
164
|
-
assert_equal(['a', 'b'], sub.
|
162
|
+
assert_equal(JSI::JSON::Pointer.new(['a', 'b']), sub.node_ptr)
|
165
163
|
parent = sub.parent_node
|
166
|
-
assert_equal(['a'], parent.
|
167
|
-
assert_equal({'b' => []}, parent.
|
164
|
+
assert_equal(JSI::JSON::Pointer.new(['a']), parent.node_ptr)
|
165
|
+
assert_equal({'b' => []}, parent.node_content)
|
168
166
|
assert_equal(node['a'], parent)
|
169
167
|
root_from_sub = sub.parent_node.parent_node
|
170
|
-
assert_equal([], root_from_sub.
|
171
|
-
assert_equal({'a' => {'b' => []}}, root_from_sub.
|
168
|
+
assert_equal(JSI::JSON::Pointer.new([]), root_from_sub.node_ptr)
|
169
|
+
assert_equal({'a' => {'b' => []}}, root_from_sub.node_content)
|
172
170
|
assert_equal(node, root_from_sub)
|
173
|
-
err = assert_raises(::JSON::
|
171
|
+
err = assert_raises(JSI::JSON::Pointer::ReferenceError) do
|
174
172
|
root_from_sub.parent_node
|
175
173
|
end
|
176
|
-
|
177
|
-
end
|
178
|
-
end
|
179
|
-
describe '#pointer_path' do
|
180
|
-
let(:document) { {'a' => {'b' => 3}} }
|
181
|
-
it 'is empty' do
|
182
|
-
assert_equal('', node.pointer_path)
|
183
|
-
end
|
184
|
-
it 'is not empty' do
|
185
|
-
assert_equal('/a', node['a'].pointer_path)
|
186
|
-
end
|
187
|
-
describe 'containing an empty string and some slashes and tildes that need escaping' do
|
188
|
-
let(:document) { {'' => {'a/b~c!d#e[f]' => []}} }
|
189
|
-
it 'matches' do
|
190
|
-
assert_equal('//a~1b~0c!d#e[f]', node['']['a/b~c!d#e[f]'].pointer_path)
|
191
|
-
end
|
192
|
-
end
|
193
|
-
end
|
194
|
-
describe '#fragment' do
|
195
|
-
let(:document) { {'a' => {'b' => 3}} }
|
196
|
-
it 'is empty' do
|
197
|
-
assert_equal('#', node.fragment)
|
198
|
-
end
|
199
|
-
it 'is not empty' do
|
200
|
-
assert_equal('#/a', node['a'].fragment)
|
201
|
-
end
|
202
|
-
describe 'containing an empty string and some slashes and tildes that need escaping' do
|
203
|
-
let(:document) { {'' => {'a/b~c!d#e[f]' => []}} }
|
204
|
-
it 'matches' do
|
205
|
-
assert_equal('#//a~1b~0c!d#e%5Bf%5D', node['']['a/b~c!d#e[f]'].fragment)
|
206
|
-
end
|
174
|
+
assert_equal('cannot access parent of root pointer: JSI::JSON::Pointer[]', err.message)
|
207
175
|
end
|
208
176
|
end
|
209
177
|
describe '#modified_copy' do
|
210
|
-
let(:
|
178
|
+
let(:node_document) { [['b', 'q'], {'c' => ['d', 'e']}] }
|
211
179
|
let(:path) { ['1', 'c'] }
|
212
180
|
it 'returns a different object' do
|
213
181
|
# simplest thing
|
214
182
|
modified_dup = node.modified_copy(&:dup)
|
215
183
|
# it is equal - being a dup
|
216
|
-
assert_equal(
|
184
|
+
assert_equal(node, modified_dup)
|
217
185
|
# but different object
|
218
186
|
refute_equal(node.object_id, modified_dup.object_id)
|
219
187
|
# the parents, obviously, are different
|
220
|
-
refute_equal(node.parent_node.
|
221
|
-
refute_equal(node.parent_node.parent_node.
|
222
|
-
# but any untouched part(s) - in this case the ['b', 'q'] at
|
223
|
-
assert_equal(node.
|
188
|
+
refute_equal(node.parent_node.node_content.object_id, modified_dup.parent_node.node_content.object_id)
|
189
|
+
refute_equal(node.parent_node.parent_node.node_content.object_id, modified_dup.parent_node.parent_node.node_content.object_id)
|
190
|
+
# but any untouched part(s) - in this case the ['b', 'q'] at node_document[0] - are untouched
|
191
|
+
assert_equal(node.document_root_node[0].node_content.object_id, modified_dup.document_root_node[0].node_content.object_id)
|
224
192
|
end
|
225
193
|
it 'returns the same object' do
|
226
194
|
unmodified_dup = node.modified_copy { |o| o }
|
@@ -228,59 +196,60 @@ describe JSI::JSON::Node do
|
|
228
196
|
# same object, since the block just returned it
|
229
197
|
refute_equal(node.object_id, unmodified_dup.object_id)
|
230
198
|
# the parents are unchanged since the object is the same
|
231
|
-
assert_equal(node.parent_node.
|
232
|
-
assert_equal(node.parent_node.parent_node.
|
233
|
-
# same as the other: any untouched part(s) - in this case the ['b', 'q'] at
|
234
|
-
assert_equal(node.
|
199
|
+
assert_equal(node.parent_node.node_content.object_id, unmodified_dup.parent_node.node_content.object_id)
|
200
|
+
assert_equal(node.parent_node.parent_node.node_content.object_id, unmodified_dup.parent_node.parent_node.node_content.object_id)
|
201
|
+
# same as the other: any untouched part(s) - in this case the ['b', 'q'] at node_document[0] - are untouched
|
202
|
+
assert_equal(node.document_root_node[0].node_content.object_id, unmodified_dup.document_root_node[0].node_content.object_id)
|
235
203
|
end
|
236
204
|
it 'raises subscripting string from array' do
|
237
|
-
err = assert_raises(TypeError) { JSI::JSON::Node.new(
|
205
|
+
err = assert_raises(TypeError) { JSI::JSON::Node.new(node_document, JSI::JSON::Pointer.new(['x'])).modified_copy(&:dup) }
|
238
206
|
assert_match(%r(\Abad subscript "x" with remaining subpath: \[\] for array: \[.*\]\z)m, err.message)
|
239
207
|
end
|
240
208
|
it 'raises subscripting from invalid subpath' do
|
241
|
-
err = assert_raises(TypeError) { JSI::JSON::Node.new(
|
209
|
+
err = assert_raises(TypeError) { JSI::JSON::Node.new(node_document, JSI::JSON::Pointer.new([0, 0, 'what'])).modified_copy(&:dup) }
|
242
210
|
assert_match(%r(bad subscript: "what" with remaining subpath: \[\] for content: "b"\z)m, err.message)
|
243
211
|
end
|
244
212
|
end
|
245
213
|
describe '#inspect' do
|
246
|
-
let(:
|
214
|
+
let(:node_document) { {'a' => {'c' => ['d', 'e']}} }
|
247
215
|
let(:path) { ['a'] }
|
248
216
|
it 'inspects' do
|
249
|
-
assert_equal(%Q(#<JSI::JSON::Node
|
217
|
+
assert_equal(%Q(#<JSI::JSON::Node #/a {"c"=>["d", "e"]}>), node.inspect)
|
250
218
|
end
|
251
219
|
end
|
252
220
|
describe '#pretty_print' do
|
253
|
-
let(:
|
221
|
+
let(:node_document) { {'a' => {'c' => ['d', 'e']}} }
|
254
222
|
let(:path) { ['a'] }
|
255
223
|
it 'pretty prints' do
|
256
|
-
assert_equal(%Q(#<JSI::JSON::Node
|
224
|
+
assert_equal(%Q(#<JSI::JSON::Node #/a {"c"=>["d", "e"]}>), node.pretty_inspect.chomp)
|
257
225
|
end
|
258
226
|
end
|
259
|
-
describe '#
|
227
|
+
describe '#jsi_fingerprint' do
|
228
|
+
let(:node_ptr) { JSI::JSON::Pointer.new([]) }
|
260
229
|
it 'hashes consistently' do
|
261
|
-
assert_equal('x', {JSI::JSON::Node.new([0],
|
230
|
+
assert_equal('x', {JSI::JSON::Node.new([0], node_ptr) => 'x'}[JSI::JSON::Node.new([0], node_ptr)])
|
262
231
|
end
|
263
232
|
it 'hashes consistently regardless of the Node being decorated as a subclass' do
|
264
|
-
assert_equal('x', {JSI::JSON::Node.new_doc([0]) => 'x'}[JSI::JSON::Node.new([0],
|
265
|
-
assert_equal('x', {JSI::JSON::Node.new([0],
|
233
|
+
assert_equal('x', {JSI::JSON::Node.new_doc([0]) => 'x'}[JSI::JSON::Node.new([0], node_ptr)])
|
234
|
+
assert_equal('x', {JSI::JSON::Node.new([0], node_ptr) => 'x'}[JSI::JSON::Node.new_doc([0])])
|
266
235
|
end
|
267
236
|
it '==' do
|
268
|
-
assert_equal(JSI::JSON::Node.new([0],
|
269
|
-
assert_equal(JSI::JSON::Node.new_doc([0]), JSI::JSON::Node.new([0],
|
270
|
-
assert_equal(JSI::JSON::Node.new([0],
|
237
|
+
assert_equal(JSI::JSON::Node.new([0], node_ptr), JSI::JSON::Node.new([0], node_ptr))
|
238
|
+
assert_equal(JSI::JSON::Node.new_doc([0]), JSI::JSON::Node.new([0], node_ptr))
|
239
|
+
assert_equal(JSI::JSON::Node.new([0], node_ptr), JSI::JSON::Node.new_doc([0]))
|
271
240
|
assert_equal(JSI::JSON::Node.new_doc([0]), JSI::JSON::Node.new_doc([0]))
|
272
241
|
end
|
273
242
|
it '!=' do
|
274
|
-
refute_equal(JSI::JSON::Node.new([0],
|
275
|
-
refute_equal(JSI::JSON::Node.new_doc([0]), JSI::JSON::Node.new({},
|
276
|
-
refute_equal(JSI::JSON::Node.new([0],
|
243
|
+
refute_equal(JSI::JSON::Node.new([0], node_ptr), JSI::JSON::Node.new({}, node_ptr))
|
244
|
+
refute_equal(JSI::JSON::Node.new_doc([0]), JSI::JSON::Node.new({}, node_ptr))
|
245
|
+
refute_equal(JSI::JSON::Node.new([0], node_ptr), JSI::JSON::Node.new_doc({}))
|
277
246
|
refute_equal(JSI::JSON::Node.new_doc([0]), JSI::JSON::Node.new_doc({}))
|
278
247
|
refute_equal({}, JSI::JSON::Node.new_doc({}))
|
279
248
|
refute_equal(JSI::JSON::Node.new_doc({}), {})
|
280
249
|
end
|
281
250
|
end
|
282
251
|
describe '#as_json' do
|
283
|
-
let(:
|
252
|
+
let(:node_document) { {'a' => 'b'} }
|
284
253
|
it '#as_json' do
|
285
254
|
assert_equal({'a' => 'b'}, node.as_json)
|
286
255
|
end
|