jsi-dev 0.0.0.pre.commonmarker

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::InplaceApplication::SomeOf
5
+ # @private
6
+ def internal_applicate_someOf(instance, visited_refs, &block)
7
+ if keyword?('allOf') && schema_content['allOf'].respond_to?(:to_ary)
8
+ schema_content['allOf'].each_index do |i|
9
+ subschema(['allOf', i]).each_inplace_applicator_schema(instance, visited_refs: visited_refs, &block)
10
+ end
11
+ end
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)
24
+ end
25
+ end
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 ? n : 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
36
+ end
37
+
38
+ applicator_idxs.each do |i|
39
+ subschema(['oneOf', i]).each_inplace_applicator_schema(instance, visited_refs: visited_refs, &block)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application::InplaceApplication
5
+ autoload :Draft04, 'jsi/schema/application/inplace_application/draft04'
6
+ autoload :Draft06, 'jsi/schema/application/inplace_application/draft06'
7
+ autoload :Draft07, 'jsi/schema/application/inplace_application/draft07'
8
+
9
+ autoload :Ref, 'jsi/schema/application/inplace_application/ref'
10
+ autoload :SomeOf, 'jsi/schema/application/inplace_application/someof'
11
+ autoload :IfThenElse, 'jsi/schema/application/inplace_application/ifthenelse'
12
+ autoload :Dependencies, 'jsi/schema/application/inplace_application/dependencies'
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Application
5
+ autoload :InplaceApplication, 'jsi/schema/application/inplace_application'
6
+ autoload :ChildApplication, 'jsi/schema/application/child_application'
7
+
8
+ autoload :Draft04, 'jsi/schema/application/draft04'
9
+ autoload :Draft06, 'jsi/schema/application/draft06'
10
+ autoload :Draft07, 'jsi/schema/application/draft07'
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema
5
+ module Draft04
6
+ include OldId
7
+ include IdWithAnchor
8
+ include IntegerDisallows0Fraction
9
+ include Schema::Application::Draft04
10
+ include Schema::Validation::Draft04
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema
5
+ module Draft06
6
+ include BigMoneyId
7
+ include IdWithAnchor
8
+ include IntegerAllows0Fraction
9
+ include Schema::Application::Draft06
10
+ include Schema::Validation::Draft06
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema
5
+ module Draft07
6
+ include BigMoneyId
7
+ include IdWithAnchor
8
+ include IntegerAllows0Fraction
9
+ include Schema::Application::Draft07
10
+ include Schema::Validation::Draft07
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema
5
+ Issue = Util::AttrStruct[*%w(
6
+ level
7
+ message
8
+ keyword
9
+ schema
10
+ )]
11
+
12
+ # an issue or problem with a schema.
13
+ #
14
+ # when the `level` is `:error`, the schema is invalid according to its specification,
15
+ # violating some "MUST" or "MUST NOT".
16
+ #
17
+ # when the `level` is `:warning`, the issue does not mean the schema is invalid, but contains something
18
+ # that does not make sense. for example, specifying `additionalItems` without an adjacent `items` has
19
+ # no effect (in specifications which define `additionalItems`), but is not an invalid schema.
20
+ #
21
+ # @!attribute level
22
+ # :error or :warning
23
+ # @return [Symbol]
24
+ # @!attribute message
25
+ # a message describing the issue
26
+ # @return [String]
27
+ # @!attribute keyword
28
+ # the keyword of the schema that has an issue
29
+ # @return [String]
30
+ # @!attribute schema
31
+ # the schema that has an issue
32
+ # @return [JSI::Schema]
33
+ class Issue
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ # A JSI::Schema::Ref is a reference to a schema identified by a URI, typically from
5
+ # a `$ref` keyword of a schema.
6
+ class Schema::Ref
7
+ # @param ref [String] A reference URI - typically the `$ref` value of the ref_schema
8
+ # @param ref_schema [JSI::Schema] A schema from which the reference originated.
9
+ #
10
+ # If the ref URI consists of only a fragment, it is resolved from the `ref_schema`'s
11
+ # {Schema#schema_resource_root}. Otherwise the resource is found in the `ref_schema`'s
12
+ # {SchemaAncestorNode#jsi_schema_registry #jsi_schema_registry} (and any fragment is resolved from there).
13
+ # @param schema_registry [SchemaRegistry] The registry in which the resource this ref refers to will be found.
14
+ # This should only be specified in the absence of a `ref_schema`.
15
+ # If neither is specified, {JSI.schema_registry} is used.
16
+ def initialize(ref, ref_schema: nil, schema_registry: nil)
17
+ raise(ArgumentError, "ref is not a string") unless ref.respond_to?(:to_str)
18
+ @ref = ref
19
+ @ref_uri = Util.uri(ref)
20
+ @ref_schema = ref_schema ? Schema.ensure_schema(ref_schema) : nil
21
+ @schema_registry = schema_registry || (ref_schema ? ref_schema.jsi_schema_registry : JSI.schema_registry)
22
+ @deref_schema = nil
23
+ end
24
+
25
+ # @return [String]
26
+ attr_reader :ref
27
+
28
+ # @return [Addressable::URI]
29
+ attr_reader :ref_uri
30
+
31
+ # @return [Schema, nil]
32
+ attr_reader :ref_schema
33
+
34
+ # @return [SchemaRegistry, nil]
35
+ attr_reader(:schema_registry)
36
+
37
+ # finds the schema this ref points to
38
+ # @return [JSI::Schema]
39
+ # @raise [JSI::Schema::NotASchemaError] when the thing this ref points to is not a schema
40
+ # @raise [JSI::Schema::ReferenceError] when this reference cannot be resolved
41
+ def deref_schema
42
+ return @deref_schema if @deref_schema
43
+
44
+ schema_resource_root = nil
45
+ check_schema_resource_root = -> {
46
+ unless schema_resource_root
47
+ raise(Schema::ReferenceError, [
48
+ "cannot find schema by ref: #{ref}",
49
+ ("from: #{ref_schema.pretty_inspect.chomp}" if ref_schema),
50
+ ].compact.join("\n"))
51
+ end
52
+ }
53
+
54
+ ref_uri_nofrag = ref_uri.merge(fragment: nil).freeze
55
+
56
+ if ref_uri_nofrag.empty?
57
+ unless ref_schema
58
+ raise(Schema::ReferenceError, [
59
+ "cannot find schema by ref: #{ref}",
60
+ "with no ref schema",
61
+ ].join("\n"))
62
+ end
63
+
64
+ # the URI only consists of a fragment (or is empty).
65
+ # for a fragment pointer, resolve using Schema#resource_root_subschema on the ref_schema.
66
+ # for a fragment anchor, bootstrap does not support anchors; otherwise use the ref_schema's schema_resource_root.
67
+ schema_resource_root = ref_schema.is_a?(MetaschemaNode::BootstrapSchema) ? nil : ref_schema.schema_resource_root
68
+ resolve_fragment_ptr = ref_schema.method(:resource_root_subschema)
69
+ else
70
+ # find the schema_resource_root from the non-fragment URI. we will resolve any fragment, either pointer or anchor, from there.
71
+
72
+ if ref_uri_nofrag.absolute?
73
+ ref_abs_uri = ref_uri_nofrag
74
+ elsif ref_schema && ref_schema.jsi_resource_ancestor_uri
75
+ ref_abs_uri = ref_schema.jsi_resource_ancestor_uri.join(ref_uri_nofrag).freeze
76
+ else
77
+ ref_abs_uri = nil
78
+ end
79
+ if ref_abs_uri
80
+ unless schema_registry
81
+ raise(Schema::ReferenceError, [
82
+ "could not resolve remote ref with no schema_registry specified",
83
+ "ref URI: #{ref_uri.to_s}",
84
+ ("from: #{ref_schema.pretty_inspect.chomp}" if ref_schema),
85
+ ].compact.join("\n"))
86
+ end
87
+ schema_resource_root = schema_registry.find(ref_abs_uri)
88
+ end
89
+
90
+ unless schema_resource_root
91
+ # HAX for how google does refs and ids
92
+ if ref_schema && ref_schema.jsi_document.respond_to?(:to_hash) && ref_schema.jsi_document['schemas'].respond_to?(:to_hash)
93
+ ref_schema.jsi_document['schemas'].each do |k, v|
94
+ if Addressable::URI.parse(v['id']) == ref_uri_nofrag
95
+ schema_resource_root = ref_schema.resource_root_subschema(['schemas', k])
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ check_schema_resource_root.call
102
+
103
+ if schema_resource_root.is_a?(Schema)
104
+ resolve_fragment_ptr = schema_resource_root.method(:resource_root_subschema)
105
+ else
106
+ # Note: Schema#resource_root_subschema will reinstantiate nonschemas as schemas.
107
+ # not implemented for remote refs when the schema_resource_root is not a schema.
108
+ resolve_fragment_ptr = -> (ptr) { schema_resource_root.jsi_descendent_node(ptr) }
109
+ end
110
+ end
111
+
112
+ fragment = ref_uri.fragment
113
+
114
+ if fragment
115
+ begin
116
+ ptr_from_fragment = Ptr.from_fragment(fragment)
117
+ rescue Ptr::PointerSyntaxError
118
+ end
119
+ end
120
+
121
+ if ptr_from_fragment
122
+ begin
123
+ result_schema = resolve_fragment_ptr.call(ptr_from_fragment)
124
+ rescue Ptr::ResolutionError
125
+ raise(Schema::ReferenceError, [
126
+ "could not resolve pointer: #{ptr_from_fragment.pointer.inspect}",
127
+ ("from: #{ref_schema.pretty_inspect.chomp}" if ref_schema),
128
+ ("in schema resource root: #{schema_resource_root.pretty_inspect.chomp}" if schema_resource_root),
129
+ ].compact.join("\n"))
130
+ end
131
+ elsif fragment.nil?
132
+ check_schema_resource_root.call
133
+ result_schema = schema_resource_root
134
+ else
135
+ check_schema_resource_root.call
136
+
137
+ # find an anchor that resembles the fragment
138
+ result_schemas = schema_resource_root.jsi_anchor_subschemas(fragment)
139
+
140
+ if result_schemas.size == 1
141
+ result_schema = result_schemas.first
142
+ elsif result_schemas.size == 0
143
+ raise(Schema::ReferenceError, [
144
+ "could not find schema by fragment: #{fragment.inspect}",
145
+ "in schema resource root: #{schema_resource_root.pretty_inspect.chomp}",
146
+ ].join("\n"))
147
+ else
148
+ raise(Schema::ReferenceError, [
149
+ "found multiple schemas for plain name fragment #{fragment.inspect}:",
150
+ *result_schemas.map { |s| s.pretty_inspect.chomp },
151
+ ].join("\n"))
152
+ end
153
+ end
154
+
155
+ Schema.ensure_schema(result_schema, msg: "object identified by uri #{ref} is not a schema:")
156
+ return @deref_schema = result_schema
157
+ end
158
+
159
+ # @return [String]
160
+ def inspect
161
+ -%Q(\#<#{self.class.name} #{ref}>)
162
+ end
163
+
164
+ alias_method :to_s, :inspect
165
+
166
+ # pretty-prints a representation of self to the given printer
167
+ # @return [void]
168
+ def pretty_print(q)
169
+ q.text '#<'
170
+ q.text self.class.name
171
+ q.text ' '
172
+ q.text ref
173
+ q.text '>'
174
+ end
175
+
176
+ # see {Util::Private::FingerprintHash}
177
+ # @api private
178
+ def jsi_fingerprint
179
+ {class: self.class, ref: ref, ref_schema: ref_schema}
180
+ end
181
+ include Util::FingerprintHash
182
+ end
183
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ # a node in a document which may contain a schema somewhere within is extended with SchemaAncestorNode, for
5
+ # tracking things necessary for a schema to function correctly
6
+ module Schema::SchemaAncestorNode
7
+ if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
8
+ def initialize(*)
9
+ super
10
+ jsi_schema_ancestor_node_initialize
11
+ end
12
+ else
13
+ def initialize(*, **)
14
+ super
15
+ jsi_schema_ancestor_node_initialize
16
+ end
17
+ end
18
+
19
+ # the base URI used to resolve the ids of schemas at or below this JSI.
20
+ # this is always an absolute URI (with no fragment).
21
+ # this may be the absolute schema URI of a parent schema or the URI from which the document was retrieved.
22
+ # @api private
23
+ # @return [Addressable::URI, nil]
24
+ attr_reader :jsi_schema_base_uri
25
+
26
+ # resources which are ancestors of this JSI in the document. this does not include self.
27
+ # @api private
28
+ # @return [Array<JSI::Schema>]
29
+ attr_reader :jsi_schema_resource_ancestors
30
+
31
+ # See {SchemaSet#new_jsi} param `schema_registry`
32
+ # @return [SchemaRegistry]
33
+ attr_reader(:jsi_schema_registry)
34
+
35
+ # the URI of the resource containing this node.
36
+ # this is always an absolute URI (with no fragment).
37
+ # if this node is a schema with an id, this is its absolute URI; otherwise a parent resource's URI,
38
+ # or nil if not contained by a resource with a URI.
39
+ # @return [Addressable::URI, nil]
40
+ def jsi_resource_ancestor_uri
41
+ (is_a?(Schema) && schema_absolute_uri) || jsi_schema_base_uri
42
+ end
43
+
44
+ # The schema at or below this node with the given anchor.
45
+ # If no schema has that anchor (or multiple schemas do, incorrectly), nil.
46
+ #
47
+ # @return [JSI::Schema, nil]
48
+ def jsi_anchor_subschema(anchor)
49
+ subschemas = @anchor_subschemas_map[anchor: anchor]
50
+ if subschemas.size == 1
51
+ subschemas.first
52
+ else
53
+ nil
54
+ end
55
+ end
56
+
57
+ # All schemas at or below this node with the given anchor.
58
+ #
59
+ # @return [Set<JSI::Schema>]
60
+ def jsi_anchor_subschemas(anchor)
61
+ @anchor_subschemas_map[anchor: anchor]
62
+ end
63
+
64
+ private
65
+
66
+ def jsi_schema_ancestor_node_initialize
67
+ @anchor_subschemas_map = jsi_memomap(&method(:jsi_anchor_subschemas_compute))
68
+ end
69
+
70
+ attr_writer :jsi_document
71
+
72
+ def jsi_ptr=(jsi_ptr)
73
+ #chkbug raise(Bug, "jsi_ptr not #{Ptr}: #{jsi_ptr}") unless jsi_ptr.is_a?(Ptr)
74
+ @jsi_ptr = jsi_ptr
75
+ end
76
+
77
+ def jsi_schema_base_uri=(jsi_schema_base_uri)
78
+ #chkbug raise(Bug) if jsi_schema_base_uri && !jsi_schema_base_uri.is_a?(Addressable::URI)
79
+ #chkbug raise(Bug) if jsi_schema_base_uri && !jsi_schema_base_uri.absolute?
80
+ #chkbug raise(Bug) if jsi_schema_base_uri && jsi_schema_base_uri.fragment
81
+
82
+ @jsi_schema_base_uri = jsi_schema_base_uri
83
+ end
84
+
85
+ def jsi_schema_resource_ancestors=(jsi_schema_resource_ancestors)
86
+ #chkbug raise(Bug) unless jsi_schema_resource_ancestors.respond_to?(:to_ary)
87
+ #chkbug jsi_schema_resource_ancestors.each { |a| Schema.ensure_schema(a) }
88
+ #chkbug # sanity check the ancestors are in order
89
+ #chkbug last_anc_ptr = nil
90
+ #chkbug jsi_schema_resource_ancestors.each do |anc|
91
+ #chkbug if last_anc_ptr.nil?
92
+ #chkbug # pass
93
+ #chkbug elsif last_anc_ptr == anc.jsi_ptr
94
+ #chkbug raise(Bug, "duplicate ancestors in #{jsi_schema_resource_ancestors.pretty_inspect}")
95
+ #chkbug elsif !last_anc_ptr.contains?(anc.jsi_ptr)
96
+ #chkbug raise(Bug, "ancestor ptr #{anc.jsi_ptr} not contained by previous: #{last_anc_ptr} in #{jsi_schema_resource_ancestors.pretty_inspect}")
97
+ #chkbug end
98
+ #chkbug if anc.jsi_ptr == jsi_ptr
99
+ #chkbug raise(Bug, "ancestor is self")
100
+ #chkbug elsif !anc.jsi_ptr.contains?(jsi_ptr)
101
+ #chkbug raise(Bug, "ancestor does not contain self")
102
+ #chkbug end
103
+ #chkbug last_anc_ptr = anc.jsi_ptr
104
+ #chkbug end
105
+
106
+ @jsi_schema_resource_ancestors = jsi_schema_resource_ancestors
107
+ end
108
+
109
+ attr_writer(:jsi_schema_registry)
110
+
111
+ def jsi_anchor_subschemas_compute(anchor: )
112
+ jsi_each_descendent_node.select do |node|
113
+ node.is_a?(Schema) && node.respond_to?(:anchor) && node.anchor == anchor
114
+ end.to_set.freeze
115
+ end
116
+
117
+ # @return [Util::MemoMap]
118
+ def jsi_memomap(**options, &block)
119
+ Util::MemoMap::Mutable.new(**options, &block)
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Validation::ArrayLength
5
+ # @private
6
+ def internal_validate_maxItems(result_builder)
7
+ if keyword?('maxItems')
8
+ value = schema_content['maxItems']
9
+ # The value of this keyword MUST be a non-negative integer.
10
+ if internal_integer?(value) && value >= 0
11
+ if result_builder.instance.respond_to?(:to_ary)
12
+ # An array instance is valid against "maxItems" if its size is less than, or equal to, the value of this keyword.
13
+ result_builder.validate(
14
+ result_builder.instance.to_ary.size <= value,
15
+ 'instance array size is greater than `maxItems` value',
16
+ keyword: 'maxItems',
17
+ )
18
+ end
19
+ else
20
+ result_builder.schema_error('`maxItems` is not a non-negative integer', 'maxItems')
21
+ end
22
+ end
23
+ end
24
+
25
+ # @private
26
+ def internal_validate_minItems(result_builder)
27
+ if keyword?('minItems')
28
+ value = schema_content['minItems']
29
+ # The value of this keyword MUST be a non-negative integer.
30
+ if internal_integer?(value) && value >= 0
31
+ if result_builder.instance.respond_to?(:to_ary)
32
+ # An array instance is valid against "minItems" if its size is greater than, or equal to, the value of this keyword.
33
+ result_builder.validate(
34
+ result_builder.instance.to_ary.size >= value,
35
+ 'instance array size is less than `minItems` value',
36
+ keyword: 'minItems',
37
+ )
38
+ end
39
+ else
40
+ result_builder.schema_error('`minItems` is not a non-negative integer', 'minItems')
41
+ end
42
+ end
43
+ end
44
+ end
45
+ module Schema::Validation::UniqueItems
46
+ # @private
47
+ def internal_validate_uniqueItems(result_builder)
48
+ if keyword?('uniqueItems')
49
+ value = schema_content['uniqueItems']
50
+ # The value of this keyword MUST be a boolean.
51
+ if value == false
52
+ # If this keyword has boolean value false, the instance validates successfully.
53
+ # (noop)
54
+ elsif value == true
55
+ if result_builder.instance.respond_to?(:to_ary)
56
+ # If it has boolean value true, the instance validates successfully if all of its elements are unique.
57
+ result_builder.validate(
58
+ result_builder.instance.uniq.size == result_builder.instance.size,
59
+ "instance array items' uniqueness does not match `uniqueItems` value",
60
+ keyword: 'uniqueItems',
61
+ )
62
+ end
63
+ else
64
+ result_builder.schema_error('`uniqueItems` is not a boolean', 'uniqueItems')
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Validation::Const
5
+ # @private
6
+ def internal_validate_const(result_builder)
7
+ if keyword?('const')
8
+ value = schema_content['const']
9
+ # The value of this keyword MAY be of any type, including null.
10
+ # An instance validates successfully against this keyword if its value is equal to the value of
11
+ # the keyword.
12
+ result_builder.validate(
13
+ result_builder.instance == value,
14
+ 'instance is not equal to `const` value',
15
+ keyword: 'const',
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Validation::Contains
5
+ # @private
6
+ def internal_validate_contains(result_builder)
7
+ if keyword?('contains')
8
+ # An array instance is valid against "contains" if at least one of its elements is valid against
9
+ # the given schema.
10
+ if result_builder.instance.respond_to?(:to_ary)
11
+ results = {}
12
+ result_builder.instance.each_index do |i|
13
+ results[i] = result_builder.child_subschema_validate(i, ['contains'])
14
+ end
15
+ result_builder.validate(
16
+ results.values.any?(&:valid?),
17
+ 'instance array does not contain any items valid against `contains` schema value',
18
+ keyword: 'contains',
19
+ results: results.values,
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Validation::Dependencies
5
+ # @private
6
+ def internal_validate_dependencies(result_builder)
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
+ # If the dependency value is an array, each element in the array, if
15
+ # any, MUST be a string, and MUST be unique. If the dependency key is
16
+ # a property in the instance, each of the items in the dependency value
17
+ # must be a property that exists in the instance.
18
+ if result_builder.instance.respond_to?(:to_hash) && result_builder.instance.key?(property_name)
19
+ missing_required = dependency.reject { |name| result_builder.instance.key?(name) }
20
+ # TODO include property_name / missing dependent required property names in the validation error
21
+ result_builder.validate(
22
+ missing_required.empty?,
23
+ 'instance object does not contain all dependent required property names specified by `dependencies` value',
24
+ keyword: 'dependencies',
25
+ )
26
+ end
27
+ else
28
+ # If the dependency value is a subschema, and the dependency key is a
29
+ # property in the instance, the entire instance must validate against
30
+ # the dependency value.
31
+ if result_builder.instance.respond_to?(:to_hash) && result_builder.instance.key?(property_name)
32
+ dependency_result = result_builder.inplace_subschema_validate(['dependencies', property_name])
33
+ # TODO include property_name in the validation error
34
+ result_builder.validate(
35
+ dependency_result.valid?,
36
+ 'instance object is not valid against the schema corresponding to a matched property name specified by `dependencies` value',
37
+ keyword: 'dependencies',
38
+ results: [dependency_result],
39
+ )
40
+ end
41
+ end
42
+ end
43
+ else
44
+ result_builder.schema_error('`dependencies` is not an object', 'dependencies')
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end