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