jsi 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/LICENSE.md +1 -1
- data/README.md +11 -6
- data/jsi.gemspec +30 -0
- data/lib/jsi/base/node.rb +183 -0
- data/lib/jsi/base.rb +135 -161
- data/lib/jsi/jsi_coder.rb +3 -3
- data/lib/jsi/metaschema.rb +0 -1
- data/lib/jsi/metaschema_node/bootstrap_schema.rb +9 -8
- data/lib/jsi/metaschema_node.rb +48 -51
- data/lib/jsi/ptr.rb +28 -17
- data/lib/jsi/schema/application/child_application/contains.rb +11 -2
- data/lib/jsi/schema/application/child_application/items.rb +3 -3
- data/lib/jsi/schema/application/child_application/properties.rb +3 -3
- data/lib/jsi/schema/application/child_application.rb +1 -3
- data/lib/jsi/schema/application/inplace_application/dependencies.rb +1 -1
- data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +3 -3
- data/lib/jsi/schema/application/inplace_application/ref.rb +1 -1
- data/lib/jsi/schema/application/inplace_application/someof.rb +26 -11
- data/lib/jsi/schema/application/inplace_application.rb +1 -6
- data/lib/jsi/schema/ref.rb +3 -2
- data/lib/jsi/schema/schema_ancestor_node.rb +11 -17
- data/lib/jsi/schema/validation/array.rb +3 -3
- data/lib/jsi/schema/validation/const.rb +1 -1
- data/lib/jsi/schema/validation/contains.rb +1 -1
- data/lib/jsi/schema/validation/dependencies.rb +1 -1
- data/lib/jsi/schema/validation/draft04/minmax.rb +6 -6
- data/lib/jsi/schema/validation/enum.rb +1 -1
- data/lib/jsi/schema/validation/ifthenelse.rb +5 -5
- data/lib/jsi/schema/validation/items.rb +4 -4
- data/lib/jsi/schema/validation/not.rb +1 -1
- data/lib/jsi/schema/validation/numeric.rb +5 -5
- data/lib/jsi/schema/validation/object.rb +2 -2
- data/lib/jsi/schema/validation/pattern.rb +1 -1
- data/lib/jsi/schema/validation/properties.rb +3 -3
- data/lib/jsi/schema/validation/property_names.rb +1 -1
- data/lib/jsi/schema/validation/ref.rb +1 -1
- data/lib/jsi/schema/validation/required.rb +1 -1
- data/lib/jsi/schema/validation/someof.rb +3 -3
- data/lib/jsi/schema/validation/string.rb +2 -2
- data/lib/jsi/schema/validation/type.rb +1 -1
- data/lib/jsi/schema/validation.rb +1 -1
- data/lib/jsi/schema.rb +91 -85
- data/lib/jsi/schema_classes.rb +70 -45
- data/lib/jsi/schema_registry.rb +15 -5
- data/lib/jsi/schema_set.rb +42 -2
- data/lib/jsi/simple_wrap.rb +23 -4
- data/lib/jsi/util/{attr_struct.rb → private/attr_struct.rb} +40 -19
- data/lib/jsi/util/private.rb +204 -0
- data/lib/jsi/{typelike_modules.rb → util/typelike.rb} +56 -82
- data/lib/jsi/util.rb +68 -148
- data/lib/jsi/version.rb +1 -1
- data/lib/jsi.rb +1 -17
- data/lib/schemas/json-schema.org/draft-04/schema.rb +3 -1
- data/lib/schemas/json-schema.org/draft-06/schema.rb +3 -1
- data/lib/schemas/json-schema.org/draft-07/schema.rb +3 -1
- metadata +11 -9
- data/lib/jsi/pathed_node.rb +0 -116
data/lib/jsi/base.rb
CHANGED
@@ -14,24 +14,17 @@ module JSI
|
|
14
14
|
#
|
15
15
|
# the JSI::Base class itself is not intended to be instantiated.
|
16
16
|
class Base
|
17
|
-
|
17
|
+
autoload :ArrayNode, 'jsi/base/node'
|
18
|
+
autoload :HashNode, 'jsi/base/node'
|
19
|
+
|
18
20
|
include Schema::SchemaAncestorNode
|
19
21
|
include Util::Memoize
|
20
22
|
|
21
|
-
#
|
22
|
-
# the class than to conditionally extend the instance.
|
23
|
-
include Enumerable
|
24
|
-
|
25
|
-
# an exception raised when #[] is invoked on an instance which is not an array or hash
|
23
|
+
# an exception raised when {Base#[]} is invoked on an instance which is not an array or hash
|
26
24
|
class CannotSubscriptError < StandardError
|
27
25
|
end
|
28
26
|
|
29
27
|
class << self
|
30
|
-
# @private @deprecated
|
31
|
-
def new_jsi(instance, **kw, &b)
|
32
|
-
new(instance, **kw, &b)
|
33
|
-
end
|
34
|
-
|
35
28
|
# @private
|
36
29
|
# is the constant JSI::SchemaClasses::<self.schema_classes_const_name> defined?
|
37
30
|
# (if so, we will prefer to use something more human-readable than that ugly mess.)
|
@@ -84,8 +77,9 @@ module JSI
|
|
84
77
|
def schema_classes_const_name
|
85
78
|
if respond_to?(:jsi_class_schemas)
|
86
79
|
schema_names = jsi_class_schemas.map do |schema|
|
87
|
-
|
88
|
-
|
80
|
+
named_ancestor_schema, tokens = schema.jsi_schema_module.send(:named_ancestor_schema_tokens)
|
81
|
+
if named_ancestor_schema
|
82
|
+
[named_ancestor_schema.jsi_schema_module.name, *tokens].join('_')
|
89
83
|
elsif schema.schema_uri
|
90
84
|
schema.schema_uri.to_s
|
91
85
|
else
|
@@ -119,78 +113,44 @@ module JSI
|
|
119
113
|
end
|
120
114
|
end
|
121
115
|
|
122
|
-
#
|
123
|
-
# from a document and pointer.
|
116
|
+
# initializes a JSI whose instance is in the given document at the given pointer.
|
124
117
|
#
|
125
|
-
#
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
# initializes this JSI from the given instance - instance is most commonly
|
131
|
-
# a parsed JSON document consisting of Hash, Array, or sometimes a basic
|
132
|
-
# type, but this is in no way enforced and a JSI may wrap any object.
|
118
|
+
# this is a private api - users should look elsewhere to instantiate JSIs, in particular:
|
119
|
+
#
|
120
|
+
# - {JSI.new_schema} and {Schema::DescribesSchema#new_schema} to instantiate schemas
|
121
|
+
# - {Schema#new_jsi} to instantiate schema instances
|
133
122
|
#
|
134
|
-
# @
|
135
|
-
# @param jsi_document [Object]
|
136
|
-
#
|
137
|
-
#
|
138
|
-
# NOT be passed if `instance` is anything other than `NOINSTANCE`.
|
139
|
-
# @param jsi_ptr [JSI::Ptr] for internal use. a pointer specifying
|
140
|
-
# the path of this instance in the `jsi_document` param. `jsi_ptr` must be passed
|
141
|
-
# iff `jsi_document` is passed, i.e. when `instance` is `NOINSTANCE`
|
142
|
-
# @param jsi_root_node [JSI::Base] for internal use, specifies the JSI at the root of the document
|
123
|
+
# @api private
|
124
|
+
# @param jsi_document [Object] the document containing the instance
|
125
|
+
# @param jsi_ptr [JSI::Ptr] a pointer pointing to the JSI's instance in the document
|
126
|
+
# @param jsi_root_node [JSI::Base] the JSI of the root of the document containing this JSI
|
143
127
|
# @param jsi_schema_base_uri [Addressable::URI] see {SchemaSet#new_jsi} param uri
|
144
|
-
# @param jsi_schema_resource_ancestors [Array<JSI::Base
|
145
|
-
def initialize(
|
146
|
-
|
147
|
-
jsi_ptr: nil,
|
128
|
+
# @param jsi_schema_resource_ancestors [Array<JSI::Base<JSI::Schema>>]
|
129
|
+
def initialize(jsi_document,
|
130
|
+
jsi_ptr: Ptr[],
|
148
131
|
jsi_root_node: nil,
|
149
132
|
jsi_schema_base_uri: nil,
|
150
|
-
jsi_schema_resource_ancestors:
|
133
|
+
jsi_schema_resource_ancestors: Util::EMPTY_ARY
|
151
134
|
)
|
152
|
-
unless respond_to?(:jsi_schemas)
|
153
|
-
raise(TypeError, "cannot instantiate #{self.class.inspect} which has no method #jsi_schemas. it is recommended to instantiate JSIs from a schema using JSI::Schema#new_jsi.")
|
154
|
-
end
|
155
|
-
|
156
|
-
if instance.is_a?(JSI::Schema)
|
157
|
-
raise(TypeError, "assigning a schema to a #{self.class.inspect} instance is incorrect. received: #{instance.pretty_inspect.chomp}")
|
158
|
-
elsif instance.is_a?(JSI::Base)
|
159
|
-
raise(TypeError, "assigning another JSI::Base instance to a #{self.class.inspect} instance is incorrect. received: #{instance.pretty_inspect.chomp}")
|
160
|
-
end
|
135
|
+
raise(Bug, "no #jsi_schemas") unless respond_to?(:jsi_schemas)
|
161
136
|
|
162
137
|
jsi_initialize_memos
|
163
138
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
if
|
168
|
-
raise(Bug, "jsi_root_node cannot be specified for root JSI") if jsi_root_node
|
169
|
-
@jsi_root_node = self
|
170
|
-
else
|
171
|
-
if !jsi_root_node.is_a?(JSI::Base)
|
172
|
-
raise(TypeError, "jsi_root_node must be a JSI::Base; got: #{jsi_root_node.inspect}")
|
173
|
-
end
|
174
|
-
if !jsi_root_node.jsi_ptr.root?
|
175
|
-
raise(Bug, "jsi_root_node ptr #{jsi_root_node.jsi_ptr.inspect} is not root")
|
176
|
-
end
|
177
|
-
@jsi_root_node = jsi_root_node
|
178
|
-
end
|
179
|
-
else
|
180
|
-
raise(Bug, 'incorrect usage') if jsi_document || jsi_ptr || jsi_root_node
|
181
|
-
@jsi_document = instance
|
182
|
-
@jsi_ptr = Ptr[]
|
139
|
+
self.jsi_document = jsi_document
|
140
|
+
self.jsi_ptr = jsi_ptr
|
141
|
+
if @jsi_ptr.root?
|
142
|
+
raise(Bug, "jsi_root_node specified for root JSI") if jsi_root_node
|
183
143
|
@jsi_root_node = self
|
144
|
+
else
|
145
|
+
raise(Bug, "jsi_root_node is not JSI::Base") if !jsi_root_node.is_a?(JSI::Base)
|
146
|
+
raise(Bug, "jsi_root_node ptr is not root") if !jsi_root_node.jsi_ptr.root?
|
147
|
+
@jsi_root_node = jsi_root_node
|
184
148
|
end
|
185
|
-
|
186
149
|
self.jsi_schema_base_uri = jsi_schema_base_uri
|
187
150
|
self.jsi_schema_resource_ancestors = jsi_schema_resource_ancestors
|
188
151
|
|
189
|
-
if
|
190
|
-
|
191
|
-
end
|
192
|
-
if self.jsi_instance.respond_to?(:to_ary)
|
193
|
-
extend PathedArrayNode
|
152
|
+
if jsi_instance.is_a?(JSI::Base)
|
153
|
+
raise(TypeError, "a JSI::Base instance must not be another JSI::Base. received: #{jsi_instance.pretty_inspect.chomp}")
|
194
154
|
end
|
195
155
|
end
|
196
156
|
|
@@ -211,53 +171,53 @@ module JSI
|
|
211
171
|
# @return [JSI::Base]
|
212
172
|
attr_reader :jsi_root_node
|
213
173
|
|
174
|
+
# the content of this node in our {#jsi_document} at our {#jsi_ptr}. the same as {#jsi_instance}.
|
175
|
+
def jsi_node_content
|
176
|
+
content = jsi_ptr.evaluate(jsi_document)
|
177
|
+
content
|
178
|
+
end
|
179
|
+
|
214
180
|
# the JSON schema instance this JSI represents - the underlying JSON data used to instantiate this JSI
|
215
181
|
alias_method :jsi_instance, :jsi_node_content
|
216
182
|
|
217
|
-
# each is overridden by PathedHashNode or PathedArrayNode when appropriate. the base #each
|
218
|
-
# is not actually implemented, along with all the methods of Enumerable.
|
219
|
-
def each(*_)
|
220
|
-
raise NoMethodError, "Enumerable methods and #each not implemented for instance that is not like a hash or array: #{jsi_instance.pretty_inspect.chomp}"
|
221
|
-
end
|
222
|
-
|
223
183
|
# yields a JSI of each node at or below this one in this JSI's document.
|
224
184
|
#
|
225
185
|
# returns an Enumerator if no block is given.
|
226
186
|
#
|
227
|
-
# @yield [JSI::Base] each node
|
187
|
+
# @yield [JSI::Base] each descendent node, starting with self
|
228
188
|
# @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
|
229
|
-
def
|
189
|
+
def jsi_each_descendent_node(&block)
|
230
190
|
return to_enum(__method__) unless block
|
231
191
|
|
232
192
|
yield self
|
233
193
|
if respond_to?(:to_hash)
|
234
194
|
each_key do |k|
|
235
|
-
self[k, as_jsi: true].
|
195
|
+
self[k, as_jsi: true].jsi_each_descendent_node(&block)
|
236
196
|
end
|
237
197
|
elsif respond_to?(:to_ary)
|
238
198
|
each_index do |i|
|
239
|
-
self[i, as_jsi: true].
|
199
|
+
self[i, as_jsi: true].jsi_each_descendent_node(&block)
|
240
200
|
end
|
241
201
|
end
|
242
202
|
nil
|
243
203
|
end
|
244
204
|
|
245
|
-
# recursively selects
|
246
|
-
#
|
205
|
+
# recursively selects descendent nodes of this JSI, returning a modified copy of self containing only
|
206
|
+
# descendent nodes for which the given block had a true-ish result.
|
247
207
|
#
|
248
208
|
# this method yields a node before recursively descending to its child nodes, so leaf nodes are yielded
|
249
|
-
# last, after their parents. if a node is not selected, its
|
209
|
+
# last, after their parents. if a node is not selected, its descendents are never recursed.
|
250
210
|
#
|
251
|
-
# @yield [JSI::Base] each
|
211
|
+
# @yield [JSI::Base] each descendent node below self
|
252
212
|
# @return [JSI::Base] modified copy of self containing only the selected nodes
|
253
|
-
def
|
213
|
+
def jsi_select_descendents_node_first(&block)
|
254
214
|
jsi_modified_copy do |instance|
|
255
215
|
if respond_to?(:to_hash)
|
256
216
|
res = instance.class.new
|
257
217
|
each_key do |k|
|
258
218
|
v = self[k, as_jsi: true]
|
259
219
|
if yield(v)
|
260
|
-
res[k] = v.
|
220
|
+
res[k] = v.jsi_select_descendents_node_first(&block).jsi_node_content
|
261
221
|
end
|
262
222
|
end
|
263
223
|
res
|
@@ -266,7 +226,7 @@ module JSI
|
|
266
226
|
each_index do |i|
|
267
227
|
e = self[i, as_jsi: true]
|
268
228
|
if yield(e)
|
269
|
-
res << e.
|
229
|
+
res << e.jsi_select_descendents_node_first(&block).jsi_node_content
|
270
230
|
end
|
271
231
|
end
|
272
232
|
res
|
@@ -276,20 +236,23 @@ module JSI
|
|
276
236
|
end
|
277
237
|
end
|
278
238
|
|
279
|
-
#
|
280
|
-
|
239
|
+
# @deprecated after v0.6
|
240
|
+
alias_method :jsi_select_children_node_first, :jsi_select_descendents_node_first
|
241
|
+
|
242
|
+
# recursively selects descendent nodes of this JSI, returning a modified copy of self containing only
|
243
|
+
# descendent nodes for which the given block had a true-ish result.
|
281
244
|
#
|
282
245
|
# this method recursively descends child nodes before yielding each node, so leaf nodes are yielded
|
283
246
|
# before their parents.
|
284
247
|
#
|
285
|
-
# @yield [JSI::Base] each
|
248
|
+
# @yield [JSI::Base] each descendent node below self
|
286
249
|
# @return [JSI::Base] modified copy of self containing only the selected nodes
|
287
|
-
def
|
250
|
+
def jsi_select_descendents_leaf_first(&block)
|
288
251
|
jsi_modified_copy do |instance|
|
289
252
|
if respond_to?(:to_hash)
|
290
253
|
res = instance.class.new
|
291
254
|
each_key do |k|
|
292
|
-
v = self[k, as_jsi: true].
|
255
|
+
v = self[k, as_jsi: true].jsi_select_descendents_leaf_first(&block)
|
293
256
|
if yield(v)
|
294
257
|
res[k] = v.jsi_node_content
|
295
258
|
end
|
@@ -298,7 +261,7 @@ module JSI
|
|
298
261
|
elsif respond_to?(:to_ary)
|
299
262
|
res = instance.class.new
|
300
263
|
each_index do |i|
|
301
|
-
e = self[i, as_jsi: true].
|
264
|
+
e = self[i, as_jsi: true].jsi_select_descendents_leaf_first(&block)
|
302
265
|
if yield(e)
|
303
266
|
res << e.jsi_node_content
|
304
267
|
end
|
@@ -310,6 +273,9 @@ module JSI
|
|
310
273
|
end
|
311
274
|
end
|
312
275
|
|
276
|
+
# @deprecated after v0.6
|
277
|
+
alias_method :jsi_select_children_leaf_first, :jsi_select_descendents_leaf_first
|
278
|
+
|
313
279
|
# an array of JSI instances above this one in the document.
|
314
280
|
#
|
315
281
|
# @return [Array<JSI::Base>]
|
@@ -327,7 +293,31 @@ module JSI
|
|
327
293
|
#
|
328
294
|
# @return [JSI::Base, nil]
|
329
295
|
def jsi_parent_node
|
330
|
-
|
296
|
+
jsi_ptr.root? ? nil : jsi_root_node.jsi_descendent_node(jsi_ptr.parent)
|
297
|
+
end
|
298
|
+
|
299
|
+
# ancestor JSI instances from this node up to the root. this node itself is always its own first ancestor.
|
300
|
+
#
|
301
|
+
# @return [Array<JSI::Base>]
|
302
|
+
def jsi_ancestor_nodes
|
303
|
+
ancestors = []
|
304
|
+
ancestor = jsi_root_node
|
305
|
+
ancestors << ancestor
|
306
|
+
|
307
|
+
jsi_ptr.tokens.each do |token|
|
308
|
+
ancestor = ancestor[token, as_jsi: true]
|
309
|
+
ancestors << ancestor
|
310
|
+
end
|
311
|
+
ancestors.reverse!.freeze
|
312
|
+
end
|
313
|
+
|
314
|
+
# the descendent node at the given pointer
|
315
|
+
#
|
316
|
+
# @param ptr [JSI::Ptr, #to_ary]
|
317
|
+
# @return [JSI::Base]
|
318
|
+
def jsi_descendent_node(ptr)
|
319
|
+
descendent = Ptr.ary_ptr(ptr).evaluate(self, as_jsi: true)
|
320
|
+
descendent
|
331
321
|
end
|
332
322
|
|
333
323
|
# subscripts to return a child value identified by the given token.
|
@@ -338,14 +328,14 @@ module JSI
|
|
338
328
|
#
|
339
329
|
# - :auto (default): by default a JSI will be returned when either:
|
340
330
|
#
|
341
|
-
# - the result is a complex value (responds to #to_ary or #to_hash)
|
331
|
+
# - the result is a complex value (responds to #to_ary or #to_hash)
|
342
332
|
# - the result is a schema (including true/false schemas)
|
343
333
|
#
|
344
334
|
# a plain value is returned when no schemas are known to describe the instance, or when the value is a
|
345
335
|
# simple type (anything unresponsive to #to_ary / #to_hash).
|
346
336
|
#
|
347
|
-
# - true: the result value will always be returned as a JSI. the #jsi_schemas of the result may be
|
348
|
-
# if no schemas describe the instance.
|
337
|
+
# - true: the result value will always be returned as a JSI. the {#jsi_schemas} of the result may be
|
338
|
+
# empty if no schemas describe the instance.
|
349
339
|
# - false: the result value will always be the plain instance.
|
350
340
|
#
|
351
341
|
# note that nil is returned (regardless of as_jsi) when there is no value to return because the token
|
@@ -381,14 +371,18 @@ module JSI
|
|
381
371
|
|
382
372
|
if token_in_range
|
383
373
|
jsi_subinstance_as_jsi(value, subinstance_schemas, as_jsi) do
|
384
|
-
jsi_subinstance_memos[
|
374
|
+
jsi_subinstance_memos[
|
375
|
+
token: token,
|
376
|
+
subinstance_schemas: subinstance_schemas,
|
377
|
+
includes: SchemaClasses.includes_for(value),
|
378
|
+
]
|
385
379
|
end
|
386
380
|
else
|
387
381
|
if use_default
|
388
382
|
defaults = Set.new
|
389
383
|
subinstance_schemas.each do |subinstance_schema|
|
390
|
-
if subinstance_schema.
|
391
|
-
defaults << subinstance_schema['default']
|
384
|
+
if subinstance_schema.keyword?('default')
|
385
|
+
defaults << subinstance_schema.jsi_node_content['default']
|
392
386
|
end
|
393
387
|
end
|
394
388
|
end
|
@@ -415,7 +409,7 @@ module JSI
|
|
415
409
|
# @param value [JSI::Base, Object] the value to be assigned
|
416
410
|
def []=(token, value)
|
417
411
|
unless respond_to?(:to_hash) || respond_to?(:to_ary)
|
418
|
-
raise(
|
412
|
+
raise(CannotSubscriptError, "cannot assign subscript (using token: #{token.inspect}) to instance: #{jsi_instance.pretty_inspect.chomp}")
|
419
413
|
end
|
420
414
|
if value.is_a?(Base)
|
421
415
|
self[token] = value.jsi_instance
|
@@ -427,12 +421,12 @@ module JSI
|
|
427
421
|
# the set of JSI schema modules corresponding to the schemas that describe this JSI
|
428
422
|
# @return [Set<Module>]
|
429
423
|
def jsi_schema_modules
|
430
|
-
jsi_schemas.map(&:jsi_schema_module)
|
424
|
+
Util.ensure_module_set(jsi_schemas.map(&:jsi_schema_module))
|
431
425
|
end
|
432
426
|
|
433
427
|
# yields the content of this JSI's instance. the block must result in
|
434
|
-
# a modified copy of the yielded instance (not
|
435
|
-
# which will be used to instantiate a new JSI with the modified content.
|
428
|
+
# a modified copy of the yielded instance (not modified in place, which would alter this JSI
|
429
|
+
# as well) which will be used to instantiate and return a new JSI with the modified content.
|
436
430
|
#
|
437
431
|
# the result may have different schemas which describe it than this JSI's schemas,
|
438
432
|
# if conditional applicator schemas apply differently to the modified instance.
|
@@ -443,17 +437,14 @@ module JSI
|
|
443
437
|
def jsi_modified_copy(&block)
|
444
438
|
if @jsi_ptr.root?
|
445
439
|
modified_document = @jsi_ptr.modified_document_copy(@jsi_document, &block)
|
446
|
-
|
447
|
-
|
448
|
-
jsi_ptr: @jsi_ptr,
|
449
|
-
jsi_schema_base_uri: @jsi_schema_base_uri,
|
450
|
-
jsi_schema_resource_ancestors: @jsi_schema_resource_ancestors, # this can only be empty but included for consistency
|
440
|
+
jsi_schemas.new_jsi(modified_document,
|
441
|
+
uri: jsi_schema_base_uri,
|
451
442
|
)
|
452
443
|
else
|
453
444
|
modified_jsi_root_node = @jsi_root_node.jsi_modified_copy do |root|
|
454
445
|
@jsi_ptr.modified_document_copy(root, &block)
|
455
446
|
end
|
456
|
-
@jsi_ptr
|
447
|
+
modified_jsi_root_node.jsi_descendent_node(@jsi_ptr)
|
457
448
|
end
|
458
449
|
end
|
459
450
|
|
@@ -470,19 +461,21 @@ module JSI
|
|
470
461
|
jsi_schemas.instance_valid?(self)
|
471
462
|
end
|
472
463
|
|
473
|
-
#
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
#
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
#
|
484
|
-
def
|
485
|
-
|
464
|
+
# queries this JSI using the [JMESPath Ruby](https://rubygems.org/gems/jmespath) gem.
|
465
|
+
# see [https://jmespath.org/](https://jmespath.org/) to learn the JMESPath query language.
|
466
|
+
#
|
467
|
+
# the JMESPath gem is not a dependency of JSI, so must be installed / added to your Gemfile to use.
|
468
|
+
# e.g. `gem 'jmespath', '~> 1.5'`. note that versions below 1.5 are not compatible with JSI.
|
469
|
+
#
|
470
|
+
# @param expression [String] a [JMESPath](https://jmespath.org/) expression
|
471
|
+
# @param runtime_options passed to [JMESPath.search](https://rubydoc.info/gems/jmespath/JMESPath#search-class_method),
|
472
|
+
# though no runtime_options are publicly documented or normally used.
|
473
|
+
# @return [Array, Object, nil] query results.
|
474
|
+
# see [JMESPath.search](https://rubydoc.info/gems/jmespath/JMESPath#search-class_method)
|
475
|
+
def jmespath_search(expression, **runtime_options)
|
476
|
+
Util.require_jmespath
|
477
|
+
|
478
|
+
JMESPath.search(expression, self, **runtime_options)
|
486
479
|
end
|
487
480
|
|
488
481
|
def dup
|
@@ -495,6 +488,8 @@ module JSI
|
|
495
488
|
"\#<#{jsi_object_group_text.join(' ')} #{jsi_instance.inspect}>"
|
496
489
|
end
|
497
490
|
|
491
|
+
alias_method :to_s, :inspect
|
492
|
+
|
498
493
|
# pretty-prints a representation of this JSI to the given printer
|
499
494
|
# @return [void]
|
500
495
|
def pretty_print(q)
|
@@ -513,27 +508,14 @@ module JSI
|
|
513
508
|
# @private
|
514
509
|
# @return [Array<String>]
|
515
510
|
def jsi_object_group_text
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
if schema_module_names.empty?
|
522
|
-
class_name
|
523
|
-
else
|
524
|
-
"#{class_name} (#{schema_module_names.join(', ')})"
|
525
|
-
end
|
526
|
-
else
|
527
|
-
schema_names = jsi_schemas.map { |schema| schema.jsi_schema_module.name_from_ancestor || schema.schema_uri }.compact
|
528
|
-
if schema_names.empty?
|
529
|
-
"JSI"
|
530
|
-
else
|
531
|
-
"JSI (#{schema_names.join(', ')})"
|
532
|
-
end
|
533
|
-
end
|
511
|
+
schema_names = jsi_schemas.map { |schema| schema.jsi_schema_module.name_from_ancestor || schema.schema_uri }.compact
|
512
|
+
if schema_names.empty?
|
513
|
+
class_txt = "JSI"
|
514
|
+
else
|
515
|
+
class_txt = "JSI (#{schema_names.join(', ')})"
|
534
516
|
end
|
535
517
|
|
536
|
-
if (is_a?(
|
518
|
+
if (is_a?(ArrayNode) || is_a?(HashNode)) && ![Array, Hash].include?(jsi_node_content.class)
|
537
519
|
if jsi_node_content.respond_to?(:jsi_object_group_text)
|
538
520
|
content_txt = jsi_node_content.jsi_object_group_text
|
539
521
|
else
|
@@ -553,7 +535,7 @@ module JSI
|
|
553
535
|
# a jsonifiable representation of the instance
|
554
536
|
# @return [Object]
|
555
537
|
def as_json(*opt)
|
556
|
-
|
538
|
+
Util.as_json(jsi_instance, *opt)
|
557
539
|
end
|
558
540
|
|
559
541
|
# an opaque fingerprint of this JSI for {Util::FingerprintHash}.
|
@@ -574,22 +556,14 @@ module JSI
|
|
574
556
|
|
575
557
|
def jsi_subinstance_schemas_memos
|
576
558
|
jsi_memomap(:subinstance_schemas, key_by: -> (i) { i[:token] }) do |token: , instance: , subinstance: |
|
577
|
-
|
578
|
-
jsi_schemas.each do |schema|
|
579
|
-
schema.each_child_applicator_schema(token, instance) do |child_app_schema|
|
580
|
-
child_app_schema.each_inplace_applicator_schema(subinstance) do |child_inpl_app_schema|
|
581
|
-
schemas << child_inpl_app_schema
|
582
|
-
end
|
583
|
-
end
|
584
|
-
end
|
585
|
-
end
|
559
|
+
jsi_schemas.child_applicator_schemas(token, instance).inplace_applicator_schemas(subinstance)
|
586
560
|
end
|
587
561
|
end
|
588
562
|
|
589
563
|
def jsi_subinstance_memos
|
590
|
-
jsi_memomap(:subinstance, key_by: -> (i) { i[:token] }) do |token: , subinstance_schemas: |
|
591
|
-
JSI::SchemaClasses.class_for_schemas(subinstance_schemas)
|
592
|
-
|
564
|
+
jsi_memomap(:subinstance, key_by: -> (i) { i[:token] }) do |token: , subinstance_schemas: , includes: |
|
565
|
+
jsi_class = JSI::SchemaClasses.class_for_schemas(subinstance_schemas, includes: includes)
|
566
|
+
jsi_class.new(@jsi_document,
|
593
567
|
jsi_ptr: @jsi_ptr[token],
|
594
568
|
jsi_root_node: @jsi_root_node,
|
595
569
|
jsi_schema_base_uri: jsi_resource_ancestor_uri,
|
@@ -599,12 +573,12 @@ module JSI
|
|
599
573
|
end
|
600
574
|
|
601
575
|
def jsi_subinstance_as_jsi(value, subinstance_schemas, as_jsi)
|
602
|
-
|
603
|
-
as_jsi
|
576
|
+
if [true, false].include?(as_jsi)
|
577
|
+
value_as_jsi = as_jsi
|
604
578
|
elsif as_jsi == :auto
|
605
|
-
complex_value =
|
606
|
-
schema_value = subinstance_schemas.any?
|
607
|
-
complex_value || schema_value
|
579
|
+
complex_value = value.respond_to?(:to_hash) || value.respond_to?(:to_ary)
|
580
|
+
schema_value = subinstance_schemas.any?(&:describes_schema?)
|
581
|
+
value_as_jsi = complex_value || schema_value
|
608
582
|
else
|
609
583
|
raise(ArgumentError, "as_jsi must be one of: :auto, true, false")
|
610
584
|
end
|
data/lib/jsi/jsi_coder.rb
CHANGED
@@ -48,7 +48,7 @@ module JSI
|
|
48
48
|
unless data.respond_to?(:to_ary)
|
49
49
|
raise TypeError, "expected array-like column data; got: #{data.class}: #{data.inspect}"
|
50
50
|
end
|
51
|
-
data.map { |el| load_object(el) }
|
51
|
+
data.to_ary.map { |el| load_object(el) }
|
52
52
|
else
|
53
53
|
load_object(data)
|
54
54
|
end
|
@@ -66,7 +66,7 @@ module JSI
|
|
66
66
|
unless object.respond_to?(:to_ary)
|
67
67
|
raise(TypeError, "expected array-like attribute; got: #{object.class}: #{object.inspect}")
|
68
68
|
end
|
69
|
-
object.map do |el|
|
69
|
+
object.to_ary.map do |el|
|
70
70
|
dump_object(el)
|
71
71
|
end
|
72
72
|
else
|
@@ -86,7 +86,7 @@ module JSI
|
|
86
86
|
# @param object [JSI::Base, Object]
|
87
87
|
# @return [Object]
|
88
88
|
def dump_object(object)
|
89
|
-
JSI::
|
89
|
+
JSI::Util.as_json(object)
|
90
90
|
end
|
91
91
|
end
|
92
92
|
end
|
data/lib/jsi/metaschema.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JSI
|
4
|
-
# @private
|
5
4
|
# internal class to bootstrap a metaschema. represents a schema without the complexity of JSI::Base. the
|
6
5
|
# schema is represented but schemas describing the schema are not.
|
7
6
|
#
|
@@ -9,9 +8,11 @@ module JSI
|
|
9
8
|
# Schema#subschema and Schema#resource_root_subschema are the intended mechanisms to instantiate subschemas
|
10
9
|
# and resolve references. #[] and #jsi_root_node are not implemented.
|
11
10
|
#
|
12
|
-
#
|
11
|
+
# schema implementation modules are attached to generated subclasses of BootstrapSchema by
|
13
12
|
# {SchemaClasses.bootstrap_schema_class}. that subclass is instantiated with a document and
|
14
13
|
# pointer, representing a schema.
|
14
|
+
#
|
15
|
+
# @api private
|
15
16
|
class MetaschemaNode::BootstrapSchema
|
16
17
|
include Util::Memoize
|
17
18
|
include Util::FingerprintHash
|
@@ -22,7 +23,7 @@ module JSI
|
|
22
23
|
if self == MetaschemaNode::BootstrapSchema
|
23
24
|
name
|
24
25
|
else
|
25
|
-
"#{name || MetaschemaNode::BootstrapSchema.name} (#{
|
26
|
+
"#{name || MetaschemaNode::BootstrapSchema.name} (#{schema_implementation_modules.map(&:inspect).join(', ')})"
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
@@ -36,9 +37,7 @@ module JSI
|
|
36
37
|
jsi_ptr: Ptr[],
|
37
38
|
jsi_schema_base_uri: nil
|
38
39
|
)
|
39
|
-
unless respond_to?(:
|
40
|
-
raise(TypeError, "cannot instantiate #{self.class.inspect} which has no method #metaschema_instance_modules")
|
41
|
-
end
|
40
|
+
raise(Bug, "no #schema_implementation_modules") unless respond_to?(:schema_implementation_modules)
|
42
41
|
|
43
42
|
jsi_initialize_memos
|
44
43
|
|
@@ -62,6 +61,8 @@ module JSI
|
|
62
61
|
"\#<#{jsi_object_group_text.join(' ')} #{schema_content.inspect}>"
|
63
62
|
end
|
64
63
|
|
64
|
+
alias_method :to_s, :inspect
|
65
|
+
|
65
66
|
# pretty-prints a representation of self to the given printer
|
66
67
|
# @return [void]
|
67
68
|
def pretty_print(q)
|
@@ -82,7 +83,7 @@ module JSI
|
|
82
83
|
def jsi_object_group_text
|
83
84
|
[
|
84
85
|
self.class.name || MetaschemaNode::BootstrapSchema.name,
|
85
|
-
"(#{
|
86
|
+
"(#{schema_implementation_modules.map(&:inspect).join(', ')})",
|
86
87
|
jsi_ptr.uri,
|
87
88
|
]
|
88
89
|
end
|
@@ -93,7 +94,7 @@ module JSI
|
|
93
94
|
class: self.class,
|
94
95
|
jsi_ptr: @jsi_ptr,
|
95
96
|
jsi_document: @jsi_document,
|
96
|
-
|
97
|
+
schema_implementation_modules: schema_implementation_modules,
|
97
98
|
}
|
98
99
|
end
|
99
100
|
end
|