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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +3 -1
  3. data/CHANGELOG.md +48 -0
  4. data/LICENSE.md +613 -0
  5. data/README.md +84 -45
  6. data/jsi.gemspec +11 -14
  7. data/lib/jsi.rb +31 -12
  8. data/lib/jsi/base.rb +310 -344
  9. data/lib/jsi/base/to_rb.rb +2 -0
  10. data/lib/jsi/jsi_coder.rb +91 -0
  11. data/lib/jsi/json-schema-fragments.rb +3 -135
  12. data/lib/jsi/json.rb +3 -0
  13. data/lib/jsi/json/node.rb +72 -197
  14. data/lib/jsi/json/pointer.rb +419 -0
  15. data/lib/jsi/metaschema.rb +7 -0
  16. data/lib/jsi/metaschema_node.rb +218 -0
  17. data/lib/jsi/pathed_node.rb +118 -0
  18. data/lib/jsi/schema.rb +168 -223
  19. data/lib/jsi/schema_classes.rb +158 -0
  20. data/lib/jsi/simple_wrap.rb +12 -0
  21. data/lib/jsi/typelike_modules.rb +71 -45
  22. data/lib/jsi/util.rb +47 -57
  23. data/lib/jsi/version.rb +1 -1
  24. data/lib/schemas/json-schema.org/draft-04/schema.rb +7 -0
  25. data/lib/schemas/json-schema.org/draft-06/schema.rb +7 -0
  26. data/resources/icons/AGPL-3.0.png +0 -0
  27. data/test/base_array_test.rb +210 -84
  28. data/test/base_hash_test.rb +201 -58
  29. data/test/base_test.rb +212 -121
  30. data/test/jsi_coder_test.rb +85 -0
  31. data/test/jsi_json_arraynode_test.rb +26 -25
  32. data/test/jsi_json_hashnode_test.rb +40 -39
  33. data/test/jsi_json_node_test.rb +95 -126
  34. data/test/jsi_json_pointer_test.rb +102 -0
  35. data/test/jsi_typelike_as_json_test.rb +53 -0
  36. data/test/metaschema_node_test.rb +19 -0
  37. data/test/schema_module_test.rb +21 -0
  38. data/test/schema_test.rb +109 -97
  39. data/test/spreedly_openapi_test.rb +8 -0
  40. data/test/test_helper.rb +42 -8
  41. data/test/util_test.rb +14 -14
  42. metadata +54 -25
  43. data/LICENSE.txt +0 -21
  44. data/lib/jsi/schema_instance_json_coder.rb +0 -83
  45. data/lib/jsi/struct_json_coder.rb +0 -30
  46. data/test/schema_instance_json_coder_test.rb +0 -121
  47. data/test/struct_json_coder_test.rb +0 -130
@@ -1,57 +1,54 @@
1
1
  require_relative 'test_helper'
2
2
 
3
- NamedSchemaInstance = JSI.class_for_schema({id: 'https://schemas.jsi.unth.net/test/base/named_schema'})
3
+ NamedSchemaInstance = JSI::Schema.new({id: 'https://schemas.jsi.unth.net/test/base/named_schema'}).jsi_schema_class
4
+
5
+ # hitting .tap(&:name) causes JSI to assign a constant name from the ID,
6
+ # meaning the name NamedSchemaInstanceTwo is not known.
7
+ NamedSchemaInstanceTwo = JSI::Schema.new({id: 'https://schemas.jsi.unth.net/test/base/named_schema_two'}).jsi_schema_class.tap(&:name)
4
8
 
5
9
  describe JSI::Base do
6
- let(:document) { {} }
7
- let(:path) { [] }
8
- let(:instance) { JSI::JSON::Node.new_by_type(document, path) }
9
10
  let(:schema_content) { {} }
10
11
  let(:schema) { JSI::Schema.new(schema_content) }
11
- let(:subject) { JSI.class_for_schema(schema).new(instance) }
12
- describe 'class .inspect + .to_s' do
12
+ let(:instance) { {} }
13
+ let(:subject) { schema.new_jsi(instance) }
14
+ describe 'class .inspect' do
13
15
  it 'is the same as Class#inspect on the base' do
14
16
  assert_equal('JSI::Base', JSI::Base.inspect)
15
- assert_equal('JSI::Base', JSI::Base.to_s)
16
17
  end
17
- it 'is SchemaClasses[] for generated subclass without id' do
18
- assert_match(%r(\AJSI::SchemaClasses\["[a-f0-9\-]+#"\]\z), subject.class.inspect)
19
- assert_match(%r(\AJSI::SchemaClasses\["[a-f0-9\-]+#"\]\z), subject.class.to_s)
18
+ it 'is (JSI Schema Class) for generated subclass without id' do
19
+ assert_equal("(JSI Schema Class: #)", subject.class.inspect)
20
20
  end
