jsi 0.2.0 → 0.6.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/.yardopts +1 -1
- data/CHANGELOG.md +36 -0
- data/LICENSE.md +613 -0
- data/README.md +153 -52
- data/lib/jsi/base.rb +485 -338
- data/lib/jsi/jsi_coder.rb +24 -18
- data/lib/jsi/metaschema.rb +7 -0
- data/lib/jsi/metaschema_node/bootstrap_schema.rb +100 -0
- data/lib/jsi/metaschema_node.rb +245 -0
- data/lib/jsi/pathed_node.rb +49 -46
- data/lib/jsi/ptr.rb +292 -0
- data/lib/jsi/schema/application/child_application/contains.rb +16 -0
- data/lib/jsi/schema/application/child_application/draft04.rb +22 -0
- data/lib/jsi/schema/application/child_application/draft06.rb +29 -0
- data/lib/jsi/schema/application/child_application/draft07.rb +29 -0
- data/lib/jsi/schema/application/child_application/items.rb +18 -0
- data/lib/jsi/schema/application/child_application/properties.rb +25 -0
- data/lib/jsi/schema/application/child_application.rb +40 -0
- data/lib/jsi/schema/application/draft04.rb +8 -0
- data/lib/jsi/schema/application/draft06.rb +8 -0
- data/lib/jsi/schema/application/draft07.rb +8 -0
- data/lib/jsi/schema/application/inplace_application/dependencies.rb +28 -0
- data/lib/jsi/schema/application/inplace_application/draft04.rb +26 -0
- data/lib/jsi/schema/application/inplace_application/draft06.rb +27 -0
- data/lib/jsi/schema/application/inplace_application/draft07.rb +33 -0
- data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +20 -0
- data/lib/jsi/schema/application/inplace_application/ref.rb +18 -0
- data/lib/jsi/schema/application/inplace_application/someof.rb +29 -0
- data/lib/jsi/schema/application/inplace_application.rb +46 -0
- data/lib/jsi/schema/application.rb +12 -0
- data/lib/jsi/schema/draft04.rb +14 -0
- data/lib/jsi/schema/draft06.rb +14 -0
- data/lib/jsi/schema/draft07.rb +14 -0
- data/lib/jsi/schema/issue.rb +36 -0
- data/lib/jsi/schema/ref.rb +159 -0
- data/lib/jsi/schema/schema_ancestor_node.rb +119 -0
- data/lib/jsi/schema/validation/array.rb +69 -0
- data/lib/jsi/schema/validation/const.rb +20 -0
- data/lib/jsi/schema/validation/contains.rb +25 -0
- data/lib/jsi/schema/validation/core.rb +39 -0
- data/lib/jsi/schema/validation/dependencies.rb +49 -0
- data/lib/jsi/schema/validation/draft04/minmax.rb +91 -0
- data/lib/jsi/schema/validation/draft04.rb +112 -0
- data/lib/jsi/schema/validation/draft06.rb +122 -0
- data/lib/jsi/schema/validation/draft07.rb +159 -0
- data/lib/jsi/schema/validation/enum.rb +25 -0
- data/lib/jsi/schema/validation/ifthenelse.rb +46 -0
- data/lib/jsi/schema/validation/items.rb +54 -0
- data/lib/jsi/schema/validation/not.rb +20 -0
- data/lib/jsi/schema/validation/numeric.rb +121 -0
- data/lib/jsi/schema/validation/object.rb +45 -0
- data/lib/jsi/schema/validation/pattern.rb +34 -0
- data/lib/jsi/schema/validation/properties.rb +101 -0
- data/lib/jsi/schema/validation/property_names.rb +32 -0
- data/lib/jsi/schema/validation/ref.rb +40 -0
- data/lib/jsi/schema/validation/required.rb +27 -0
- data/lib/jsi/schema/validation/someof.rb +90 -0
- data/lib/jsi/schema/validation/string.rb +47 -0
- data/lib/jsi/schema/validation/type.rb +49 -0
- data/lib/jsi/schema/validation.rb +51 -0
- data/lib/jsi/schema.rb +528 -233
- data/lib/jsi/schema_classes.rb +238 -51
- data/lib/jsi/schema_registry.rb +141 -0
- data/lib/jsi/schema_set.rb +141 -0
- data/lib/jsi/simple_wrap.rb +8 -3
- data/lib/jsi/typelike_modules.rb +75 -68
- data/lib/jsi/util/attr_struct.rb +106 -0
- data/lib/jsi/util.rb +167 -64
- data/lib/jsi/validation/error.rb +34 -0
- data/lib/jsi/validation/result.rb +210 -0
- data/lib/jsi/validation.rb +15 -0
- data/lib/jsi/version.rb +3 -1
- data/lib/jsi.rb +72 -9
- data/lib/schemas/json-schema.org/draft-04/schema.rb +12 -0
- data/lib/schemas/json-schema.org/draft-06/schema.rb +12 -0
- data/lib/schemas/json-schema.org/draft-07/schema.rb +12 -0
- data/readme.rb +138 -0
- data/{resources}/schemas/json-schema.org/draft-04/schema.json +149 -0
- data/{resources}/schemas/json-schema.org/draft-06/schema.json +154 -0
- data/{resources}/schemas/json-schema.org/draft-07/schema.json +168 -0
- metadata +80 -107
- data/.simplecov +0 -1
- data/LICENSE.txt +0 -21
- data/Rakefile.rb +0 -9
- data/jsi.gemspec +0 -31
- data/lib/jsi/base/to_rb.rb +0 -126
- data/lib/jsi/json/node.rb +0 -243
- data/lib/jsi/json/pointer.rb +0 -330
- data/lib/jsi/json-schema-fragments.rb +0 -59
- data/lib/jsi/json.rb +0 -8
- data/test/base_array_test.rb +0 -209
- data/test/base_hash_test.rb +0 -204
- data/test/base_test.rb +0 -422
- data/test/jsi_coder_test.rb +0 -85
- data/test/jsi_json_arraynode_test.rb +0 -150
- data/test/jsi_json_hashnode_test.rb +0 -132
- data/test/jsi_json_node_test.rb +0 -310
- data/test/jsi_json_pointer_test.rb +0 -106
- data/test/jsi_test.rb +0 -11
- data/test/jsi_typelike_as_json_test.rb +0 -53
- data/test/schema_test.rb +0 -196
- data/test/spreedly_openapi_test.rb +0 -8
- data/test/test_helper.rb +0 -63
- data/test/util_test.rb +0 -62
data/test/base_hash_test.rb
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
require_relative 'test_helper'
|
|
2
|
-
|
|
3
|
-
describe JSI::BaseHash do
|
|
4
|
-
let(:document) do
|
|
5
|
-
{'foo' => {'x' => 'y'}, 'bar' => [9], 'baz' => true}
|
|
6
|
-
end
|
|
7
|
-
let(:path) { [] }
|
|
8
|
-
let(:pointer) { JSI::JSON::Pointer.new(path) }
|
|
9
|
-
let(:instance) { JSI::JSON::Node.new_by_type(document, pointer) }
|
|
10
|
-
let(:schema_content) do
|
|
11
|
-
{
|
|
12
|
-
'type' => 'object',
|
|
13
|
-
'properties' => {
|
|
14
|
-
'foo' => {'type' => 'object'},
|
|
15
|
-
'bar' => {},
|
|
16
|
-
},
|
|
17
|
-
}
|
|
18
|
-
end
|
|
19
|
-
let(:schema) { JSI::Schema.new(schema_content) }
|
|
20
|
-
let(:class_for_schema) { JSI.class_for_schema(schema) }
|
|
21
|
-
let(:subject) { class_for_schema.new(instance) }
|
|
22
|
-
|
|
23
|
-
describe '#[] with a default that is a basic type' do
|
|
24
|
-
let(:schema_content) do
|
|
25
|
-
{
|
|
26
|
-
'type' => 'object',
|
|
27
|
-
'properties' => {
|
|
28
|
-
'foo' => {'default' => 'foo'},
|
|
29
|
-
},
|
|
30
|
-
}
|
|
31
|
-
end
|
|
32
|
-
describe 'default value' do
|
|
33
|
-
let(:document) { {'bar' => 3} }
|
|
34
|
-
it 'returns the default value' do
|
|
35
|
-
assert_equal('foo', subject.foo)
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
describe 'nondefault value (basic type)' do
|
|
39
|
-
let(:document) { {'foo' => 'who'} }
|
|
40
|
-
it 'returns the nondefault value' do
|
|
41
|
-
assert_equal('who', subject.foo)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
describe 'nondefault value (nonbasic type)' do
|
|
45
|
-
let(:document) { {'foo' => [2]} }
|
|
46
|
-
it 'returns the nondefault value' do
|
|
47
|
-
assert_instance_of(JSI.class_for_schema(schema['properties']['foo']), subject.foo)
|
|
48
|
-
assert_equal([2], subject.foo.as_json)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
describe '#[] with a default that is a nonbasic type' do
|
|
53
|
-
let(:schema_content) do
|
|
54
|
-
{
|
|
55
|
-
'type' => 'object',
|
|
56
|
-
'properties' => {
|
|
57
|
-
'foo' => {'default' => {'foo' => 2}},
|
|
58
|
-
},
|
|
59
|
-
}
|
|
60
|
-
end
|
|
61
|
-
describe 'default value' do
|
|
62
|
-
let(:document) { {'bar' => 3} }
|
|
63
|
-
it 'returns the default value' do
|
|
64
|
-
assert_instance_of(JSI.class_for_schema(schema['properties']['foo']), subject.foo)
|
|
65
|
-
assert_equal({'foo' => 2}, subject.foo.as_json)
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
describe 'nondefault value (basic type)' do
|
|
69
|
-
let(:document) { {'foo' => 'who'} }
|
|
70
|
-
it 'returns the nondefault value' do
|
|
71
|
-
assert_equal('who', subject.foo)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
describe 'nondefault value (nonbasic type)' do
|
|
75
|
-
let(:document) { {'foo' => [2]} }
|
|
76
|
-
it 'returns the nondefault value' do
|
|
77
|
-
assert_instance_of(JSI.class_for_schema(schema['properties']['foo']), subject.foo)
|
|
78
|
-
assert_equal([2], subject.foo.as_json)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
describe 'hashlike []=' do
|
|
83
|
-
it 'sets a property' do
|
|
84
|
-
orig_foo = subject['foo']
|
|
85
|
-
|
|
86
|
-
subject['foo'] = {'y' => 'z'}
|
|
87
|
-
|
|
88
|
-
assert_equal({'y' => 'z'}, subject['foo'].as_json)
|
|
89
|
-
assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), orig_foo)
|
|
90
|
-
assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), subject['foo'])
|
|
91
|
-
end
|
|
92
|
-
it 'sets a property to a schema instance with a different schema' do
|
|
93
|
-
assert(subject['foo'])
|
|
94
|
-
|
|
95
|
-
subject['foo'] = subject['bar']
|
|
96
|
-
|
|
97
|
-
# the content of the subscripts' instances is the same but the subscripts' classes are different
|
|
98
|
-
assert_equal([9], subject['foo'].as_json)
|
|
99
|
-
assert_equal([9], subject['bar'].as_json)
|
|
100
|
-
assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), subject['foo'])
|
|
101
|
-
assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['bar']), subject['bar'])
|
|
102
|
-
end
|
|
103
|
-
it 'sets a property to a schema instance with the same schema' do
|
|
104
|
-
other_subject = class_for_schema.new(JSI::JSON::Node.new_doc({'foo' => {'x' => 'y'}, 'bar' => [9], 'baz' => true}))
|
|
105
|
-
# Given
|
|
106
|
-
assert_equal(other_subject, subject)
|
|
107
|
-
|
|
108
|
-
# When:
|
|
109
|
-
subject['foo'] = other_subject['foo']
|
|
110
|
-
|
|
111
|
-
# Then:
|
|
112
|
-
# still equal
|
|
113
|
-
assert_equal(other_subject, subject)
|
|
114
|
-
# but different instances
|
|
115
|
-
refute_equal(other_subject['foo'].object_id, subject['foo'].object_id)
|
|
116
|
-
end
|
|
117
|
-
it 'modifies the instance, visible to other references to the same instance' do
|
|
118
|
-
orig_instance = subject.instance
|
|
119
|
-
|
|
120
|
-
subject['foo'] = {'y' => 'z'}
|
|
121
|
-
|
|
122
|
-
assert_equal(orig_instance, subject.instance)
|
|
123
|
-
assert_equal({'y' => 'z'}, orig_instance['foo'].as_json)
|
|
124
|
-
assert_equal({'y' => 'z'}, subject.instance['foo'].as_json)
|
|
125
|
-
assert_equal(orig_instance.class, subject.instance.class)
|
|
126
|
-
end
|
|
127
|
-
describe 'when the instance is not hashlike' do
|
|
128
|
-
let(:instance) { nil }
|
|
129
|
-
it 'errors' do
|
|
130
|
-
err = assert_raises(NoMethodError) { subject['foo'] = 0 }
|
|
131
|
-
assert_match(%r(\Aundefined method `\[\]=' for #<JSI::SchemaClasses::.*>\z), err.message)
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
# these methods just delegate to Hash so not going to test excessively
|
|
136
|
-
describe 'key only methods' do
|
|
137
|
-
it('#each_key') { assert_equal(['foo', 'bar', 'baz'], subject.each_key.to_a) }
|
|
138
|
-
it('#empty?') { assert_equal(false, subject.empty?) }
|
|
139
|
-
it('#has_key?') { assert_equal(true, subject.has_key?('bar')) }
|
|
140
|
-
it('#include?') { assert_equal(false, subject.include?('q')) }
|
|
141
|
-
it('#key?') { assert_equal(true, subject.key?('baz')) }
|
|
142
|
-
it('#keys') { assert_equal(['foo', 'bar', 'baz'], subject.keys) }
|
|
143
|
-
it('#length') { assert_equal(3, subject.length) }
|
|
144
|
-
it('#member?') { assert_equal(false, subject.member?(0)) }
|
|
145
|
-
it('#size') { assert_equal(3, subject.size) }
|
|
146
|
-
end
|
|
147
|
-
describe 'key + value methods' do
|
|
148
|
-
it('#<') { assert_equal(true, subject < {'foo' => subject['foo'], 'bar' => subject['bar'], 'baz' => true, 'x' => 'y'}) } if {}.respond_to?(:<)
|
|
149
|
-
it('#<=') { assert_equal(true, subject <= subject) } if {}.respond_to?(:<=)
|
|
150
|
-
it('#>') { assert_equal(true, subject > {}) } if {}.respond_to?(:>)
|
|
151
|
-
it('#>=') { assert_equal(false, subject >= {'foo' => 'bar'}) } if {}.respond_to?(:>=)
|
|
152
|
-
it('#any?') { assert_equal(false, subject.any? { |k, v| v == 3 }) }
|
|
153
|
-
it('#assoc') { assert_equal(['foo', subject['foo']], subject.assoc('foo')) }
|
|
154
|
-
it('#dig') { assert_equal(9, subject.dig('bar', 0)) } if {}.respond_to?(:dig)
|
|
155
|
-
it('#each_pair') { assert_equal([['foo', subject['foo']], ['bar', subject['bar']], ['baz', true]], subject.each_pair.to_a) }
|
|
156
|
-
it('#each_value') { assert_equal([subject['foo'], subject['bar'], true], subject.each_value.to_a) }
|
|
157
|
-
it('#fetch') { assert_equal(true, subject.fetch('baz')) }
|
|
158
|
-
it('#fetch_values') { assert_equal([true], subject.fetch_values('baz')) } if {}.respond_to?(:fetch_values)
|
|
159
|
-
it('#has_value?') { assert_equal(true, subject.has_value?(true)) }
|
|
160
|
-
it('#invert') { assert_equal({subject['foo'] => 'foo', subject['bar'] => 'bar', true => 'baz'}, subject.invert) }
|
|
161
|
-
it('#key') { assert_equal('baz', subject.key(true)) }
|
|
162
|
-
it('#rassoc') { assert_equal(['baz', true], subject.rassoc(true)) }
|
|
163
|
-
it('#to_h') { assert_equal({'foo' => subject['foo'], 'bar' => subject['bar'], 'baz' => true}, subject.to_h) }
|
|
164
|
-
it('#to_proc') { assert_equal(true, subject.to_proc.call('baz')) } if {}.respond_to?(:to_proc)
|
|
165
|
-
if {}.respond_to?(:transform_values)
|
|
166
|
-
it('#transform_values') { assert_equal({'foo' => nil, 'bar' => nil, 'baz' => nil}, subject.transform_values { |_| nil }) }
|
|
167
|
-
end
|
|
168
|
-
it('#value?') { assert_equal(false, subject.value?('0')) }
|
|
169
|
-
it('#values') { assert_equal([subject['foo'], subject['bar'], true], subject.values) }
|
|
170
|
-
it('#values_at') { assert_equal([true], subject.values_at('baz')) }
|
|
171
|
-
end
|
|
172
|
-
describe 'with an instance that has to_hash but not other hash instance methods' do
|
|
173
|
-
let(:instance) { SortOfHash.new({'foo' => SortOfHash.new({'a' => 'b'})}) }
|
|
174
|
-
describe 'delegating instance methods to #to_hash' do
|
|
175
|
-
it('#each_key') { assert_equal(['foo'], subject.each_key.to_a) }
|
|
176
|
-
it('#each_pair') { assert_equal([['foo', subject['foo']]], subject.each_pair.to_a) }
|
|
177
|
-
it('#[]') { assert_equal(SortOfHash.new({'a' => 'b'}), subject['foo'].instance) }
|
|
178
|
-
it('#as_json') { assert_equal({'foo' => {'a' => 'b'}}, subject.as_json) }
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
describe 'modified copy methods' do
|
|
182
|
-
# I'm going to rely on the #merge test above to test the modified copy functionality and just do basic
|
|
183
|
-
# tests of all the modified copy methods here
|
|
184
|
-
it('#merge') { assert_equal(subject, subject.merge({})) }
|
|
185
|
-
it('#reject') { assert_equal(class_for_schema.new(JSI::JSON::HashNode.new({}, pointer)), subject.reject { true }) }
|
|
186
|
-
it('#select') { assert_equal(class_for_schema.new(JSI::JSON::HashNode.new({}, pointer)), subject.select { false }) }
|
|
187
|
-
describe '#select' do
|
|
188
|
-
it 'yields properly too' do
|
|
189
|
-
subject.select do |k, v|
|
|
190
|
-
assert_equal(subject[k], v)
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
|
-
# Hash#compact only available as of ruby 2.5.0
|
|
195
|
-
if {}.respond_to?(:compact)
|
|
196
|
-
it('#compact') { assert_equal(subject, subject.compact) }
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
JSI::Hashlike::DESTRUCTIVE_METHODS.each do |destructive_method_name|
|
|
200
|
-
it("does not respond to destructive method #{destructive_method_name}") do
|
|
201
|
-
assert(!subject.respond_to?(destructive_method_name))
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
end
|
data/test/base_test.rb
DELETED
|
@@ -1,422 +0,0 @@
|
|
|
1
|
-
require_relative 'test_helper'
|
|
2
|
-
|
|
3
|
-
NamedSchemaInstance = JSI.class_for_schema({id: 'https://schemas.jsi.unth.net/test/base/named_schema'})
|
|
4
|
-
|
|
5
|
-
describe JSI::Base do
|
|
6
|
-
let(:document) { {} }
|
|
7
|
-
let(:path) { [] }
|
|
8
|
-
let(:pointer) { JSI::JSON::Pointer.new(path) }
|
|
9
|
-
let(:instance) { JSI::JSON::Node.new_by_type(document, pointer) }
|
|
10
|
-
let(:schema_content) { {} }
|
|
11
|
-
let(:schema) { JSI::Schema.new(schema_content) }
|
|
12
|
-
let(:subject) { JSI.class_for_schema(schema).new(instance) }
|
|
13
|
-
describe 'class .inspect + .to_s' do
|
|
14
|
-
it 'is the same as Class#inspect on the base' do
|
|
15
|
-
assert_equal('JSI::Base', JSI::Base.inspect)
|
|
16
|
-
assert_equal('JSI::Base', JSI::Base.to_s)
|
|
17
|
-
end
|
|
18
|
-
it 'is SchemaClasses[] for generated subclass without id' do
|
|
19
|
-
assert_match(%r(\AJSI::SchemaClasses\["[a-f0-9\-]+#"\]\z), subject.class.inspect)
|
|
20
|
-
assert_match(%r(\AJSI::SchemaClasses\["[a-f0-9\-]+#"\]\z), subject.class.to_s)
|
|
21
|
-
end
|
|
22
|
-
describe 'with schema id' do
|
|
23
|
-
let(:schema_content) { {'id' => 'https://jsi/foo'} }
|
|
24
|
-
it 'is SchemaClasses[] for generated subclass with id' do
|
|
25
|
-
assert_equal(%q(JSI::SchemaClasses["https://jsi/foo#"]), subject.class.inspect)
|
|
26
|
-
assert_equal(%q(JSI::SchemaClasses["https://jsi/foo#"]), subject.class.to_s)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
it 'is the constant name (plus id for .inspect) for a class assigned to a constant' do
|
|
30
|
-
assert_equal(%q(NamedSchemaInstance (https://schemas.jsi.unth.net/test/base/named_schema#)), NamedSchemaInstance.inspect)
|
|
31
|
-
assert_equal(%q(NamedSchemaInstance), NamedSchemaInstance.to_s)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
describe 'class name' do
|
|
35
|
-
let(:schema_content) { {'id' => 'https://jsi/BaseTest'} }
|
|
36
|
-
it 'generates a class name from schema_id' do
|
|
37
|
-
assert_equal('JSI::SchemaClasses::Https___jsi_BaseTest_', subject.class.name)
|
|
38
|
-
end
|
|
39
|
-
it 'uses an existing name' do
|
|
40
|
-
assert_equal('NamedSchemaInstance', NamedSchemaInstance.name)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
describe 'class for schema .schema' do
|
|
44
|
-
it '.schema' do
|
|
45
|
-
assert_equal(schema, JSI.class_for_schema(schema).schema)
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
describe 'class for schema .schema_id' do
|
|
49
|
-
it '.schema_id' do
|
|
50
|
-
assert_equal(schema.schema_id, JSI.class_for_schema(schema).schema_id)
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
describe 'module for schema .inspect' do
|
|
54
|
-
it '.inspect' do
|
|
55
|
-
assert_match(%r(\A#<Module for Schema: .+#>\z), JSI::SchemaClasses.module_for_schema(schema).inspect)
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
describe 'module for schema .schema' do
|
|
59
|
-
it '.schema' do
|
|
60
|
-
assert_equal(schema, JSI::SchemaClasses.module_for_schema(schema).schema)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
describe 'SchemaClasses[]' do
|
|
64
|
-
it 'stores the class for the schema' do
|
|
65
|
-
assert_equal(JSI.class_for_schema(schema), JSI::SchemaClasses[schema.schema_id])
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
describe '.class_for_schema' do
|
|
69
|
-
it 'returns a class from a schema' do
|
|
70
|
-
class_for_schema = JSI.class_for_schema(schema)
|
|
71
|
-
# same class every time
|
|
72
|
-
assert_equal(JSI.class_for_schema(schema), class_for_schema)
|
|
73
|
-
assert_operator(class_for_schema, :<, JSI::Base)
|
|
74
|
-
end
|
|
75
|
-
it 'returns a class from a hash' do
|
|
76
|
-
assert_equal(JSI.class_for_schema(schema), JSI.class_for_schema(schema.schema_node.content))
|
|
77
|
-
end
|
|
78
|
-
it 'returns a class from a schema node' do
|
|
79
|
-
assert_equal(JSI.class_for_schema(schema), JSI.class_for_schema(schema.schema_node))
|
|
80
|
-
end
|
|
81
|
-
it 'returns a class from a Base' do
|
|
82
|
-
assert_equal(JSI.class_for_schema(schema), JSI.class_for_schema(JSI.class_for_schema({}).new(schema.schema_node)))
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
describe 'JSI::SchemaClasses.module_for_schema' do
|
|
86
|
-
it 'returns a module from a schema' do
|
|
87
|
-
module_for_schema = JSI::SchemaClasses.module_for_schema(schema)
|
|
88
|
-
# same module every time
|
|
89
|
-
assert_equal(JSI::SchemaClasses.module_for_schema(schema), module_for_schema)
|
|
90
|
-
end
|
|
91
|
-
it 'returns a module from a hash' do
|
|
92
|
-
assert_equal(JSI::SchemaClasses.module_for_schema(schema), JSI::SchemaClasses.module_for_schema(schema.schema_node.content))
|
|
93
|
-
end
|
|
94
|
-
it 'returns a module from a schema node' do
|
|
95
|
-
assert_equal(JSI::SchemaClasses.module_for_schema(schema), JSI::SchemaClasses.module_for_schema(schema.schema_node))
|
|
96
|
-
end
|
|
97
|
-
it 'returns a module from a Base' do
|
|
98
|
-
assert_equal(JSI::SchemaClasses.module_for_schema(schema), JSI::SchemaClasses.module_for_schema(JSI.class_for_schema({}).new(schema.schema_node)))
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
describe 'initialization' do
|
|
102
|
-
describe 'on Base' do
|
|
103
|
-
it 'errors' do
|
|
104
|
-
err = assert_raises(TypeError) { JSI::Base.new({}) }
|
|
105
|
-
assert_equal('cannot instantiate JSI::Base which has no method #schema. please use JSI.class_for_schema', err.message)
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
describe 'nil' do
|
|
109
|
-
let(:instance) { nil }
|
|
110
|
-
it 'initializes with nil instance' do
|
|
111
|
-
assert_equal(nil, subject.instance)
|
|
112
|
-
assert(!subject.respond_to?(:to_ary))
|
|
113
|
-
assert(!subject.respond_to?(:to_hash))
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
describe 'arbitrary instance' do
|
|
117
|
-
let(:instance) { Object.new }
|
|
118
|
-
it 'initializes' do
|
|
119
|
-
assert_equal(instance, subject.instance)
|
|
120
|
-
assert(!subject.respond_to?(:to_ary))
|
|
121
|
-
assert(!subject.respond_to?(:to_hash))
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
describe 'hash' do
|
|
125
|
-
let(:instance) { {'foo' => 'bar'} }
|
|
126
|
-
let(:schema_content) { {'type' => 'object'} }
|
|
127
|
-
it 'initializes' do
|
|
128
|
-
assert_equal({'foo' => 'bar'}, subject.instance)
|
|
129
|
-
assert(!subject.respond_to?(:to_ary))
|
|
130
|
-
assert(subject.respond_to?(:to_hash))
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
describe 'JSI::JSON::Hashnode' do
|
|
134
|
-
let(:document) { {'foo' => 'bar'} }
|
|
135
|
-
let(:schema_content) { {'type' => 'object'} }
|
|
136
|
-
it 'initializes' do
|
|
137
|
-
assert_equal(JSI::JSON::HashNode.new({'foo' => 'bar'}, JSI::JSON::Pointer.new([])), subject.instance)
|
|
138
|
-
assert(!subject.respond_to?(:to_ary))
|
|
139
|
-
assert(subject.respond_to?(:to_hash))
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
describe 'array' do
|
|
143
|
-
let(:instance) { ['foo'] }
|
|
144
|
-
let(:schema_content) { {'type' => 'array'} }
|
|
145
|
-
it 'initializes' do
|
|
146
|
-
assert_equal(['foo'], subject.instance)
|
|
147
|
-
assert(subject.respond_to?(:to_ary))
|
|
148
|
-
assert(!subject.respond_to?(:to_hash))
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
describe 'JSI::JSON::Arraynode' do
|
|
152
|
-
let(:document) { ['foo'] }
|
|
153
|
-
let(:schema_content) { {'type' => 'array'} }
|
|
154
|
-
it 'initializes' do
|
|
155
|
-
assert_equal(JSI::JSON::ArrayNode.new(['foo'], JSI::JSON::Pointer.new([])), subject.instance)
|
|
156
|
-
assert(subject.respond_to?(:to_ary))
|
|
157
|
-
assert(!subject.respond_to?(:to_hash))
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
describe 'another JSI::Base invalid' do
|
|
161
|
-
let(:schema_content) { {'type' => 'object'} }
|
|
162
|
-
let(:instance) { JSI.class_for_schema(schema).new({'foo' => 'bar'}) }
|
|
163
|
-
it 'initializes with an error' do
|
|
164
|
-
err = assert_raises(TypeError) { subject }
|
|
165
|
-
assert_match(%r(\Aassigning another JSI::Base instance to JSI::SchemaClasses\[\".*#\"\] instance is incorrect. received: #\{<JSI::SchemaClasses\[.*\] Hash>\s*"foo" => "bar"\s*\}\z)m, err.message)
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
describe 'Schema invalid' do
|
|
169
|
-
let(:instance) { JSI::Schema.new({}) }
|
|
170
|
-
it 'initializes with an error' do
|
|
171
|
-
err = assert_raises(TypeError) { subject }
|
|
172
|
-
assert_match(%r(\Aassigning a schema to JSI::SchemaClasses\[\".*#\"\] instance is incorrect. received: #<JSI::Schema schema_id=.*>\z)m, err.message)
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
describe '#parent_jsis, #parent_jsi' do
|
|
177
|
-
let(:schema_content) { {'properties' => {'foo' => {'properties' => {'bar' => {'properties' => {'baz' => {}}}}}}} }
|
|
178
|
-
let(:document) { {'foo' => {'bar' => {'baz' => {}}}} }
|
|
179
|
-
describe 'no parent_jsis' do
|
|
180
|
-
it 'has none' do
|
|
181
|
-
assert_equal([], subject.parents)
|
|
182
|
-
assert_equal([], subject.parent_jsis.to_a)
|
|
183
|
-
assert_equal(nil, subject.parent)
|
|
184
|
-
assert_equal(nil, subject.parent_jsi)
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
describe 'one parent_jsi' do
|
|
188
|
-
it 'has one' do
|
|
189
|
-
assert_equal([subject], subject.foo.parents)
|
|
190
|
-
assert_equal([subject], subject.foo.parent_jsis.to_a)
|
|
191
|
-
assert_equal(subject, subject.foo.parent)
|
|
192
|
-
assert_equal(subject, subject.foo.parent_jsi)
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
describe 'more parent_jsis' do
|
|
196
|
-
it 'has more' do
|
|
197
|
-
assert_equal([subject.foo.bar, subject.foo, subject], subject.foo.bar.baz.parents)
|
|
198
|
-
assert_equal([subject.foo.bar, subject.foo, subject], subject.foo.bar.baz.parent_jsis.to_a)
|
|
199
|
-
assert_equal(subject.foo.bar, subject.foo.bar.baz.parent)
|
|
200
|
-
assert_equal(subject.foo.bar, subject.foo.bar.baz.parent_jsi)
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
describe '#each, Enumerable methods' do
|
|
205
|
-
let(:document) { 'a string' }
|
|
206
|
-
it "raises NoMethodError calling each or Enumerable methods" do
|
|
207
|
-
assert_raises(NoMethodError) { subject.each { nil } }
|
|
208
|
-
assert_raises(NoMethodError) { subject.map { nil } }
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
describe '#modified_copy' do
|
|
212
|
-
describe 'with an instance that does not have #modified_copy' do
|
|
213
|
-
let(:instance) { Object.new }
|
|
214
|
-
it 'yields the instance to modify' do
|
|
215
|
-
new_instance = Object.new
|
|
216
|
-
modified = subject.modified_copy do |o|
|
|
217
|
-
assert_equal(instance, o)
|
|
218
|
-
new_instance
|
|
219
|
-
end
|
|
220
|
-
assert_equal(new_instance, modified.instance)
|
|
221
|
-
assert_equal(instance, subject.instance)
|
|
222
|
-
refute_equal(instance, modified)
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
describe 'with an instance that does have #modified_copy' do
|
|
226
|
-
it 'yields the instance to modify' do
|
|
227
|
-
modified = subject.modified_copy do |o|
|
|
228
|
-
assert_equal({}, o)
|
|
229
|
-
{'a' => 'b'}
|
|
230
|
-
end
|
|
231
|
-
assert_equal({'a' => 'b'}, modified.instance.content)
|
|
232
|
-
assert_equal({}, subject.instance.content)
|
|
233
|
-
refute_equal(instance, modified)
|
|
234
|
-
end
|
|
235
|
-
end
|
|
236
|
-
describe 'no modification' do
|
|
237
|
-
it 'yields the instance to modify' do
|
|
238
|
-
modified = subject.modified_copy { |o| o }
|
|
239
|
-
# this doesn't really need to be tested but ... whatever
|
|
240
|
-
assert_equal(subject.instance.content.object_id, modified.instance.content.object_id)
|
|
241
|
-
assert_equal(subject, modified)
|
|
242
|
-
refute_equal(subject.object_id, modified.object_id)
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
describe 'resulting in a different type' do
|
|
246
|
-
let(:schema_content) { {'type' => 'object'} }
|
|
247
|
-
it 'works' do
|
|
248
|
-
# I'm not really sure the best thing to do here, but this is how it is for now. this is subject to change.
|
|
249
|
-
modified = subject.modified_copy do |o|
|
|
250
|
-
o.to_s
|
|
251
|
-
end
|
|
252
|
-
assert_equal('{}', modified.instance.content)
|
|
253
|
-
assert_equal({}, subject.instance.content)
|
|
254
|
-
refute_equal(instance, modified)
|
|
255
|
-
# interesting side effect
|
|
256
|
-
assert(subject.respond_to?(:to_hash))
|
|
257
|
-
assert(!modified.respond_to?(:to_hash))
|
|
258
|
-
assert_equal(JSI::JSON::HashNode, subject.instance.class)
|
|
259
|
-
assert_equal(JSI::JSON::Node, modified.instance.class)
|
|
260
|
-
end
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
it('#fragment') { assert_equal('#', subject.fragment) }
|
|
264
|
-
describe 'validation' do
|
|
265
|
-
describe 'without errors' do
|
|
266
|
-
it '#fully_validate' do
|
|
267
|
-
assert_equal([], subject.fully_validate)
|
|
268
|
-
end
|
|
269
|
-
it '#validate' do
|
|
270
|
-
assert_equal(true, subject.validate)
|
|
271
|
-
end
|
|
272
|
-
it '#validate!' do
|
|
273
|
-
assert_equal(true, subject.validate!)
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
end
|
|
277
|
-
describe 'property accessors' do
|
|
278
|
-
let(:schema_content) do
|
|
279
|
-
{
|
|
280
|
-
'type' => 'object',
|
|
281
|
-
'properties' => {
|
|
282
|
-
'foo' => {'type' => 'object'},
|
|
283
|
-
'bar' => {'type' => 'array'},
|
|
284
|
-
'baz' => {},
|
|
285
|
-
},
|
|
286
|
-
}
|
|
287
|
-
end
|
|
288
|
-
let(:document) do
|
|
289
|
-
{'foo' => {'x' => 'y'}, 'bar' => [3.14159], 'baz' => true, 'qux' => []}
|
|
290
|
-
end
|
|
291
|
-
describe 'readers' do
|
|
292
|
-
it 'reads attributes described as properties' do
|
|
293
|
-
assert_equal({'x' => 'y'}, subject.foo.as_json)
|
|
294
|
-
assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), subject.foo)
|
|
295
|
-
assert_respond_to(subject.foo, :to_hash)
|
|
296
|
-
refute_respond_to(subject.foo, :to_ary)
|
|
297
|
-
assert_equal([3.14159], subject.bar.as_json)
|
|
298
|
-
assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['bar']), subject.bar)
|
|
299
|
-
refute_respond_to(subject.bar, :to_hash)
|
|
300
|
-
assert_respond_to(subject.bar, :to_ary)
|
|
301
|
-
assert_equal(true, subject.baz)
|
|
302
|
-
refute_respond_to(subject.baz, :to_hash)
|
|
303
|
-
refute_respond_to(subject.baz, :to_ary)
|
|
304
|
-
refute_respond_to(subject, :qux)
|
|
305
|
-
end
|
|
306
|
-
describe 'when the instance is not hashlike' do
|
|
307
|
-
let(:instance) { nil }
|
|
308
|
-
it 'errors' do
|
|
309
|
-
err = assert_raises(NoMethodError) { subject.foo }
|
|
310
|
-
assert_match(%r(\Aschema instance of class .* does not respond to \[\]; cannot call reader 'foo'. instance is )m, err.message)
|
|
311
|
-
end
|
|
312
|
-
end
|
|
313
|
-
describe 'properties with the same names as instance methods' do
|
|
314
|
-
let(:schema_content) do
|
|
315
|
-
{
|
|
316
|
-
'type' => 'object',
|
|
317
|
-
'properties' => {
|
|
318
|
-
'foo' => {}, # not an instance method
|
|
319
|
-
'initialize' => {}, # Base
|
|
320
|
-
'inspect' => {}, # Base
|
|
321
|
-
'pretty_inspect' => {}, # Kernel
|
|
322
|
-
'as_json' => {}, # Base::OverrideFromExtensions, extended on initialization
|
|
323
|
-
'each' => {}, # BaseHash / BaseArray
|
|
324
|
-
'instance_exec' => {}, # BasicObject
|
|
325
|
-
'instance' => {}, # Base
|
|
326
|
-
'schema' => {}, # module_for_schema singleton definition
|
|
327
|
-
},
|
|
328
|
-
}
|
|
329
|
-
end
|
|
330
|
-
let(:document) do
|
|
331
|
-
{
|
|
332
|
-
'foo' => 'bar',
|
|
333
|
-
'initialize' => 'hi',
|
|
334
|
-
'inspect' => 'hi',
|
|
335
|
-
'pretty_inspect' => 'hi',
|
|
336
|
-
'as_json' => 'hi',
|
|
337
|
-
'each' => 'hi',
|
|
338
|
-
'instance_exec' => 'hi',
|
|
339
|
-
'instance' => 'hi',
|
|
340
|
-
'schema' => 'hi',
|
|
341
|
-
}
|
|
342
|
-
end
|
|
343
|
-
it 'does not define readers' do
|
|
344
|
-
assert_equal('bar', subject.foo)
|
|
345
|
-
assert_equal(JSI::SchemaClasses.module_for_schema(subject.schema, conflicting_modules: [JSI::Base, JSI::BaseArray, JSI::BaseHash]), subject.method(:foo).owner)
|
|
346
|
-
|
|
347
|
-
assert_equal(JSI::Base, subject.method(:initialize).owner)
|
|
348
|
-
assert_equal('hi', subject['initialize'])
|
|
349
|
-
assert_match(%r(\A#\{<JSI::SchemaClasses\[".*#"\].*}\z)m, subject.inspect)
|
|
350
|
-
assert_equal('hi', subject['inspect'])
|
|
351
|
-
assert_match(%r(\A#\{<JSI::SchemaClasses\[".*#"\].*}\Z)m, subject.pretty_inspect)
|
|
352
|
-
assert_equal(document, subject.as_json)
|
|
353
|
-
assert_equal(subject, subject.each { })
|
|
354
|
-
assert_equal(2, subject.instance_exec { 2 })
|
|
355
|
-
assert_equal(instance, subject.instance)
|
|
356
|
-
assert_equal(schema, subject.schema)
|
|
357
|
-
end
|
|
358
|
-
end
|
|
359
|
-
end
|
|
360
|
-
describe 'writers' do
|
|
361
|
-
it 'writes attributes described as properties' do
|
|
362
|
-
orig_foo = subject.foo
|
|
363
|
-
|
|
364
|
-
subject.foo = {'y' => 'z'}
|
|
365
|
-
|
|
366
|
-
assert_equal({'y' => 'z'}, subject.foo.as_json)
|
|
367
|
-
assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), orig_foo)
|
|
368
|
-
assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), subject.foo)
|
|
369
|
-
end
|
|
370
|
-
it 'modifies the instance, visible to other references to the same instance' do
|
|
371
|
-
orig_instance = subject.instance
|
|
372
|
-
|
|
373
|
-
subject.foo = {'y' => 'z'}
|
|
374
|
-
|
|
375
|
-
assert_equal(orig_instance, subject.instance)
|
|
376
|
-
assert_equal({'y' => 'z'}, orig_instance['foo'].as_json)
|
|
377
|
-
assert_equal({'y' => 'z'}, subject.instance['foo'].as_json)
|
|
378
|
-
assert_equal(orig_instance.class, subject.instance.class)
|
|
379
|
-
end
|
|
380
|
-
describe 'when the instance is not hashlike' do
|
|
381
|
-
let(:instance) { nil }
|
|
382
|
-
it 'errors' do
|
|
383
|
-
err = assert_raises(NoMethodError) { subject.foo = 0 }
|
|
384
|
-
assert_match(%r(\Aschema instance of class .* does not respond to \[\]=; cannot call writer 'foo='. instance is )m, err.message)
|
|
385
|
-
end
|
|
386
|
-
end
|
|
387
|
-
end
|
|
388
|
-
end
|
|
389
|
-
describe '#inspect' do
|
|
390
|
-
# if the instance is hash-like, #inspect gets overridden
|
|
391
|
-
let(:document) { Object.new }
|
|
392
|
-
it 'inspects' do
|
|
393
|
-
assert_match(%r(\A#<JSI::SchemaClasses\["[^"]+#"\] #<JSI::JSON::Node fragment="#" #<Object:[^<>]*>>>\z), subject.inspect)
|
|
394
|
-
end
|
|
395
|
-
end
|
|
396
|
-
describe '#pretty_print' do
|
|
397
|
-
# if the instance is hash-like, #pretty_print gets overridden
|
|
398
|
-
let(:document) { Object.new }
|
|
399
|
-
it 'pretty_prints' do
|
|
400
|
-
assert_match(%r(\A#<JSI::SchemaClasses\["[^"]+#"\]\n #<JSI::JSON::Node fragment="#" #<Object:[^<>]*>>\n>\z), subject.pretty_inspect.chomp)
|
|
401
|
-
end
|
|
402
|
-
end
|
|
403
|
-
describe '#as_json' do
|
|
404
|
-
it '#as_json' do
|
|
405
|
-
assert_equal({'a' => 'b'}, JSI.class_for_schema({}).new(JSI::JSON::Node.new_doc({'a' => 'b'})).as_json)
|
|
406
|
-
assert_equal({'a' => 'b'}, JSI.class_for_schema({'type' => 'object'}).new(JSI::JSON::Node.new_doc({'a' => 'b'})).as_json)
|
|
407
|
-
assert_equal(['a', 'b'], JSI.class_for_schema({'type' => 'array'}).new(JSI::JSON::Node.new_doc(['a', 'b'])).as_json)
|
|
408
|
-
assert_equal(['a'], JSI.class_for_schema({}).new(['a']).as_json(some_option: true))
|
|
409
|
-
end
|
|
410
|
-
end
|
|
411
|
-
describe 'equality between different classes of JSI::Base subclasses' do
|
|
412
|
-
let(:subject_subclass) { Class.new(JSI.class_for_schema(schema)).new(instance) }
|
|
413
|
-
|
|
414
|
-
it 'considers a Base subclass (class_for_schema) and subsubclass to be equal with the same instance' do
|
|
415
|
-
assert_equal(subject.hash, subject_subclass.hash)
|
|
416
|
-
assert(subject == subject_subclass)
|
|
417
|
-
assert(subject_subclass == subject)
|
|
418
|
-
assert(subject.eql?(subject_subclass))
|
|
419
|
-
assert(subject_subclass.eql?(subject))
|
|
420
|
-
end
|
|
421
|
-
end
|
|
422
|
-
end
|