jsi-dev 0.0.8 → 0.0.10

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 (149) 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 +313 -0
  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 +593 -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 +245 -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/docs/glossary.md +0 -281
  105. data/lib/jsi/metaschema.rb +0 -6
  106. data/lib/jsi/schema/application/child_application/contains.rb +0 -25
  107. data/lib/jsi/schema/application/child_application/draft04.rb +0 -21
  108. data/lib/jsi/schema/application/child_application/draft06.rb +0 -28
  109. data/lib/jsi/schema/application/child_application/draft07.rb +0 -28
  110. data/lib/jsi/schema/application/child_application/items.rb +0 -18
  111. data/lib/jsi/schema/application/child_application/properties.rb +0 -25
  112. data/lib/jsi/schema/application/child_application.rb +0 -13
  113. data/lib/jsi/schema/application/draft04.rb +0 -8
  114. data/lib/jsi/schema/application/draft06.rb +0 -8
  115. data/lib/jsi/schema/application/draft07.rb +0 -8
  116. data/lib/jsi/schema/application/inplace_application/dependencies.rb +0 -28
  117. data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -25
  118. data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -26
  119. data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -32
  120. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +0 -20
  121. data/lib/jsi/schema/application/inplace_application/ref.rb +0 -18
  122. data/lib/jsi/schema/application/inplace_application/someof.rb +0 -44
  123. data/lib/jsi/schema/application/inplace_application.rb +0 -14
  124. data/lib/jsi/schema/application.rb +0 -12
  125. data/lib/jsi/schema/ref.rb +0 -183
  126. data/lib/jsi/schema/validation/array.rb +0 -69
  127. data/lib/jsi/schema/validation/contains.rb +0 -25
  128. data/lib/jsi/schema/validation/dependencies.rb +0 -49
  129. data/lib/jsi/schema/validation/draft04/minmax.rb +0 -91
  130. data/lib/jsi/schema/validation/draft04.rb +0 -110
  131. data/lib/jsi/schema/validation/draft06.rb +0 -120
  132. data/lib/jsi/schema/validation/draft07.rb +0 -157
  133. data/lib/jsi/schema/validation/enum.rb +0 -25
  134. data/lib/jsi/schema/validation/ifthenelse.rb +0 -46
  135. data/lib/jsi/schema/validation/items.rb +0 -54
  136. data/lib/jsi/schema/validation/not.rb +0 -20
  137. data/lib/jsi/schema/validation/numeric.rb +0 -121
  138. data/lib/jsi/schema/validation/object.rb +0 -45
  139. data/lib/jsi/schema/validation/pattern.rb +0 -34
  140. data/lib/jsi/schema/validation/properties.rb +0 -101
  141. data/lib/jsi/schema/validation/property_names.rb +0 -32
  142. data/lib/jsi/schema/validation/ref.rb +0 -40
  143. data/lib/jsi/schema/validation/required.rb +0 -27
  144. data/lib/jsi/schema/validation/someof.rb +0 -90
  145. data/lib/jsi/schema/validation/string.rb +0 -47
  146. data/lib/jsi/schema/validation/type.rb +0 -49
  147. data/lib/jsi/schema/validation.rb +0 -49
  148. data/lib/jsi/schema_registry.rb +0 -190
  149. data/lib/jsi/util/private/attr_struct.rb +0 -130
