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.
- checksums.yaml +4 -4
- data/.yardopts +3 -4
- data/CHANGELOG.md +19 -0
- data/LICENSE.md +2 -3
- data/README.md +87 -43
- data/docs/{glossary.md → Glossary.md} +84 -52
- data/jsi.gemspec +1 -1
- data/lib/jsi/base/mutability.rb +48 -0
- data/lib/jsi/base/node.rb +66 -52
- data/lib/jsi/base.rb +592 -176
- data/lib/jsi/jsi_coder.rb +4 -2
- data/lib/jsi/metaschema_node/bootstrap_schema.rb +118 -59
- data/lib/jsi/metaschema_node.rb +244 -154
- data/lib/jsi/ptr.rb +45 -17
- data/lib/jsi/ref.rb +197 -0
- data/lib/jsi/registry.rb +311 -0
- data/lib/jsi/schema/cxt/child_application.rb +35 -0
- data/lib/jsi/schema/cxt/inplace_application.rb +37 -0
- data/lib/jsi/schema/cxt.rb +80 -0
- data/lib/jsi/schema/dialect.rb +137 -0
- data/lib/jsi/schema/draft04.rb +113 -5
- data/lib/jsi/schema/draft06.rb +123 -5
- data/lib/jsi/schema/draft07.rb +157 -5
- data/lib/jsi/schema/draft202012.rb +303 -0
- data/lib/jsi/schema/dynamic_anchor_map.rb +63 -0
- data/lib/jsi/schema/element.rb +69 -0
- data/lib/jsi/schema/elements/anchor.rb +13 -0
- data/lib/jsi/schema/elements/array_validation.rb +82 -0
- data/lib/jsi/schema/elements/comment.rb +10 -0
- data/lib/jsi/schema/{validation → elements}/const.rb +11 -7
- data/lib/jsi/schema/elements/contains.rb +59 -0
- data/lib/jsi/schema/elements/contains_minmax.rb +91 -0
- data/lib/jsi/schema/elements/content_encoding.rb +10 -0
- data/lib/jsi/schema/elements/content_media_type.rb +10 -0
- data/lib/jsi/schema/elements/content_schema.rb +16 -0
- data/lib/jsi/schema/elements/default.rb +11 -0
- data/lib/jsi/schema/elements/definitions.rb +19 -0
- data/lib/jsi/schema/elements/dependencies.rb +99 -0
- data/lib/jsi/schema/elements/dependent_required.rb +49 -0
- data/lib/jsi/schema/elements/dependent_schemas.rb +69 -0
- data/lib/jsi/schema/elements/dynamic_ref.rb +69 -0
- data/lib/jsi/schema/elements/enum.rb +26 -0
- data/lib/jsi/schema/elements/examples.rb +10 -0
- data/lib/jsi/schema/elements/format.rb +10 -0
- data/lib/jsi/schema/elements/id.rb +30 -0
- data/lib/jsi/schema/elements/if_then_else.rb +82 -0
- data/lib/jsi/schema/elements/info_bool.rb +10 -0
- data/lib/jsi/schema/elements/info_string.rb +10 -0
- data/lib/jsi/schema/elements/items.rb +93 -0
- data/lib/jsi/schema/elements/items_prefixed.rb +96 -0
- data/lib/jsi/schema/elements/not.rb +31 -0
- data/lib/jsi/schema/elements/numeric.rb +137 -0
- data/lib/jsi/schema/elements/numeric_draft04.rb +77 -0
- data/lib/jsi/schema/elements/object_validation.rb +55 -0
- data/lib/jsi/schema/elements/pattern.rb +35 -0
- data/lib/jsi/schema/elements/properties.rb +145 -0
- data/lib/jsi/schema/elements/property_names.rb +48 -0
- data/lib/jsi/schema/elements/ref.rb +62 -0
- data/lib/jsi/schema/elements/required.rb +34 -0
- data/lib/jsi/schema/elements/self.rb +24 -0
- data/lib/jsi/schema/elements/some_of.rb +180 -0
- data/lib/jsi/schema/elements/string_validation.rb +57 -0
- data/lib/jsi/schema/elements/type.rb +43 -0
- data/lib/jsi/schema/elements/unevaluated_items.rb +54 -0
- data/lib/jsi/schema/elements/unevaluated_properties.rb +54 -0
- data/lib/jsi/schema/elements/xschema.rb +10 -0
- data/lib/jsi/schema/elements/xvocabulary.rb +10 -0
- data/lib/jsi/schema/elements.rb +101 -0
- data/lib/jsi/schema/issue.rb +3 -4
- data/lib/jsi/schema/schema_ancestor_node.rb +105 -52
- data/lib/jsi/schema/vocabulary.rb +36 -0
- data/lib/jsi/schema.rb +598 -383
- data/lib/jsi/schema_classes.rb +195 -141
- data/lib/jsi/schema_set.rb +85 -128
- data/lib/jsi/set.rb +23 -0
- data/lib/jsi/simple_wrap.rb +14 -17
- data/lib/jsi/struct.rb +57 -0
- data/lib/jsi/uri.rb +40 -0
- data/lib/jsi/util/private/memo_map.rb +9 -13
- data/lib/jsi/util/private.rb +59 -31
- data/lib/jsi/util/typelike.rb +19 -60
- data/lib/jsi/util.rb +53 -34
- data/lib/jsi/validation/error.rb +45 -2
- data/lib/jsi/validation/result.rb +121 -90
- data/lib/jsi/validation.rb +1 -6
- data/lib/jsi/version.rb +1 -1
- data/lib/jsi.rb +170 -36
- data/lib/schemas/json-schema.org/draft/2020-12/schema.rb +62 -0
- data/lib/schemas/json-schema.org/draft-04/schema.rb +60 -109
- data/lib/schemas/json-schema.org/draft-06/schema.rb +53 -108
- data/lib/schemas/json-schema.org/draft-07/schema.rb +63 -127
- data/readme.rb +4 -4
- data/{resources}/schemas/2020-12_strict.json +19 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/applicator.json +48 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/content.json +17 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/core.json +51 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-annotation.json +14 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-assertion.json +14 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/meta-data.json +37 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/unevaluated.json +15 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/validation.json +98 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/schema.json +58 -0
- metadata +73 -52
- data/lib/jsi/metaschema.rb +0 -6
- data/lib/jsi/schema/application/child_application/contains.rb +0 -25
- data/lib/jsi/schema/application/child_application/draft04.rb +0 -21
- data/lib/jsi/schema/application/child_application/draft06.rb +0 -28
- data/lib/jsi/schema/application/child_application/draft07.rb +0 -28
- data/lib/jsi/schema/application/child_application/items.rb +0 -18
- data/lib/jsi/schema/application/child_application/properties.rb +0 -25
- data/lib/jsi/schema/application/child_application.rb +0 -13
- data/lib/jsi/schema/application/draft04.rb +0 -8
- data/lib/jsi/schema/application/draft06.rb +0 -8
- data/lib/jsi/schema/application/draft07.rb +0 -8
- data/lib/jsi/schema/application/inplace_application/dependencies.rb +0 -28
- data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -25
- data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -26
- data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -32
- data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +0 -20
- data/lib/jsi/schema/application/inplace_application/ref.rb +0 -18
- data/lib/jsi/schema/application/inplace_application/someof.rb +0 -44
- data/lib/jsi/schema/application/inplace_application.rb +0 -14
- data/lib/jsi/schema/application.rb +0 -12
- data/lib/jsi/schema/ref.rb +0 -183
- data/lib/jsi/schema/validation/array.rb +0 -69
- data/lib/jsi/schema/validation/contains.rb +0 -25
- data/lib/jsi/schema/validation/dependencies.rb +0 -49
- data/lib/jsi/schema/validation/draft04/minmax.rb +0 -91
- data/lib/jsi/schema/validation/draft04.rb +0 -110
- data/lib/jsi/schema/validation/draft06.rb +0 -120
- data/lib/jsi/schema/validation/draft07.rb +0 -157
- data/lib/jsi/schema/validation/enum.rb +0 -25
- data/lib/jsi/schema/validation/ifthenelse.rb +0 -46
- data/lib/jsi/schema/validation/items.rb +0 -54
- data/lib/jsi/schema/validation/not.rb +0 -20
- data/lib/jsi/schema/validation/numeric.rb +0 -121
- data/lib/jsi/schema/validation/object.rb +0 -45
- data/lib/jsi/schema/validation/pattern.rb +0 -34
- data/lib/jsi/schema/validation/properties.rb +0 -101
- data/lib/jsi/schema/validation/property_names.rb +0 -32
- data/lib/jsi/schema/validation/ref.rb +0 -40
- data/lib/jsi/schema/validation/required.rb +0 -27
- data/lib/jsi/schema/validation/someof.rb +0 -90
- data/lib/jsi/schema/validation/string.rb +0 -47
- data/lib/jsi/schema/validation/type.rb +0 -49
- data/lib/jsi/schema/validation.rb +0 -49
- data/lib/jsi/schema_registry.rb +0 -190
- data/lib/jsi/util/private/attr_struct.rb +0 -130
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
module Schema::Elements
|
|
5
|
+
ITEMS = element_map do
|
|
6
|
+
Schema::Element.new(keywords: %w(items additionalItems)) do |element|
|
|
7
|
+
element.add_action(:subschema) do
|
|
8
|
+
if keyword?('items')
|
|
9
|
+
#> The value of "items" MUST be either a valid JSON Schema or an array of valid JSON Schemas.
|
|
10
|
+
if keyword_value_ary?('items')
|
|
11
|
+
schema_content['items'].each_index do |i|
|
|
12
|
+
cxt_yield(['items', i])
|
|
13
|
+
end
|
|
14
|
+
else
|
|
15
|
+
cxt_yield(['items'])
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
if keyword?('additionalItems')
|
|
20
|
+
#> The value of "additionalItems" MUST be a valid JSON Schema.
|
|
21
|
+
cxt_yield(['additionalItems'])
|
|
22
|
+
end
|
|
23
|
+
end # element.add_action(:subschema)
|
|
24
|
+
|
|
25
|
+
element.add_action(:child_applicate) do
|
|
26
|
+
if instance.respond_to?(:to_ary)
|
|
27
|
+
if keyword?('items') && schema_content['items'].respond_to?(:to_ary)
|
|
28
|
+
if schema_content['items'].size > token
|
|
29
|
+
child_subschema_applicate(['items', token])
|
|
30
|
+
elsif keyword?('additionalItems')
|
|
31
|
+
child_subschema_applicate(['additionalItems'])
|
|
32
|
+
end
|
|
33
|
+
elsif keyword?('items')
|
|
34
|
+
child_subschema_applicate(['items'])
|
|
35
|
+
end
|
|
36
|
+
end # if instance.respond_to?(:to_ary)
|
|
37
|
+
end # element.add_action(:child_applicate)
|
|
38
|
+
|
|
39
|
+
element.add_action(:validate) do
|
|
40
|
+
if instance.respond_to?(:to_ary)
|
|
41
|
+
if keyword?('items')
|
|
42
|
+
#> The value of "items" MUST be either a valid JSON Schema or an array of valid JSON Schemas.
|
|
43
|
+
if schema_content['items'].respond_to?(:to_ary)
|
|
44
|
+
#> If "items" is an array of schemas, validation succeeds if each element of the instance
|
|
45
|
+
#> validates against the schema at the same position, if any.
|
|
46
|
+
items_results = {}
|
|
47
|
+
additionalItems_results = {}
|
|
48
|
+
instance.each_index do |i|
|
|
49
|
+
if i < schema_content['items'].size
|
|
50
|
+
items_results[i] = child_subschema_validate(i, ['items', i])
|
|
51
|
+
elsif keyword?('additionalItems')
|
|
52
|
+
additionalItems_results[i] = child_subschema_validate(i, ['additionalItems'])
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
child_results_validate(
|
|
56
|
+
items_results.each_value.all?(&:valid?),
|
|
57
|
+
'validation.keyword.items.array.invalid',
|
|
58
|
+
"instance array items are not all valid against corresponding `items` schemas",
|
|
59
|
+
keyword: 'items',
|
|
60
|
+
child_results: items_results,
|
|
61
|
+
instance_indexes_valid: items_results.inject({}) { |h, (i, r)| h.update({i => r.valid?}) }.freeze,
|
|
62
|
+
)
|
|
63
|
+
child_results_validate(
|
|
64
|
+
additionalItems_results.each_value.all?(&:valid?),
|
|
65
|
+
'validation.keyword.additionalItems.invalid',
|
|
66
|
+
"instance array items after `items` schemas are not all valid against `additionalItems` schema",
|
|
67
|
+
keyword: 'additionalItems',
|
|
68
|
+
child_results: additionalItems_results,
|
|
69
|
+
instance_indexes_valid: additionalItems_results.inject({}) { |h, (i, r)| h.update({i => r.valid?}) }.freeze,
|
|
70
|
+
)
|
|
71
|
+
else
|
|
72
|
+
#> If "items" is a schema, validation succeeds if all elements in the array successfully
|
|
73
|
+
#> validate against that schema.
|
|
74
|
+
items_results = {}
|
|
75
|
+
instance.each_index do |i|
|
|
76
|
+
items_results[i] = child_subschema_validate(i, ['items'])
|
|
77
|
+
end
|
|
78
|
+
child_results_validate(
|
|
79
|
+
items_results.each_value.all?(&:valid?),
|
|
80
|
+
'validation.keyword.items.schema.invalid',
|
|
81
|
+
"instance array items are not all valid against `items` schema",
|
|
82
|
+
keyword: 'items',
|
|
83
|
+
child_results: items_results,
|
|
84
|
+
instance_indexes_valid: items_results.inject({}) { |h, (i, r)| h.update({i => r.valid?}) }.freeze,
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end # element.add_action(:validate)
|
|
90
|
+
end # Schema::Element.new
|
|
91
|
+
end # ITEMS = element_map
|
|
92
|
+
end # module Schema::Elements
|
|
93
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
module Schema::Elements
|
|
5
|
+
ITEMS_PREFIXED = element_map do
|
|
6
|
+
Schema::Element.new(keywords: %w(items prefixItems)) do |element|
|
|
7
|
+
element.add_action(:subschema) do
|
|
8
|
+
# https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01#name-prefixitems
|
|
9
|
+
#> The value of "prefixItems" MUST be a non-empty array of valid JSON Schemas.
|
|
10
|
+
if keyword_value_ary?('prefixItems')
|
|
11
|
+
schema_content['prefixItems'].each_index do |i|
|
|
12
|
+
cxt_yield(['prefixItems', i])
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01#name-items
|
|
17
|
+
#> The value of "items" MUST be a valid JSON Schema.
|
|
18
|
+
if keyword?('items')
|
|
19
|
+
cxt_yield(['items'])
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
element.add_action(:child_applicate) do
|
|
24
|
+
next if !instance.respond_to?(:to_ary)
|
|
25
|
+
if keyword_value_ary?('prefixItems') && schema_content['prefixItems'].size > token
|
|
26
|
+
child_subschema_applicate(['prefixItems', token])
|
|
27
|
+
elsif keyword?('items')
|
|
28
|
+
child_subschema_applicate(['items'])
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
element.add_action(:validate) do
|
|
33
|
+
next if !instance.respond_to?(:to_ary)
|
|
34
|
+
i = 0
|
|
35
|
+
if keyword_value_ary?('prefixItems')
|
|
36
|
+
prefixItems_results = {}
|
|
37
|
+
|
|
38
|
+
while i < schema_content['prefixItems'].size && i < instance.size
|
|
39
|
+
prefixItems_results[i] = child_subschema_validate(i, ['prefixItems', i])
|
|
40
|
+
|
|
41
|
+
i += 1
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
#> Validation succeeds if each element of the instance validates
|
|
45
|
+
#> against the schema at the same position, if any.
|
|
46
|
+
#> This keyword does not constrain the length of the array.
|
|
47
|
+
#> If the array is longer than this keyword's value, this keyword
|
|
48
|
+
#> validates only the prefix of matching length.
|
|
49
|
+
child_results_validate(
|
|
50
|
+
prefixItems_results.each_value.all?(&:valid?),
|
|
51
|
+
'validation.keyword.prefixItems.invalid',
|
|
52
|
+
"instance array items are not all valid against corresponding `prefixItems` schemas",
|
|
53
|
+
keyword: 'prefixItems',
|
|
54
|
+
child_results: prefixItems_results,
|
|
55
|
+
instance_indexes_valid: prefixItems_results.inject({}) { |h, (ri, r)| h.update({ri => r.valid?}) }.freeze,
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if keyword?('items')
|
|
60
|
+
items_results = {}
|
|
61
|
+
|
|
62
|
+
while i < instance.size
|
|
63
|
+
#> This keyword applies its subschema to all instance elements at indexes
|
|
64
|
+
#> greater than the length of the "prefixItems" array in the same schema
|
|
65
|
+
#> object, as reported by the annotation result of that "prefixItems" keyword.
|
|
66
|
+
#> If no such annotation result exists, "items" applies its subschema to all instance array elements.
|
|
67
|
+
#> Note that the behavior of "items" without "prefixItems" is identical
|
|
68
|
+
#> to that of the schema form of "items" in prior drafts.
|
|
69
|
+
#> When "prefixItems" is present, the behavior of "items" is identical
|
|
70
|
+
#> to the former "additionalItems" keyword.
|
|
71
|
+
items_results[i] = child_subschema_validate(i, ['items'])
|
|
72
|
+
|
|
73
|
+
i += 1
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
if keyword_value_ary?('prefixItems')
|
|
77
|
+
error_key = 'validation.keyword.items.after_prefixItems.invalid'
|
|
78
|
+
error_msg = "instance array items after `prefixItems` are not all valid against `items` schema"
|
|
79
|
+
else
|
|
80
|
+
error_key = 'validation.keyword.items.invalid'
|
|
81
|
+
error_msg = "instance array items are not all valid against `items` schema"
|
|
82
|
+
end
|
|
83
|
+
child_results_validate(
|
|
84
|
+
items_results.each_value.all?(&:valid?),
|
|
85
|
+
error_key,
|
|
86
|
+
error_msg,
|
|
87
|
+
keyword: 'items',
|
|
88
|
+
child_results: items_results,
|
|
89
|
+
instance_indexes_valid: items_results.inject({}) { |h, (ri, r)| h.update({ri => r.valid?}) }.freeze,
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
module Schema::Elements
|
|
5
|
+
NOT = element_map do
|
|
6
|
+
Schema::Element.new(keyword: 'not') do |element|
|
|
7
|
+
element.add_action(:subschema) do
|
|
8
|
+
if keyword?('not')
|
|
9
|
+
#> This keyword's value MUST be a valid JSON Schema.
|
|
10
|
+
cxt_yield(['not'])
|
|
11
|
+
end
|
|
12
|
+
end # element.add_action(:subschema)
|
|
13
|
+
|
|
14
|
+
element.add_action(:validate) do
|
|
15
|
+
if keyword?('not')
|
|
16
|
+
# This keyword's value MUST be a valid JSON Schema.
|
|
17
|
+
# An instance is valid against this keyword if it fails to validate successfully against the schema
|
|
18
|
+
# defined by this keyword.
|
|
19
|
+
not_valid = inplace_subschema_validate(['not']).valid?
|
|
20
|
+
validate(
|
|
21
|
+
!not_valid,
|
|
22
|
+
'validation.keyword.not.valid',
|
|
23
|
+
"instance is valid against `not` schema",
|
|
24
|
+
keyword: 'not',
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
end # element.add_action(:validate)
|
|
28
|
+
end # Schema::Element.new
|
|
29
|
+
end # NOT = element_map
|
|
30
|
+
end # module Schema::Elements
|
|
31
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
module Schema::Elements
|
|
5
|
+
MULTIPLE_OF = element_map do
|
|
6
|
+
Schema::Element.new(keyword: 'multipleOf') do |element|
|
|
7
|
+
element.add_action(:validate) do
|
|
8
|
+
if keyword?('multipleOf')
|
|
9
|
+
value = schema_content['multipleOf']
|
|
10
|
+
# The value of "multipleOf" MUST be a number, strictly greater than 0.
|
|
11
|
+
if value.is_a?(Numeric) && value > 0
|
|
12
|
+
# A numeric instance is valid only if division by this keyword's value results in an integer.
|
|
13
|
+
if instance.is_a?(Numeric)
|
|
14
|
+
if instance.is_a?(Integer) && value.is_a?(Integer)
|
|
15
|
+
valid = instance % value == 0
|
|
16
|
+
else
|
|
17
|
+
quotient = instance / value
|
|
18
|
+
if quotient.finite?
|
|
19
|
+
valid = quotient % 1.0 == 0.0
|
|
20
|
+
else
|
|
21
|
+
valid = BigDecimal(instance, Float::DIG) % BigDecimal(value, Float::DIG) == 0
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
validate(
|
|
25
|
+
valid,
|
|
26
|
+
'validation.keyword.multipleOf.not_multiple',
|
|
27
|
+
'instance is not a multiple of `multipleOf` value',
|
|
28
|
+
keyword: 'multipleOf',
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end # element.add_action(:validate)
|
|
34
|
+
end # Schema::Element.new
|
|
35
|
+
end # MULTIPLE_OF = element_map
|
|
36
|
+
end # module Schema::Elements
|
|
37
|
+
|
|
38
|
+
module Schema::Elements
|
|
39
|
+
MAXIMUM = element_map do
|
|
40
|
+
Schema::Element.new(keyword: 'maximum') do |element|
|
|
41
|
+
element.add_action(:validate) do
|
|
42
|
+
if keyword?('maximum')
|
|
43
|
+
value = schema_content['maximum']
|
|
44
|
+
# The value of "maximum" MUST be a number, representing an inclusive upper limit for a numeric instance.
|
|
45
|
+
if value.is_a?(Numeric)
|
|
46
|
+
# If the instance is a number, then this keyword validates only if the instance is less than or
|
|
47
|
+
# exactly equal to "maximum".
|
|
48
|
+
if instance.is_a?(Numeric)
|
|
49
|
+
validate(
|
|
50
|
+
instance <= value,
|
|
51
|
+
'validation.keyword.maximum.greater',
|
|
52
|
+
"instance is greater than `maximum` value",
|
|
53
|
+
keyword: 'maximum',
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end # element.add_action(:validate)
|
|
59
|
+
end # Schema::Element.new
|
|
60
|
+
end # MAXIMUM = element_map
|
|
61
|
+
end # module Schema::Elements
|
|
62
|
+
|
|
63
|
+
module Schema::Elements
|
|
64
|
+
EXCLUSIVE_MAXIMUM = element_map do
|
|
65
|
+
Schema::Element.new(keyword: 'exclusiveMaximum') do |element|
|
|
66
|
+
element.add_action(:validate) do
|
|
67
|
+
if keyword?('exclusiveMaximum')
|
|
68
|
+
value = schema_content['exclusiveMaximum']
|
|
69
|
+
# The value of "exclusiveMaximum" MUST be number, representing an exclusive upper limit for a numeric instance.
|
|
70
|
+
if value.is_a?(Numeric)
|
|
71
|
+
# If the instance is a number, then the instance is valid only if it has a value strictly less than
|
|
72
|
+
# (not equal to) "exclusiveMaximum".
|
|
73
|
+
if instance.is_a?(Numeric)
|
|
74
|
+
validate(
|
|
75
|
+
instance < value,
|
|
76
|
+
'validation.keyword.exclusiveMaximum.greater_or_equal',
|
|
77
|
+
"instance is greater than or equal to `exclusiveMaximum` value",
|
|
78
|
+
keyword: 'exclusiveMaximum',
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end # element.add_action(:validate)
|
|
84
|
+
end # Schema::Element.new
|
|
85
|
+
end # EXCLUSIVE_MAXIMUM = element_map
|
|
86
|
+
end # module Schema::Elements
|
|
87
|
+
|
|
88
|
+
module Schema::Elements
|
|
89
|
+
MINIMUM = element_map do
|
|
90
|
+
Schema::Element.new(keyword: 'minimum') do |element|
|
|
91
|
+
element.add_action(:validate) do
|
|
92
|
+
if keyword?('minimum')
|
|
93
|
+
value = schema_content['minimum']
|
|
94
|
+
# The value of "minimum" MUST be a number, representing an inclusive lower limit for a numeric instance.
|
|
95
|
+
if value.is_a?(Numeric)
|
|
96
|
+
# If the instance is a number, then this keyword validates only if the instance is greater than or
|
|
97
|
+
# exactly equal to "minimum".
|
|
98
|
+
if instance.is_a?(Numeric)
|
|
99
|
+
validate(
|
|
100
|
+
instance >= value,
|
|
101
|
+
'validation.keyword.minimum.less',
|
|
102
|
+
"instance is less than `minimum` value",
|
|
103
|
+
keyword: 'minimum',
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end # element.add_action(:validate)
|
|
109
|
+
end # Schema::Element.new
|
|
110
|
+
end # MINIMUM = element_map
|
|
111
|
+
end # module Schema::Elements
|
|
112
|
+
|
|
113
|
+
module Schema::Elements
|
|
114
|
+
EXCLUSIVE_MINIMUM = element_map do
|
|
115
|
+
Schema::Element.new(keyword: 'exclusiveMinimum') do |element|
|
|
116
|
+
element.add_action(:validate) do
|
|
117
|
+
if keyword?('exclusiveMinimum')
|
|
118
|
+
value = schema_content['exclusiveMinimum']
|
|
119
|
+
# The value of "exclusiveMinimum" MUST be number, representing an exclusive lower limit for a numeric instance.
|
|
120
|
+
if value.is_a?(Numeric)
|
|
121
|
+
# If the instance is a number, then the instance is valid only if it has a value strictly greater
|
|
122
|
+
# than (not equal to) "exclusiveMinimum".
|
|
123
|
+
if instance.is_a?(Numeric)
|
|
124
|
+
validate(
|
|
125
|
+
instance > value,
|
|
126
|
+
'validation.keyword.exclusiveMaximum.less_or_equal',
|
|
127
|
+
"instance is less than or equal to `exclusiveMinimum` value",
|
|
128
|
+
keyword: 'exclusiveMinimum',
|
|
129
|
+
)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end # element.add_action(:validate)
|
|
134
|
+
end # Schema::Element.new
|
|
135
|
+
end # EXCLUSIVE_MINIMUM = element_map
|
|
136
|
+
end # module Schema::Elements
|
|
137
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
module Schema::Elements
|
|
5
|
+
MAXIMUM_BOOLEAN_EXCLUSIVE = element_map do
|
|
6
|
+
Schema::Element.new(keywords: %w(maximum exclusiveMaximum)) do |element|
|
|
7
|
+
element.add_action(:validate) do
|
|
8
|
+
if keyword?('maximum')
|
|
9
|
+
value = schema_content['maximum']
|
|
10
|
+
# The value of "maximum" MUST be a JSON number.
|
|
11
|
+
if value.is_a?(Numeric)
|
|
12
|
+
if instance.is_a?(Numeric)
|
|
13
|
+
# Successful validation depends on the presence and value of "exclusiveMaximum":
|
|
14
|
+
if schema_content['exclusiveMaximum'] == true
|
|
15
|
+
# if "exclusiveMaximum" has boolean value true, the instance is valid if it is strictly lower
|
|
16
|
+
# than the value of "maximum".
|
|
17
|
+
validate(
|
|
18
|
+
instance < value,
|
|
19
|
+
'validation.keyword.maximum.with_exclusiveMaximum.greater_or_equal',
|
|
20
|
+
"instance is greater than or equal to `maximum` value with `exclusiveMaximum` = true",
|
|
21
|
+
keyword: 'maximum',
|
|
22
|
+
)
|
|
23
|
+
else
|
|
24
|
+
# if "exclusiveMaximum" is not present, or has boolean value false, then the instance is
|
|
25
|
+
# valid if it is lower than, or equal to, the value of "maximum"
|
|
26
|
+
validate(
|
|
27
|
+
instance <= value,
|
|
28
|
+
'validation.keyword.maximum.greater',
|
|
29
|
+
"instance is greater than `maximum` value",
|
|
30
|
+
keyword: 'maximum',
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end # element.add_action(:validate)
|
|
37
|
+
end # Schema::Element.new
|
|
38
|
+
end # MAXIMUM_BOOLEAN_EXCLUSIVE = element_map
|
|
39
|
+
end # module Schema::Elements
|
|
40
|
+
|
|
41
|
+
module Schema::Elements
|
|
42
|
+
MINIMUM_BOOLEAN_EXCLUSIVE = element_map do
|
|
43
|
+
Schema::Element.new(keywords: %w(minimum exclusiveMinimum)) do |element|
|
|
44
|
+
element.add_action(:validate) do
|
|
45
|
+
if keyword?('minimum')
|
|
46
|
+
value = schema_content['minimum']
|
|
47
|
+
# The value of "minimum" MUST be a JSON number.
|
|
48
|
+
if value.is_a?(Numeric)
|
|
49
|
+
if instance.is_a?(Numeric)
|
|
50
|
+
# Successful validation depends on the presence and value of "exclusiveMinimum":
|
|
51
|
+
if schema_content['exclusiveMinimum'] == true
|
|
52
|
+
# if "exclusiveMinimum" is present and has boolean value true, the instance is valid if it is
|
|
53
|
+
# strictly greater than the value of "minimum".
|
|
54
|
+
validate(
|
|
55
|
+
instance > value,
|
|
56
|
+
'validation.keyword.minimum.with_exclusiveMinimum.less_or_equal',
|
|
57
|
+
"instance is less than or equal to `minimum` value with `exclusiveMinimum` = true",
|
|
58
|
+
keyword: 'minimum',
|
|
59
|
+
)
|
|
60
|
+
else
|
|
61
|
+
# if "exclusiveMinimum" is not present, or has boolean value false, then the instance is
|
|
62
|
+
# valid if it is greater than, or equal to, the value of "minimum"
|
|
63
|
+
validate(
|
|
64
|
+
instance >= value,
|
|
65
|
+
'validation.keyword.minimum.less',
|
|
66
|
+
"instance is less than `minimum` value",
|
|
67
|
+
keyword: 'minimum',
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end # element.add_action(:validate)
|
|
74
|
+
end # Schema::Element.new
|
|
75
|
+
end # MINIMUM_BOOLEAN_EXCLUSIVE = element_map
|
|
76
|
+
end # module Schema::Elements
|
|
77
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
module Schema::Elements
|
|
5
|
+
MAX_PROPERTIES = element_map do
|
|
6
|
+
Schema::Element.new(keyword: 'maxProperties') do |element|
|
|
7
|
+
element.add_action(:validate) do
|
|
8
|
+
if keyword?('maxProperties')
|
|
9
|
+
value = schema_content['maxProperties']
|
|
10
|
+
# The value of this keyword MUST be a non-negative integer.
|
|
11
|
+
if internal_integer?(value) && value >= 0
|
|
12
|
+
if instance.respond_to?(:to_hash)
|
|
13
|
+
# An object instance is valid against "maxProperties" if its number of properties is less than, or equal to, the value of this keyword.
|
|
14
|
+
size = instance.size
|
|
15
|
+
validate(
|
|
16
|
+
size <= value,
|
|
17
|
+
'validation.keyword.maxProperties.properties_count_greater',
|
|
18
|
+
"instance object properties count is greater than `maxProperties` value",
|
|
19
|
+
keyword: 'maxProperties',
|
|
20
|
+
instance_properties_count: size,
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end # element.add_action(:validate)
|
|
26
|
+
end # Schema::Element.new
|
|
27
|
+
end # MAX_PROPERTIES = element_map
|
|
28
|
+
end # module Schema::Elements
|
|
29
|
+
|
|
30
|
+
module Schema::Elements
|
|
31
|
+
MIN_PROPERTIES = element_map do
|
|
32
|
+
Schema::Element.new(keyword: 'minProperties') do |element|
|
|
33
|
+
element.add_action(:validate) do
|
|
34
|
+
if keyword?('minProperties')
|
|
35
|
+
value = schema_content['minProperties']
|
|
36
|
+
# The value of this keyword MUST be a non-negative integer.
|
|
37
|
+
if internal_integer?(value) && value >= 0
|
|
38
|
+
if instance.respond_to?(:to_hash)
|
|
39
|
+
# An object instance is valid against "minProperties" if its number of properties is greater than, or equal to, the value of this keyword.
|
|
40
|
+
size = instance.size
|
|
41
|
+
validate(
|
|
42
|
+
size >= value,
|
|
43
|
+
'validation.keyword.minProperties.properties_count_less',
|
|
44
|
+
"instance object properties count is less than `minProperties` value",
|
|
45
|
+
keyword: 'minProperties',
|
|
46
|
+
instance_properties_count: size,
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end # element.add_action(:validate)
|
|
52
|
+
end # Schema::Element.new
|
|
53
|
+
end # MIN_PROPERTIES = element_map
|
|
54
|
+
end # module Schema::Elements
|
|
55
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
module Schema::Elements
|
|
5
|
+
PATTERN = element_map do
|
|
6
|
+
Schema::Element.new(keyword: 'pattern') do |element|
|
|
7
|
+
element.add_action(:validate) do
|
|
8
|
+
if keyword?('pattern')
|
|
9
|
+
value = schema_content['pattern']
|
|
10
|
+
# The value of this keyword MUST be a string.
|
|
11
|
+
if value.respond_to?(:to_str)
|
|
12
|
+
if instance.respond_to?(:to_str)
|
|
13
|
+
begin
|
|
14
|
+
# This string SHOULD be a valid regular expression, according to the ECMA 262 regular expression
|
|
15
|
+
# dialect.
|
|
16
|
+
# TODO
|
|
17
|
+
regexp = Regexp.new(value)
|
|
18
|
+
#> A string instance is considered valid if the regular expression matches the instance successfully.
|
|
19
|
+
validate(
|
|
20
|
+
regexp.match(instance),
|
|
21
|
+
'validation.keyword.pattern.not_match',
|
|
22
|
+
'instance string does not match `pattern` regular expression value',
|
|
23
|
+
keyword: 'pattern',
|
|
24
|
+
)
|
|
25
|
+
rescue RegexpError
|
|
26
|
+
# cannot validate
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end # element.add_action(:validate)
|
|
32
|
+
end # Schema::Element.new
|
|
33
|
+
end # PATTERN = element_map
|
|
34
|
+
end # module Schema::Elements
|
|
35
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
module Schema::Elements
|
|
5
|
+
PROPERTIES = element_map do
|
|
6
|
+
Schema::Element.new(keywords: %w(properties patternProperties additionalProperties)) do |element|
|
|
7
|
+
element.add_action(:subschema) do
|
|
8
|
+
#> The value of "properties" MUST be an object.
|
|
9
|
+
if keyword_value_hash?('properties')
|
|
10
|
+
schema_content['properties'].each_key do |property_name|
|
|
11
|
+
#> Each value of this object MUST be a valid JSON Schema.
|
|
12
|
+
cxt_yield(['properties', property_name])
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#> The value of "patternProperties" MUST be an object.
|
|
17
|
+
if keyword_value_hash?('patternProperties')
|
|
18
|
+
schema_content['patternProperties'].each_key do |pattern|
|
|
19
|
+
#> Each property value of this object MUST be a valid JSON Schema.
|
|
20
|
+
cxt_yield(['patternProperties', pattern])
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if keyword?('additionalProperties')
|
|
25
|
+
#> The value of "additionalProperties" MUST be a valid JSON Schema.
|
|
26
|
+
cxt_yield(['additionalProperties'])
|
|
27
|
+
end
|
|
28
|
+
end # element.add_action(:subschema)
|
|
29
|
+
|
|
30
|
+
element.add_action(:described_object_property_names) do
|
|
31
|
+
next if !keyword_value_hash?('properties')
|
|
32
|
+
schema_content['properties'].each_key(&block)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
element.add_action(:child_applicate) do
|
|
36
|
+
if instance.respond_to?(:to_hash)
|
|
37
|
+
apply_additional = true
|
|
38
|
+
if keyword?('properties') && schema_content['properties'].respond_to?(:to_hash) && schema_content['properties'].key?(token)
|
|
39
|
+
apply_additional = false
|
|
40
|
+
child_subschema_applicate(['properties', token])
|
|
41
|
+
end
|
|
42
|
+
if keyword?('patternProperties') && schema_content['patternProperties'].respond_to?(:to_hash)
|
|
43
|
+
schema_content['patternProperties'].each_key do |pattern|
|
|
44
|
+
if pattern.respond_to?(:to_str) && token.to_s =~ Regexp.new(pattern) # TODO map pattern to ruby syntax
|
|
45
|
+
apply_additional = false
|
|
46
|
+
child_subschema_applicate(['patternProperties', pattern])
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
if apply_additional && keyword?('additionalProperties')
|
|
51
|
+
child_subschema_applicate(['additionalProperties'])
|
|
52
|
+
end
|
|
53
|
+
end # if instance.respond_to?(:to_hash)
|
|
54
|
+
end # element.add_action(:child_applicate)
|
|
55
|
+
|
|
56
|
+
element.add_action(:validate) do
|
|
57
|
+
evaluated_property_names = Set[]
|
|
58
|
+
|
|
59
|
+
if keyword?('properties')
|
|
60
|
+
value = schema_content['properties']
|
|
61
|
+
# The value of "properties" MUST be an object. Each value of this object MUST be a valid JSON Schema.
|
|
62
|
+
if value.respond_to?(:to_hash)
|
|
63
|
+
# Validation succeeds if, for each name that appears in both the instance and as a name within this
|
|
64
|
+
# keyword's value, the child instance for that name successfully validates against the corresponding
|
|
65
|
+
# schema.
|
|
66
|
+
if instance.respond_to?(:to_hash)
|
|
67
|
+
results = {}
|
|
68
|
+
instance.each_key do |property_name|
|
|
69
|
+
if value.key?(property_name)
|
|
70
|
+
evaluated_property_names << property_name
|
|
71
|
+
results[property_name] = child_subschema_validate(property_name, ['properties', property_name])
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
child_results_validate(
|
|
75
|
+
results.each_value.all?(&:valid?),
|
|
76
|
+
'validation.keyword.properties.invalid',
|
|
77
|
+
"instance object properties are not all valid against corresponding `properties` schemas",
|
|
78
|
+
keyword: 'properties',
|
|
79
|
+
child_results: results,
|
|
80
|
+
instance_properties_valid: results.inject({}) { |h, (k, r)| h.update({k => r.valid?}) }.freeze,
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
if keyword?('patternProperties')
|
|
87
|
+
value = schema_content['patternProperties']
|
|
88
|
+
# The value of "patternProperties" MUST be an object. Each property name of this object SHOULD be a
|
|
89
|
+
# valid regular expression, according to the ECMA 262 regular expression dialect. Each property value
|
|
90
|
+
# of this object MUST be a valid JSON Schema.
|
|
91
|
+
if value.respond_to?(:to_hash)
|
|
92
|
+
# Validation succeeds if, for each instance name that matches any regular expressions that appear as
|
|
93
|
+
# a property name in this keyword's value, the child instance for that name successfully validates
|
|
94
|
+
# against each schema that corresponds to a matching regular expression.
|
|
95
|
+
if instance.respond_to?(:to_hash)
|
|
96
|
+
results = {}
|
|
97
|
+
instance.each_key do |property_name|
|
|
98
|
+
value.each_key do |value_property_pattern|
|
|
99
|
+
begin
|
|
100
|
+
# TODO ECMA 262
|
|
101
|
+
if value_property_pattern.respond_to?(:to_str) && Regexp.new(value_property_pattern).match(property_name.to_s)
|
|
102
|
+
evaluated_property_names << property_name
|
|
103
|
+
results[property_name] = child_subschema_validate(property_name, ['patternProperties', value_property_pattern])
|
|
104
|
+
end
|
|
105
|
+
rescue ::RegexpError
|
|
106
|
+
# cannot validate
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
child_results_validate(
|
|
111
|
+
results.each_value.all?(&:valid?),
|
|
112
|
+
'validation.keyword.patternProperties.invalid',
|
|
113
|
+
"instance object properties are not all valid against matching `patternProperties` schemas",
|
|
114
|
+
keyword: 'patternProperties',
|
|
115
|
+
child_results: results,
|
|
116
|
+
instance_properties_valid: results.inject({}) { |h, (k, r)| h.update({k => r.valid?}) }.freeze,
|
|
117
|
+
)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
if keyword?('additionalProperties')
|
|
123
|
+
# The value of "additionalProperties" MUST be a valid JSON Schema.
|
|
124
|
+
if instance.respond_to?(:to_hash)
|
|
125
|
+
results = {}
|
|
126
|
+
instance.each_key do |property_name|
|
|
127
|
+
if !evaluated_property_names.include?(property_name)
|
|
128
|
+
results[property_name] = child_subschema_validate(property_name, ['additionalProperties'])
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
child_results_validate(
|
|
132
|
+
results.each_value.all?(&:valid?),
|
|
133
|
+
'validation.keyword.additionalProperties.invalid',
|
|
134
|
+
"instance object additional properties are not all valid against `additionalProperties` schema",
|
|
135
|
+
keyword: 'additionalProperties',
|
|
136
|
+
child_results: results,
|
|
137
|
+
instance_properties_valid: results.inject({}) { |h, (k, r)| h.update({k => r.valid?}) }.freeze,
|
|
138
|
+
)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end # element.add_action(:validate)
|
|
142
|
+
end # Schema::Element.new
|
|
143
|
+
end # PROPERTIES = element_map
|
|
144
|
+
end # module Schema::Elements
|
|
145
|
+
end
|