json_skooma 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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