jsi 0.4.0 → 0.7.0

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