jsi-dev 0.0.0.pre.commonmarker

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +8 -0
  3. data/CHANGELOG.md +101 -0
  4. data/LICENSE.md +613 -0
  5. data/README.md +303 -0
  6. data/docs/glossary.md +281 -0
  7. data/jsi.gemspec +30 -0
  8. data/lib/jsi/base/node.rb +373 -0
  9. data/lib/jsi/base.rb +738 -0
  10. data/lib/jsi/jsi_coder.rb +92 -0
  11. data/lib/jsi/metaschema.rb +6 -0
  12. data/lib/jsi/metaschema_node/bootstrap_schema.rb +126 -0
  13. data/lib/jsi/metaschema_node.rb +262 -0
  14. data/lib/jsi/ptr.rb +314 -0
  15. data/lib/jsi/schema/application/child_application/contains.rb +25 -0
  16. data/lib/jsi/schema/application/child_application/draft04.rb +21 -0
  17. data/lib/jsi/schema/application/child_application/draft06.rb +28 -0
  18. data/lib/jsi/schema/application/child_application/draft07.rb +28 -0
  19. data/lib/jsi/schema/application/child_application/items.rb +18 -0
  20. data/lib/jsi/schema/application/child_application/properties.rb +25 -0
  21. data/lib/jsi/schema/application/child_application.rb +13 -0
  22. data/lib/jsi/schema/application/draft04.rb +8 -0
  23. data/lib/jsi/schema/application/draft06.rb +8 -0
  24. data/lib/jsi/schema/application/draft07.rb +8 -0
  25. data/lib/jsi/schema/application/inplace_application/dependencies.rb +28 -0
  26. data/lib/jsi/schema/application/inplace_application/draft04.rb +25 -0
  27. data/lib/jsi/schema/application/inplace_application/draft06.rb +26 -0
  28. data/lib/jsi/schema/application/inplace_application/draft07.rb +32 -0
  29. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +20 -0
  30. data/lib/jsi/schema/application/inplace_application/ref.rb +18 -0
  31. data/lib/jsi/schema/application/inplace_application/someof.rb +44 -0
  32. data/lib/jsi/schema/application/inplace_application.rb +14 -0
  33. data/lib/jsi/schema/application.rb +12 -0
  34. data/lib/jsi/schema/draft04.rb +13 -0
  35. data/lib/jsi/schema/draft06.rb +13 -0
  36. data/lib/jsi/schema/draft07.rb +13 -0
  37. data/lib/jsi/schema/issue.rb +36 -0
  38. data/lib/jsi/schema/ref.rb +183 -0
  39. data/lib/jsi/schema/schema_ancestor_node.rb +122 -0
  40. data/lib/jsi/schema/validation/array.rb +69 -0
  41. data/lib/jsi/schema/validation/const.rb +20 -0
  42. data/lib/jsi/schema/validation/contains.rb +25 -0
  43. data/lib/jsi/schema/validation/dependencies.rb +49 -0
  44. data/lib/jsi/schema/validation/draft04/minmax.rb +91 -0
  45. data/lib/jsi/schema/validation/draft04.rb +110 -0
  46. data/lib/jsi/schema/validation/draft06.rb +120 -0
  47. data/lib/jsi/schema/validation/draft07.rb +157 -0
  48. data/lib/jsi/schema/validation/enum.rb +25 -0
  49. data/lib/jsi/schema/validation/ifthenelse.rb +46 -0
  50. data/lib/jsi/schema/validation/items.rb +54 -0
  51. data/lib/jsi/schema/validation/not.rb +20 -0
  52. data/lib/jsi/schema/validation/numeric.rb +121 -0
  53. data/lib/jsi/schema/validation/object.rb +45 -0
  54. data/lib/jsi/schema/validation/pattern.rb +34 -0
  55. data/lib/jsi/schema/validation/properties.rb +101 -0
  56. data/lib/jsi/schema/validation/property_names.rb +32 -0
  57. data/lib/jsi/schema/validation/ref.rb +40 -0
  58. data/lib/jsi/schema/validation/required.rb +27 -0
  59. data/lib/jsi/schema/validation/someof.rb +90 -0
  60. data/lib/jsi/schema/validation/string.rb +47 -0
  61. data/lib/jsi/schema/validation/type.rb +49 -0
  62. data/lib/jsi/schema/validation.rb +49 -0
  63. data/lib/jsi/schema.rb +792 -0
  64. data/lib/jsi/schema_classes.rb +357 -0
  65. data/lib/jsi/schema_registry.rb +190 -0
  66. data/lib/jsi/schema_set.rb +219 -0
  67. data/lib/jsi/simple_wrap.rb +26 -0
  68. data/lib/jsi/util/private/attr_struct.rb +130 -0
  69. data/lib/jsi/util/private/memo_map.rb +75 -0
  70. data/lib/jsi/util/private.rb +202 -0
  71. data/lib/jsi/util/typelike.rb +225 -0
  72. data/lib/jsi/util.rb +227 -0
  73. data/lib/jsi/validation/error.rb +34 -0
  74. data/lib/jsi/validation/result.rb +212 -0
  75. data/lib/jsi/validation.rb +15 -0
  76. data/lib/jsi/version.rb +5 -0
  77. data/lib/jsi.rb +105 -0
  78. data/lib/schemas/json-schema.org/draft-04/schema.rb +169 -0
  79. data/lib/schemas/json-schema.org/draft-06/schema.rb +171 -0
  80. data/lib/schemas/json-schema.org/draft-07/schema.rb +198 -0
  81. data/readme.rb +138 -0
  82. data/{resources}/schemas/json-schema.org/draft-04/schema.json +149 -0
  83. data/{resources}/schemas/json-schema.org/draft-06/schema.json +154 -0
  84. data/{resources}/schemas/json-schema.org/draft-07/schema.json +168 -0
  85. metadata +155 -0