21
21
  describe 'with schema id' do
22
22
  let(:schema_content) { {'id' => 'https://jsi/foo'} }
23
- it 'is SchemaClasses[] for generated subclass with id' do
24
- assert_equal(%q(JSI::SchemaClasses["https://jsi/foo#"]), subject.class.inspect)
25
- assert_equal(%q(JSI::SchemaClasses["https://jsi/foo#"]), subject.class.to_s)
23
+ it 'is (JSI Schema Class: ...) for generated subclass with id' do
24
+ assert_equal("(JSI Schema Class: https://jsi/foo#)", subject.class.inspect)
26
25
  end
27
26
  end
28
- it 'is the constant name (plus id for .inspect) for a class assigned to a constant' do
27
+ it 'is the constant name plus id for a class assigned to a constant' do
29
28
  assert_equal(%q(NamedSchemaInstance (https://schemas.jsi.unth.net/test/base/named_schema#)), NamedSchemaInstance.inspect)
30
- assert_equal(%q(NamedSchemaInstance), NamedSchemaInstance.to_s)
29
+ end
30
+ it 'is not the constant name when the constant name has been generated from the schema_id' do
31
+ assert_equal("JSI::SchemaClasses::Xhttps___schemas_jsi_unth_net_test_base_named_schema_two_", NamedSchemaInstanceTwo.name)
32
+ assert_equal("(JSI Schema Class: https://schemas.jsi.unth.net/test/base/named_schema_two#)", NamedSchemaInstanceTwo.inspect)
31
33
  end
32
34
  end
33
35
  describe 'class name' do
34
36
  let(:schema_content) { {'id' => 'https://jsi/BaseTest'} }
35
37
  it 'generates a class name from schema_id' do
36
- assert_equal('JSI::SchemaClasses::Https___jsi_BaseTest_', subject.class.name)
38
+ assert_equal('JSI::SchemaClasses::Xhttps___jsi_BaseTest_', subject.class.name)
37
39
  end
38
40
  it 'uses an existing name' do
39
41
  assert_equal('NamedSchemaInstance', NamedSchemaInstance.name)
40
42
  end
41
43
  end
42
- describe 'class for schema .schema' do
43
- it '.schema' do
44
- assert_equal(schema, JSI.class_for_schema(schema).schema)
45
- end
46
- end
47
- describe 'class for schema .schema_id' do
48
- it '.schema_id' do
49
- assert_equal(schema.schema_id, JSI.class_for_schema(schema).schema_id)
44
+ describe 'class for schema .jsi_class_schemas' do
45
+ it '.jsi_class_schemas' do
46
+ assert_equal(Set.new << schema, schema.jsi_schema_class.jsi_class_schemas)
50
47
  end
51
48
  end
52
49
  describe 'module for schema .inspect' do
53
50
  it '.inspect' do
54
- assert_match(%r(\A#<Module for Schema: .+#>\z), JSI::SchemaClasses.module_for_schema(schema).inspect)
51
+ assert_equal("(JSI Schema Module: #)", JSI::SchemaClasses.module_for_schema(schema).inspect)
55
52
  end
56
53
  end
57
54
  describe 'module for schema .schema' do
@@ -59,26 +56,15 @@ describe JSI::Base do
59
56
  assert_equal(schema, JSI::SchemaClasses.module_for_schema(schema).schema)
60
57
  end
61
58
  end
62
- describe 'SchemaClasses[]' do
63
- it 'stores the class for the schema' do
64
- assert_equal(JSI.class_for_schema(schema), JSI::SchemaClasses[schema.schema_id])
65
- end
66
- end
67
- describe '.class_for_schema' do
59
+ describe '.class_for_schemas' do
68
60
  it 'returns a class from a schema' do
69
- class_for_schema = JSI.class_for_schema(schema)
61
+ class_for_schema = JSI.class_for_schemas([schema])
70
62
  # same class every time
71
- assert_equal(JSI.class_for_schema(schema), class_for_schema)
63
+ assert_equal(JSI.class_for_schemas([schema]), class_for_schema)
72
64
  assert_operator(class_for_schema, :<, JSI::Base)
73
65
  end
74
- it 'returns a class from a hash' do
75
- assert_equal(JSI.class_for_schema(schema), JSI.class_for_schema(schema.schema_node.content))
76
- end
77
- it 'returns a class from a schema node' do
78
- assert_equal(JSI.class_for_schema(schema), JSI.class_for_schema(schema.schema_node))
79
- end
80
- it 'returns a class from a Base' do
81
- assert_equal(JSI.class_for_schema(schema), JSI.class_for_schema(JSI.class_for_schema({}).new(schema.schema_node)))
66
+ it 'returns the same class from a hash' do
67
+ assert_equal(JSI.class_for_schemas([schema]), JSI.class_for_schemas([schema_content]))
82
68
  end
83
69
  end
84
70
  describe 'JSI::SchemaClasses.module_for_schema' do
@@ -88,26 +74,20 @@ describe JSI::Base do
88
74
  assert_equal(JSI::SchemaClasses.module_for_schema(schema), module_for_schema)
89
75
  end
90
76
  it 'returns a module from a hash' do
91
- assert_equal(JSI::SchemaClasses.module_for_schema(schema), JSI::SchemaClasses.module_for_schema(schema.schema_node.content))
92
- end
93
- it 'returns a module from a schema node' do
94
- assert_equal(JSI::SchemaClasses.module_for_schema(schema), JSI::SchemaClasses.module_for_schema(schema.schema_node))
95
- end
96
- it 'returns a module from a Base' do
97
- assert_equal(JSI::SchemaClasses.module_for_schema(schema), JSI::SchemaClasses.module_for_schema(JSI.class_for_schema({}).new(schema.schema_node)))
77
+ assert_equal(JSI::SchemaClasses.module_for_schema(schema), JSI::SchemaClasses.module_for_schema(schema.jsi_instance))
98
78
  end
99
79
  end
100
80
  describe 'initialization' do
101
81
  describe 'on Base' do
102
82
  it 'errors' do
103
83
  err = assert_raises(TypeError) { JSI::Base.new({}) }
104
- assert_equal('cannot instantiate JSI::Base which has no method #schema. please use JSI.class_for_schema', err.message)
84
+ assert_equal('cannot instantiate JSI::Base which has no method #jsi_schemas. it is recommended to instantiate JSIs from a schema using JSI::Schema#new_jsi.', err.message)
105
85
  end
106
86
  end
107
87
  describe 'nil' do
108
88
  let(:instance) { nil }
109
89
  it 'initializes with nil instance' do
110
- assert_equal(JSI::JSON::Node.new_doc(nil), subject.instance)
90
+ assert_equal(nil, subject.jsi_instance)
111
91
  assert(!subject.respond_to?(:to_ary))
112
92
  assert(!subject.respond_to?(:to_hash))
113
93
  end
@@ -115,7 +95,7 @@ describe JSI::Base do
115
95
  describe 'arbitrary instance' do
116
96
  let(:instance) { Object.new }
117
97
  it 'initializes' do
118
- assert_equal(JSI::JSON::Node.new_doc(instance), subject.instance)
98
+ assert_equal(instance, subject.jsi_instance)
119
99
  assert(!subject.respond_to?(:to_ary))
120
100
  assert(!subject.respond_to?(:to_hash))
121
101
  end
@@ -124,16 +104,16 @@ describe JSI::Base do
124
104
  let(:instance) { {'foo' => 'bar'} }
125
105
  let(:schema_content) { {'type' => 'object'} }
126
106
  it 'initializes' do
127
- assert_equal(JSI::JSON::Node.new_doc({'foo' => 'bar'}), subject.instance)
107
+ assert_equal({'foo' => 'bar'}, subject.jsi_instance)
128
108
  assert(!subject.respond_to?(:to_ary))
129
109
  assert(subject.respond_to?(:to_hash))
130
110
  end
131
111
  end
132
- describe 'JSI::JSON::Hashnode' do
133
- let(:document) { {'foo' => 'bar'} }
112
+ describe 'JSI::JSON::HashNode' do
113
+ let(:instance) { JSI::JSON::HashNode.new({'foo' => 'bar'}, JSI::JSON::Pointer.new([])) }
134
114
  let(:schema_content) { {'type' => 'object'} }
135
115
  it 'initializes' do
136
- assert_equal(JSI::JSON::HashNode.new({'foo' => 'bar'}, []), subject.instance)
116
+ assert_equal(JSI::JSON::HashNode.new({'foo' => 'bar'}, JSI::JSON::Pointer.new([])), subject.jsi_instance)
137
117
  assert(!subject.respond_to?(:to_ary))
138
118
  assert(subject.respond_to?(:to_hash))
139
119
  end
@@ -142,69 +122,93 @@ describe JSI::Base do
142
122
  let(:instance) { ['foo'] }
143
123
  let(:schema_content) { {'type' => 'array'} }
144
124
  it 'initializes' do
145
- assert_equal(JSI::JSON::Node.new_doc(['foo']), subject.instance)
125
+ assert_equal(['foo'], subject.jsi_instance)
146
126
  assert(subject.respond_to?(:to_ary))
147
127
  assert(!subject.respond_to?(:to_hash))
148
128
  end
149
129
  end
150
- describe 'JSI::JSON::Arraynode' do
151
- let(:document) { ['foo'] }
130
+ describe 'JSI::JSON::ArrayNode' do
131
+ let(:instance) { JSI::JSON::ArrayNode.new(['foo'], JSI::JSON::Pointer.new([])) }
152
132
  let(:schema_content) { {'type' => 'array'} }
153
133
  it 'initializes' do
154
- assert_equal(JSI::JSON::ArrayNode.new(['foo'], []), subject.instance)
134
+ assert_equal(JSI::JSON::ArrayNode.new(['foo'], JSI::JSON::Pointer.new([])), subject.jsi_instance)
155
135
  assert(subject.respond_to?(:to_ary))
156
136
  assert(!subject.respond_to?(:to_hash))
157
137
  end
158
138
  end
159
- describe 'another Base' do
139
+ describe 'another JSI::Base invalid' do
160
140
  let(:schema_content) { {'type' => 'object'} }
161
- let(:instance) { JSI.class_for_schema(schema).new({'foo' => 'bar'}) }
162
- it 'initializes with a warning' do
163
- assert_output(nil, /assigning instance to a Base instance is incorrect. received: #\{<JSI::SchemaClasses\["[^"]+#"\][^>]*>[^}]+}/) do
164
- subject
165
- end
166
- assert_equal(JSI::JSON::HashNode.new({'foo' => 'bar'}, []), subject.instance)
141
+ let(:instance) { schema.new_jsi({'foo' => 'bar'}) }
142
+ it 'initializes with an error' do
143
+ err = assert_raises(TypeError) { subject }
144
+ assert_equal("assigning another JSI::Base instance to a (JSI Schema Class: #) instance is incorrect. received: \#{<JSI> \"foo\" => \"bar\"}", err.message)
145
+ end
146
+ end
147
+ describe 'Schema invalid' do
148
+ let(:instance) { JSI::Schema.new({}) }
149
+ it 'initializes with an error' do
150
+ err = assert_raises(TypeError) { subject }
151
+ assert_equal("assigning a schema to a (JSI Schema Class: #) instance is incorrect. received: \#{<JSI (JSI::JSONSchemaOrgDraft06) Schema>}", err.message)
167
152
  end
168
153
  end
169
154
  end
170
- describe '#parents, #parent' do
155
+ describe '#parent_jsis, #parent_jsi' do
171
156
  let(:schema_content) { {'properties' => {'foo' => {'properties' => {'bar' => {'properties' => {'baz' => {}}}}}}} }
172
- let(:document) { {'foo' => {'bar' => {'baz' => {}}}} }
173
- describe 'no parents' do
157
+ let(:instance) { {'foo' => {'bar' => {'baz' => {}}}} }
158
+ describe 'no parent_jsis' do
174
159
  it 'has none' do
175
160
  assert_equal([], subject.parents)
161
+ assert_equal([], subject.parent_jsis)
176
162
  assert_equal(nil, subject.parent)
163
+ assert_equal(nil, subject.parent_jsi)
177
164
  end
178
165
  end
179
- describe 'one parent' do
166
+ describe 'one parent_jsi' do
180
167
  it 'has one' do
181
168
  assert_equal([subject], subject.foo.parents)
169
+ assert_equal([subject], subject.foo.parent_jsis)
182
170
  assert_equal(subject, subject.foo.parent)
171
+ assert_equal(subject, subject.foo.parent_jsi)
183
172
  end
184
173
  end
185
- describe 'more parents' do
174
+ describe 'more parent_jsis' do
186
175
  it 'has more' do
187
176
  assert_equal([subject.foo.bar, subject.foo, subject], subject.foo.bar.baz.parents)
177
+ assert_equal([subject.foo.bar, subject.foo, subject], subject.foo.bar.baz.parent_jsis)
188
178
  assert_equal(subject.foo.bar, subject.foo.bar.baz.parent)
179
+ assert_equal(subject.foo.bar, subject.foo.bar.baz.parent_jsi)
189
180
  end
190
181
  end
191
182
  end
192
183
  describe '#each, Enumerable methods' do
193
- let(:document) { 'a string' }
184
+ let(:instance) { 'a string' }
194
185
  it "raises NoMethodError calling each or Enumerable methods" do
195
186
  assert_raises(NoMethodError) { subject.each { nil } }
196
187
  assert_raises(NoMethodError) { subject.map { nil } }
197
188
  end
198
189
  end
199
190
  describe '#modified_copy' do
191
+ describe 'with an instance that does not have #modified_copy' do
192
+ let(:instance) { Object.new }
193
+ it 'yields the instance to modify' do
194
+ new_instance = Object.new
195
+ modified = subject.modified_copy do |o|
196
+ assert_equal(instance, o)
197
+ new_instance
198
+ end
199
+ assert_equal(new_instance, modified.jsi_instance)
200
+ assert_equal(instance, subject.jsi_instance)
201
+ refute_equal(instance, modified)
202
+ end
203
+ end
200
204
  describe 'with an instance that does have #modified_copy' do
201
205
  it 'yields the instance to modify' do
202
206
  modified = subject.modified_copy do |o|
203
207
  assert_equal({}, o)
204
208
  {'a' => 'b'}
205
209
  end
206
- assert_equal({'a' => 'b'}, modified.instance.content)
207
- assert_equal({}, subject.instance.content)
210
+ assert_equal({'a' => 'b'}, modified.jsi_instance)
211
+ assert_equal({}, subject.jsi_instance)
208
212
  refute_equal(instance, modified)
209
213
  end
210
214
  end
@@ -212,7 +216,7 @@ describe JSI::Base do
212
216
  it 'yields the instance to modify' do
213
217
  modified = subject.modified_copy { |o| o }
214
218
  # this doesn't really need to be tested but ... whatever
215
- assert_equal(subject.instance.content.object_id, modified.instance.content.object_id)
219
+ assert_equal(subject.jsi_instance.object_id, modified.jsi_instance.object_id)
216
220
  assert_equal(subject, modified)
217
221
  refute_equal(subject.object_id, modified.object_id)
218
222
  end
@@ -224,18 +228,15 @@ describe JSI::Base do
224
228
  modified = subject.modified_copy do |o|
225
229
  o.to_s
226
230
  end
227
- assert_equal('{}', modified.instance.content)
228
- assert_equal({}, subject.instance.content)
231
+ assert_equal('{}', modified.jsi_instance)
232
+ assert_equal({}, subject.jsi_instance)
229
233
  refute_equal(instance, modified)
230
234
  # interesting side effect
231
235
  assert(subject.respond_to?(:to_hash))
232
236
  assert(!modified.respond_to?(:to_hash))
233
- assert_equal(JSI::JSON::HashNode, subject.instance.class)
234
- assert_equal(JSI::JSON::Node, modified.instance.class)
235
237
  end
236
238
  end
237
239
  end
238
- it('#fragment') { assert_equal('#', subject.fragment) }
239
240
  describe 'validation' do
240
241
  describe 'without errors' do
241
242
  it '#fully_validate' do
@@ -248,6 +249,94 @@ describe JSI::Base do
248
249
  assert_equal(true, subject.validate!)
249
250
  end
250
251
  end
252
+ describe 'with errors' do
253
+ let(:schema_content) {
254
+ {
255
+ 'id' => 'https://schemas.jsi.unth.net/test/JSI::Base::validation::with errors',
256
+ 'type' => 'object',
257
+ 'properties' => {
258
+ 'some_number' => {
259
+ 'type' => 'number'
260
+ },
261
+ 'a_required_property' => {
262
+ 'type' => 'string'
263
+ }
264
+ }
265
+ }
266
+ }
267
+ let(:instance) { "this is a string" }
268
+
269
+ it '#validate' do
270
+ assert_equal(false, subject.validate)
271
+ end
272
+ it '#validate!' do
273
+ assert_raises JSON::Schema::ValidationError do
274
+ subject.validate!
275
+ end
276
+ end
277
+ describe 'fully_validate' do
278
+ it '#fully_validate ' do
279
+ assert_equal(["The property '#/' of type string did not match the following type: object in schema https://schemas.jsi.unth.net/test/JSI::Base::validation::with errors"], subject.fully_validate)
280
+ end
281
+ it '#fully_validate :errors_as_objects' do
282
+ expected = [
283
+ {
284
+ :schema => Addressable::URI.parse('https://schemas.jsi.unth.net/test/JSI::Base::validation::with errors'),
285
+ :fragment => "#/",
286
+ :message => "The property '#/' of type string did not match the following type: object in schema https://schemas.jsi.unth.net/test/JSI::Base::validation::with errors",
287
+ :failed_attribute=>"TypeV4"
288
+ }
289
+ ]
290
+ assert_equal(expected, subject.fully_validate(:errors_as_objects => true))
291
+ end
292
+ end
293
+ end
294
+ describe 'at a depth' do
295
+ let(:schema_content) do
296
+ {
297
+ 'id' => 'https://schemas.jsi.unth.net/test/JSI::Base::validation::at a depth',
298
+ 'description' => 'hash schema',
299
+ 'type' => 'object',
300
+ 'properties' => {
301
+ 'foo' => {'type' => 'object'},
302
+ 'bar' => {},
303
+ 'baz' => {'type' => 'array'},
304
+ },
305
+ 'additionalProperties' => {'not' => {}},
306
+ }
307
+ end
308
+
309
+ describe 'without errors' do
310
+ let(:instance) { {'foo' => {'x' => 'y'}, 'bar' => [9], 'baz' => [true]} }
311
+
312
+ it '#fully_validate' do
313
+ assert_equal([], subject.foo.fully_validate)
314
+ assert_equal([], subject.bar.fully_validate)
315
+ end
316
+ it '#validate' do
317
+ assert_equal(true, subject.foo.validate)
318
+ assert_equal(true, subject.bar.validate)
319
+ end
320
+ end
321
+ describe 'with errors' do
322
+ let(:instance) { {'foo' => [true], 'bar' => [9], 'baz' => {'x' => 'y'}, 'more' => {}} }
323
+
324
+ it '#fully_validate' do
325
+ assert_equal(["The property '#/' of type array did not match the following type: object in schema https://schemas.jsi.unth.net/test/JSI::Base::validation::at a depth"], subject.foo.fully_validate)
326
+ assert_equal([], subject.bar.fully_validate)
327
+ assert_equal(["The property '#/' of type object did not match the following type: array in schema https://schemas.jsi.unth.net/test/JSI::Base::validation::at a depth"], subject.baz.fully_validate)
328
+ assert_equal(["The property '#/' of type object matched the disallowed schema in schema https://schemas.jsi.unth.net/test/JSI::Base::validation::at a depth"], subject['more'].fully_validate)
329
+ assert_equal(["The property '#/foo' of type array did not match the following type: object in schema https://schemas.jsi.unth.net/test/JSI::Base::validation::at a depth", "The property '#/baz' of type object did not match the following type: array in schema https://schemas.jsi.unth.net/test/JSI::Base::validation::at a depth", "The property '#/more' of type object matched the disallowed schema in schema https://schemas.jsi.unth.net/test/JSI::Base::validation::at a depth"], subject.fully_validate)
330
+ end
331
+ it '#validate' do
332
+ assert_equal(false, subject.foo.validate)
333
+ assert_equal(true, subject.bar.validate)
334
+ assert_equal(false, subject.baz.validate)
335
+ assert_equal(false, subject['more'].validate)
336
+ assert_equal(false, subject.validate)
337
+ end
338
+ end
339
+ end
251
340
  end
252
341
  describe 'property accessors' do
253
342
  let(:schema_content) do
@@ -260,17 +349,17 @@ describe JSI::Base do
260
349
  },
261
350
  }
262
351
  end
263
- let(:document) do
352
+ let(:instance) do
264
353
  {'foo' => {'x' => 'y'}, 'bar' => [3.14159], 'baz' => true, 'qux' => []}
265
354
  end
266
355
  describe 'readers' do
267
356
  it 'reads attributes described as properties' do
268
357
  assert_equal({'x' => 'y'}, subject.foo.as_json)
269
- assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), subject.foo)
358
+ assert_is_a(schema.properties['foo'].jsi_schema_module, subject.foo)
270
359
  assert_respond_to(subject.foo, :to_hash)
271
360
  refute_respond_to(subject.foo, :to_ary)
272
361
  assert_equal([3.14159], subject.bar.as_json)
273
- assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['bar']), subject.bar)
362
+ assert_is_a(schema.properties['bar'].jsi_schema_module, subject.bar)
274
363
  refute_respond_to(subject.bar, :to_hash)
