jsi 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +6 -1
- data/CHANGELOG.md +33 -0
- data/LICENSE.md +1 -1
- data/README.md +29 -23
- data/jsi.gemspec +29 -0
- data/lib/jsi/base/mutability.rb +44 -0
- data/lib/jsi/base/node.rb +348 -0
- data/lib/jsi/base.rb +497 -339
- data/lib/jsi/jsi_coder.rb +19 -17
- data/lib/jsi/metaschema_node/bootstrap_schema.rb +61 -26
- data/lib/jsi/metaschema_node.rb +161 -133
- data/lib/jsi/ptr.rb +80 -47
- data/lib/jsi/schema/application/child_application/contains.rb +11 -2
- data/lib/jsi/schema/application/child_application/draft04.rb +0 -1
- data/lib/jsi/schema/application/child_application/draft06.rb +0 -1
- data/lib/jsi/schema/application/child_application/draft07.rb +0 -1
- 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 +0 -27
- data/lib/jsi/schema/application/inplace_application/dependencies.rb +1 -1
- data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -1
- data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -1
- data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -1
- data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +3 -3
- data/lib/jsi/schema/application/inplace_application/ref.rb +2 -2
- data/lib/jsi/schema/application/inplace_application/someof.rb +26 -11
- data/lib/jsi/schema/application/inplace_application.rb +0 -32
- data/lib/jsi/schema/draft04.rb +0 -1
- data/lib/jsi/schema/draft06.rb +0 -1
- data/lib/jsi/schema/draft07.rb +0 -1
- data/lib/jsi/schema/ref.rb +46 -19
- data/lib/jsi/schema/schema_ancestor_node.rb +69 -66
- 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 +2 -2
- data/lib/jsi/schema/validation/dependencies.rb +1 -1
- data/lib/jsi/schema/validation/draft04/minmax.rb +8 -6
- data/lib/jsi/schema/validation/draft04.rb +0 -2
- data/lib/jsi/schema/validation/draft06.rb +0 -2
- data/lib/jsi/schema/validation/draft07.rb +0 -2
- 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 +7 -7
- 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 +2 -2
- data/lib/jsi/schema/validation/properties.rb +7 -7
- data/lib/jsi/schema/validation/property_names.rb +1 -1
- data/lib/jsi/schema/validation/ref.rb +2 -2
- 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 -3
- data/lib/jsi/schema.rb +443 -226
- data/lib/jsi/schema_classes.rb +241 -147
- data/lib/jsi/schema_registry.rb +78 -19
- data/lib/jsi/schema_set.rb +114 -28
- data/lib/jsi/simple_wrap.rb +18 -4
- data/lib/jsi/util/private/attr_struct.rb +141 -0
- data/lib/jsi/util/private/memo_map.rb +75 -0
- data/lib/jsi/util/private.rb +185 -0
- data/lib/jsi/{typelike_modules.rb → util/typelike.rb} +79 -105
- data/lib/jsi/util.rb +157 -153
- data/lib/jsi/validation/error.rb +4 -0
- data/lib/jsi/validation/result.rb +18 -32
- data/lib/jsi/version.rb +1 -1
- data/lib/jsi.rb +65 -39
- data/lib/schemas/json-schema.org/draft-04/schema.rb +160 -3
- data/lib/schemas/json-schema.org/draft-06/schema.rb +162 -3
- data/lib/schemas/json-schema.org/draft-07/schema.rb +189 -3
- metadata +27 -11
- data/lib/jsi/metaschema.rb +0 -7
- data/lib/jsi/pathed_node.rb +0 -116
- data/lib/jsi/schema/validation/core.rb +0 -39
- data/lib/jsi/util/attr_struct.rb +0 -106
data/lib/jsi/ptr.rb
CHANGED
@@ -16,12 +16,17 @@ module JSI
|
|
16
16
|
class ResolutionError < Error
|
17
17
|
end
|
18
18
|
|
19
|
+
POS_INT_RE = /\A[1-9]\d*\z/
|
20
|
+
private_constant :POS_INT_RE
|
21
|
+
|
19
22
|
# instantiates a pointer or returns the given pointer
|
20
23
|
# @param ary_ptr [#to_ary, JSI::Ptr] an array of tokens, or a pointer
|
21
24
|
# @return [JSI::Ptr]
|
22
25
|
def self.ary_ptr(ary_ptr)
|
23
26
|
if ary_ptr.is_a?(Ptr)
|
24
27
|
ary_ptr
|
28
|
+
elsif ary_ptr == Util::EMPTY_ARY
|
29
|
+
EMPTY
|
25
30
|
else
|
26
31
|
new(ary_ptr)
|
27
32
|
end
|
@@ -31,7 +36,7 @@ module JSI
|
|
31
36
|
#
|
32
37
|
# JSI::Ptr[]
|
33
38
|
#
|
34
|
-
#
|
39
|
+
# instantiates a root pointer.
|
35
40
|
#
|
36
41
|
# JSI::Ptr['a', 'b']
|
37
42
|
# JSI::Ptr['a']['b']
|
@@ -42,7 +47,7 @@ module JSI
|
|
42
47
|
# @param tokens any number of tokens
|
43
48
|
# @return [JSI::Ptr]
|
44
49
|
def self.[](*tokens)
|
45
|
-
new(tokens)
|
50
|
+
tokens.empty? ? EMPTY : new(tokens.freeze)
|
46
51
|
end
|
47
52
|
|
48
53
|
# parse a URI-escaped fragment and instantiate as a JSI::Ptr
|
@@ -55,6 +60,12 @@ module JSI
|
|
55
60
|
# JSI::Ptr.from_fragment('/foo%20bar')
|
56
61
|
# => JSI::Ptr["foo bar"]
|
57
62
|
#
|
63
|
+
# Note: A fragment does not include a leading '#'. The string "#/foo" is a URI containing the
|
64
|
+
# fragment "/foo", which should be parsed by `Addressable::URI` before passing to this method, e.g.:
|
65
|
+
#
|
66
|
+
# JSI::Ptr.from_fragment(Addressable::URI.parse("#/foo").fragment)
|
67
|
+
# => JSI::Ptr["foo"]
|
68
|
+
#
|
58
69
|
# @param fragment [String] a fragment containing a pointer
|
59
70
|
# @return [JSI::Ptr]
|
60
71
|
# @raise [JSI::Ptr::PointerSyntaxError] when the fragment does not contain a pointer with
|
@@ -75,13 +86,17 @@ module JSI
|
|
75
86
|
# @return [JSI::Ptr]
|
76
87
|
# @raise [JSI::Ptr::PointerSyntaxError] when the pointer_string does not have valid pointer syntax
|
77
88
|
def self.from_pointer(pointer_string)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
89
|
+
pointer_string = pointer_string.to_str
|
90
|
+
if pointer_string[0] == ?/
|
91
|
+
tokens = pointer_string.split('/', -1).map! do |piece|
|
92
|
+
piece.gsub!('~1', '/')
|
93
|
+
piece.gsub!('~0', '~')
|
94
|
+
piece.freeze
|
95
|
+
end
|
96
|
+
tokens.shift
|
97
|
+
new(tokens.freeze)
|
98
|
+
elsif pointer_string.empty?
|
99
|
+
EMPTY
|
85
100
|
else
|
86
101
|
raise(PointerSyntaxError, "Invalid pointer syntax in #{pointer_string.inspect}: pointer must begin with /")
|
87
102
|
end
|
@@ -94,14 +109,11 @@ module JSI
|
|
94
109
|
unless tokens.respond_to?(:to_ary)
|
95
110
|
raise(TypeError, "tokens must be an array. got: #{tokens.inspect}")
|
96
111
|
end
|
97
|
-
@tokens = tokens.to_ary
|
112
|
+
@tokens = Util.deep_to_frozen(tokens.to_ary, not_implemented: proc { |o| o })
|
98
113
|
end
|
99
114
|
|
100
115
|
attr_reader :tokens
|
101
116
|
|
102
|
-
# @private @deprecated
|
103
|
-
alias_method :reference_tokens, :tokens
|
104
|
-
|
105
117
|
# takes a root json document and evaluates this pointer through the document, returning the value
|
106
118
|
# pointed to by this pointer.
|
107
119
|
#
|
@@ -109,9 +121,9 @@ module JSI
|
|
109
121
|
# @param a arguments are passed to each invocation of `#[]`
|
110
122
|
# @return [Object] the content of the document pointed to by this pointer
|
111
123
|
# @raise [JSI::Ptr::ResolutionError] the document does not contain the path this pointer references
|
112
|
-
def evaluate(document, *a)
|
124
|
+
def evaluate(document, *a, **kw)
|
113
125
|
res = tokens.inject(document) do |value, token|
|
114
|
-
_, child = node_subscript_token_child(value, token, *a)
|
126
|
+
_, child = node_subscript_token_child(value, token, *a, **kw)
|
115
127
|
child
|
116
128
|
end
|
117
129
|
res
|
@@ -120,19 +132,19 @@ module JSI
|
|
120
132
|
# the pointer string representation of this pointer
|
121
133
|
# @return [String]
|
122
134
|
def pointer
|
123
|
-
tokens.map { |t| '/' + t.to_s.gsub('~', '~0').gsub('/', '~1') }.join('')
|
135
|
+
tokens.map { |t| '/' + t.to_s.gsub('~', '~0').gsub('/', '~1') }.join('').freeze
|
124
136
|
end
|
125
137
|
|
126
138
|
# the fragment string representation of this pointer
|
127
139
|
# @return [String]
|
128
140
|
def fragment
|
129
|
-
Addressable::URI.escape(pointer)
|
141
|
+
Addressable::URI.escape(pointer).freeze
|
130
142
|
end
|
131
143
|
|
132
144
|
# a URI consisting of a fragment containing this pointer's fragment string representation
|
133
145
|
# @return [Addressable::URI]
|
134
146
|
def uri
|
135
|
-
Addressable::URI.new(fragment: fragment)
|
147
|
+
Addressable::URI.new(fragment: fragment).freeze
|
136
148
|
end
|
137
149
|
|
138
150
|
# whether this pointer is empty, i.e. it has no tokens
|
@@ -151,26 +163,32 @@ module JSI
|
|
151
163
|
def parent
|
152
164
|
if root?
|
153
165
|
raise(Ptr::Error, "cannot access parent of root pointer: #{pretty_inspect.chomp}")
|
154
|
-
else
|
155
|
-
Ptr.new(tokens[0...-1])
|
156
166
|
end
|
167
|
+
tokens.size == 1 ? EMPTY : Ptr.new(tokens[0...-1].freeze)
|
157
168
|
end
|
158
169
|
|
159
|
-
# whether this pointer
|
160
|
-
#
|
170
|
+
# whether this pointer is an ancestor of `other_ptr`, a descendent pointer.
|
171
|
+
# `ancestor_of?` is inclusive; a pointer is an ancestor of itself.
|
172
|
+
#
|
161
173
|
# @return [Boolean]
|
174
|
+
def ancestor_of?(other_ptr)
|
175
|
+
tokens == other_ptr.tokens[0...tokens.size]
|
176
|
+
end
|
177
|
+
|
178
|
+
# @deprecated
|
162
179
|
def contains?(other_ptr)
|
163
|
-
|
180
|
+
ancestor_of?(other_ptr)
|
164
181
|
end
|
165
182
|
|
166
183
|
# part of this pointer relative to the given ancestor_ptr
|
167
184
|
# @return [JSI::Ptr]
|
168
185
|
# @raise [JSI::Ptr::Error] if the given ancestor_ptr is not an ancestor of this pointer
|
169
|
-
def
|
170
|
-
|
186
|
+
def relative_to(ancestor_ptr)
|
187
|
+
return self if ancestor_ptr.empty?
|
188
|
+
unless ancestor_ptr.ancestor_of?(self)
|
171
189
|
raise(Error, "ancestor_ptr #{ancestor_ptr.inspect} is not ancestor of #{inspect}")
|
172
190
|
end
|
173
|
-
Ptr.new(tokens[ancestor_ptr.tokens.size..-1])
|
191
|
+
ancestor_ptr.tokens.size == tokens.size ? EMPTY : Ptr.new(tokens[ancestor_ptr.tokens.size..-1].freeze)
|
174
192
|
end
|
175
193
|
|
176
194
|
# a pointer with the tokens of this one plus the given `ptr`'s.
|
@@ -182,9 +200,9 @@ module JSI
|
|
182
200
|
elsif ptr.respond_to?(:to_ary)
|
183
201
|
ptr_tokens = ptr
|
184
202
|
else
|
185
|
-
raise(TypeError, "ptr must be a
|
203
|
+
raise(TypeError, "ptr must be a #{Ptr} or Array of tokens; got: #{ptr.inspect}")
|
186
204
|
end
|
187
|
-
Ptr.new(
|
205
|
+
ptr_tokens.empty? ? self : Ptr.new((tokens + ptr_tokens).freeze)
|
188
206
|
end
|
189
207
|
|
190
208
|
# a pointer consisting of the first `n` of our tokens
|
@@ -192,10 +210,10 @@ module JSI
|
|
192
210
|
# @return [JSI::Ptr]
|
193
211
|
# @raise [ArgumentError] if n is not between 0 and the size of our tokens
|
194
212
|
def take(n)
|
195
|
-
unless (0
|
213
|
+
unless n.is_a?(Integer) && n >= 0 && n <= tokens.size
|
196
214
|
raise(ArgumentError, "n not in range (0..#{tokens.size}): #{n.inspect}")
|
197
215
|
end
|
198
|
-
Ptr.new(tokens.take(n))
|
216
|
+
n == tokens.size ? self : Ptr.new(tokens.take(n).freeze)
|
199
217
|
end
|
200
218
|
|
201
219
|
# appends the given token to this pointer's tokens and returns the result
|
@@ -203,7 +221,7 @@ module JSI
|
|
203
221
|
# @param token [Object]
|
204
222
|
# @return [JSI::Ptr] pointer to a child node of this pointer with the given token
|
205
223
|
def [](token)
|
206
|
-
Ptr.new(tokens
|
224
|
+
Ptr.new(tokens.dup.push(token).freeze)
|
207
225
|
end
|
208
226
|
|
209
227
|
# takes a document and a block. the block is yielded the content of the given document at this
|
@@ -225,10 +243,10 @@ module JSI
|
|
225
243
|
# or hash in the path above the node we point to. this node's content is modified by the
|
226
244
|
# caller, and that is recursively merged up to the document root.
|
227
245
|
if empty?
|
228
|
-
|
246
|
+
Util.modified_copy(document, &block)
|
229
247
|
else
|
230
248
|
car = tokens[0]
|
231
|
-
cdr = Ptr.new(tokens[1..-1])
|
249
|
+
cdr = tokens.size == 1 ? EMPTY : Ptr.new(tokens[1..-1].freeze)
|
232
250
|
token, document_child = node_subscript_token_child(document, car)
|
233
251
|
modified_document_child = cdr.modified_document_copy(document_child, &block)
|
234
252
|
if modified_document_child.object_id == document_child.object_id
|
@@ -247,42 +265,57 @@ module JSI
|
|
247
265
|
# a string representation of this pointer
|
248
266
|
# @return [String]
|
249
267
|
def inspect
|
250
|
-
"#{self.class.name}[#{tokens.map(&:inspect).join(", ")}]"
|
268
|
+
-"#{self.class.name}[#{tokens.map(&:inspect).join(", ")}]"
|
251
269
|
end
|
252
270
|
|
253
|
-
|
271
|
+
def to_s
|
272
|
+
inspect
|
273
|
+
end
|
254
274
|
|
255
|
-
#
|
275
|
+
# see {Util::Private::FingerprintHash}
|
276
|
+
# @api private
|
256
277
|
def jsi_fingerprint
|
257
|
-
{class: Ptr, tokens: tokens}
|
278
|
+
{class: Ptr, tokens: tokens}.freeze
|
258
279
|
end
|
259
|
-
include Util::FingerprintHash
|
280
|
+
include Util::FingerprintHash::Immutable
|
281
|
+
|
282
|
+
EMPTY = new(Util::EMPTY_ARY)
|
260
283
|
|
261
284
|
private
|
262
285
|
|
263
|
-
def node_subscript_token_child(value, token, *a)
|
286
|
+
def node_subscript_token_child(value, token, *a, **kw)
|
264
287
|
if value.respond_to?(:to_ary)
|
265
|
-
if token.is_a?(String) && token =~
|
288
|
+
if token.is_a?(String) && (token == '0' || token =~ POS_INT_RE)
|
266
289
|
token = token.to_i
|
267
290
|
elsif token == '-'
|
268
291
|
# per rfc6901, - refers "to the (nonexistent) member after the last array element" and is
|
269
292
|
# expected to raise an error condition.
|
270
293
|
raise(ResolutionError, "Invalid resolution: #{token.inspect} refers to a nonexistent element in array #{value.inspect}")
|
271
294
|
end
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
unless (0...(value.respond_to?(:size) ? value : value.to_ary).size).include?(token)
|
276
|
-
raise(ResolutionError, "Invalid resolution: #{token.inspect} is not a valid index of #{value.inspect}")
|
295
|
+
size = (value.respond_to?(:size) ? value : value.to_ary).size
|
296
|
+
unless token.is_a?(Integer) && token >= 0 && token < size
|
297
|
+
raise(ResolutionError, "Invalid resolution: #{token.inspect} is not a valid array index of #{value.inspect}")
|
277
298
|
end
|
278
299
|
|
279
|
-
|
300
|
+
ary = (value.respond_to?(:[]) ? value : value.to_ary)
|
301
|
+
if kw.empty?
|
302
|
+
# TODO remove eventually (keyword argument compatibility)
|
303
|
+
child = ary[token, *a]
|
304
|
+
else
|
305
|
+
child = ary[token, *a, **kw]
|
306
|
+
end
|
280
307
|
elsif value.respond_to?(:to_hash)
|
281
308
|
unless (value.respond_to?(:key?) ? value : value.to_hash).key?(token)
|
282
309
|
raise(ResolutionError, "Invalid resolution: #{token.inspect} is not a valid key of #{value.inspect}")
|
283
310
|
end
|
284
311
|
|
285
|
-
|
312
|
+
hsh = (value.respond_to?(:[]) ? value : value.to_hash)
|
313
|
+
if kw.empty?
|
314
|
+
# TODO remove eventually (keyword argument compatibility)
|
315
|
+
child = hsh[token, *a]
|
316
|
+
else
|
317
|
+
child = hsh[token, *a, **kw]
|
318
|
+
end
|
286
319
|
else
|
287
320
|
raise(ResolutionError, "Invalid resolution: #{token.inspect} cannot be resolved in #{value.inspect}")
|
288
321
|
end
|
@@ -4,11 +4,20 @@ module JSI
|
|
4
4
|
module Schema::Application::ChildApplication::Contains
|
5
5
|
# @private
|
6
6
|
def internal_applicate_contains(idx, instance, &block)
|
7
|
-
if
|
7
|
+
if keyword?('contains')
|
8
8
|
contains_schema = subschema(['contains'])
|
9
9
|
|
10
|
-
|
10
|
+
child_idx_valid = Hash.new { |h, i| h[i] = contains_schema.instance_valid?(instance[i]) }
|
11
|
+
|
12
|
+
if child_idx_valid[idx]
|
11
13
|
yield contains_schema
|
14
|
+
else
|
15
|
+
instance_valid = instance.each_index.any? { |i| child_idx_valid[i] }
|
16
|
+
|
17
|
+
unless instance_valid
|
18
|
+
# invalid application: if contains_schema does not validate against any child, it applies to every child
|
19
|
+
yield contains_schema
|
20
|
+
end
|
12
21
|
end
|
13
22
|
end
|
14
23
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module JSI
|
4
4
|
module Schema::Application::ChildApplication::Draft06
|
5
|
-
include Schema::Application::ChildApplication
|
6
5
|
include Schema::Application::ChildApplication::Items
|
7
6
|
include Schema::Application::ChildApplication::Contains
|
8
7
|
include Schema::Application::ChildApplication::Properties
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module JSI
|
4
4
|
module Schema::Application::ChildApplication::Draft07
|
5
|
-
include Schema::Application::ChildApplication
|
6
5
|
include Schema::Application::ChildApplication::Items
|
7
6
|
include Schema::Application::ChildApplication::Contains
|
8
7
|
include Schema::Application::ChildApplication::Properties
|
@@ -4,13 +4,13 @@ module JSI
|
|
4
4
|
module Schema::Application::ChildApplication::Items
|
5
5
|
# @private
|
6
6
|
def internal_applicate_items(idx, &block)
|
7
|
-
if schema_content['items'].respond_to?(:to_ary)
|
7
|
+
if keyword?('items') && schema_content['items'].respond_to?(:to_ary)
|
8
8
|
if schema_content['items'].each_index.to_a.include?(idx)
|
9
9
|
yield subschema(['items', idx])
|
10
|
-
elsif
|
10
|
+
elsif keyword?('additionalItems')
|
11
11
|
yield subschema(['additionalItems'])
|
12
12
|
end
|
13
|
-
elsif
|
13
|
+
elsif keyword?('items')
|
14
14
|
yield subschema(['items'])
|
15
15
|
end
|
16
16
|
end
|
@@ -5,11 +5,11 @@ module JSI
|
|
5
5
|
# @private
|
6
6
|
def internal_applicate_properties(property_name, &block)
|
7
7
|
apply_additional = true
|
8
|
-
if
|
8
|
+
if keyword?('properties') && schema_content['properties'].respond_to?(:to_hash) && schema_content['properties'].key?(property_name)
|
9
9
|
apply_additional = false
|
10
10
|
yield subschema(['properties', property_name])
|
11
11
|
end
|
12
|
-
if schema_content['patternProperties'].respond_to?(:to_hash)
|
12
|
+
if keyword?('patternProperties') && schema_content['patternProperties'].respond_to?(:to_hash)
|
13
13
|
schema_content['patternProperties'].each_key do |pattern|
|
14
14
|
if pattern.respond_to?(:to_str) && property_name.to_s =~ Regexp.new(pattern) # TODO map pattern to ruby syntax
|
15
15
|
apply_additional = false
|
@@ -17,7 +17,7 @@ module JSI
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
20
|
-
if apply_additional &&
|
20
|
+
if apply_additional && keyword?('additionalProperties')
|
21
21
|
yield subschema(['additionalProperties'])
|
22
22
|
end
|
23
23
|
end
|
@@ -9,32 +9,5 @@ module JSI
|
|
9
9
|
autoload :Items, 'jsi/schema/application/child_application/items'
|
10
10
|
autoload :Contains, 'jsi/schema/application/child_application/contains'
|
11
11
|
autoload :Properties, 'jsi/schema/application/child_application/properties'
|
12
|
-
|
13
|
-
# a set of child applicator subschemas of this schema which apply to the child of the given instance
|
14
|
-
# on the given token.
|
15
|
-
#
|
16
|
-
# @param token [Object] the array index or object property name for the child instance
|
17
|
-
# @param instance [Object] the instance to check any child applicators against
|
18
|
-
# @return [JSI::SchemaSet] child applicator subschemas of this schema for the given token
|
19
|
-
# of the instance
|
20
|
-
def child_applicator_schemas(token, instance)
|
21
|
-
SchemaSet.new(each_child_applicator_schema(token, instance))
|
22
|
-
end
|
23
|
-
|
24
|
-
# yields each child applicator subschema (from properties, items, etc.) which applies to the child of
|
25
|
-
# the given instance on the given token.
|
26
|
-
#
|
27
|
-
# @param (see #child_applicator_schemas)
|
28
|
-
# @yield [JSI::Schema]
|
29
|
-
# @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
|
30
|
-
def each_child_applicator_schema(token, instance, &block)
|
31
|
-
return to_enum(__method__, token, instance) unless block
|
32
|
-
|
33
|
-
if schema_content.respond_to?(:to_hash)
|
34
|
-
internal_child_applicate_keywords(token, instance, &block)
|
35
|
-
end
|
36
|
-
|
37
|
-
nil
|
38
|
-
end
|
39
12
|
end
|
40
13
|
end
|
@@ -4,7 +4,7 @@ module JSI
|
|
4
4
|
module Schema::Application::InplaceApplication::Dependencies
|
5
5
|
# @private
|
6
6
|
def internal_applicate_dependencies(instance, visited_refs, &block)
|
7
|
-
if
|
7
|
+
if keyword?('dependencies')
|
8
8
|
value = schema_content['dependencies']
|
9
9
|
# This keyword's value MUST be an object. Each property specifies a dependency. Each dependency
|
10
10
|
# value MUST be an array or a valid JSON Schema.
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module JSI
|
4
4
|
module Schema::Application::InplaceApplication::Draft04
|
5
|
-
include Schema::Application::InplaceApplication
|
6
5
|
include Schema::Application::InplaceApplication::Ref
|
7
6
|
include Schema::Application::InplaceApplication::Dependencies
|
8
7
|
include Schema::Application::InplaceApplication::SomeOf
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module JSI
|
4
4
|
module Schema::Application::InplaceApplication::Draft06
|
5
|
-
include Schema::Application::InplaceApplication
|
6
5
|
include Schema::Application::InplaceApplication::Ref
|
7
6
|
include Schema::Application::InplaceApplication::Dependencies
|
8
7
|
include Schema::Application::InplaceApplication::SomeOf
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module JSI
|
4
4
|
module Schema::Application::InplaceApplication::Draft07
|
5
|
-
include Schema::Application::InplaceApplication
|
6
5
|
include Schema::Application::InplaceApplication::Ref
|
7
6
|
include Schema::Application::InplaceApplication::Dependencies
|
8
7
|
include Schema::Application::InplaceApplication::IfThenElse
|
@@ -4,13 +4,13 @@ module JSI
|
|
4
4
|
module Schema::Application::InplaceApplication::IfThenElse
|
5
5
|
# @private
|
6
6
|
def internal_applicate_ifthenelse(instance, visited_refs, &block)
|
7
|
-
if
|
7
|
+
if keyword?('if')
|
8
8
|
if subschema(['if']).instance_valid?(instance)
|
9
|
-
if
|
9
|
+
if keyword?('then')
|
10
10
|
subschema(['then']).each_inplace_applicator_schema(instance, visited_refs: visited_refs, &block)
|
11
11
|
end
|
12
12
|
else
|
13
|
-
if
|
13
|
+
if keyword?('else')
|
14
14
|
subschema(['else']).each_inplace_applicator_schema(instance, visited_refs: visited_refs, &block)
|
15
15
|
end
|
16
16
|
end
|
@@ -4,8 +4,8 @@ module JSI
|
|
4
4
|
module Schema::Application::InplaceApplication::Ref
|
5
5
|
# @private
|
6
6
|
def internal_applicate_ref(instance, visited_refs, throw_done: false, &block)
|
7
|
-
if schema_content['$ref'].respond_to?(:to_str)
|
8
|
-
ref =
|
7
|
+
if keyword?('$ref') && schema_content['$ref'].respond_to?(:to_str)
|
8
|
+
ref = schema_ref('$ref')
|
9
9
|
unless visited_refs.include?(ref)
|
10
10
|
ref.deref_schema.each_inplace_applicator_schema(instance, visited_refs: visited_refs + [ref], &block)
|
11
11
|
if throw_done
|
@@ -4,24 +4,39 @@ module JSI
|
|
4
4
|
module Schema::Application::InplaceApplication::SomeOf
|
5
5
|
# @private
|
6
6
|
def internal_applicate_someOf(instance, visited_refs, &block)
|
7
|
-
if schema_content['allOf'].respond_to?(:to_ary)
|
7
|
+
if keyword?('allOf') && schema_content['allOf'].respond_to?(:to_ary)
|
8
8
|
schema_content['allOf'].each_index do |i|
|
9
9
|
subschema(['allOf', i]).each_inplace_applicator_schema(instance, visited_refs: visited_refs, &block)
|
10
10
|
end
|
11
11
|
end
|
12
|
-
if schema_content['anyOf'].respond_to?(:to_ary)
|
13
|
-
schema_content['anyOf'].each_index
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
if keyword?('anyOf') && schema_content['anyOf'].respond_to?(:to_ary)
|
13
|
+
anyOf = schema_content['anyOf'].each_index.map { |i| subschema(['anyOf', i]) }
|
14
|
+
validOf = anyOf.select { |schema| schema.instance_valid?(instance) }
|
15
|
+
if !validOf.empty?
|
16
|
+
applicators = validOf
|
17
|
+
else
|
18
|
+
# invalid application: if none of the anyOf were valid, we apply them all
|
19
|
+
applicators = anyOf
|
20
|
+
end
|
21
|
+
|
22
|
+
applicators.each do |applicator|
|
23
|
+
applicator.each_inplace_applicator_schema(instance, visited_refs: visited_refs, &block)
|
17
24
|
end
|
18
25
|
end
|
19
|
-
if schema_content['oneOf'].respond_to?(:to_ary)
|
20
|
-
|
21
|
-
|
26
|
+
if keyword?('oneOf') && schema_content['oneOf'].respond_to?(:to_ary)
|
27
|
+
oneOf_idxs = schema_content['oneOf'].each_index
|
28
|
+
subschema_idx_valid = Hash.new { |h, i| h[i] = subschema(['oneOf', i]).instance_valid?(instance) }
|
29
|
+
# count up to 2 `oneOf` subschemas which `instance` validates against
|
30
|
+
nvalid = oneOf_idxs.inject(0) { |n, i| n <= 1 && subschema_idx_valid[i] ? n + 1 : n }
|
31
|
+
if nvalid == 1
|
32
|
+
applicator_idxs = oneOf_idxs.select { |i| subschema_idx_valid[i] }
|
33
|
+
else
|
34
|
+
# invalid application: if none or multiple of the oneOf were valid, we apply them all
|
35
|
+
applicator_idxs = oneOf_idxs
|
22
36
|
end
|
23
|
-
|
24
|
-
|
37
|
+
|
38
|
+
applicator_idxs.each do |i|
|
39
|
+
subschema(['oneOf', i]).each_inplace_applicator_schema(instance, visited_refs: visited_refs, &block)
|
25
40
|
end
|
26
41
|
end
|
27
42
|
end
|
@@ -10,37 +10,5 @@ module JSI
|
|
10
10
|
autoload :SomeOf, 'jsi/schema/application/inplace_application/someof'
|
11
11
|
autoload :IfThenElse, 'jsi/schema/application/inplace_application/ifthenelse'
|
12
12
|
autoload :Dependencies, 'jsi/schema/application/inplace_application/dependencies'
|
13
|
-
|
14
|
-
# a set of inplace applicator schemas of this schema (from $ref, allOf, etc.) which apply to the
|
15
|
-
# given instance.
|
16
|
-
#
|
17
|
-
# the returned set will contain this schema itself, unless this schema contains a $ref keyword.
|
18
|
-
#
|
19
|
-
# @param instance [Object] the instance to check any applicators against
|
20
|
-
# @return [JSI::SchemaSet] matched applicator schemas
|
21
|
-
def inplace_applicator_schemas(instance)
|
22
|
-
SchemaSet.new(each_inplace_applicator_schema(instance))
|
23
|
-
end
|
24
|
-
|
25
|
-
# yields each inplace applicator schema which applies to the given instance.
|
26
|
-
#
|
27
|
-
# @param instance (see #inplace_applicator_schemas)
|
28
|
-
# @param visited_refs [Enumerable<JSI::Schema::Ref>]
|
29
|
-
# @yield [JSI::Schema]
|
30
|
-
# @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
|
31
|
-
def each_inplace_applicator_schema(instance, visited_refs: [], &block)
|
32
|
-
return to_enum(__method__, instance, visited_refs: visited_refs) unless block
|
33
|
-
|
34
|
-
catch(:jsi_application_done) do
|
35
|
-
if schema_content.respond_to?(:to_hash)
|
36
|
-
internal_inplace_applicate_keywords(instance, visited_refs, &block)
|
37
|
-
else
|
38
|
-
# self is the only applicator schema if there are no keywords
|
39
|
-
yield self
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
nil
|
44
|
-
end
|
45
13
|
end
|
46
14
|
end
|
data/lib/jsi/schema/draft04.rb
CHANGED
data/lib/jsi/schema/draft06.rb
CHANGED