jsi 0.6.0 → 0.7.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/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
|