@@ -1,101 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSI
4
- module Schema::Validation::Properties
5
- # @private
6
- def internal_validate_properties(result_builder)
7
- evaluated_property_names = Set[]
8
-
9
- if keyword?('properties')
10
- value = schema_content['properties']
11
- # The value of "properties" MUST be an object. Each value of this object MUST be a valid JSON Schema.
12
- if value.respond_to?(:to_hash)
13
- # Validation succeeds if, for each name that appears in both the instance and as a name within this
14
- # keyword's value, the child instance for that name successfully validates against the corresponding
15
- # schema.
16
- if result_builder.instance.respond_to?(:to_hash)
17
- results = {}
18
- result_builder.instance.keys.each do |property_name|
19
- if value.key?(property_name)
20
- evaluated_property_names << property_name
21
- results[property_name] = result_builder.child_subschema_validate(
22
- property_name,
23
- ['properties', property_name],
24
- )
25
- end
26
- end
27
- result_builder.validate(
28
- results.values.all?(&:valid?),
29
- 'instance object properties are not all valid against corresponding `properties` schema values',
30
- keyword: 'properties',
31
- results: results.values,
32
- )
33
- end
34
- else
35
- result_builder.schema_error('`properties` is not an object', 'properties')
36
- end
37
- end
38
-
39
- if keyword?('patternProperties')
40
- value = schema_content['patternProperties']
41
- # The value of "patternProperties" MUST be an object. Each property name of this object SHOULD be a
42
- # valid regular expression, according to the ECMA 262 regular expression dialect. Each property value
43
- # of this object MUST be a valid JSON Schema.
44
- if value.respond_to?(:to_hash)
45
- # Validation succeeds if, for each instance name that matches any regular expressions that appear as
46
- # a property name in this keyword's value, the child instance for that name successfully validates
47
- # against each schema that corresponds to a matching regular expression.
48
- if result_builder.instance.respond_to?(:to_hash)
49
- results = {}
50
- result_builder.instance.keys.each do |property_name|
51
- value.keys.each do |value_property_pattern|
52
- begin
53
- # TODO ECMA 262
54
- if value_property_pattern.respond_to?(:to_str) && Regexp.new(value_property_pattern).match(property_name.to_s)
55
- evaluated_property_names << property_name
56
- results[property_name] = result_builder.child_subschema_validate(
57
- property_name,
58
- ['patternProperties', value_property_pattern],
59
- )
60
- end
61
- rescue ::RegexpError
62
- result_builder.schema_error("`patternProperties` key #{property_name.inspect} is not a valid regular expression: #{e.message}", 'patternProperties')
63
- end
64
- end
65
- end
66
- result_builder.validate(
67
- results.values.all?(&:valid?),
68
- 'instance object properties are not all valid against corresponding `patternProperties` schema values',
69
- keyword: 'patternProperties',
70
- results: results.values,
71
- )
72
- end
73
- else
74
- result_builder.schema_error('`patternProperties` is not an object', 'patternProperties')
75
- end
76
- end
77
-
78
- if keyword?('additionalProperties')
79
- value = schema_content['additionalProperties']
80
- # The value of "additionalProperties" MUST be a valid JSON Schema.
81
- if result_builder.instance.respond_to?(:to_hash)
82
- results = {}
83
- result_builder.instance.keys.each do |property_name|
84
- if !evaluated_property_names.include?(property_name)
85
- results[property_name] = result_builder.child_subschema_validate(
86
- property_name,
87
- ['additionalProperties'],
88
- )
89
- end
90
- end.compact
91
- result_builder.validate(
92
- results.values.all?(&:valid?),
93
- 'instance object additional properties are not all valid against `additionalProperties` schema value',
94
- keyword: 'additionalProperties',
95
- results: results.values,
96
- )
97
- end
98
- end
99
- end
100
- end
101
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSI
4
- module Schema::Validation::PropertyNames
5
- # @private
6
- def internal_validate_propertyNames(result_builder)
7
- if keyword?('propertyNames')
8
- # The value of "propertyNames" MUST be a valid JSON Schema.
9
- #
10
- # If the instance is an object, this keyword validates if every property name in the instance
11
- # validates against the provided schema. Note the property name that the schema is testing will
12
- # always be a string.
13
- if result_builder.instance.respond_to?(:to_hash)
14
- results = {}
15
- result_builder.instance.keys.each do |property_name|
16
- results[property_name] = subschema(['propertyNames']).internal_validate_instance(
17
- Ptr[],
18
- property_name,
19
- validate_only: result_builder.validate_only,
20
- )
21
- end
22
- result_builder.validate(
23
- results.values.all?(&:valid?),
24
- 'instance object property names are not all valid against `propertyNames` schema value',
25
- keyword: 'propertyNames',
26
- results: results.values,
27
- )
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSI
4
- module Schema::Validation::Ref
5
- # @private
6
- # @param throw_result [Boolean] if a $ref is present, whether to throw the result being built after
7
- # validating the $ref, bypassing subsequent keyword validation
8
- def internal_validate_ref(result_builder, throw_result: false)
9
- if keyword?('$ref')
10
- value = schema_content['$ref']
11
-
12
- if value.respond_to?(:to_str)
13
- schema_ref = self.schema_ref
14
-
15
- if result_builder.visited_refs.include?(schema_ref)
16
- result_builder.schema_error('self-referential schema structure', '$ref')
17
- else
18
- ref_result = schema_ref.deref_schema.internal_validate_instance(
19
- result_builder.instance_ptr,
20
- result_builder.instance_document,
21
- validate_only: result_builder.validate_only,
22
- visited_refs: result_builder.visited_refs + [schema_ref],
23
- )
24
- result_builder.validate(
25
- ref_result.valid?,
26
- 'instance is not valid against the schema referenced by `$ref` value',
27
- keyword: '$ref',
28
- results: [ref_result],
29
- )
30
- if throw_result
31
- throw(:jsi_validation_result, result_builder.result)
32
- end
33
- end
34
- else
35
- result_builder.schema_error("`$ref` is not a string", '$ref')
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSI
4
- module Schema::Validation::Required
5
- # @private
6
- def internal_validate_required(result_builder)
7
- if keyword?('required')
8
- value = schema_content['required']
9
- # The value of this keyword MUST be an array. Elements of this array, if any, MUST be strings, and MUST be unique.
10
- if value.respond_to?(:to_ary)
11
- if result_builder.instance.respond_to?(:to_hash)
12
- # An object instance is valid against this keyword if every item in the array is the name of a property in the instance.
13
- missing_required = value.reject { |property_name| result_builder.instance.key?(property_name) }
14
- # TODO include missing required property names in the validation error
15
- result_builder.validate(
16
- missing_required.empty?,
17
- 'instance object does not contain all property names specified by `required` value',
18
- keyword: 'required',
19
- )
20
- end
21
- else
22
- result_builder.schema_error('`required` is not an array', 'required')
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,90 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSI
4
- module Schema::Validation::AllOf
5
- # @private
6
- def internal_validate_allOf(result_builder)
7
- if keyword?('allOf')
8
- value = schema_content['allOf']
9
- # This keyword's value MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
10
- if value.respond_to?(:to_ary)
11
- # An instance validates successfully against this keyword if it validates successfully against all
12
- # schemas defined by this keyword's value.
13
- allOf_results = value.each_index.map do |i|
14
- result_builder.inplace_subschema_validate(['allOf', i])
15
- end
16
- result_builder.validate(
17
- allOf_results.all?(&:valid?),
18
- 'instance is not valid against all schemas specified by `allOf` value',
19
- keyword: 'allOf',
20
- results: allOf_results,
21
- )
22
- else
23
- result_builder.schema_error('`allOf` is not an array', 'allOf')
24
- end
25
- end
26
- end
27
- end
28
-
29
- module Schema::Validation::AnyOf
30
- # @private
31
- def internal_validate_anyOf(result_builder)
32
- if keyword?('anyOf')
33
- value = schema_content['anyOf']
34
- # This keyword's value MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
35
- if value.respond_to?(:to_ary)
36
- # An instance validates successfully against this keyword if it validates successfully against at
37
- # least one schema defined by this keyword's value.
38
- # Note that when annotations are being collected, all subschemas MUST be examined so that
39
- # annotations are collected from each subschema that validates successfully.
40
- anyOf_results = value.each_index.map do |i|
41
- result_builder.inplace_subschema_validate(['anyOf', i])
42
- end
43
- result_builder.validate(
44
- anyOf_results.any?(&:valid?),
45
- 'instance is not valid against any schemas specified by `anyOf` value',
46
- keyword: 'anyOf',
47
- results: anyOf_results,
48
- )
49
- else
50
- result_builder.schema_error('`anyOf` is not an array', 'anyOf')
51
- end
52
- end
53
- end
54
- end
55
-
56
- module Schema::Validation::OneOf
57
- # @private
58
- def internal_validate_oneOf(result_builder)
59
- if keyword?('oneOf')
60
- value = schema_content['oneOf']
61
- # This keyword's value MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
62
- if value.respond_to?(:to_ary)
63
- # An instance validates successfully against this keyword if it validates successfully against
64
- # exactly one schema defined by this keyword's value.
65
- oneOf_results = value.each_index.map do |i|
66
- result_builder.inplace_subschema_validate(['oneOf', i])
67
- end
68
- if oneOf_results.none?(&:valid?)
69
- result_builder.validate(
70
- false,
71
- 'instance is not valid against any schemas specified by `oneOf` value',
72
- keyword: 'oneOf',
73
- results: oneOf_results,
74
- )
75
- else
76
- # TODO better info on what schemas passed/failed validation
77
- result_builder.validate(
78
- oneOf_results.select(&:valid?).size == 1,
79
- 'instance is valid against more than one schema specified by `oneOf` value',
80
- keyword: 'oneOf',
81
- results: oneOf_results,
82
- )
83
- end
84
- else
85
- result_builder.schema_error('`oneOf` is not an array', 'oneOf')
86
- end
87
- end
88
- end
89
- end
90
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSI
4
- module Schema::Validation::StringLength
5
- # @private
6
- def internal_validate_maxLength(result_builder)
7
- if keyword?('maxLength')
8
- value = schema_content['maxLength']
9
- # The value of this keyword MUST be a non-negative integer.
10
- if internal_integer?(value) && value >= 0
11
- if result_builder.instance.respond_to?(:to_str)
12
- # A string instance is valid against this keyword if its length is less than, or equal to, the
13
- # value of this keyword.
14
- result_builder.validate(
15
- result_builder.instance.to_str.length <= value,
16
- 'instance string length is not less than or equal to `maxLength` value',
17
- keyword: 'maxLength',
18
- )
19
- end
20
- else
21
- result_builder.schema_error('`maxLength` is not a non-negative integer', 'maxLength')
22
- end
23
- end
24
- end
25
-
26
- # @private
27
- def internal_validate_minLength(result_builder)
28
- if keyword?('minLength')
29
- value = schema_content['minLength']
30
- # The value of this keyword MUST be a non-negative integer.
31
- if internal_integer?(value) && value >= 0
32
- if result_builder.instance.respond_to?(:to_str)
33
- # A string instance is valid against this keyword if its length is greater than, or equal to, the
34
- # value of this keyword.
35
- result_builder.validate(
36
- result_builder.instance.to_str.length >= value,
37
- 'instance string length is not greater than or equal to `minLength` value',
38
- keyword: 'minLength',
39
- )
40
- end
41
- else
42
- result_builder.schema_error('`minLength` is not a non-negative integer', 'minLength')
43
- end
44
- end
45
- end
46
- end
47
- end
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSI
4
- module Schema::Validation::Type
5
- # @private
6
- def internal_validate_type(result_builder)
7
- if keyword?('type')
8
- value = schema_content['type']
9
- instance = result_builder.instance
10
- # The value of this keyword MUST be either a string or an array. If it is an array, elements of
11
- # the array MUST be strings and MUST be unique.
12
- if value.respond_to?(:to_str) || value.respond_to?(:to_ary)
13
- types = value.respond_to?(:to_str) ? [value] : value
14
- matched_type = types.each_with_index.any? do |type, i|
15
- if type.respond_to?(:to_str)
16
- case type.to_str
17
- when 'null'
18
- instance == nil
19
- when 'boolean'
20
- instance == true || instance == false
21
- when 'object'
22
- instance.respond_to?(:to_hash)
23
- when 'array'
24
- instance.respond_to?(:to_ary)
25
- when 'string'
26
- instance.respond_to?(:to_str)
27
- when 'number'
28
- instance.is_a?(Numeric)
29
- when 'integer'
30
- internal_integer?(instance)
31
- else
32
- result_builder.schema_error("`type` is not one of: null, boolean, object, array, string, number, or integer", 'type')
33
- end
34
- else
35
- result_builder.schema_error("`type` is not a string at index #{i}", 'type')
36
- end
37
- end
38
- result_builder.validate(
39
- matched_type,
40
- 'instance type does not match `type` value',
41
- keyword: 'type',
42
- )
43
- else
44
- result_builder.schema_error('`type` is not a string or array', 'type')
45
- end
46
- end
47
- end
48
- end
49
- end
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSI
4
- module Schema::Validation
5
- autoload :Draft04, 'jsi/schema/validation/draft04'
6
- autoload :Draft06, 'jsi/schema/validation/draft06'
7
- autoload :Draft07, 'jsi/schema/validation/draft07'
8
-
9
- # ref application
10
- autoload :Ref, 'jsi/schema/validation/ref'
11
-
12
- # inplace subschema application
13
- autoload :AllOf, 'jsi/schema/validation/someof'
14
- autoload :AnyOf, 'jsi/schema/validation/someof'
15
- autoload :OneOf, 'jsi/schema/validation/someof'
16
- autoload :IfThenElse, 'jsi/schema/validation/ifthenelse'
17
-
18
- # child subschema application
19
- autoload :Items, 'jsi/schema/validation/items'
20
- autoload :Contains, 'jsi/schema/validation/contains'
21
- autoload :Properties, 'jsi/schema/validation/properties'
22
-
23
- # property names subschema application
24
- autoload :PropertyNames, 'jsi/schema/validation/property_names'
25
-
26
- # any type validation
27
- autoload :Type, 'jsi/schema/validation/type'
28
- autoload :Enum, 'jsi/schema/validation/enum'
29
- autoload :Const, 'jsi/schema/validation/const'
30
- autoload :Not, 'jsi/schema/validation/not'
31
-
32
- # object validation
33
- autoload :Required, 'jsi/schema/validation/required'
34
- autoload :Dependencies, 'jsi/schema/validation/dependencies'
35
- autoload :MinMaxProperties, 'jsi/schema/validation/object'
36
-
37
- # array validation
38
- autoload :ArrayLength, 'jsi/schema/validation/array'
39
- autoload :UniqueItems, 'jsi/schema/validation/array'
40
-
41
- # string validation
42
- autoload :StringLength, 'jsi/schema/validation/string'
43
- autoload :Pattern, 'jsi/schema/validation/pattern'
44
-
45
- # numeric validation
46
- autoload :MultipleOf, 'jsi/schema/validation/numeric'
47
- autoload :MinMax, 'jsi/schema/validation/numeric'
48
- end
49
- end
@@ -1,190 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSI
4
- class SchemaRegistry
5
- # an exception raised when an attempt is made to register a resource using a URI which is already
6
- # registered with another resource
7
- class Collision < StandardError
8
- end
9
-
10
- # an exception raised when an attempt is made to access (register or find) a resource of the
11
- # registry using a URI which is not absolute (it is a relative URI or it contains a fragment)
12
- class NonAbsoluteURI < StandardError
13
- end
14
-
15
- # an exception raised when a URI we are looking for has not been registered
16
- class ResourceNotFound < StandardError
17
- attr_accessor :uri
18
- end
19
-
20
- def initialize
21
- @resources = {}
22
- @autoload_uris = {}
23
- @resources_mutex = Mutex.new
24
- end
25
-
26
- # registers the given resource and/or schema resources it contains in the registry.
27
- #
28
- # each descendent node of the resource (including the resource itself) is registered if it is a schema
29
- # that has an absolute URI (generally defined by the '$id' keyword).
30
- #
31
- # the given resource itself will be registered, whether or not it is a schema, if it is the root
32
- # of its document and was instantiated with the option `uri` specified.
33
- #
34
- # @param resource [JSI::Base] a JSI containing resources to register
35
- # @return [void]
36
- def register(resource)
37
- unless resource.is_a?(JSI::Base)
38
- raise(ArgumentError, "resource must be a JSI::Base. got: #{resource.pretty_inspect.chomp}")
39
- end
40
- unless resource.is_a?(JSI::Schema) || resource.jsi_ptr.root?
41
- # unsure, should this be allowed? the given JSI is not a "resource" as we define it, but
42
- # if this check is removed it will just register any resources (schemas) below the given JSI.
43
- raise(ArgumentError, "undefined behavior: registration of a JSI which is not a schema and is not at the root of a document")
44
- end
45
-
46
- # allow for registration of resources at the root of a document whether or not they are schemas.
47
- # jsi_schema_base_uri at the root comes from the `uri` parameter to new_jsi / new_schema.
48
- if resource.jsi_schema_base_uri && resource.jsi_ptr.root?
49
- register_single(resource.jsi_schema_base_uri, resource)
50
- end
51
-
52
- resource.jsi_each_descendent_node do |node|
53
- if node.is_a?(JSI::Schema) && node.schema_absolute_uri
54
- register_single(node.schema_absolute_uri, node)
55
- end
56
- end
57
-
58
- nil
59
- end
60
-
61
- # takes a URI identifying a resource to be loaded by the given block
62
- # when a reference to the URI is followed.
63
- #
64
- # for example:
65
- #
66
- # JSI.schema_registry.autoload_uri('http://example.com/schema.json') do
67
- # JSI.new_schema({
68
- # '$schema' => 'http://json-schema.org/draft-07/schema#',
69
- # '$id' => 'http://example.com/schema.json',
70
- # 'title' => 'my schema',
71
- # })
72
- # end
73
- #
74
- # the block would normally load JSON from the filesystem or similar.
75
- #
76
- # @param uri [Addressable::URI]
77
- # @yieldreturn [JSI::Base] a JSI instance containing the resource identified by the given uri
78
- # @return [void]
79
- def autoload_uri(uri, &block)
80
- uri = ensure_uri_absolute(uri)
81
- mutating
82
- unless block
83
- raise(ArgumentError, ["#{SchemaRegistry}#autoload_uri must be invoked with a block", "URI: #{uri}"].join("\n"))
84
- end
85
- if @autoload_uris.key?(uri)
86
- raise(Collision, ["already registered URI for autoload", "URI: #{uri}", "loader: #{@autoload_uris[uri]}"].join("\n"))
87
- end
88
- @autoload_uris[uri] = block
89
- nil
90
- end
91
-
92
- # @param uri [Addressable::URI, #to_str]
93
- # @return [JSI::Base]
94
- # @raise [JSI::SchemaRegistry::ResourceNotFound]
95
- def find(uri)
96
- uri = ensure_uri_absolute(uri)
97
- if @autoload_uris.key?(uri)
98
- autoloaded = @autoload_uris[uri].call
99
- register(autoloaded)
100
- @autoload_uris.delete(uri)
101
- end
102
- if !@resources.key?(uri)
103
- if autoloaded
104
- msg = [
105
- "URI #{uri} was registered with autoload_uri but the result did not contain a resource with that URI.",
106
- "the resource resulting from autoload_uri was:",
107
- autoloaded.pretty_inspect.chomp,
108
- ]
109
- else
110
- msg = ["URI #{uri} is not registered. registered URIs:", *(@resources.keys | @autoload_uris.keys)]
111
- end
112
- raise(ResourceNotFound.new(msg.join("\n")).tap { |e| e.uri = uri })
113
- end
114
- @resources[uri]
115
- end
116
-
117
- def inspect
118
- [
119
- '#<JSI::SchemaRegistry',
120
- *[['autoload', @autoload_uris.keys], ['resources', @resources.keys]].map do |label, uris|
121
- [
122
- " #{label} (#{uris.size})#{uris.empty? ? "" : ":"}",
123
- *uris.map do |uri|
124
- " #{uri}"
125
- end,
126
- ]
127
- end.inject([], &:+),
128
- '>',
129
- ].join("\n").freeze
130
- end
131
-
132
- alias_method :to_s, :inspect
133
-
134
- def dup
135
- self.class.new.tap do |reg|
136
- @resources.each do |uri, resource|
137
- reg.register_single(uri, resource)
138
- end
139
- @autoload_uris.each do |uri, autoload|
140
- reg.autoload_uri(uri, &autoload)
141
- end
142
- end
143
- end
144
-
145
- def freeze
146
- @resources.freeze
147
- @autoload_uris.freeze
148
- @resources_mutex = nil
149
- super
150
- end
151
-
152
- protected
153
- # @param uri [Addressable::URI]
154
- # @param resource [JSI::Base]
155
- # @return [void]
156
- def register_single(uri, resource)
157
- mutating
158
- @resources_mutex.synchronize do
159
- ensure_uri_absolute(uri)
160
- if @resources.key?(uri)
161
- if @resources[uri] != resource
162
- raise(Collision, "URI collision on #{uri}.\nexisting:\n#{@resources[uri].pretty_inspect.chomp}\nnew:\n#{resource.pretty_inspect.chomp}")
163
- end
164
- else
165
- @resources[uri] = resource
166
- end
167
- end
168
- nil
169
- end
170
-
171
- private
172
-
173
- def ensure_uri_absolute(uri)
174
- uri = Util.uri(uri)
175
- if uri.fragment
176
- raise(NonAbsoluteURI, "#{self.class} only registers absolute URIs. cannot access URI with fragment: #{uri}")
177
- end
178
- if uri.relative?
179
- raise(NonAbsoluteURI, "#{self.class} only registers absolute URIs. cannot access relative URI: #{uri}")
180
- end
181
- uri
182
- end
183
-
184
- def mutating
185
- if frozen?
186
- raise(FrozenError, "cannot modify frozen #{self.class}")
187
- end
188
- end
189
- end
190
- end