json_skooma 0.1.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 (141) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +22 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +107 -0
  5. data/data/draft-2019-09/README.md +60 -0
  6. data/data/draft-2019-09/hyper-schema.json +26 -0
  7. data/data/draft-2019-09/links.json +91 -0
  8. data/data/draft-2019-09/meta/applicator.json +56 -0
  9. data/data/draft-2019-09/meta/content.json +17 -0
  10. data/data/draft-2019-09/meta/core.json +57 -0
  11. data/data/draft-2019-09/meta/format.json +14 -0
  12. data/data/draft-2019-09/meta/hyper-schema.json +29 -0
  13. data/data/draft-2019-09/meta/meta-data.json +37 -0
  14. data/data/draft-2019-09/meta/validation.json +98 -0
  15. data/data/draft-2019-09/output/hyper-schema.json +62 -0
  16. data/data/draft-2019-09/output/schema.json +86 -0
  17. data/data/draft-2019-09/output/verbose-example.json +130 -0
  18. data/data/draft-2019-09/schema.json +42 -0
  19. data/data/draft-2020-12/README.md +89 -0
  20. data/data/draft-2020-12/adr/README.md +15 -0
  21. data/data/draft-2020-12/archive/hyper-schema.json +28 -0
  22. data/data/draft-2020-12/archive/links.json +93 -0
  23. data/data/draft-2020-12/archive/meta/hyper-schema.json +30 -0
  24. data/data/draft-2020-12/hyper-schema.json +27 -0
  25. data/data/draft-2020-12/links.json +85 -0
  26. data/data/draft-2020-12/meta/applicator.json +48 -0
  27. data/data/draft-2020-12/meta/content.json +17 -0
  28. data/data/draft-2020-12/meta/core.json +51 -0
  29. data/data/draft-2020-12/meta/format-annotation.json +14 -0
  30. data/data/draft-2020-12/meta/format-assertion.json +14 -0
  31. data/data/draft-2020-12/meta/hyper-schema.json +29 -0
  32. data/data/draft-2020-12/meta/meta-data.json +37 -0
  33. data/data/draft-2020-12/meta/unevaluated.json +15 -0
  34. data/data/draft-2020-12/meta/validation.json +98 -0
  35. data/data/draft-2020-12/output/hyper-schema.json +62 -0
  36. data/data/draft-2020-12/output/schema.json +96 -0
  37. data/data/draft-2020-12/output/verbose-example.json +130 -0
  38. data/data/draft-2020-12/schema.json +58 -0
  39. data/lib/json_skooma/dialects/draft201909.rb +137 -0
  40. data/lib/json_skooma/dialects/draft202012.rb +146 -0
  41. data/lib/json_skooma/formatters.rb +135 -0
  42. data/lib/json_skooma/inflector.rb +13 -0
  43. data/lib/json_skooma/json_node.rb +100 -0
  44. data/lib/json_skooma/json_pointer.rb +79 -0
  45. data/lib/json_skooma/json_schema.rb +176 -0
  46. data/lib/json_skooma/keywords/applicator/additional_properties.rb +37 -0
  47. data/lib/json_skooma/keywords/applicator/all_of.rb +25 -0
  48. data/lib/json_skooma/keywords/applicator/any_of.rb +26 -0
  49. data/lib/json_skooma/keywords/applicator/contains.rb +31 -0
  50. data/lib/json_skooma/keywords/applicator/dependent_schemas.rb +35 -0
  51. data/lib/json_skooma/keywords/applicator/else.rb +22 -0
  52. data/lib/json_skooma/keywords/applicator/if.rb +17 -0
  53. data/lib/json_skooma/keywords/applicator/items.rb +36 -0
  54. data/lib/json_skooma/keywords/applicator/not.rb +19 -0
  55. data/lib/json_skooma/keywords/applicator/one_of.rb +35 -0
  56. data/lib/json_skooma/keywords/applicator/pattern_properties.rb +46 -0
  57. data/lib/json_skooma/keywords/applicator/prefix_items.rb +31 -0
  58. data/lib/json_skooma/keywords/applicator/properties.rb +34 -0
  59. data/lib/json_skooma/keywords/applicator/property_names.rb +25 -0
  60. data/lib/json_skooma/keywords/applicator/then.rb +22 -0
  61. data/lib/json_skooma/keywords/base.rb +74 -0
  62. data/lib/json_skooma/keywords/base_annotation.rb +12 -0
  63. data/lib/json_skooma/keywords/content/content_encoding.rb +12 -0
  64. data/lib/json_skooma/keywords/content/content_media_type.rb +12 -0
  65. data/lib/json_skooma/keywords/content/content_schema.rb +19 -0
  66. data/lib/json_skooma/keywords/core/anchor.rb +22 -0
  67. data/lib/json_skooma/keywords/core/comment.rb +12 -0
  68. data/lib/json_skooma/keywords/core/defs.rb +13 -0
  69. data/lib/json_skooma/keywords/core/dynamic_anchor.rb +22 -0
  70. data/lib/json_skooma/keywords/core/dynamic_ref.rb +67 -0
  71. data/lib/json_skooma/keywords/core/id.rb +28 -0
  72. data/lib/json_skooma/keywords/core/ref.rb +35 -0
  73. data/lib/json_skooma/keywords/core/schema.rb +26 -0
  74. data/lib/json_skooma/keywords/core/vocabulary.rb +34 -0
  75. data/lib/json_skooma/keywords/draft_2019_09/additional_items.rb +40 -0
  76. data/lib/json_skooma/keywords/draft_2019_09/items.rb +41 -0
  77. data/lib/json_skooma/keywords/draft_2019_09/recursive_anchor.rb +12 -0
  78. data/lib/json_skooma/keywords/draft_2019_09/recursive_ref.rb +46 -0
  79. data/lib/json_skooma/keywords/draft_2019_09/unevaluated_items.rb +56 -0
  80. data/lib/json_skooma/keywords/format_annotation/format.rb +27 -0
  81. data/lib/json_skooma/keywords/meta_data/default.rb +11 -0
  82. data/lib/json_skooma/keywords/meta_data/deprecated.rb +11 -0
  83. data/lib/json_skooma/keywords/meta_data/description.rb +11 -0
  84. data/lib/json_skooma/keywords/meta_data/examples.rb +11 -0
  85. data/lib/json_skooma/keywords/meta_data/read_only.rb +11 -0
  86. data/lib/json_skooma/keywords/meta_data/title.rb +11 -0
  87. data/lib/json_skooma/keywords/meta_data/write_only.rb +11 -0
  88. data/lib/json_skooma/keywords/unevaluated/unevaluated_items.rb +59 -0
  89. data/lib/json_skooma/keywords/unevaluated/unevaluated_properties.rb +43 -0
  90. data/lib/json_skooma/keywords/unknown.rb +21 -0
  91. data/lib/json_skooma/keywords/validation/const.rb +17 -0
  92. data/lib/json_skooma/keywords/validation/dependent_required.rb +24 -0
  93. data/lib/json_skooma/keywords/validation/enum.rb +19 -0
  94. data/lib/json_skooma/keywords/validation/exclusive_maximum.rb +18 -0
  95. data/lib/json_skooma/keywords/validation/exclusive_minimum.rb +18 -0
  96. data/lib/json_skooma/keywords/validation/max_contains.rb +24 -0
  97. data/lib/json_skooma/keywords/validation/max_items.rb +18 -0
  98. data/lib/json_skooma/keywords/validation/max_length.rb +18 -0
  99. data/lib/json_skooma/keywords/validation/max_properties.rb +18 -0
  100. data/lib/json_skooma/keywords/validation/maximum.rb +18 -0
  101. data/lib/json_skooma/keywords/validation/min_contains.rb +31 -0
  102. data/lib/json_skooma/keywords/validation/min_items.rb +18 -0
  103. data/lib/json_skooma/keywords/validation/min_length.rb +18 -0
  104. data/lib/json_skooma/keywords/validation/min_properties.rb +18 -0
  105. data/lib/json_skooma/keywords/validation/minimum.rb +18 -0
  106. data/lib/json_skooma/keywords/validation/multiple_of.rb +20 -0
  107. data/lib/json_skooma/keywords/validation/pattern.rb +23 -0
  108. data/lib/json_skooma/keywords/validation/required.rb +19 -0
  109. data/lib/json_skooma/keywords/validation/type.rb +26 -0
  110. data/lib/json_skooma/keywords/validation/unique_items.rb +20 -0
  111. data/lib/json_skooma/keywords/value_schemas.rb +87 -0
  112. data/lib/json_skooma/memoizable.rb +21 -0
  113. data/lib/json_skooma/metaschema.rb +32 -0
  114. data/lib/json_skooma/registry.rb +130 -0
  115. data/lib/json_skooma/result.rb +125 -0
  116. data/lib/json_skooma/sources.rb +55 -0
  117. data/lib/json_skooma/validators/base.rb +31 -0
  118. data/lib/json_skooma/validators/date.rb +18 -0
  119. data/lib/json_skooma/validators/date_time.rb +24 -0
  120. data/lib/json_skooma/validators/duration.rb +25 -0
  121. data/lib/json_skooma/validators/email.rb +36 -0
  122. data/lib/json_skooma/validators/hostname.rb +17 -0
  123. data/lib/json_skooma/validators/idn_email.rb +30 -0
  124. data/lib/json_skooma/validators/idn_hostname.rb +15 -0
  125. data/lib/json_skooma/validators/ipv4.rb +20 -0
  126. data/lib/json_skooma/validators/ipv6.rb +16 -0
  127. data/lib/json_skooma/validators/iri.rb +47 -0
  128. data/lib/json_skooma/validators/iri_reference.rb +15 -0
  129. data/lib/json_skooma/validators/json_pointer.rb +19 -0
  130. data/lib/json_skooma/validators/regex.rb +15 -0
  131. data/lib/json_skooma/validators/relative_json_pointer.rb +18 -0
  132. data/lib/json_skooma/validators/time.rb +32 -0
  133. data/lib/json_skooma/validators/uri.rb +60 -0
  134. data/lib/json_skooma/validators/uri_reference.rb +15 -0
  135. data/lib/json_skooma/validators/uri_template.rb +26 -0
  136. data/lib/json_skooma/validators/uuid.rb +15 -0
  137. data/lib/json_skooma/validators.rb +17 -0
  138. data/lib/json_skooma/version.rb +5 -0
  139. data/lib/json_skooma/vocabulary.rb +12 -0
  140. data/lib/json_skooma.rb +39 -0
  141. metadata +244 -0
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Applicator
6
+ class PrefixItems < Base
7
+ self.key = "prefixItems"
8
+ self.instance_types = "array"
9
+ self.value_schema = :array_of_schemas
10
+
11
+ def evaluate(instance, result)
12
+ annotation = nil
13
+ error = []
14
+ instance.take(json.length).each_with_index do |item, index|
15
+ annotation = index
16
+ result.call(item, index.to_s) do |subresult|
17
+ unless json[index].evaluate(item, subresult).passed?
18
+ error << index
19
+ end
20
+ end
21
+ end
22
+ return result.failure(error) unless error.empty?
23
+ return if annotation.nil?
24
+
25
+ annotation = true if annotation == instance.length - 1
26
+ result.annotate(annotation)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Applicator
6
+ class Properties < Base
7
+ self.key = "properties"
8
+ self.instance_types = "object"
9
+ self.value_schema = :object_of_schemas
10
+
11
+ def evaluate(instance, result)
12
+ annotation = []
13
+ err_names = []
14
+ instance.each do |name, item|
15
+ next unless json.value.key?(name)
16
+
17
+ result.call(item, name) do |subresult|
18
+ json[name].evaluate(item, subresult)
19
+ if subresult.passed?
20
+ annotation << name
21
+ else
22
+ err_names << name
23
+ end
24
+ end
25
+ end
26
+
27
+ return result.annotate(annotation) if err_names.empty?
28
+
29
+ result.failure("Properties #{err_names} are invalid")
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Applicator
6
+ class PropertyNames < Base
7
+ self.key = "propertyNames"
8
+ self.instance_types = "object"
9
+ self.value_schema = :schema
10
+
11
+ def evaluate(instance, result)
12
+ error = []
13
+ instance.each do |name,|
14
+ next if json.evaluate(JSONNode.new(name, key: name, parent: instance), result).passed?
15
+
16
+ error << name
17
+ result.success
18
+ end
19
+
20
+ result.failure(error) if error.any?
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Applicator
6
+ class Then < Base
7
+ self.key = "then"
8
+ self.value_schema = :schema
9
+ self.depends_on = %w[if]
10
+
11
+ def evaluate(instance, result)
12
+ condition = result.sibling(instance, "if")
13
+ if condition&.valid?
14
+ json.evaluate(instance, result)
15
+ else
16
+ result.discard
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ class Base
6
+ attr_reader :json, :parent_schema
7
+
8
+ class << self
9
+ attr_accessor :static, :depends_on, :schema_value_class
10
+ attr_writer :key
11
+ attr_reader :instance_types
12
+
13
+ def key
14
+ @key or raise "Key must be defined"
15
+ end
16
+
17
+ def instance_types=(value)
18
+ @instance_types = Array(value)
19
+ end
20
+
21
+ def value_schema=(schema_keys)
22
+ Array(schema_keys).each do |k|
23
+ prepend ValueSchemas[k]
24
+ end
25
+ end
26
+
27
+ def set_defaults
28
+ @instance_types = %w[boolean object array string number null]
29
+ @static = false
30
+ @depends_on = []
31
+ end
32
+
33
+ def inherited(subclass)
34
+ subclass.set_defaults
35
+ end
36
+ end
37
+
38
+ def initialize(parent_schema, value)
39
+ @parent_schema = parent_schema
40
+ @json = wrap_value(value)
41
+ end
42
+
43
+ def key
44
+ self.class.key
45
+ end
46
+
47
+ def static
48
+ self.class.static
49
+ end
50
+
51
+ def instance_types
52
+ self.class.instance_types
53
+ end
54
+
55
+ def evaluate(instance, result)
56
+ end
57
+
58
+ def each_schema(&block)
59
+ end
60
+
61
+ def resolve
62
+ each_schema do |schema|
63
+ schema.resolve_references if schema.respond_to?(:resolve_references)
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def wrap_value(value)
70
+ JSONNode.new(value, key: key, parent: parent_schema)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ class BaseAnnotation < Base
6
+ def evaluate(instance, result)
7
+ result.annotate(json.value)
8
+ result.skip_assertion
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Content
6
+ class ContentEncoding < BaseAnnotation
7
+ self.key = "contentEncoding"
8
+ self.instance_types = %w[string]
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Content
6
+ class ContentMediaType < BaseAnnotation
7
+ self.key = "contentMediaType"
8
+ self.instance_types = %w[string]
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Content
6
+ class ContentSchema < BaseAnnotation
7
+ self.key = "contentSchema"
8
+ self.instance_types = %w[string]
9
+ self.depends_on = %w[contentMediaType]
10
+
11
+ def evaluate(instance, result)
12
+ return super if result.sibling(instance, "contentMediaType")
13
+
14
+ result.discard
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Core
6
+ class Anchor < Base
7
+ self.key = "$anchor"
8
+ self.static = true
9
+
10
+ def initialize(parent_schema, value)
11
+ super
12
+
13
+ base_uri = parent_schema.base_uri
14
+ raise Error, "No base URI for `$anchor` value `#{value}`" if base_uri.nil?
15
+
16
+ uri = base_uri.dup.tap { |u| u.fragment = value }
17
+ parent_schema.registry.add_schema(uri, parent_schema, cache_id: parent_schema.cache_id)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Core
6
+ class Comment < Base
7
+ self.key = "$comment"
8
+ self.static = true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Core
6
+ class Defs < Base
7
+ self.key = "$defs"
8
+ self.static = true
9
+ self.value_schema = :object_of_schemas
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Core
6
+ class DynamicAnchor < Base
7
+ self.key = "$dynamicAnchor"
8
+ self.static = true
9
+
10
+ def initialize(parent_schema, value)
11
+ super
12
+
13
+ base_uri = parent_schema.base_uri
14
+ raise Error, "No base URI for `$dynamicAnchor` value `#{value}`" if base_uri.nil?
15
+
16
+ uri = base_uri.dup.tap { |u| u.fragment = value }
17
+ parent_schema.registry.add_schema(uri, parent_schema, cache_id: parent_schema.cache_id)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Core
6
+ class DynamicRef < Base
7
+ self.key = "$dynamicRef"
8
+
9
+ def initialize(parent_schema, value)
10
+ super
11
+ @fragment = URI.parse(value).fragment
12
+ @dynamic = false
13
+ end
14
+
15
+ def resolve
16
+ uri = URI.parse(json)
17
+ if uri.relative?
18
+ base_uri = parent_schema.base_uri
19
+ if base_uri.nil?
20
+ raise "No base URI against which to resolve the `$dynamicRef` value `#{uri}`"
21
+ end
22
+
23
+ uri = base_uri + uri
24
+ end
25
+ @ref_schema = parent_schema.registry.schema(
26
+ uri,
27
+ metaschema_uri: parent_schema.metaschema_uri,
28
+ cache_id: parent_schema.cache_id
29
+ )
30
+ dynamic_anchor = @ref_schema["$dynamicAnchor"]
31
+ @dynamic = dynamic_anchor == @fragment
32
+ end
33
+
34
+ def evaluate(instance, result)
35
+ ref_schema = @ref_schema
36
+ if @dynamic
37
+ target = result
38
+ checked_uris = Set.new
39
+ while target
40
+ base_uri = target.schema.base_uri
41
+ if base_uri && !checked_uris.include?(base_uri)
42
+ checked_uris << base_uri
43
+ target_uri = base_uri.dup.tap { |u| u.fragment = @fragment }
44
+
45
+ begin
46
+ found_schema = parent_schema.registry.schema(
47
+ target_uri,
48
+ cache_id: parent_schema.cache_id
49
+ )
50
+ dynamic_anchor = found_schema["$dynamicAnchor"]
51
+ ref_schema = found_schema if dynamic_anchor == @fragment
52
+ rescue RegistryError, Hana::Pointer::FormatError
53
+ # do nothing
54
+ end
55
+ end
56
+
57
+ target = target.parent
58
+ end
59
+ end
60
+
61
+ ref_schema.evaluate(instance, result)
62
+ result.ref_schema = ref_schema
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Core
6
+ class Id < Base
7
+ self.key = "$id"
8
+ self.static = true
9
+
10
+ def initialize(parent_schema, value)
11
+ super
12
+
13
+ uri = URI.parse(value)
14
+ if uri.relative?
15
+ base_uri = parent_schema.base_uri
16
+ if base_uri
17
+ uri = base_uri + uri
18
+ else
19
+ raise "No base URI against which to resolve the `$id` value `#{value}`"
20
+ end
21
+ end
22
+
23
+ parent_schema.uri = uri
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Core
6
+ class Ref < Base
7
+ self.key = "$ref"
8
+
9
+ def resolve
10
+ uri = resolve_uri
11
+ @ref_schema = parent_schema.registry.schema(
12
+ uri,
13
+ metaschema_uri: parent_schema.metaschema_uri,
14
+ cache_id: parent_schema.cache_id
15
+ )
16
+ end
17
+
18
+ def evaluate(instance, result)
19
+ @ref_schema.evaluate(instance, result)
20
+ result.ref_schema = @ref_schema
21
+ end
22
+
23
+ private
24
+
25
+ def resolve_uri
26
+ uri = URI.parse(json)
27
+ return uri if uri.absolute?
28
+ return parent_schema.base_uri + uri if parent_schema.base_uri
29
+
30
+ raise Error, "No base URI against which to resolve the `$ref` value `#{uri}`"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Core
6
+ class Schema < Base
7
+ self.key = "$schema"
8
+ self.static = true
9
+
10
+ def initialize(parent_schema, value)
11
+ super
12
+
13
+ # todo: validate URI.validate(require_scheme=True, require_normalized=True)
14
+ uri = URI.parse(value)
15
+ parent_schema.metaschema_uri = uri
16
+
17
+ if parent_schema.is_a?(Metaschema)
18
+ metaschema = parent_schema.registry.metaschema(uri)
19
+ parent_schema.core_vocabulary ||= metaschema.core_vocabulary
20
+ parent_schema.default_vocabularies ||= metaschema.default_vocabularies
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Core
6
+ class Vocabulary < Base
7
+ self.key = "$vocabulary"
8
+ self.static = true
9
+
10
+ def initialize(parent_schema, value)
11
+ super
12
+
13
+ return unless parent_schema.is_a?(Metaschema)
14
+
15
+ core_vocab_uri = parent_schema.core_vocabulary&.uri.to_s
16
+ if core_vocab_uri.empty? || !value[core_vocab_uri]
17
+ raise Error, "The `$vocabulary` keyword must list the core vocabulary with a value of true"
18
+ end
19
+
20
+ value.each do |vocabulary_uri, vocabulary_required|
21
+ uri = URI.parse(vocabulary_uri)
22
+
23
+ vocabulary = parent_schema.registry.vocabulary(uri)
24
+ parent_schema.kw_classes.merge!(vocabulary.kw_classes)
25
+ rescue RegistryError
26
+ if vocabulary_required
27
+ raise Error, "The metaschema requires an unrecognized vocabulary #{vocabulary_uri}"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Draft201909
6
+ class AdditionalItems < Base
7
+ self.key = "additionalItems"
8
+ self.instance_types = "array"
9
+ self.value_schema = :schema
10
+ self.depends_on = %w[items]
11
+
12
+ def evaluate(instance, result)
13
+ items = result.sibling(instance, "items")
14
+ return result.discard unless items&.annotation.is_a?(Integer)
15
+
16
+ annotation = nil
17
+ error = []
18
+
19
+ instance.each_with_index do |item, index|
20
+ next if items.annotation >= index
21
+
22
+ if json.evaluate(item, result).passed?
23
+ annotation = true
24
+ else
25
+ error << index
26
+ # reset to passed for the next iteration
27
+ result.success
28
+ end
29
+ end
30
+
31
+ if error.any?
32
+ result.failure(error)
33
+ else
34
+ result.annotate(annotation)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Draft201909
6
+ class Items < Base
7
+ self.key = "items"
8
+ self.instance_types = %w[array]
9
+ self.value_schema = [:schema, :array_of_schemas]
10
+
11
+ def evaluate(instance, result)
12
+ return if instance.empty?
13
+
14
+ if json.type == "boolean"
15
+ json.evaluate(instance, result)
16
+ elsif json.is_a?(JSONSchema)
17
+ instance.each do |item|
18
+ json.evaluate(item, result)
19
+ end
20
+ result.annotate(true) if result.passed?
21
+ elsif json.type == "array"
22
+ annotation = nil
23
+ error = []
24
+
25
+ instance.take(json.length).each_with_index do |item, index|
26
+ annotation = index
27
+ result.call(item, index.to_s) do |subresult|
28
+ unless json[index].evaluate(item, subresult).passed?
29
+ error << index
30
+ end
31
+ end
32
+ end
33
+ return result.failure(error) unless error.empty?
34
+
35
+ result.annotate(annotation)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Draft201909
6
+ class RecursiveAnchor < Base
7
+ self.key = "$recursiveAnchor"
8
+ self.static = true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONSkooma
4
+ module Keywords
5
+ module Draft201909
6
+ class RecursiveRef < Base
7
+ self.key = "$recursiveRef"
8
+
9
+ def initialize(parent_schema, value)
10
+ super
11
+ raise "`$recursiveRef` may only take the value `#`" if value != "#"
12
+ end
13
+
14
+ def resolve
15
+ base_uri = parent_schema.base_uri
16
+ if base_uri.nil?
17
+ raise "No base URI against which to resolve the `$recursiveRef`"
18
+ end
19
+
20
+ @ref_schema = parent_schema.registry.schema(
21
+ base_uri,
22
+ metaschema_uri: parent_schema.metaschema_uri,
23
+ cache_id: parent_schema.cache_id
24
+ )
25
+ end
26
+
27
+ def evaluate(instance, result)
28
+ ref_schema = @ref_schema
29
+ recursive_anchor = ref_schema["$recursiveAnchor"]
30
+ if recursive_anchor&.value
31
+ target = result
32
+ while target
33
+ base_anchor = target.schema["$recursiveAnchor"]
34
+ if base_anchor&.value
35
+ ref_schema = target.schema
36
+ end
37
+ target = target.parent
38
+ end
39
+ end
40
+ ref_schema.evaluate(instance, result)
41
+ result.ref_schema = ref_schema
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end