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
@@ -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