jsi-dev 0.0.8 → 0.0.9

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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -4
  3. data/CHANGELOG.md +19 -0
  4. data/LICENSE.md +2 -3
  5. data/README.md +87 -43
  6. data/docs/{glossary.md → Glossary.md} +84 -52
  7. data/jsi.gemspec +1 -1
  8. data/lib/jsi/base/mutability.rb +48 -0
  9. data/lib/jsi/base/node.rb +66 -52
  10. data/lib/jsi/base.rb +592 -176
  11. data/lib/jsi/jsi_coder.rb +4 -2
  12. data/lib/jsi/metaschema_node/bootstrap_schema.rb +118 -59
  13. data/lib/jsi/metaschema_node.rb +244 -154
  14. data/lib/jsi/ptr.rb +45 -17
  15. data/lib/jsi/ref.rb +197 -0
  16. data/lib/jsi/registry.rb +311 -0
  17. data/lib/jsi/schema/cxt/child_application.rb +35 -0
  18. data/lib/jsi/schema/cxt/inplace_application.rb +37 -0
  19. data/lib/jsi/schema/cxt.rb +80 -0
  20. data/lib/jsi/schema/dialect.rb +137 -0
  21. data/lib/jsi/schema/draft04.rb +113 -5
  22. data/lib/jsi/schema/draft06.rb +123 -5
  23. data/lib/jsi/schema/draft07.rb +157 -5
  24. data/lib/jsi/schema/draft202012.rb +303 -0
  25. data/lib/jsi/schema/dynamic_anchor_map.rb +63 -0
  26. data/lib/jsi/schema/element.rb +69 -0
  27. data/lib/jsi/schema/elements/anchor.rb +13 -0
  28. data/lib/jsi/schema/elements/array_validation.rb +82 -0
  29. data/lib/jsi/schema/elements/comment.rb +10 -0
  30. data/lib/jsi/schema/{validation → elements}/const.rb +11 -7
  31. data/lib/jsi/schema/elements/contains.rb +59 -0
  32. data/lib/jsi/schema/elements/contains_minmax.rb +91 -0
  33. data/lib/jsi/schema/elements/content_encoding.rb +10 -0
  34. data/lib/jsi/schema/elements/content_media_type.rb +10 -0
  35. data/lib/jsi/schema/elements/content_schema.rb +16 -0
  36. data/lib/jsi/schema/elements/default.rb +11 -0
  37. data/lib/jsi/schema/elements/definitions.rb +19 -0
  38. data/lib/jsi/schema/elements/dependencies.rb +99 -0
  39. data/lib/jsi/schema/elements/dependent_required.rb +49 -0
  40. data/lib/jsi/schema/elements/dependent_schemas.rb +69 -0
  41. data/lib/jsi/schema/elements/dynamic_ref.rb +69 -0
  42. data/lib/jsi/schema/elements/enum.rb +26 -0
  43. data/lib/jsi/schema/elements/examples.rb +10 -0
  44. data/lib/jsi/schema/elements/format.rb +10 -0
  45. data/lib/jsi/schema/elements/id.rb +30 -0
  46. data/lib/jsi/schema/elements/if_then_else.rb +82 -0
  47. data/lib/jsi/schema/elements/info_bool.rb +10 -0
  48. data/lib/jsi/schema/elements/info_string.rb +10 -0
  49. data/lib/jsi/schema/elements/items.rb +93 -0
  50. data/lib/jsi/schema/elements/items_prefixed.rb +96 -0
  51. data/lib/jsi/schema/elements/not.rb +31 -0
  52. data/lib/jsi/schema/elements/numeric.rb +137 -0
  53. data/lib/jsi/schema/elements/numeric_draft04.rb +77 -0
  54. data/lib/jsi/schema/elements/object_validation.rb +55 -0
  55. data/lib/jsi/schema/elements/pattern.rb +35 -0
  56. data/lib/jsi/schema/elements/properties.rb +145 -0
  57. data/lib/jsi/schema/elements/property_names.rb +48 -0
  58. data/lib/jsi/schema/elements/ref.rb +62 -0
  59. data/lib/jsi/schema/elements/required.rb +34 -0
  60. data/lib/jsi/schema/elements/self.rb +24 -0
  61. data/lib/jsi/schema/elements/some_of.rb +180 -0
  62. data/lib/jsi/schema/elements/string_validation.rb +57 -0
  63. data/lib/jsi/schema/elements/type.rb +43 -0
  64. data/lib/jsi/schema/elements/unevaluated_items.rb +54 -0
  65. data/lib/jsi/schema/elements/unevaluated_properties.rb +54 -0
  66. data/lib/jsi/schema/elements/xschema.rb +10 -0
  67. data/lib/jsi/schema/elements/xvocabulary.rb +10 -0
  68. data/lib/jsi/schema/elements.rb +101 -0
  69. data/lib/jsi/schema/issue.rb +3 -4
  70. data/lib/jsi/schema/schema_ancestor_node.rb +105 -52
  71. data/lib/jsi/schema/vocabulary.rb +36 -0
  72. data/lib/jsi/schema.rb +598 -383
  73. data/lib/jsi/schema_classes.rb +195 -141
  74. data/lib/jsi/schema_set.rb +85 -128
  75. data/lib/jsi/set.rb +23 -0
  76. data/lib/jsi/simple_wrap.rb +14 -17
  77. data/lib/jsi/struct.rb +57 -0
  78. data/lib/jsi/uri.rb +40 -0
  79. data/lib/jsi/util/private/memo_map.rb +9 -13
  80. data/lib/jsi/util/private.rb +59 -31
  81. data/lib/jsi/util/typelike.rb +19 -60
  82. data/lib/jsi/util.rb +53 -34
  83. data/lib/jsi/validation/error.rb +45 -2
  84. data/lib/jsi/validation/result.rb +121 -90
  85. data/lib/jsi/validation.rb +1 -6
  86. data/lib/jsi/version.rb +1 -1
  87. data/lib/jsi.rb +170 -36
  88. data/lib/schemas/json-schema.org/draft/2020-12/schema.rb +62 -0
  89. data/lib/schemas/json-schema.org/draft-04/schema.rb +60 -109
  90. data/lib/schemas/json-schema.org/draft-06/schema.rb +53 -108
  91. data/lib/schemas/json-schema.org/draft-07/schema.rb +63 -127
  92. data/readme.rb +4 -4
  93. data/{resources}/schemas/2020-12_strict.json +19 -0
  94. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/applicator.json +48 -0
  95. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/content.json +17 -0
  96. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/core.json +51 -0
  97. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-annotation.json +14 -0
  98. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-assertion.json +14 -0
  99. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/meta-data.json +37 -0
  100. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/unevaluated.json +15 -0
  101. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/validation.json +98 -0
  102. data/{resources}/schemas/json-schema.org/draft/2020-12/schema.json +58 -0
  103. metadata +73 -52
  104. data/lib/jsi/metaschema.rb +0 -6
  105. data/lib/jsi/schema/application/child_application/contains.rb +0 -25
  106. data/lib/jsi/schema/application/child_application/draft04.rb +0 -21
  107. data/lib/jsi/schema/application/child_application/draft06.rb +0 -28
  108. data/lib/jsi/schema/application/child_application/draft07.rb +0 -28
  109. data/lib/jsi/schema/application/child_application/items.rb +0 -18
  110. data/lib/jsi/schema/application/child_application/properties.rb +0 -25
  111. data/lib/jsi/schema/application/child_application.rb +0 -13
  112. data/lib/jsi/schema/application/draft04.rb +0 -8
  113. data/lib/jsi/schema/application/draft06.rb +0 -8
  114. data/lib/jsi/schema/application/draft07.rb +0 -8
  115. data/lib/jsi/schema/application/inplace_application/dependencies.rb +0 -28
  116. data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -25
  117. data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -26
  118. data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -32
  119. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +0 -20
  120. data/lib/jsi/schema/application/inplace_application/ref.rb +0 -18
  121. data/lib/jsi/schema/application/inplace_application/someof.rb +0 -44
  122. data/lib/jsi/schema/application/inplace_application.rb +0 -14
  123. data/lib/jsi/schema/application.rb +0 -12
  124. data/lib/jsi/schema/ref.rb +0 -183
  125. data/lib/jsi/schema/validation/array.rb +0 -69
  126. data/lib/jsi/schema/validation/contains.rb +0 -25
  127. data/lib/jsi/schema/validation/dependencies.rb +0 -49
  128. data/lib/jsi/schema/validation/draft04/minmax.rb +0 -91
  129. data/lib/jsi/schema/validation/draft04.rb +0 -110
  130. data/lib/jsi/schema/validation/draft06.rb +0 -120
  131. data/lib/jsi/schema/validation/draft07.rb +0 -157
  132. data/lib/jsi/schema/validation/enum.rb +0 -25
  133. data/lib/jsi/schema/validation/ifthenelse.rb +0 -46
  134. data/lib/jsi/schema/validation/items.rb +0 -54
  135. data/lib/jsi/schema/validation/not.rb +0 -20
  136. data/lib/jsi/schema/validation/numeric.rb +0 -121
  137. data/lib/jsi/schema/validation/object.rb +0 -45
  138. data/lib/jsi/schema/validation/pattern.rb +0 -34
  139. data/lib/jsi/schema/validation/properties.rb +0 -101
  140. data/lib/jsi/schema/validation/property_names.rb +0 -32
  141. data/lib/jsi/schema/validation/ref.rb +0 -40
  142. data/lib/jsi/schema/validation/required.rb +0 -27
  143. data/lib/jsi/schema/validation/someof.rb +0 -90
  144. data/lib/jsi/schema/validation/string.rb +0 -47
  145. data/lib/jsi/schema/validation/type.rb +0 -49
  146. data/lib/jsi/schema/validation.rb +0 -49
  147. data/lib/jsi/schema_registry.rb +0 -190
  148. data/lib/jsi/util/private/attr_struct.rb +0 -130
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ CONTAINS_MINMAX = element_map do
6
+ Schema::Element.new(keywords: ['contains', 'minContains', 'maxContains']) do |element|
7
+ element.add_action(:subschema) do
8
+ if keyword?('contains')
9
+ #> The value of this keyword MUST be a valid JSON Schema.
10
+ cxt_yield(['contains'])
11
+ end
12
+ end # element.add_action(:subschema)
13
+
14
+ element.add_action(:child_applicate) do
15
+ if instance.respond_to?(:to_ary)
16
+ if keyword?('contains')
17
+ contains_schema = subschema(['contains'])
18
+
19
+ child_idx_valid = Hash.new { |h, i| h[i] = contains_schema.instance_valid?(instance[i]) }
20
+
21
+ if child_idx_valid[token]
22
+ child_schema_applicate(contains_schema)
23
+ else
24
+ minContains = keyword_value_numeric?('minContains') ? schema_content['minContains'] : 1
25
+ child_valid_count = instance.each_index.inject(0) { |n, i| n < minContains && child_idx_valid[i] ? n + 1 : n }
26
+
27
+ if child_valid_count < minContains
28
+ # invalid application: if contains_schema does not validate against `minContains` children,
29
+ # it applies to every child.
30
+ # note: this does not consider maxContains; a validation error from maxContains is not from any
31
+ # validation error of child application (which invalid application is intended to preserve), but
32
+ # rather from too few failures in child application. children that don't validate against contains
33
+ # are correctly not described by contains when contains validation failure comes from maxContains.
34
+ child_schema_applicate(contains_schema)
35
+ end
36
+ end
37
+ end
38
+ end # if instance.respond_to?(:to_ary)
39
+ end # element.add_action(:child_applicate)
40
+
41
+ element.add_action(:validate) do
42
+ if keyword?('contains')
43
+ # https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01#name-contains
44
+ #> An array instance is valid against "contains" if at least one of its elements is valid against
45
+ #> the given schema, except when "minContains" is present and has a value of 0,
46
+ #> in which case an array instance MUST be considered valid against the "contains" keyword,
47
+ #> even if none of its elements is valid against the given schema.
48
+ if instance.respond_to?(:to_ary)
49
+ results = {}
50
+ instance.each_index do |i|
51
+ results[i] = child_subschema_validate(i, ['contains'])
52
+ end
53
+ child_valid_count = instance.each_index.count { |i| results[i].valid? }
54
+
55
+ minContains = keyword_value_numeric?('minContains') ? schema_content['minContains'] : nil
56
+ if minContains
57
+ # https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#name-mincontains
58
+ child_results_validate(
59
+ child_valid_count >= minContains,
60
+ 'validation.keyword.contains.fewer_than_minContains',
61
+ "instance array contains fewer items that are valid against `contains` schema than `minContains` value",
62
+ keyword: 'contains',
63
+ child_results: results,
64
+ )
65
+ else
66
+ child_results_validate(
67
+ child_valid_count > 0,
68
+ 'validation.keyword.contains.none',
69
+ "instance array does not contain any items that are valid against `contains` schema",
70
+ keyword: 'contains',
71
+ child_results: results,
72
+ )
73
+ end
74
+
75
+ maxContains = keyword_value_numeric?('maxContains') ? schema_content['maxContains'] : nil
76
+ if maxContains
77
+ # https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#name-maxcontains
78
+ validate(
79
+ child_valid_count <= maxContains,
80
+ 'validation.keyword.maxContains.more_than_maxContains',
81
+ "instance array contains more items that are valid against `contains` schema than `maxContains` value",
82
+ keyword: 'maxContains',
83
+ )
84
+ end
85
+ end
86
+ end
87
+ end # element.add_action(:validate)
88
+ end # Schema::Element.new
89
+ end # CONTAINS_MINMAX = element_map
90
+ end # module Schema::Elements
91
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ CONTENT_ENCODING = element_map do
6
+ Schema::Element.new(keyword: 'contentEncoding') do |element|
7
+ end # Schema::Element.new
8
+ end # CONTENT_ENCODING = element_map
9
+ end # module Schema::Elements
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ CONTENT_MEDIA_TYPE = element_map do
6
+ Schema::Element.new(keyword: 'contentMediaType') do |element|
7
+ end # Schema::Element.new
8
+ end # CONTENT_MEDIA_TYPE = element_map
9
+ end # module Schema::Elements
10
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ CONTENT_SCHEMA = element_map do
6
+ Schema::Element.new(keyword: 'contentSchema') do |element|
7
+ element.add_action(:subschema) do
8
+ if keyword?('contentSchema')
9
+ #> The value of this property MUST be a valid JSON schema.
10
+ cxt_yield(['contentSchema'])
11
+ end
12
+ end # element.add_action(:subschema)
13
+ end # Schema::Element.new
14
+ end # CONTENT_SCHEMA = element_map
15
+ end # module Schema::Elements
16
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ DEFAULT = element_map do
6
+ Schema::Element.new(keyword: 'default') do |element|
7
+ element.add_action(:default) { cxt_yield(schema_content['default']) if keyword?('default') }
8
+ end # Schema::Element.new
9
+ end # DEFAULT = element_map
10
+ end # module Schema::Elements
11
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ DEFINITIONS = element_map do |keyword: |
6
+ Schema::Element.new(keyword: keyword) do |element|
7
+ element.add_action(:subschema) do
8
+ #> This keyword's value MUST be an object.
9
+ if keyword_value_hash?(keyword)
10
+ schema_content[keyword].each_key do |property_name|
11
+ #> Each member value of this object MUST be a valid JSON Schema.
12
+ cxt_yield([keyword, property_name])
13
+ end
14
+ end
15
+ end # element.add_action(:subschema)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ DEPENDENCIES = element_map do
6
+ Schema::Element.new(keyword: 'dependencies') do |element|
7
+ element.add_action(:subschema) do
8
+ #> This keyword's value MUST be an object.
9
+ if keyword_value_hash?('dependencies')
10
+ schema_content['dependencies'].each_pair do |property_name, dependency|
11
+ #> Each property specifies a dependency.
12
+ #> Each dependency value MUST be an array or a valid JSON Schema.
13
+ if !dependency.respond_to?(:to_ary)
14
+ cxt_yield(['dependencies', property_name])
15
+ end
16
+ end
17
+ end
18
+ end # element.add_action(:subschema)
19
+
20
+ element.add_action(:described_object_property_names) do
21
+ next if !keyword_value_hash?('dependencies')
22
+ schema_content['dependencies'].each do |property_name, dependency|
23
+ cxt_yield(property_name)
24
+ if dependency.respond_to?(:to_ary)
25
+ dependency.each(&block)
26
+ end
27
+ end
28
+ end
29
+
30
+ element.add_action(:inplace_application_requires_instance) do
31
+ next if !keyword_value_hash?('dependencies')
32
+ next if schema_content['dependencies'].each_value.all? { |dependency| dependency.respond_to?(:to_ary) }
33
+ cxt_yield(true)
34
+ end
35
+
36
+ element.add_action(:inplace_applicate) do
37
+ next if !keyword_value_hash?('dependencies')
38
+ next if schema_content['dependencies'].each_value.all? { |dependency| dependency.respond_to?(:to_ary) }
39
+ next if !instance.respond_to?(:to_hash)
40
+ #> This keyword's value MUST be an object. Each property specifies a dependency. Each dependency
41
+ #> value MUST be an array or a valid JSON Schema.
42
+ schema_content['dependencies'].each_pair do |property_name, dependency|
43
+ if dependency.respond_to?(:to_ary)
44
+ # noop: array-form dependencies has no in-place applicator schema
45
+ else
46
+ # If the dependency value is a subschema, and the dependency key is a
47
+ # property in the instance, the entire instance must validate against
48
+ # the dependency value.
49
+ if instance.key?(property_name)
50
+ inplace_subschema_applicate(['dependencies', property_name])
51
+ end
52
+ end
53
+ end
54
+ end # element.add_action(:inplace_applicate)
55
+
56
+ element.add_action(:validate) do
57
+ #> This keyword's value MUST be an object. Each property specifies a dependency. Each dependency
58
+ #> value MUST be an array or a valid JSON Schema.
59
+ next if !keyword_value_hash?('dependencies')
60
+ next if !instance.respond_to?(:to_hash)
61
+ schema_content['dependencies'].each_pair do |property_name, dependency|
62
+ if dependency.respond_to?(:to_ary)
63
+ # If the dependency value is an array, each element in the array, if
64
+ # any, MUST be a string, and MUST be unique. If the dependency key is
65
+ # a property in the instance, each of the items in the dependency value
66
+ # must be a property that exists in the instance.
67
+ if instance.respond_to?(:to_hash) && instance.key?(property_name)
68
+ missing_required = dependency.reject { |name| instance.key?(name) }.freeze
69
+ validate(
70
+ missing_required.empty?,
71
+ 'validation.keyword.dependencies.dependent_required.missing_property_names',
72
+ 'instance object does not contain all dependent required property names specified by `dependencies` value',
73
+ keyword: 'dependencies',
74
+ property_name: property_name,
75
+ missing_dependent_required_property_names: missing_required,
76
+ )
77
+ end
78
+ else
79
+ # If the dependency value is a subschema, and the dependency key is a
80
+ # property in the instance, the entire instance must validate against
81
+ # the dependency value.
82
+ if instance.key?(property_name)
83
+ dependency_result = inplace_subschema_validate(['dependencies', property_name])
84
+ inplace_results_validate(
85
+ dependency_result.valid?,
86
+ 'validation.keyword.dependencies.dependent_schema.invalid',
87
+ 'instance object is not valid against the schema corresponding to a matched property name specified by `dependencies` value',
88
+ keyword: 'dependencies',
89
+ results: [dependency_result],
90
+ property_name: property_name,
91
+ )
92
+ end
93
+ end
94
+ end
95
+ end # element.add_action(:validate)
96
+ end # Schema::Element.new
97
+ end # DEPENDENCIES = element_map
98
+ end # module Schema::Elements
99
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ DEPENDENT_REQUIRED = element_map do
6
+ Schema::Element.new(keyword: 'dependentRequired') do |element|
7
+ element.add_action(:described_object_property_names) do
8
+ next if !keyword_value_hash?('dependentRequired')
9
+ schema_content['dependentRequired'].each do |property_name, dependent_property_names|
10
+ cxt_yield(property_name)
11
+ next if !dependent_property_names.respond_to?(:to_ary)
12
+ dependent_property_names.each(&block)
13
+ end
14
+ end
15
+
16
+ element.add_action(:validate) do
17
+ #> The value of this keyword MUST be an object.
18
+ next if !keyword_value_hash?('dependentRequired')
19
+ next if !instance.respond_to?(:to_hash)
20
+
21
+ #> This keyword specifies properties that are required if a specific other property is
22
+ #> present. Their requirement is dependent on the presence of the other property.
23
+ #
24
+ #> Validation succeeds if, for each name that appears in both the instance and as a name
25
+ #> within this keyword's value, every item in the corresponding array is also the name of
26
+ #> a property in the instance.
27
+ missing_dependent_required = {}
28
+ schema_content['dependentRequired'].each do |property_name, dependent_property_names|
29
+ #> Properties in this object, if any, MUST be arrays.
30
+ next if !dependent_property_names.respond_to?(:to_ary)
31
+ if instance.key?(property_name)
32
+ missing_required = dependent_property_names.reject { |name| instance.key?(name) }
33
+ unless missing_required.empty?
34
+ missing_dependent_required[property_name] = missing_required
35
+ end
36
+ end
37
+ end
38
+ validate(
39
+ missing_dependent_required.empty?,
40
+ 'validation.keyword.dependentRequired.missing_property_names',
41
+ "instance object does not contain all dependent required property names specified by `dependentRequired`",
42
+ keyword: 'dependentRequired',
43
+ missing_dependent_required: missing_dependent_required,
44
+ )
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ DEPENDENT_SCHEMAS = element_map do
6
+ Schema::Element.new(keyword: 'dependentSchemas') do |element|
7
+ element.add_action(:subschema) do
8
+ #> This keyword's value MUST be an object.
9
+ next if !keyword_value_hash?('dependentSchemas')
10
+
11
+ #> Each value in the object MUST be a valid JSON Schema.
12
+ schema_content['dependentSchemas'].each_key do |property_name|
13
+ cxt_yield(['dependentSchemas', property_name])
14
+ end
15
+ end
16
+
17
+ element.add_action(:described_object_property_names) do
18
+ next if !keyword_value_hash?('dependentSchemas')
19
+ schema_content['dependentSchemas'].each_key(&block)
20
+ end
21
+
22
+ element.add_action(:inplace_application_requires_instance) { cxt_yield(true) if keyword?('dependentSchemas') }
23
+
24
+ element.add_action(:inplace_applicate) do
25
+ #> This keyword's value MUST be an object.
26
+ next if !keyword_value_hash?('dependentSchemas')
27
+ next if !instance.respond_to?(:to_hash)
28
+
29
+ #> This keyword specifies subschemas that are evaluated if the
30
+ #> instance is an object and contains a certain property.
31
+ #
32
+ #> If the object key is a property in the instance, the entire instance must validate
33
+ #> against the subschema. Its use is dependent on the presence of the property.
34
+ schema_content['dependentSchemas'].each_key do |property_name|
35
+ if instance.key?(property_name)
36
+ inplace_subschema_applicate(['dependentSchemas', property_name])
37
+ end
38
+ end
39
+ end
40
+
41
+ element.add_action(:validate) do
42
+ #> This keyword's value MUST be an object.
43
+ next if !keyword_value_hash?('dependentSchemas')
44
+ next if !instance.respond_to?(:to_hash)
45
+
46
+ #> This keyword specifies subschemas that are evaluated if the
47
+ #> instance is an object and contains a certain property.
48
+ #
49
+ #> If the object key is a property in the instance, the entire instance must validate
50
+ #> against the subschema. Its use is dependent on the presence of the property.
51
+ results = {}
52
+ schema_content['dependentSchemas'].each_key do |property_name|
53
+ if instance.key?(property_name)
54
+ results[property_name] = inplace_subschema_validate(['dependentSchemas', property_name])
55
+ end
56
+ end
57
+ inplace_results_validate(
58
+ results.each_value.all?(&:valid?),
59
+ 'validation.keyword.dependentSchemas.invalid',
60
+ "instance object is not valid against all schemas corresponding to matched property names specified by `dependentSchemas`",
61
+ keyword: 'dependentSchemas',
62
+ results: results.each_value,
63
+ dependentSchemas_properties_valid: results.inject({}) { |h, (k, r)| h.update({k => r.valid?}) }.freeze,
64
+ )
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ DYNAMIC_REF = element_map do
6
+ Schema::Element.new(keyword: '$dynamicRef') do |element|
7
+ resolve_dynamicRef = proc do
8
+ next unless keyword_value_str?('$dynamicRef')
9
+
10
+ dynamic_anchor_map = schema.jsi_next_schema_dynamic_anchor_map
11
+
12
+ #> Resolved against the current URI base, it produces the URI used as the starting point for runtime resolution.
13
+ #> This initial resolution is safe to perform on schema load.
14
+ ref = schema.schema_ref(schema_content['$dynamicRef'])
15
+
16
+ initial_resolution = ref.resolve
17
+
18
+ #> If the initially resolved starting point URI includes a
19
+ #> fragment that was created by the "$dynamicAnchor" keyword,
20
+ resolve_dynamically = \
21
+ # did resolution resolve a fragment?
22
+ ref.ref_uri.fragment &&
23
+ # does the fragment correspond to a dynamicAnchor? (not a regular anchor, not a pointer)
24
+ initial_resolution.dialect_invoke_each(:dynamicAnchor).include?(ref.ref_uri.fragment) &&
25
+ # is the anchor in our dynamic_anchor_map?
26
+ dynamic_anchor_map.key?(ref.ref_uri.fragment)
27
+ if resolve_dynamically
28
+ #> the initial URI MUST be replaced by the URI (including the fragment)
29
+ #> for the outermost schema resource in the dynamic scope (Section 7.1)
30
+ #> that defines an identically named fragment with "$dynamicAnchor".
31
+
32
+ # our dynamic resolution doesn't use a stack of dynamic scope URIs.
33
+ # we replace the initially resolved resource with the resource from dynamic_anchor_map.
34
+
35
+ scope_schema, subptrs = dynamic_anchor_map[ref.ref_uri.fragment]
36
+ resolved_schema = subptrs.inject(scope_schema, &:subschema)
37
+ else
38
+ #> Otherwise, its behavior is identical to "$ref", and no runtime resolution is needed.
39
+ resolved_schema = initial_resolution
40
+ end
41
+
42
+ [resolved_schema.with_dynamic_scope_from(schema), ref]
43
+ end
44
+
45
+ element.add_action(:inplace_applicate) do
46
+ resolved_schema, ref = *instance_exec(&resolve_dynamicRef) || next
47
+ inplace_schema_applicate(resolved_schema, ref: ref)
48
+ end
49
+
50
+ element.add_action(:validate) do
51
+ resolved_schema, ref = *instance_exec(&resolve_dynamicRef) || next
52
+ ref_result = resolved_schema.internal_validate_instance(
53
+ instance_ptr,
54
+ instance_document,
55
+ validate_only: validate_only,
56
+ visited_refs: Util.add_visited_ref(visited_refs, ref),
57
+ )
58
+ inplace_results_validate(
59
+ ref_result.valid?,
60
+ 'validation.keyword.$dynamicRef.invalid',
61
+ "instance is not valid against the schema referenced by `$dynamicRef`",
62
+ keyword: '$dynamicRef',
63
+ results: [ref_result],
64
+ )
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ ENUM = element_map do
6
+ Schema::Element.new(keyword: 'enum') do |element|
7
+ element.add_action(:validate) do
8
+ if keyword?('enum')
9
+ value = schema_content['enum']
10
+ #> The value of this keyword MUST be an array.
11
+ if value.respond_to?(:to_ary)
12
+ # An instance validates successfully against this keyword if its value is equal to one of the
13
+ # elements in this keyword's array value.
14
+ validate(
15
+ value.include?(instance),
16
+ 'validation.keyword.enum.none_equal',
17
+ "instance is not equal to any `enum` item",
18
+ keyword: 'enum',
19
+ )
20
+ end
21
+ end
22
+ end # element.add_action(:validate)
23
+ end # Schema::Element.new
24
+ end # ENUM = element_map
25
+ end # module Schema::Elements
26
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ EXAMPLES = element_map do
6
+ Schema::Element.new(keyword: 'examples') do |element|
7
+ end # Schema::Element.new
8
+ end # EXAMPLES = element_map
9
+ end # module Schema::Elements
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ FORMAT = element_map do
6
+ Schema::Element.new(keyword: 'format') do |element|
7
+ end # Schema::Element.new
8
+ end # FORMAT = element_map
9
+ end # module Schema::Elements
10
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ ID = element_map do |keyword: , fragment_is_anchor: |
6
+ Schema::Element.new(keyword: keyword) do |element|
7
+ element.add_action(:id) { cxt_yield(schema_content[keyword]) if keyword_value_str?(keyword) }
8
+
9
+ element.add_action(:id_without_fragment) do
10
+ next if !keyword_value_str?(keyword)
11
+ id_without_fragment = Util.uri(schema_content[keyword]).merge(fragment: nil)
12
+
13
+ if !id_without_fragment.empty?
14
+ cxt_yield(id_without_fragment)
15
+ end
16
+ end
17
+
18
+ if fragment_is_anchor
19
+ element.add_action(:anchor) do
20
+ next if !keyword_value_str?(keyword)
21
+ id_fragment = Util.uri(schema_content[keyword]).fragment
22
+ if id_fragment && !id_fragment.empty?
23
+ cxt_yield(id_fragment)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ IF_THEN_ELSE = element_map do
6
+ Schema::Element.new(keywords: %w(if then else)) do |element|
7
+ element.add_action(:subschema) do
8
+ if keyword?('if')
9
+ #> This keyword's value MUST be a valid JSON Schema.
10
+ cxt_yield(['if'])
11
+ end
12
+
13
+ if keyword?('then')
14
+ #> This keyword's value MUST be a valid JSON Schema.
15
+ cxt_yield(['then'])
16
+ end
17
+
18
+ if keyword?('else')
19
+ #> This keyword's value MUST be a valid JSON Schema.
20
+ cxt_yield(['else'])
21
+ end
22
+ end # element.add_action(:subschema)
23
+
24
+ element.add_action(:inplace_application_requires_instance) { cxt_yield(true) if keyword?('if') }
25
+
26
+ element.add_action(:inplace_applicate) do
27
+ if keyword?('if')
28
+ if subschema(['if']).instance_valid?(instance)
29
+ if collect_evaluated
30
+ inplace_subschema_applicate(['if'], applicate: false)
31
+ end
32
+ if keyword?('then')
33
+ inplace_subschema_applicate(['then'])
34
+ end
35
+ else
36
+ if keyword?('else')
37
+ inplace_subschema_applicate(['else'])
38
+ end
39
+ end
40
+ end
41
+ end # element.add_action(:inplace_applicate)
42
+
43
+ element.add_action(:validate) do
44
+ if keyword?('if')
45
+ # This keyword's value MUST be a valid JSON Schema.
46
+ # This validation outcome of this keyword's subschema has no direct effect on the overall validation
47
+ # result. Rather, it controls which of the "then" or "else" keywords are evaluated.
48
+ if_result = inplace_subschema_validate(['if'])
49
+
50
+ if if_result.valid?
51
+ result.evaluated_tokens.merge(if_result.evaluated_tokens)
52
+ end
53
+
54
+ if if_result.valid?
55
+ if keyword?('then')
56
+ then_result = inplace_subschema_validate(['then'])
57
+ inplace_results_validate(
58
+ then_result.valid?,
59
+ 'validation.keyword.then.invalid',
60
+ "instance is not valid against `then` schema after validating against `if` schema",
61
+ keyword: 'if',
62
+ results: [then_result],
63
+ )
64
+ end
65
+ else
66
+ if keyword?('else')
67
+ else_result = inplace_subschema_validate(['else'])
68
+ inplace_results_validate(
69
+ else_result.valid?,
70
+ 'validation.keyword.else.invalid',
71
+ "instance is not valid against `else` schema after not validating against `if` schema",
72
+ keyword: 'if',
73
+ results: [else_result],
74
+ )
75
+ end
76
+ end
77
+ end
78
+ end # element.add_action(:validate)
79
+ end # Schema::Element.new
80
+ end # IF_THEN_ELSE = element_map
81
+ end # module Schema::Elements
82
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ INFO_BOOL = element_map do |keyword: |
6
+ Schema::Element.new(keyword: keyword) do |element|
7
+ end # Schema::Element.new
8
+ end # INFO_BOOL = element_map
9
+ end # module Schema::Elements
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Schema::Elements
5
+ INFO_STRING = element_map do |keyword: |
6
+ Schema::Element.new(keyword: keyword) do |element|
7
+ end # Schema::Element.new
8
+ end # INFO_STRING = element_map
9
+ end # module Schema::Elements
10
+ end