jsi 0.2.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/lib/jsi/base.rb
CHANGED
@@ -1,180 +1,324 @@
|
|
1
|
-
|
2
|
-
require 'jsi/typelike_modules'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module JSI
|
5
|
-
# the base class
|
4
|
+
# JSI::Base is the base class of every JSI instance of a JSON schema.
|
6
5
|
#
|
7
|
-
# a
|
8
|
-
#
|
6
|
+
# instances are described by a set of one or more JSON schemas. JSI dynamically creates a subclass of
|
7
|
+
# JSI::Base for each set of JSON schemas which describe an instance that is to be instantiated.
|
9
8
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
9
|
+
# a JSI instance of such a subclass represents a JSON schema instance described by that set of schemas.
|
10
|
+
#
|
11
|
+
# this subclass includes the JSI Schema Module of each schema it represents.
|
12
|
+
#
|
13
|
+
# the method {Base#jsi_schemas} is defined to indicate the schemas the class represents.
|
14
|
+
#
|
15
|
+
# the JSI::Base class itself is not intended to be instantiated.
|
13
16
|
class Base
|
14
|
-
include Memoize
|
15
|
-
include Enumerable
|
16
17
|
include PathedNode
|
18
|
+
include Schema::SchemaAncestorNode
|
19
|
+
include Util::Memoize
|
20
|
+
|
21
|
+
# not every JSI::Base is necessarily an Enumerable, but it's better to include Enumerable on
|
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
|
26
|
+
class CannotSubscriptError < StandardError
|
27
|
+
end
|
17
28
|
|
18
29
|
class << self
|
19
|
-
#
|
20
|
-
|
21
|
-
|
30
|
+
# @private @deprecated
|
31
|
+
def new_jsi(instance, **kw, &b)
|
32
|
+
new(instance, **kw, &b)
|
33
|
+
end
|
22
34
|
|
23
|
-
# @
|
24
|
-
#
|
25
|
-
|
26
|
-
|
35
|
+
# @private
|
36
|
+
# is the constant JSI::SchemaClasses::<self.schema_classes_const_name> defined?
|
37
|
+
# (if so, we will prefer to use something more human-readable than that ugly mess.)
|
38
|
+
def in_schema_classes
|
39
|
+
# #name sets @in_schema_classes
|
40
|
+
name
|
41
|
+
@in_schema_classes
|
27
42
|
end
|
28
43
|
|
29
|
-
#
|
44
|
+
# a string indicating a class name if one is defined, as well as the schema module name
|
45
|
+
# and/or schema URI of each schema the class represents.
|
46
|
+
# @return [String]
|
30
47
|
def inspect
|
31
|
-
|
32
|
-
if !respond_to?(:schema)
|
48
|
+
if !respond_to?(:jsi_class_schemas)
|
33
49
|
super
|
34
|
-
elsif in_schema_classes
|
35
|
-
%Q(#{SchemaClasses.inspect}[#{schema_id.inspect}])
|
36
|
-
elsif !name
|
37
|
-
%Q(#<Class for Schema: #{schema_id}>)
|
38
50
|
else
|
39
|
-
|
40
|
-
|
41
|
-
|
51
|
+
schema_names = jsi_class_schemas.map do |schema|
|
52
|
+
mod = schema.jsi_schema_module
|
53
|
+
if mod.name && schema.schema_uri
|
54
|
+
"#{mod.name} (#{schema.schema_uri})"
|
55
|
+
elsif mod.name
|
56
|
+
mod.name
|
57
|
+
elsif schema.schema_uri
|
58
|
+
schema.schema_uri.to_s
|
59
|
+
else
|
60
|
+
schema.jsi_ptr.uri.to_s
|
61
|
+
end
|
62
|
+
end
|
42
63
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
64
|
+
if name && !in_schema_classes
|
65
|
+
if jsi_class_schemas.empty?
|
66
|
+
"#{name} (0 schemas)"
|
67
|
+
else
|
68
|
+
"#{name} (#{schema_names.join(', ')})"
|
69
|
+
end
|
70
|
+
else
|
71
|
+
if schema_names.empty?
|
72
|
+
"(JSI Schema Class for 0 schemas)"
|
73
|
+
else
|
74
|
+
"(JSI Schema Class: #{schema_names.join(', ')})"
|
75
|
+
end
|
76
|
+
end
|
52
77
|
end
|
53
78
|
end
|
54
79
|
|
55
|
-
|
56
|
-
|
80
|
+
alias_method :to_s, :inspect
|
81
|
+
|
82
|
+
# @private
|
83
|
+
# see {.name}
|
57
84
|
def schema_classes_const_name
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
85
|
+
if respond_to?(:jsi_class_schemas)
|
86
|
+
schema_names = jsi_class_schemas.map do |schema|
|
87
|
+
if schema.jsi_schema_module.name
|
88
|
+
schema.jsi_schema_module.name
|
89
|
+
elsif schema.schema_uri
|
90
|
+
schema.schema_uri.to_s
|
91
|
+
else
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
if !schema_names.any?(&:nil?) && !schema_names.empty?
|
96
|
+
schema_names.sort.map { |n| 'X' + n.to_s.gsub(/[^\w]/, '_') }.join('')
|
97
|
+
end
|
98
|
+
end
|
62
99
|
end
|
63
100
|
|
64
|
-
#
|
101
|
+
# a constant name of this class. this is generated from the schema module name or URI of each schema
|
102
|
+
# this class represents. nil if any represented schema has no schema module name or schema URI.
|
103
|
+
#
|
104
|
+
# this generated name is not too pretty but can be more helpful than an anonymous class, especially
|
105
|
+
# in error messages.
|
106
|
+
#
|
107
|
+
# @return [String]
|
65
108
|
def name
|
66
|
-
unless
|
67
|
-
|
68
|
-
|
109
|
+
unless instance_variable_defined?(:@in_schema_classes)
|
110
|
+
const_name = schema_classes_const_name
|
111
|
+
if super || !const_name || SchemaClasses.const_defined?(const_name)
|
112
|
+
@in_schema_classes = false
|
113
|
+
else
|
114
|
+
SchemaClasses.const_set(const_name, self)
|
115
|
+
@in_schema_classes = true
|
116
|
+
end
|
69
117
|
end
|
70
118
|
super
|
71
119
|
end
|
72
120
|
end
|
73
121
|
|
74
122
|
# NOINSTANCE is a magic value passed to #initialize when instantiating a JSI
|
75
|
-
# from a document and
|
76
|
-
|
123
|
+
# from a document and pointer.
|
124
|
+
#
|
125
|
+
# @private
|
126
|
+
NOINSTANCE = Object.new
|
127
|
+
[:inspect, :to_s].each(&(-> (s, m) { NOINSTANCE.define_singleton_method(m) { s } }.curry.("#{JSI::Base}::NOINSTANCE")))
|
128
|
+
NOINSTANCE.freeze
|
77
129
|
|
78
130
|
# initializes this JSI from the given instance - instance is most commonly
|
79
131
|
# a parsed JSON document consisting of Hash, Array, or sometimes a basic
|
80
132
|
# type, but this is in no way enforced and a JSI may wrap any object.
|
81
133
|
#
|
82
|
-
# @param instance [Object] the JSON Schema instance
|
134
|
+
# @param instance [Object] the JSON Schema instance to be represented as a JSI
|
83
135
|
# @param jsi_document [Object] for internal use. the instance may be specified as a
|
84
136
|
# node in the `jsi_document` param, pointed to by `jsi_ptr`. the param `instance`
|
85
137
|
# MUST be `NOINSTANCE` to use the jsi_document + jsi_ptr form. `jsi_document` MUST
|
86
138
|
# NOT be passed if `instance` is anything other than `NOINSTANCE`.
|
87
|
-
# @param jsi_ptr [JSI::
|
139
|
+
# @param jsi_ptr [JSI::Ptr] for internal use. a pointer specifying
|
88
140
|
# the path of this instance in the `jsi_document` param. `jsi_ptr` must be passed
|
89
141
|
# iff `jsi_document` is passed, i.e. when `instance` is `NOINSTANCE`
|
90
|
-
# @param
|
91
|
-
#
|
92
|
-
|
93
|
-
|
94
|
-
|
142
|
+
# @param jsi_root_node [JSI::Base] for internal use, specifies the JSI at the root of the document
|
143
|
+
# @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(instance,
|
146
|
+
jsi_document: nil,
|
147
|
+
jsi_ptr: nil,
|
148
|
+
jsi_root_node: nil,
|
149
|
+
jsi_schema_base_uri: nil,
|
150
|
+
jsi_schema_resource_ancestors: []
|
151
|
+
)
|
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.")
|
95
154
|
end
|
96
155
|
|
97
|
-
if instance.is_a?(JSI::
|
98
|
-
raise(TypeError, "assigning
|
99
|
-
elsif instance.is_a?(JSI::
|
100
|
-
raise(TypeError, "assigning
|
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}")
|
101
160
|
end
|
102
161
|
|
162
|
+
jsi_initialize_memos
|
163
|
+
|
103
164
|
if instance == NOINSTANCE
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
else
|
110
|
-
raise(Bug, 'incorrect usage') if jsi_document || jsi_ptr || ancestor_jsi
|
111
|
-
if instance.is_a?(PathedNode)
|
112
|
-
@jsi_document = instance.document_root_node
|
113
|
-
# this can result in the unusual situation where ancestor_jsi is nil, though jsi_ptr is not root.
|
114
|
-
# #document_root_node will then return a JSI::JSON::Pointer instead of a root JSI.
|
115
|
-
@jsi_ptr = instance.node_ptr
|
165
|
+
self.jsi_document = jsi_document
|
166
|
+
self.jsi_ptr = jsi_ptr
|
167
|
+
if @jsi_ptr.root?
|
168
|
+
raise(Bug, "jsi_root_node cannot be specified for root JSI") if jsi_root_node
|
169
|
+
@jsi_root_node = self
|
116
170
|
else
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
if !ancestor_jsi.jsi_ptr.contains?(@jsi_ptr)
|
126
|
-
raise(Bug, "ancestor_jsi ptr #{ancestor_jsi.jsi_ptr.inspect} is not ancestor of #{@jsi_ptr.inspect}")
|
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
|
127
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[]
|
183
|
+
@jsi_root_node = self
|
128
184
|
end
|
129
|
-
|
185
|
+
|
186
|
+
self.jsi_schema_base_uri = jsi_schema_base_uri
|
187
|
+
self.jsi_schema_resource_ancestors = jsi_schema_resource_ancestors
|
130
188
|
|
131
189
|
if self.jsi_instance.respond_to?(:to_hash)
|
132
|
-
extend
|
133
|
-
|
134
|
-
|
190
|
+
extend PathedHashNode
|
191
|
+
end
|
192
|
+
if self.jsi_instance.respond_to?(:to_ary)
|
193
|
+
extend PathedArrayNode
|
135
194
|
end
|
136
195
|
end
|
137
196
|
|
138
|
-
#
|
197
|
+
# @!method jsi_schemas
|
198
|
+
# the set of schemas which describe this instance
|
199
|
+
# @return [JSI::SchemaSet]
|
200
|
+
# note: defined on subclasses by JSI::SchemaClasses.class_for_schemas
|
201
|
+
|
202
|
+
|
203
|
+
# document containing the instance of this JSI at our {#jsi_ptr}
|
139
204
|
attr_reader :jsi_document
|
140
205
|
|
141
|
-
# JSI::
|
206
|
+
# {JSI::Ptr} pointing to this JSI's instance within our {#jsi_document}
|
207
|
+
# @return [JSI::Ptr]
|
142
208
|
attr_reader :jsi_ptr
|
143
209
|
|
144
|
-
#
|
145
|
-
|
210
|
+
# the JSI at the root of this JSI's document
|
211
|
+
# @return [JSI::Base]
|
212
|
+
attr_reader :jsi_root_node
|
146
213
|
|
147
|
-
|
148
|
-
alias_method :
|
214
|
+
# the JSON schema instance this JSI represents - the underlying JSON data used to instantiate this JSI
|
215
|
+
alias_method :jsi_instance, :jsi_node_content
|
149
216
|
|
150
|
-
#
|
151
|
-
|
152
|
-
|
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
|
153
222
|
|
154
|
-
#
|
155
|
-
#
|
156
|
-
|
157
|
-
|
223
|
+
# yields a JSI of each node at or below this one in this JSI's document.
|
224
|
+
#
|
225
|
+
# returns an Enumerator if no block is given.
|
226
|
+
#
|
227
|
+
# @yield [JSI::Base] each node in the document, starting with self
|
228
|
+
# @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
|
229
|
+
def jsi_each_child_node(&block)
|
230
|
+
return to_enum(__method__) unless block
|
231
|
+
|
232
|
+
yield self
|
233
|
+
if respond_to?(:to_hash)
|
234
|
+
each_key do |k|
|
235
|
+
self[k, as_jsi: true].jsi_each_child_node(&block)
|
236
|
+
end
|
237
|
+
elsif respond_to?(:to_ary)
|
238
|
+
each_index do |i|
|
239
|
+
self[i, as_jsi: true].jsi_each_child_node(&block)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
nil
|
158
243
|
end
|
159
244
|
|
160
|
-
#
|
161
|
-
#
|
245
|
+
# recursively selects child nodes of this JSI, returning a modified copy of self containing only
|
246
|
+
# child nodes for which the given block had a true-ish result.
|
162
247
|
#
|
163
|
-
#
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
248
|
+
# 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 children are never recursed.
|
250
|
+
#
|
251
|
+
# @yield [JSI::Base] each child node below self
|
252
|
+
# @return [JSI::Base] modified copy of self containing only the selected nodes
|
253
|
+
def jsi_select_children_node_first(&block)
|
254
|
+
jsi_modified_copy do |instance|
|
255
|
+
if respond_to?(:to_hash)
|
256
|
+
res = instance.class.new
|
257
|
+
each_key do |k|
|
258
|
+
v = self[k, as_jsi: true]
|
259
|
+
if yield(v)
|
260
|
+
res[k] = v.jsi_select_children_node_first(&block).jsi_node_content
|
261
|
+
end
|
262
|
+
end
|
263
|
+
res
|
264
|
+
elsif respond_to?(:to_ary)
|
265
|
+
res = instance.class.new
|
266
|
+
each_index do |i|
|
267
|
+
e = self[i, as_jsi: true]
|
268
|
+
if yield(e)
|
269
|
+
res << e.jsi_select_children_node_first(&block).jsi_node_content
|
270
|
+
end
|
271
|
+
end
|
272
|
+
res
|
273
|
+
else
|
274
|
+
instance
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# recursively selects child nodes of this JSI, returning a modified copy of self containing only
|
280
|
+
# child nodes for which the given block had a true-ish result.
|
281
|
+
#
|
282
|
+
# this method recursively descends child nodes before yielding each node, so leaf nodes are yielded
|
283
|
+
# before their parents.
|
284
|
+
#
|
285
|
+
# @yield [JSI::Base] each child node below self
|
286
|
+
# @return [JSI::Base] modified copy of self containing only the selected nodes
|
287
|
+
def jsi_select_children_leaf_first(&block)
|
288
|
+
jsi_modified_copy do |instance|
|
289
|
+
if respond_to?(:to_hash)
|
290
|
+
res = instance.class.new
|
291
|
+
each_key do |k|
|
292
|
+
v = self[k, as_jsi: true].jsi_select_children_leaf_first(&block)
|
293
|
+
if yield(v)
|
294
|
+
res[k] = v.jsi_node_content
|
295
|
+
end
|
296
|
+
end
|
297
|
+
res
|
298
|
+
elsif respond_to?(:to_ary)
|
299
|
+
res = instance.class.new
|
300
|
+
each_index do |i|
|
301
|
+
e = self[i, as_jsi: true].jsi_select_children_leaf_first(&block)
|
302
|
+
if yield(e)
|
303
|
+
res << e.jsi_node_content
|
304
|
+
end
|
305
|
+
end
|
306
|
+
res
|
173
307
|
else
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
308
|
+
instance
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# an array of JSI instances above this one in the document.
|
314
|
+
#
|
315
|
+
# @return [Array<JSI::Base>]
|
316
|
+
def jsi_parent_nodes
|
317
|
+
parent = jsi_root_node
|
318
|
+
|
319
|
+
jsi_ptr.tokens.map do |token|
|
320
|
+
parent.tap do
|
321
|
+
parent = parent[token, as_jsi: true]
|
178
322
|
end
|
179
323
|
end.reverse
|
180
324
|
end
|
@@ -182,291 +326,294 @@ module JSI
|
|
182
326
|
# the immediate parent of this JSI. nil if there is no parent.
|
183
327
|
#
|
184
328
|
# @return [JSI::Base, nil]
|
185
|
-
def
|
186
|
-
|
187
|
-
end
|
188
|
-
|
189
|
-
# @return [JSI::PathedNode] a pathed node at the root of the document. this is generally a JSI::Base
|
190
|
-
# but may be a JSI::JSON::Node in unusual circumstances.
|
191
|
-
def document_root_node
|
192
|
-
if @jsi_ptr.root?
|
193
|
-
self
|
194
|
-
elsif @ancestor_jsi
|
195
|
-
@ancestor_jsi.document_root_node
|
196
|
-
elsif instance.is_a?(PathedNode)
|
197
|
-
instance.document_root_node
|
198
|
-
else
|
199
|
-
JSI::JSON::Node.new_doc(@jsi_document)
|
200
|
-
end
|
329
|
+
def jsi_parent_node
|
330
|
+
jsi_parent_nodes.first
|
201
331
|
end
|
202
332
|
|
203
|
-
#
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
333
|
+
# subscripts to return a child value identified by the given token.
|
334
|
+
#
|
335
|
+
# @param token [String, Integer, Object] an array index or hash key (JSON object property name)
|
336
|
+
# of the instance identifying the child value
|
337
|
+
# @param as_jsi [:auto, true, false] whether to return the result value as a JSI. one of:
|
338
|
+
#
|
339
|
+
# - :auto (default): by default a JSI will be returned when either:
|
340
|
+
#
|
341
|
+
# - the result is a complex value (responds to #to_ary or #to_hash) and is described by some schemas
|
342
|
+
# - the result is a schema (including true/false schemas)
|
343
|
+
#
|
344
|
+
# a plain value is returned when no schemas are known to describe the instance, or when the value is a
|
345
|
+
# simple type (anything unresponsive to #to_ary / #to_hash).
|
346
|
+
#
|
347
|
+
# - true: the result value will always be returned as a JSI. the #jsi_schemas of the result may be empty
|
348
|
+
# if no schemas describe the instance.
|
349
|
+
# - false: the result value will always be the plain instance.
|
350
|
+
#
|
351
|
+
# note that nil is returned (regardless of as_jsi) when there is no value to return because the token
|
352
|
+
# is not a hash key or array index of the instance and no default value applies.
|
353
|
+
# (one exception is when this JSI's instance is a Hash with a default or default_proc, which has
|
354
|
+
# unspecified behavior.)
|
355
|
+
# @param use_default [true, false] whether to return a schema default value when the token is not in
|
356
|
+
# range. if the token is not an array index or hash key of the instance, and one schema for the child
|
357
|
+
# instance specifies a default value, that default is returned.
|
358
|
+
#
|
359
|
+
# if the result with the default value is a JSI (per the `as_jsi` param), that JSI is not a child of
|
360
|
+
# this JSI - this JSI is not modified to fill in the default value. the result is a JSI within a new
|
361
|
+
# document containing the filled-in default.
|
362
|
+
#
|
363
|
+
# if the child instance's schemas do not indicate a single default value (that is, if zero or multiple
|
364
|
+
# defaults are specified across those schemas), nil is returned.
|
365
|
+
# (one exception is when this JSI's instance is a Hash with a default or default_proc, which has
|
366
|
+
# unspecified behavior.)
|
367
|
+
# @return [JSI::Base, Object] the child value identified by the subscript token
|
368
|
+
def [](token, as_jsi: :auto, use_default: true)
|
369
|
+
if respond_to?(:to_hash)
|
370
|
+
token_in_range = jsi_node_content_hash_pubsend(:key?, token)
|
371
|
+
value = jsi_node_content_hash_pubsend(:[], token)
|
372
|
+
elsif respond_to?(:to_ary)
|
373
|
+
token_in_range = jsi_node_content_ary_pubsend(:each_index).include?(token)
|
374
|
+
value = jsi_node_content_ary_pubsend(:[], token)
|
214
375
|
else
|
215
|
-
|
376
|
+
raise(CannotSubscriptError, "cannot subscript (using token: #{token.inspect}) from instance: #{jsi_instance.pretty_inspect.chomp}")
|
216
377
|
end
|
217
|
-
end
|
218
378
|
|
219
|
-
|
220
|
-
|
221
|
-
# @deprecated
|
222
|
-
alias_method :parent, :parent_jsi
|
379
|
+
begin
|
380
|
+
subinstance_schemas = jsi_subinstance_schemas_memos[token: token, instance: jsi_node_content, subinstance: value]
|
223
381
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
# JSI is not a $ref object, the block is not called. if we are a $ref which cannot be followed
|
229
|
-
# (e.g. a $ref to an external document, which is not yet supported), the block is not called.
|
230
|
-
# @return [JSI::Base, self]
|
231
|
-
def deref(&block)
|
232
|
-
node_ptr_deref do |deref_ptr|
|
233
|
-
jsi_from_root = deref_ptr.evaluate(document_root_node)
|
234
|
-
if jsi_from_root.is_a?(JSI::Base)
|
235
|
-
return jsi_from_root.tap(&(block || Util::NOOP))
|
382
|
+
if token_in_range
|
383
|
+
jsi_subinstance_as_jsi(value, subinstance_schemas, as_jsi) do
|
384
|
+
jsi_subinstance_memos[token: token, subinstance_schemas: subinstance_schemas]
|
385
|
+
end
|
236
386
|
else
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
387
|
+
if use_default
|
388
|
+
defaults = Set.new
|
389
|
+
subinstance_schemas.each do |subinstance_schema|
|
390
|
+
if subinstance_schema.respond_to?(:to_hash) && subinstance_schema.key?('default')
|
391
|
+
defaults << subinstance_schema['default']
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
if use_default && defaults.size == 1
|
397
|
+
# use the default value
|
398
|
+
# we are using #dup so that we get a modified copy of self, in which we set dup[token]=default.
|
399
|
+
dup.tap { |o| o[token] = defaults.first }[token, as_jsi: as_jsi]
|
241
400
|
else
|
242
|
-
|
401
|
+
# I kind of want to just return nil here. the preferred mechanism for
|
402
|
+
# a JSI's default value should be its schema. but returning nil ignores
|
403
|
+
# any value returned by Hash#default/#default_proc. there's no compelling
|
404
|
+
# reason not to support both, so I'll return that.
|
405
|
+
value
|
243
406
|
end
|
244
|
-
return derefed.tap(&(block || Util::NOOP))
|
245
407
|
end
|
246
408
|
end
|
247
|
-
return self
|
248
409
|
end
|
249
410
|
|
250
|
-
#
|
251
|
-
# a
|
252
|
-
#
|
253
|
-
# the
|
254
|
-
# @
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
modified_ancestor = @ancestor_jsi.modified_copy do |anc|
|
262
|
-
mod_anc = @jsi_ptr.ptr_relative_to(@ancestor_jsi.jsi_ptr).modified_document_copy(anc, &block)
|
263
|
-
mod_anc
|
264
|
-
end
|
265
|
-
self.class.new(Base::NOINSTANCE, jsi_document: modified_ancestor.jsi_document, jsi_ptr: @jsi_ptr, ancestor_jsi: modified_ancestor)
|
411
|
+
# assigns the subscript of the instance identified by the given token to the given value.
|
412
|
+
# if the value is a JSI, its instance is assigned instead of the JSI value itself.
|
413
|
+
#
|
414
|
+
# @param token [String, Integer, Object] token identifying the subscript to assign
|
415
|
+
# @param value [JSI::Base, Object] the value to be assigned
|
416
|
+
def []=(token, value)
|
417
|
+
unless respond_to?(:to_hash) || respond_to?(:to_ary)
|
418
|
+
raise(NoMethodError, "cannot assign subscript (using token: #{token.inspect}) to instance: #{jsi_instance.pretty_inspect.chomp}")
|
419
|
+
end
|
420
|
+
if value.is_a?(Base)
|
421
|
+
self[token] = value.jsi_instance
|
266
422
|
else
|
423
|
+
jsi_instance[token] = value
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
# the set of JSI schema modules corresponding to the schemas that describe this JSI
|
428
|
+
# @return [Set<Module>]
|
429
|
+
def jsi_schema_modules
|
430
|
+
jsi_schemas.map(&:jsi_schema_module).to_set.freeze
|
431
|
+
end
|
432
|
+
|
433
|
+
# yields the content of this JSI's instance. the block must result in
|
434
|
+
# a modified copy of the yielded instance (not destructively modifying it)
|
435
|
+
# which will be used to instantiate a new JSI with the modified content.
|
436
|
+
#
|
437
|
+
# the result may have different schemas which describe it than this JSI's schemas,
|
438
|
+
# if conditional applicator schemas apply differently to the modified instance.
|
439
|
+
#
|
440
|
+
# @yield [Object] this JSI's instance. the block should result
|
441
|
+
# in a nondestructively modified copy of this.
|
442
|
+
# @return [JSI::Base subclass] the modified copy of self
|
443
|
+
def jsi_modified_copy(&block)
|
444
|
+
if @jsi_ptr.root?
|
267
445
|
modified_document = @jsi_ptr.modified_document_copy(@jsi_document, &block)
|
268
|
-
self.class.new(Base::NOINSTANCE,
|
446
|
+
self.class.new(Base::NOINSTANCE,
|
447
|
+
jsi_document: modified_document,
|
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
|
451
|
+
)
|
452
|
+
else
|
453
|
+
modified_jsi_root_node = @jsi_root_node.jsi_modified_copy do |root|
|
454
|
+
@jsi_ptr.modified_document_copy(root, &block)
|
455
|
+
end
|
456
|
+
@jsi_ptr.evaluate(modified_jsi_root_node, as_jsi: true)
|
269
457
|
end
|
270
458
|
end
|
271
459
|
|
272
|
-
#
|
273
|
-
|
274
|
-
|
460
|
+
# validates this JSI's instance against its schemas
|
461
|
+
#
|
462
|
+
# @return [JSI::Validation::FullResult]
|
463
|
+
def jsi_validate
|
464
|
+
jsi_schemas.instance_validate(self)
|
275
465
|
end
|
276
466
|
|
277
|
-
#
|
278
|
-
|
279
|
-
|
467
|
+
# whether this JSI's instance is valid against all of its schemas
|
468
|
+
# @return [Boolean]
|
469
|
+
def jsi_valid?
|
470
|
+
jsi_schemas.instance_valid?(self)
|
280
471
|
end
|
281
472
|
|
282
|
-
# @
|
473
|
+
# @private
|
474
|
+
def fully_validate(errors_as_objects: false)
|
475
|
+
raise(NotImplementedError, "Base#fully_validate removed: see new validation interface Base#jsi_validate")
|
476
|
+
end
|
477
|
+
|
478
|
+
# @private
|
283
479
|
def validate
|
284
|
-
|
480
|
+
raise(NotImplementedError, "Base#validate renamed: see Base#jsi_valid?")
|
285
481
|
end
|
286
482
|
|
287
|
-
# @
|
288
|
-
# indicate a valid instance.
|
289
|
-
# @raise [::JSON::Schema::ValidationError] raises if the instance has
|
290
|
-
# validation errors
|
483
|
+
# @private
|
291
484
|
def validate!
|
292
|
-
|
485
|
+
raise(NotImplementedError, "Base#validate! removed")
|
293
486
|
end
|
294
487
|
|
295
488
|
def dup
|
296
|
-
|
489
|
+
jsi_modified_copy(&:dup)
|
297
490
|
end
|
298
491
|
|
299
|
-
#
|
300
|
-
#
|
492
|
+
# a string representing this JSI, indicating any named schemas and inspecting its instance
|
493
|
+
# @return [String]
|
301
494
|
def inspect
|
302
|
-
"\#<#{
|
495
|
+
"\#<#{jsi_object_group_text.join(' ')} #{jsi_instance.inspect}>"
|
303
496
|
end
|
304
497
|
|
305
|
-
# pretty-prints a representation this JSI to the given printer
|
498
|
+
# pretty-prints a representation of this JSI to the given printer
|
306
499
|
# @return [void]
|
307
500
|
def pretty_print(q)
|
308
|
-
q.
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
}
|
501
|
+
q.text '#<'
|
502
|
+
q.text jsi_object_group_text.join(' ')
|
503
|
+
q.group_sub {
|
504
|
+
q.nest(2) {
|
505
|
+
q.breakable ' '
|
506
|
+
q.pp jsi_instance
|
315
507
|
}
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
end
|
320
|
-
|
321
|
-
# @return [String] the instance's object_group_text
|
322
|
-
def object_group_text
|
323
|
-
instance.respond_to?(:object_group_text) ? instance.object_group_text : instance.class.inspect
|
324
|
-
end
|
325
|
-
|
326
|
-
# @return [Object] a jsonifiable representation of the instance
|
327
|
-
def as_json(*opt)
|
328
|
-
Typelike.as_json(instance, *opt)
|
329
|
-
end
|
330
|
-
|
331
|
-
# @return [Object] an opaque fingerprint of this JSI for FingerprintHash. JSIs are equal
|
332
|
-
# if their instances are equal, and if the JSIs are of the same JSI class or subclass.
|
333
|
-
def fingerprint
|
334
|
-
{class: jsi_class, jsi_document: jsi_document, jsi_ptr: jsi_ptr}
|
508
|
+
}
|
509
|
+
q.breakable ''
|
510
|
+
q.text '>'
|
335
511
|
end
|
336
|
-
include FingerprintHash
|
337
512
|
|
338
|
-
private
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
# module extending a {JSI::Base} object when its instance is Hash-like (responds to #to_hash)
|
360
|
-
module BaseHash
|
361
|
-
include PathedHashNode
|
362
|
-
|
363
|
-
alias_method :jsi_instance_hash_pubsend, :node_content_hash_pubsend
|
364
|
-
|
365
|
-
# @param property_name [String, Object] the property name to subscript
|
366
|
-
# @return [JSI::Base, Object] the instance's subscript value at the given
|
367
|
-
# key property_name_. if there is a subschema defined for that property
|
368
|
-
# on this JSI's schema, returns the instance's subscript as a JSI
|
369
|
-
# instiation of that subschema.
|
370
|
-
def [](property_name)
|
371
|
-
instance_property_key_ = jsi_instance_hash_pubsend(:key?, property_name)
|
372
|
-
if !instance_property_key_
|
373
|
-
deref do |deref_jsi|
|
374
|
-
return deref_jsi[property_name]
|
513
|
+
# @private
|
514
|
+
# @return [Array<String>]
|
515
|
+
def jsi_object_group_text
|
516
|
+
class_name = self.class.name unless self.class.in_schema_classes
|
517
|
+
class_txt = begin
|
518
|
+
if class_name
|
519
|
+
# ignore ID
|
520
|
+
schema_module_names = jsi_schemas.map { |schema| schema.jsi_schema_module.name }.compact
|
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
|
375
533
|
end
|
376
534
|
end
|
377
|
-
instance_property_value_ = jsi_instance_sub(property_name)
|
378
|
-
memoize(:[], property_name, instance_property_value_, instance_property_key_) do |property_name_, instance_property_value, instance_property_key|
|
379
|
-
begin
|
380
|
-
property_schema = schema.subschema_for_property(property_name_)
|
381
|
-
property_schema = property_schema && property_schema.match_to_instance(instance_property_value)
|
382
535
|
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
# this avoids duplication of code with #modified_copy and below in #[] to handle pathing and such.
|
389
|
-
dup.tap { |o| o[property_name_] = default }[property_name_]
|
390
|
-
else
|
391
|
-
default
|
392
|
-
end
|
393
|
-
elsif property_schema && (instance_property_value.respond_to?(:to_hash) || instance_property_value.respond_to?(:to_ary))
|
394
|
-
class_for_schema(property_schema).new(Base::NOINSTANCE, jsi_document: @jsi_document, jsi_ptr: @jsi_ptr[property_name_], ancestor_jsi: @ancestor_jsi || self)
|
395
|
-
else
|
396
|
-
instance_property_value
|
397
|
-
end
|
536
|
+
if (is_a?(PathedArrayNode) || is_a?(PathedHashNode)) && ![Array, Hash].include?(jsi_node_content.class)
|
537
|
+
if jsi_node_content.respond_to?(:jsi_object_group_text)
|
538
|
+
content_txt = jsi_node_content.jsi_object_group_text
|
539
|
+
else
|
540
|
+
content_txt = [jsi_node_content.class.to_s]
|
398
541
|
end
|
542
|
+
else
|
543
|
+
content_txt = []
|
399
544
|
end
|
400
|
-
end
|
401
545
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
# property_name
|
408
|
-
def []=(property_name, value)
|
409
|
-
subscript_assign(property_name, value)
|
546
|
+
[
|
547
|
+
class_txt,
|
548
|
+
is_a?(Metaschema) ? "Metaschema" : is_a?(Schema) ? "Schema" : nil,
|
549
|
+
*content_txt,
|
550
|
+
].compact
|
410
551
|
end
|
411
552
|
|
412
|
-
|
413
|
-
|
414
|
-
# @param token [String, Object]
|
553
|
+
# a jsonifiable representation of the instance
|
415
554
|
# @return [Object]
|
416
|
-
def
|
417
|
-
|
555
|
+
def as_json(*opt)
|
556
|
+
Typelike.as_json(jsi_instance, *opt)
|
418
557
|
end
|
419
|
-
end
|
420
|
-
|
421
|
-
# module extending a {JSI::Base} object when its instance is Array-like (responds to #to_ary)
|
422
|
-
module BaseArray
|
423
|
-
include PathedArrayNode
|
424
558
|
|
425
|
-
|
559
|
+
# an opaque fingerprint of this JSI for {Util::FingerprintHash}.
|
560
|
+
def jsi_fingerprint
|
561
|
+
{
|
562
|
+
class: jsi_class,
|
563
|
+
jsi_document: jsi_document,
|
564
|
+
jsi_ptr: jsi_ptr,
|
565
|
+
# for instances in documents with schemas:
|
566
|
+
jsi_resource_ancestor_uri: jsi_resource_ancestor_uri,
|
567
|
+
# only defined for JSI::Schema instances:
|
568
|
+
jsi_schema_instance_modules: is_a?(Schema) ? jsi_schema_instance_modules : nil,
|
569
|
+
}
|
570
|
+
end
|
571
|
+
include Util::FingerprintHash
|
426
572
|
|
427
|
-
|
428
|
-
# @return [JSI::Base, Object] the instance's subscript value at the given index
|
429
|
-
# i. if there is a subschema defined for that index on this JSI's schema,
|
430
|
-
# returns the instance's subscript as a JSI instiation of that subschema.
|
431
|
-
def [](i)
|
432
|
-
memoize(:[], i, jsi_instance_sub(i), jsi_instance_ary_pubsend(:each_index).to_a.include?(i)) do |i_, instance_idx_value, i_in_range|
|
433
|
-
begin
|
434
|
-
index_schema = schema.subschema_for_index(i_)
|
435
|
-
index_schema = index_schema && index_schema.match_to_instance(instance_idx_value)
|
573
|
+
private
|
436
574
|
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
default
|
575
|
+
def jsi_subinstance_schemas_memos
|
576
|
+
jsi_memomap(:subinstance_schemas, key_by: -> (i) { i[:token] }) do |token: , instance: , subinstance: |
|
577
|
+
SchemaSet.build do |schemas|
|
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
|
446
583
|
end
|
447
|
-
elsif index_schema && (instance_idx_value.respond_to?(:to_hash) || instance_idx_value.respond_to?(:to_ary))
|
448
|
-
class_for_schema(index_schema).new(Base::NOINSTANCE, jsi_document: @jsi_document, jsi_ptr: @jsi_ptr[i_], ancestor_jsi: @ancestor_jsi || self)
|
449
|
-
else
|
450
|
-
instance_idx_value
|
451
584
|
end
|
452
585
|
end
|
453
586
|
end
|
454
587
|
end
|
455
588
|
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
589
|
+
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).new(Base::NOINSTANCE,
|
592
|
+
jsi_document: @jsi_document,
|
593
|
+
jsi_ptr: @jsi_ptr[token],
|
594
|
+
jsi_root_node: @jsi_root_node,
|
595
|
+
jsi_schema_base_uri: jsi_resource_ancestor_uri,
|
596
|
+
jsi_schema_resource_ancestors: is_a?(Schema) ? jsi_subschema_resource_ancestors : jsi_schema_resource_ancestors,
|
597
|
+
)
|
598
|
+
end
|
462
599
|
end
|
463
600
|
|
464
|
-
|
601
|
+
def jsi_subinstance_as_jsi(value, subinstance_schemas, as_jsi)
|
602
|
+
value_as_jsi = if [true, false].include?(as_jsi)
|
603
|
+
as_jsi
|
604
|
+
elsif as_jsi == :auto
|
605
|
+
complex_value = subinstance_schemas.any? && (value.respond_to?(:to_hash) || value.respond_to?(:to_ary))
|
606
|
+
schema_value = subinstance_schemas.any? { |subinstance_schema| subinstance_schema.describes_schema? }
|
607
|
+
complex_value || schema_value
|
608
|
+
else
|
609
|
+
raise(ArgumentError, "as_jsi must be one of: :auto, true, false")
|
610
|
+
end
|
465
611
|
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
612
|
+
if value_as_jsi
|
613
|
+
yield
|
614
|
+
else
|
615
|
+
value
|
616
|
+
end
|
470
617
|
end
|
471
618
|
end
|
472
619
|
end
|