275
364
  assert_respond_to(subject.bar, :to_ary)
276
365
  assert_equal(true, subject.baz)
@@ -281,8 +370,8 @@ describe JSI::Base do
281
370
  describe 'when the instance is not hashlike' do
282
371
  let(:instance) { nil }
283
372
  it 'errors' do
284
- err = assert_raises(NoMethodError) { subject.foo }
285
- assert_match(%r(\Ainstance does not respond to \[\]; cannot call reader `foo' for: #<JSI::SchemaClasses\["[^"]+#"\].*nil.*>\z)m, err.message)
373
+ err = assert_raises(JSI::Base::CannotSubscriptError) { subject.foo }
374
+ assert_equal(%q(cannot subcript (using token: "foo") from instance: nil), err.message)
286
375
  end
287
376
  end
288
377
  describe 'properties with the same names as instance methods' do
@@ -295,14 +384,14 @@ describe JSI::Base do
295
384
  'inspect' => {}, # Base
296
385
  'pretty_inspect' => {}, # Kernel
297
386
  'as_json' => {}, # Base::OverrideFromExtensions, extended on initialization
298
- 'each' => {}, # BaseHash / BaseArray
387
+ 'each' => {}, # PathedHashNode / PathedArrayNode
299
388
  'instance_exec' => {}, # BasicObject
300
- 'instance' => {}, # Base
301
- 'schema' => {}, # module_for_schema singleton definition
389
+ 'jsi_instance' => {}, # Base
390
+ 'jsi_schemas' => {}, # module_for_schema singleton definition
302
391
  },
303
392
  }
304
393
  end
305
- let(:document) do
394
+ let(:instance) do
306
395
  {
307
396
  'foo' => 'bar',
308
397
  'initialize' => 'hi',
@@ -311,24 +400,23 @@ describe JSI::Base do
311
400
  'as_json' => 'hi',
312
401
  'each' => 'hi',
313
402
  'instance_exec' => 'hi',
314
- 'instance' => 'hi',
315
- 'schema' => 'hi',
403
+ 'jsi_instance' => 'hi',
404
+ 'jsi_schemas' => 'hi',
316
405
  }
317
406
  end
318
407
  it 'does not define readers' do
319
- assert_equal('bar', subject.foo)
320
- assert_equal(JSI::SchemaClasses.module_for_schema(subject.schema), subject.method(:foo).owner)
408
+ assert_equal('bar', subject.foo) # this one is defined
321
409
 
322
410
  assert_equal(JSI::Base, subject.method(:initialize).owner)
323
411
  assert_equal('hi', subject['initialize'])
324
- assert_match(%r(\A#\{<JSI::SchemaClasses\[".*#"\].*}\z)m, subject.inspect)
412
+ assert_equal(%q(#{<JSI> "foo" => "bar", "initialize" => "hi", "inspect" => "hi", "pretty_inspect" => "hi", "as_json" => "hi", "each" => "hi", "instance_exec" => "hi", "jsi_instance" => "hi", "jsi_schemas" => "hi"}), subject.inspect)
325
413
  assert_equal('hi', subject['inspect'])
326
- assert_match(%r(\A#\{<JSI::SchemaClasses\[".*#"\].*}\Z)m, subject.pretty_inspect)
327
- assert_equal(document, subject.as_json)
414
+ assert_equal(%Q(\#{<JSI>\n "foo" => "bar",\n "initialize" => "hi",\n "inspect" => "hi",\n "pretty_inspect" => "hi",\n "as_json" => "hi",\n "each" => "hi",\n "instance_exec" => "hi",\n "jsi_instance" => "hi",\n "jsi_schemas" => "hi"\n}\n), subject.pretty_inspect)
415
+ assert_equal(instance, subject.as_json)
328
416
  assert_equal(subject, subject.each { })
329
417
  assert_equal(2, subject.instance_exec { 2 })
330
- assert_equal(instance, subject.instance)
331
- assert_equal(schema, subject.schema)
418
+ assert_equal(instance, subject.jsi_instance)
419
+ assert_equal(Set.new << schema, subject.jsi_schemas)
332
420
  end
333
421
  end
334
422
  end
@@ -339,57 +427,60 @@ describe JSI::Base do
339
427
  subject.foo = {'y' => 'z'}
340
428
 
341
429
  assert_equal({'y' => 'z'}, subject.foo.as_json)
342
- assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), orig_foo)
343
- assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), subject.foo)
430
+ assert_is_a(schema.properties['foo'].jsi_schema_module, orig_foo)
431
+ assert_is_a(schema.properties['foo'].jsi_schema_module, subject.foo)
344
432
  end
345
433
  it 'modifies the instance, visible to other references to the same instance' do
346
- orig_instance = subject.instance
434
+ orig_instance = subject.jsi_instance
347
435
 
348
436
  subject.foo = {'y' => 'z'}
349
437
 
350
- assert_equal(orig_instance, subject.instance)
351
- assert_equal({'y' => 'z'}, orig_instance['foo'].as_json)
352
- assert_equal({'y' => 'z'}, subject.instance['foo'].as_json)
353
- assert_equal(orig_instance.class, subject.instance.class)
438
+ assert_equal(orig_instance, subject.jsi_instance)
439
+ assert_equal({'y' => 'z'}, orig_instance['foo'])
440
+ assert_equal({'y' => 'z'}, subject.jsi_instance['foo'])
441
+ assert_equal(orig_instance.class, subject.jsi_instance.class)
354
442
  end
355
443
  describe 'when the instance is not hashlike' do
356
444
  let(:instance) { nil }
357
445
  it 'errors' do
358
446
  err = assert_raises(NoMethodError) { subject.foo = 0 }
359
- assert_match(%r(\Ainstance does not respond to \[\]=; cannot call writer `foo=' for: #<JSI::SchemaClasses\["[^"]+#"\].*nil.*>\z)m, err.message)
447
+ assert_equal('cannot assign subcript (using token: "foo") to instance: nil', err.message)
360
448
  end
361
449
  end
362
450
  end
363
451
  end
364
452
  describe '#inspect' do
365
453
  # if the instance is hash-like, #inspect gets overridden
366
- let(:document) { Object.new }
454
+ let(:instance) { Object.new }
367
455
  it 'inspects' do
368
- assert_match(%r(\A#<JSI::SchemaClasses\["[^"]+#"\] #<JSI::JSON::Node fragment="#" #<Object:[^<>]*>>>\z), subject.inspect)
456
+ assert_match(%r(\A\#<JSI\ \#<Object:[^<>]*>>\z), subject.inspect)
369
457
  end
370
458
  end
371
459
  describe '#pretty_print' do
372
460
  # if the instance is hash-like, #pretty_print gets overridden
373
- let(:document) { Object.new }
461
+ let(:instance) { Object.new }
374
462
  it 'pretty_prints' do
375
- assert_match(%r(\A#<JSI::SchemaClasses\["[^"]+#"\]\n #<JSI::JSON::Node fragment="#" #<Object:[^<>]*>>\n>\z), subject.pretty_inspect.chomp)
463
+ assert_match(%r(\A\#<JSI\ \#<Object:[^<>]*>>\z), subject.pretty_inspect.chomp)
376
464
  end
377
465
  end
378
466
  describe '#as_json' do
379
467
  it '#as_json' do
380
- assert_equal({'a' => 'b'}, JSI.class_for_schema({}).new(JSI::JSON::Node.new_doc({'a' => 'b'})).as_json)
381
- assert_equal({'a' => 'b'}, JSI.class_for_schema({'type' => 'object'}).new(JSI::JSON::Node.new_doc({'a' => 'b'})).as_json)
382
- assert_equal(['a', 'b'], JSI.class_for_schema({'type' => 'array'}).new(JSI::JSON::Node.new_doc(['a', 'b'])).as_json)
383
- assert_equal(['a'], JSI.class_for_schema({}).new(['a']).as_json(some_option: true))
468
+ assert_equal({'a' => 'b'}, JSI::Schema.new({}).new_jsi({'a' => 'b'}).as_json)
469
+ assert_equal({'a' => 'b'}, JSI::Schema.new({}).new_jsi(JSI::JSON::Node.new_doc({'a' => 'b'})).as_json)
470
+ assert_equal({'a' => 'b'}, JSI::Schema.new({'type' => 'object'}).new_jsi(JSI::JSON::Node.new_doc({'a' => 'b'})).as_json)
471
+ assert_equal(['a', 'b'], JSI::Schema.new({'type' => 'array'}).new_jsi(JSI::JSON::Node.new_doc(['a', 'b'])).as_json)
472
+ assert_equal(['a'], JSI::Schema.new({}).new_jsi(['a']).as_json(some_option: true))
384
473
  end
385
474
  end
386
- describe 'overwrite schema instance with instance=' do
387
- # this error message indicates an internal bug (hence Bug class), so there isn't an intended way to
388
- # trigger it using JSI::Base properly. we use it improperly just to test that code path. this
389
- # is definitely not defined behavior.
390
- it 'errors' do
391
- err = assert_raises(JSI::Bug) { subject.send(:instance=, {'foo' => 'bar'}) }
392
- assert_match(%r(\Aoverwriting instance is not supported\z), err.message)
475
+ describe 'equality between different classes of JSI::Base subclasses' do
476
+ let(:subject_subclass) { Class.new(schema.jsi_schema_class).new(instance) }
477
+
478
+ it 'considers a Base subclass (class_for_schema) and subsubclass to be equal with the same instance' do
479
+ assert_equal(subject.hash, subject_subclass.hash)
480
+ assert(subject == subject_subclass)
481
+ assert(subject_subclass == subject)
482
+ assert(subject.eql?(subject_subclass))
483
+ assert(subject_subclass.eql?(subject))
393
484
  end
394
485
  end
395
486
  end