data/lib/jsi/ptr.rb ADDED
@@ -0,0 +1,314 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ # a representation to work with JSON Pointer, as described by RFC 6901 https://tools.ietf.org/html/rfc6901
5
+ #
6
+ # a pointer is a sequence of tokens pointing to a node in a document.
7
+ class Ptr
8
+ class Error < StandardError
9
+ end
10
+
11
+ # raised when attempting to parse a JSON Pointer string with invalid syntax
12
+ class PointerSyntaxError < Error
13
+ end
14
+
15
+ # raised when a pointer refers to a path in a document that could not be resolved
16
+ class ResolutionError < Error
17
+ end
18
+
19
+ POS_INT_RE = /\A[1-9]\d*\z/
20
+ private_constant :POS_INT_RE
21
+
22
+ # instantiates a pointer or returns the given pointer
23
+ # @param ary_ptr [#to_ary, JSI::Ptr] an array of tokens, or a pointer
24
+ # @return [JSI::Ptr]
25
+ def self.ary_ptr(ary_ptr)
26
+ if ary_ptr.is_a?(Ptr)
27
+ ary_ptr
28
+ else
29
+ new(ary_ptr)
30
+ end
31
+ end
32
+
33
+ # instantiates a pointer from the given tokens.
34
+ #
35
+ # JSI::Ptr[]
36
+ #
37
+ # instantiates a root pointer.
38
+ #
39
+ # JSI::Ptr['a', 'b']
40
+ # JSI::Ptr['a']['b']
41
+ #
42
+ # are both ways to instantiate a pointer with tokens ['a', 'b']. the latter example chains the
43
+ # class .[] method with the instance #[] method.
44
+ #
45
+ # @param tokens any number of tokens
46
+ # @return [JSI::Ptr]
47
+ def self.[](*tokens)
48
+ tokens.empty? ? EMPTY : new(tokens.freeze)
49
+ end
50
+
51
+ # parse a URI-escaped fragment and instantiate as a JSI::Ptr
52
+ #
53
+ # JSI::Ptr.from_fragment('/foo/bar')
54
+ # => JSI::Ptr["foo", "bar"]
55
+ #
56
+ # with URI escaping:
57
+ #
58
+ # JSI::Ptr.from_fragment('/foo%20bar')
59
+ # => JSI::Ptr["foo bar"]
60
+ #
61
+ # Note: A fragment does not include a leading '#'. The string "#/foo" is a URI containing the
62
+ # fragment "/foo", which should be parsed by `Addressable::URI` before passing to this method, e.g.:
63
+ #
64
+ # JSI::Ptr.from_fragment(Addressable::URI.parse("#/foo").fragment)
65
+ # => JSI::Ptr["foo"]
66
+ #
67
+ # @param fragment [String] a fragment containing a pointer
68
+ # @return [JSI::Ptr]
69
+ # @raise [JSI::Ptr::PointerSyntaxError] when the fragment does not contain a pointer with
70
+ # valid pointer syntax
71
+ def self.from_fragment(fragment)
72
+ from_pointer(Addressable::URI.unescape(fragment))
73
+ end
74
+
75
+ # parse a pointer string and instantiate as a JSI::Ptr
76
+ #
77
+ # JSI::Ptr.from_pointer('/foo')
78
+ # => JSI::Ptr["foo"]
79
+ #
80
+ # JSI::Ptr.from_pointer('/foo~0bar/baz~1qux')
81
+ # => JSI::Ptr["foo~bar", "baz/qux"]
82
+ #
83
+ # @param pointer_string [String] a pointer string
84
+ # @return [JSI::Ptr]
85
+ # @raise [JSI::Ptr::PointerSyntaxError] when the pointer_string does not have valid pointer syntax
86
+ def self.from_pointer(pointer_string)
87
+ pointer_string = pointer_string.to_str
88
+ if pointer_string[0] == ?/
89
+ tokens = pointer_string.split('/', -1).map! do |piece|
90
+ piece.gsub!('~1', '/')
91
+ piece.gsub!('~0', '~')
92
+ piece.freeze
93
+ end
94
+ tokens.shift
95
+ new(tokens.freeze)
96
+ elsif pointer_string.empty?
97
+ EMPTY
98
+ else
99
+ raise(PointerSyntaxError, "Invalid pointer syntax in #{pointer_string.inspect}: pointer must begin with /")
100
+ end
101
+ end
102
+
103
+ # initializes a JSI::Ptr from the given tokens.
104
+ #
105
+ # @param tokens [Array<Object>]
106
+ def initialize(tokens)
107
+ unless tokens.respond_to?(:to_ary)
108
+ raise(TypeError, "tokens must be an array. got: #{tokens.inspect}")
109
+ end
110
+ @tokens = Util.deep_to_frozen(tokens.to_ary, not_implemented: proc { |o| o })
111
+ end
112
+
113
+ attr_reader :tokens
114
+
115
+ # takes a root json document and evaluates this pointer through the document, returning the value
116
+ # pointed to by this pointer.
117
+ #
118
+ # @param document [#to_ary, #to_hash] the document against which we will evaluate this pointer
119
+ # @param a arguments are passed to each invocation of `#[]`
120
+ # @return [Object] the content of the document pointed to by this pointer
121
+ # @raise [JSI::Ptr::ResolutionError] the document does not contain the path this pointer references
122
+ def evaluate(document, *a, **kw)
123
+ res = tokens.inject(document) do |value, token|
124
+ _, child = node_subscript_token_child(value, token, *a, **kw)
125
+ child
126
+ end
127
+ res
128
+ end
129
+
130
+ # the pointer string representation of this pointer
131
+ # @return [String]
132
+ def pointer
133
+ tokens.map { |t| '/' + t.to_s.gsub('~', '~0').gsub('/', '~1') }.join('').freeze
134
+ end
135
+
136
+ # the fragment string representation of this pointer
137
+ # @return [String]
138
+ def fragment
139
+ Addressable::URI.escape(pointer).freeze
140
+ end
141
+
142
+ # a URI consisting of a fragment containing this pointer's fragment string representation
143
+ # @return [Addressable::URI]
144
+ def uri
145
+ Addressable::URI.new(fragment: fragment).freeze
146
+ end
147
+
148
+ # whether this pointer is empty, i.e. it has no tokens
149
+ # @return [Boolean]
150
+ def empty?
151
+ tokens.empty?
152
+ end
153
+
154
+ # whether this is a root pointer, indicated by an empty array of tokens
155
+ # @return [Boolean]
156
+ alias_method :root?, :empty?
157
+
158
+ # pointer to the parent of where this pointer points
159
+ # @return [JSI::Ptr]
160
+ # @raise [JSI::Ptr::Error] if this pointer has no parent (points to the root)
161
+ def parent
162
+ if root?
163
+ raise(Ptr::Error, "cannot access parent of root pointer: #{pretty_inspect.chomp}")
164
+ end
165
+ tokens.size == 1 ? EMPTY : Ptr.new(tokens[0...-1].freeze)
166
+ end
167
+
168
+ # whether this pointer contains the other_ptr - that is, whether this pointer is an ancestor
169
+ # of `other_ptr`, a descendent pointer. `contains?` is inclusive; a pointer does contain itself.
170
+ # @return [Boolean]
171
+ def contains?(other_ptr)
172
+ tokens == other_ptr.tokens[0...tokens.size]
173
+ end
174
+
175
+ # part of this pointer relative to the given ancestor_ptr
176
+ # @return [JSI::Ptr]
177
+ # @raise [JSI::Ptr::Error] if the given ancestor_ptr is not an ancestor of this pointer
178
+ def relative_to(ancestor_ptr)
179
+ unless ancestor_ptr.contains?(self)
180
+ raise(Error, "ancestor_ptr #{ancestor_ptr.inspect} is not ancestor of #{inspect}")
181
+ end
182
+ ancestor_ptr.tokens.size == tokens.size ? EMPTY : Ptr.new(tokens[ancestor_ptr.tokens.size..-1].freeze)
183
+ end
184
+
185
+ # a pointer with the tokens of this one plus the given `ptr`'s.
186
+ # @param ptr [JSI::Ptr, #to_ary]
187
+ # @return [JSI::Ptr]
188
+ def +(ptr)
189
+ if ptr.is_a?(Ptr)
190
+ ptr_tokens = ptr.tokens
191
+ elsif ptr.respond_to?(:to_ary)
192
+ ptr_tokens = ptr
193
+ else
194
+ raise(TypeError, "ptr must be a #{Ptr} or Array of tokens; got: #{ptr.inspect}")
195
+ end
196
+ ptr_tokens.empty? ? self : Ptr.new((tokens + ptr_tokens).freeze)
197
+ end
198
+
199
+ # a pointer consisting of the first `n` of our tokens
200
+ # @param n [Integer]
201
+ # @return [JSI::Ptr]
202
+ # @raise [ArgumentError] if n is not between 0 and the size of our tokens
203
+ def take(n)
204
+ unless n.is_a?(Integer) && n >= 0 && n <= tokens.size
205
+ raise(ArgumentError, "n not in range (0..#{tokens.size}): #{n.inspect}")
206
+ end
207
+ n == tokens.size ? self : Ptr.new(tokens.take(n).freeze)
208
+ end
209
+
210
+ # appends the given token to this pointer's tokens and returns the result
211
+ #
212
+ # @param token [Object]
213
+ # @return [JSI::Ptr] pointer to a child node of this pointer with the given token
214
+ def [](token)
215
+ Ptr.new(tokens.dup.push(token).freeze)
216
+ end
217
+
218
+ # takes a document and a block. the block is yielded the content of the given document at this
219
+ # pointer's location. the block must result a modified copy of that content (and MUST NOT modify
220
+ # the object it is given). this modified copy of that content is incorporated into a modified copy
221
+ # of the given document, which is then returned. the structure and contents of the document outside
222
+ # the path pointed to by this pointer is not modified.
223
+ #
224
+ # @param document [Object] the document to apply this pointer to
225
+ # @yield [Object] the content this pointer applies to in the given document
226
+ # the block must result in the new content which will be placed in the modified document copy.
227
+ # @return [Object] a copy of the given document, with the content this pointer applies to
228
+ # replaced by the result of the block
229
+ def modified_document_copy(document, &block)
230
+ # we need to preserve the rest of the document, but modify the content at our path.
231
+ #
232
+ # this is actually a bit tricky. we can't modify the original document, obviously.
233
+ # we could do a deep copy, but that's expensive. instead, we make a copy of each array
234
+ # or hash in the path above the node we point to. this node's content is modified by the
235
+ # caller, and that is recursively merged up to the document root.
236
+ if empty?
237
+ Util.modified_copy(document, &block)
238
+ else
239
+ car = tokens[0]
240
+ cdr = Ptr.new(tokens[1..-1].freeze)
241
+ token, document_child = node_subscript_token_child(document, car)
242
+ modified_document_child = cdr.modified_document_copy(document_child, &block)
243
+ if modified_document_child.object_id == document_child.object_id
244
+ document
245
+ else
246
+ modified_document = document.respond_to?(:[]=) ? document.dup :
247
+ document.respond_to?(:to_hash) ? document.to_hash.dup :
248
+ document.respond_to?(:to_ary) ? document.to_ary.dup :
249
+ raise(Bug) # not possible; node_subscript_token_child would have raised
250
+ modified_document[token] = modified_document_child
251
+ modified_document
252
+ end
253
+ end
254
+ end
255
+
256
+ # a string representation of this pointer
257
+ # @return [String]
258
+ def inspect
259
+ -"#{self.class.name}[#{tokens.map(&:inspect).join(", ")}]"
260
+ end
261
+
262
+ alias_method :to_s, :inspect
263
+
264
+ # see {Util::Private::FingerprintHash}
265
+ # @api private
266
+ def jsi_fingerprint
267
+ {class: Ptr, tokens: tokens}
268
+ end
269
+ include Util::FingerprintHash::Immutable
270
+
271
+ EMPTY = new(Util::EMPTY_ARY)
272
+
273
+ private
274
+
275
+ def node_subscript_token_child(value, token, *a, **kw)
276
+ if value.respond_to?(:to_ary)
277
+ if token.is_a?(String) && (token == '0' || token =~ POS_INT_RE)
278
+ token = token.to_i
279
+ elsif token == '-'
280
+ # per rfc6901, - refers "to the (nonexistent) member after the last array element" and is
281
+ # expected to raise an error condition.
282
+ raise(ResolutionError, "Invalid resolution: #{token.inspect} refers to a nonexistent element in array #{value.inspect}")
283
+ end
284
+ size = (value.respond_to?(:size) ? value : value.to_ary).size
285
+ unless token.is_a?(Integer) && token >= 0 && token < size
286
+ raise(ResolutionError, "Invalid resolution: #{token.inspect} is not a valid array index of #{value.inspect}")
287
+ end
288
+
289
+ ary = (value.respond_to?(:[]) ? value : value.to_ary)
290
+ if kw.empty?
291
+ # TODO remove eventually (keyword argument compatibility)
292
+ child = ary[token, *a]
293
+ else
294
+ child = ary[token, *a, **kw]
295
+ end
296
+ elsif value.respond_to?(:to_hash)
297
+ unless (value.respond_to?(:key?) ? value : value.to_hash).key?(token)
298
+ raise(ResolutionError, "Invalid resolution: #{token.inspect} is not a valid key of #{value.inspect}")
299
+ end
300
+
301
+ hsh = (value.respond_to?(:[]) ? value : value.to_hash)
302
+ if kw.empty?
303
+ # TODO remove eventually (keyword argument compatibility)
304
+ child = hsh[token, *a]
305
+ else
306
+ child = hsh[token, *a, **kw]
307
+ end
308
+ else
309
+ raise(ResolutionError, "Invalid resolution: #{token.inspect} cannot be resolved in #{value.inspect}")
310
+ end
311
+ [token, child]
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::ChildApplication::Contains
5
+ # @private
6
+ def internal_applicate_contains(idx, instance, &block)
7
+ if keyword?('contains')
8
+ contains_schema = subschema(['contains'])
9
+
10
+ child_idx_valid = Hash.new { |h, i| h[i] = contains_schema.instance_valid?(instance[i]) }
11
+
12
+ if child_idx_valid[idx]
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
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::ChildApplication::Draft04
5
+ include Schema::Application::ChildApplication::Items
6
+ include Schema::Application::ChildApplication::Properties
7
+
8
+ # @private
9
+ def internal_child_applicate_keywords(token, instance, &block)
10
+ if instance.respond_to?(:to_ary)
11
+ # 5.3.1. additionalItems and items
12
+ internal_applicate_items(token, &block)
13
+ end
14
+
15
+ if instance.respond_to?(:to_hash)
16
+ # 5.4.4. additionalProperties, properties and patternProperties
17
+ internal_applicate_properties(token, &block)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::ChildApplication::Draft06
5
+ include Schema::Application::ChildApplication::Items
6
+ include Schema::Application::ChildApplication::Contains
7
+ include Schema::Application::ChildApplication::Properties
8
+
9
+ # @private
10
+ def internal_child_applicate_keywords(token, instance, &block)
11
+ if instance.respond_to?(:to_ary)
12
+ # json-schema-validation 6.9. items
13
+ # json-schema-validation 6.10. additionalItems
14
+ internal_applicate_items(token, &block)
15
+
16
+ # json-schema-validation 6.14. contains
17
+ internal_applicate_contains(token, instance, &block)
18
+ end
19
+
20
+ if instance.respond_to?(:to_hash)
21
+ # json-schema-validation 6.18. properties
22
+ # json-schema-validation 6.19. patternProperties
23
+ # json-schema-validation 6.20. additionalProperties
24
+ internal_applicate_properties(token, &block)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::ChildApplication::Draft07
5
+ include Schema::Application::ChildApplication::Items
6
+ include Schema::Application::ChildApplication::Contains
7
+ include Schema::Application::ChildApplication::Properties
8
+
9
+ # @private
10
+ def internal_child_applicate_keywords(token, instance, &block)
11
+ if instance.respond_to?(:to_ary)
12
+ # 6.4.1. items
13
+ # 6.4.2. additionalItems
14
+ internal_applicate_items(token, &block)
15
+
16
+ # 6.4.6. contains
17
+ internal_applicate_contains(token, instance, &block)
18
+ end
19
+
20
+ if instance.respond_to?(:to_hash)
21
+ # 6.5.4. properties
22
+ # 6.5.5. patternProperties
23
+ # 6.5.6. additionalProperties
24
+ internal_applicate_properties(token, &block)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::ChildApplication::Items
5
+ # @private
6
+ def internal_applicate_items(idx, &block)
7
+ if keyword?('items') && schema_content['items'].respond_to?(:to_ary)
8
+ if schema_content['items'].each_index.to_a.include?(idx)
9
+ yield subschema(['items', idx])
10
+ elsif keyword?('additionalItems')
11
+ yield subschema(['additionalItems'])
12
+ end
13
+ elsif keyword?('items')
14
+ yield subschema(['items'])
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::ChildApplication::Properties
5
+ # @private
6
+ def internal_applicate_properties(property_name, &block)
7
+ apply_additional = true
8
+ if keyword?('properties') && schema_content['properties'].respond_to?(:to_hash) && schema_content['properties'].key?(property_name)
9
+ apply_additional = false
10
+ yield subschema(['properties', property_name])
11
+ end
12
+ if keyword?('patternProperties') && schema_content['patternProperties'].respond_to?(:to_hash)
13
+ schema_content['patternProperties'].each_key do |pattern|
14
+ if pattern.respond_to?(:to_str) && property_name.to_s =~ Regexp.new(pattern) # TODO map pattern to ruby syntax
15
+ apply_additional = false
16
+ yield subschema(['patternProperties', pattern])
17
+ end
18
+ end
19
+ end
20
+ if apply_additional && keyword?('additionalProperties')
21
+ yield subschema(['additionalProperties'])
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::ChildApplication
5
+ autoload :Draft04, 'jsi/schema/application/child_application/draft04'
6
+ autoload :Draft06, 'jsi/schema/application/child_application/draft06'
7
+ autoload :Draft07, 'jsi/schema/application/child_application/draft07'
8
+
9
+ autoload :Items, 'jsi/schema/application/child_application/items'
10
+ autoload :Contains, 'jsi/schema/application/child_application/contains'
11
+ autoload :Properties, 'jsi/schema/application/child_application/properties'
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::Draft04
5
+ include Schema::Application::InplaceApplication::Draft04
6
+ include Schema::Application::ChildApplication::Draft04
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::Draft06
5
+ include Schema::Application::InplaceApplication::Draft06
6
+ include Schema::Application::ChildApplication::Draft06
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::Draft07
5
+ include Schema::Application::InplaceApplication::Draft07
6
+ include Schema::Application::ChildApplication::Draft07
7
+ end
8
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::InplaceApplication::Dependencies
5
+ # @private
6
+ def internal_applicate_dependencies(instance, visited_refs, &block)
7
+ if keyword?('dependencies')
8
+ value = schema_content['dependencies']
9
+ # This keyword's value MUST be an object. Each property specifies a dependency. Each dependency
10
+ # value MUST be an array or a valid JSON Schema.
11
+ if value.respond_to?(:to_hash)
12
+ value.each_pair do |property_name, dependency|
13
+ if dependency.respond_to?(:to_ary)
14
+ # noop: array-form dependencies has no inplace applicator schema
15
+ else
16
+ # If the dependency value is a subschema, and the dependency key is a
17
+ # property in the instance, the entire instance must validate against
18
+ # the dependency value.
19
+ if instance.respond_to?(:to_hash) && instance.key?(property_name)
20
+ subschema(['dependencies', property_name]).each_inplace_applicator_schema(instance, visited_refs: visited_refs, &block)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::InplaceApplication::Draft04
5
+ include Schema::Application::InplaceApplication::Ref
6
+ include Schema::Application::InplaceApplication::Dependencies
7
+ include Schema::Application::InplaceApplication::SomeOf
8
+
9
+ # @private
10
+ def internal_inplace_applicate_keywords(instance, visited_refs, &block)
11
+ internal_applicate_ref(instance, visited_refs, throw_done: true, &block)
12
+
13
+ # self is the first applicator schema if $ref has not short-circuited it
14
+ yield self
15
+
16
+ # 5.4.5. dependencies
17
+ internal_applicate_dependencies(instance, visited_refs, &block)
18
+
19
+ # 5.5.3. allOf
20
+ # 5.5.4. anyOf
21
+ # 5.5.5. oneOf
22
+ internal_applicate_someOf(instance, visited_refs, &block)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::InplaceApplication::Draft06
5
+ include Schema::Application::InplaceApplication::Ref
6
+ include Schema::Application::InplaceApplication::Dependencies
7
+ include Schema::Application::InplaceApplication::SomeOf
8
+
9
+ # @private
10
+ def internal_inplace_applicate_keywords(instance, visited_refs, &block)
11
+ # json-schema 8. Schema references with $ref
12
+ internal_applicate_ref(instance, visited_refs, throw_done: true, &block)
13
+
14
+ # self is the first applicator schema if $ref has not short-circuited it
15
+ yield self
16
+
17
+ # json-schema-validation 6.21. dependencies
18
+ internal_applicate_dependencies(instance, visited_refs, &block)
19
+
20
+ # json-schema-validation 6.26. allOf
21
+ # json-schema-validation 6.27. anyOf
22
+ # json-schema-validation 6.28. oneOf
23
+ internal_applicate_someOf(instance, visited_refs, &block)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::InplaceApplication::Draft07
5
+ include Schema::Application::InplaceApplication::Ref
6
+ include Schema::Application::InplaceApplication::Dependencies
7
+ include Schema::Application::InplaceApplication::IfThenElse
8
+ include Schema::Application::InplaceApplication::SomeOf
9
+
10
+ # @private
11
+ def internal_inplace_applicate_keywords(instance, visited_refs, &block)
12
+ # json-schema 8. Schema references with $ref
13
+ internal_applicate_ref(instance, visited_refs, throw_done: true, &block)
14
+
15
+ # self is the first applicator schema if $ref has not short-circuited it
16
+ block.call(self)
17
+
18
+ # 6.5.7. dependencies
19
+ internal_applicate_dependencies(instance, visited_refs, &block)
20
+
21
+ # 6.6.1. if
22
+ # 6.6.2. then
23
+ # 6.6.3. else
24
+ internal_applicate_ifthenelse(instance, visited_refs, &block)
25
+
26
+ # 6.7.1. allOf
27
+ # 6.7.2. anyOf
28
+ # 6.7.3. oneOf
29
+ internal_applicate_someOf(instance, visited_refs, &block)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::InplaceApplication::IfThenElse
5
+ # @private
6
+ def internal_applicate_ifthenelse(instance, visited_refs, &block)
7
+ if keyword?('if')
8
+ if subschema(['if']).instance_valid?(instance)
9
+ if keyword?('then')
10
+ subschema(['then']).each_inplace_applicator_schema(instance, visited_refs: visited_refs, &block)
11
+ end
12
+ else
13
+ if keyword?('else')
14
+ subschema(['else']).each_inplace_applicator_schema(instance, visited_refs: visited_refs, &block)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::InplaceApplication::Ref
5
+ # @private
6
+ def internal_applicate_ref(instance, visited_refs, throw_done: false, &block)
7
+ if keyword?('$ref') && schema_content['$ref'].respond_to?(:to_str)
8
+ ref = schema_ref
9
+ unless visited_refs.include?(ref)
10
+ ref.deref_schema.each_inplace_applicator_schema(instance, visited_refs: visited_refs + [ref], &block)
11
+ if throw_done
12
+ throw(:jsi_application_done)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end