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
data/lib/jsi/schema_set.rb
CHANGED
|
@@ -4,31 +4,17 @@ module JSI
|
|
|
4
4
|
# a Set of JSI Schemas. always frozen.
|
|
5
5
|
#
|
|
6
6
|
# any schema instance is described by a set of schemas.
|
|
7
|
-
class SchemaSet <
|
|
7
|
+
class SchemaSet < Set
|
|
8
|
+
COMPARE_BY_IDENTITY_DEFINED = method_defined?(:compare_by_identity)
|
|
9
|
+
private_constant(:COMPARE_BY_IDENTITY_DEFINED)
|
|
10
|
+
|
|
8
11
|
class << self
|
|
9
|
-
#
|
|
12
|
+
# Builds a SchemaSet, yielding a yielder to be called with each schema of the SchemaSet.
|
|
10
13
|
#
|
|
11
|
-
# @yield [
|
|
14
|
+
# @yield [Enumerator::Yielder]
|
|
12
15
|
# @return [SchemaSet]
|
|
13
|
-
def build
|
|
14
|
-
|
|
15
|
-
yield mutable_set
|
|
16
|
-
new(mutable_set)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# ensures the given param becomes a SchemaSet. returns the param if it is already SchemaSet, otherwise
|
|
20
|
-
# initializes a SchemaSet from it.
|
|
21
|
-
#
|
|
22
|
-
# @param schemas [SchemaSet, Enumerable] the object to ensure becomes a SchemaSet
|
|
23
|
-
# @return [SchemaSet] the given SchemaSet, or a SchemaSet initialized from the given Enumerable
|
|
24
|
-
# @raise [ArgumentError] when the schemas param is not an Enumerable
|
|
25
|
-
# @raise [Schema::NotASchemaError] when the schemas param contains objects which are not Schemas
|
|
26
|
-
def ensure_schema_set(schemas)
|
|
27
|
-
if schemas.is_a?(SchemaSet)
|
|
28
|
-
schemas
|
|
29
|
-
else
|
|
30
|
-
new(schemas)
|
|
31
|
-
end
|
|
16
|
+
def build(&block)
|
|
17
|
+
new(Enumerator.new(&block))
|
|
32
18
|
end
|
|
33
19
|
end
|
|
34
20
|
|
|
@@ -38,7 +24,7 @@ module JSI
|
|
|
38
24
|
# if no block is given, the enum must contain only Schemas.
|
|
39
25
|
#
|
|
40
26
|
# @param enum [#each] the schemas to be included in the SchemaSet, or items to be passed to the block
|
|
41
|
-
# @yieldparam yields each element of enum for preprocessing into a Schema
|
|
27
|
+
# @yieldparam yields each element of `enum` for preprocessing into a Schema
|
|
42
28
|
# @yieldreturn [JSI::Schema]
|
|
43
29
|
# @raise [JSI::Schema::NotASchemaError]
|
|
44
30
|
def initialize(enum, &block)
|
|
@@ -55,7 +41,20 @@ module JSI
|
|
|
55
41
|
raise(ArgumentError, "#{SchemaSet} initialized with non-Enumerable: #{enum.pretty_inspect.chomp}")
|
|
56
42
|
end
|
|
57
43
|
|
|
58
|
-
super
|
|
44
|
+
super(&nil) # note super() does implicitly pass block without &nil
|
|
45
|
+
if COMPARE_BY_IDENTITY_DEFINED
|
|
46
|
+
compare_by_identity
|
|
47
|
+
else
|
|
48
|
+
# TODO rm when Set#compare_by_identity is universally available.
|
|
49
|
+
# note does not work on JRuby, but JRuby has Set#compare_by_identity.
|
|
50
|
+
@hash.compare_by_identity
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
if block
|
|
54
|
+
enum.each_entry { |o| add(block[o]) }
|
|
55
|
+
else
|
|
56
|
+
merge(enum)
|
|
57
|
+
end
|
|
59
58
|
|
|
60
59
|
not_schemas = reject { |s| s.is_a?(Schema) }
|
|
61
60
|
if !not_schemas.empty?
|
|
@@ -69,122 +68,87 @@ module JSI
|
|
|
69
68
|
end
|
|
70
69
|
|
|
71
70
|
# Instantiates a new JSI whose content comes from the given `instance` param.
|
|
72
|
-
#
|
|
71
|
+
#
|
|
72
|
+
# The schemas of the JSI (its {Base#jsi_schemas}) are in-place
|
|
73
73
|
# applicators of this set's schemas which apply to the given instance.
|
|
74
|
+
# The JSI's {Base#jsi_indicated_schemas} set is this set.
|
|
75
|
+
#
|
|
76
|
+
# The resulting JSI is an instance of a number of modules:
|
|
77
|
+
#
|
|
78
|
+
# - The {SchemaModule JSI schema module} of each applicator schema.
|
|
79
|
+
# - {Base::HashNode}, {Base::ArrayNode}, or {Base::StringNode} if the instance is
|
|
80
|
+
# a hash/object, array, or string.
|
|
81
|
+
# - A module defining readers for properties described by applicator schemas.
|
|
82
|
+
# If the instance is mutable, writers as well.
|
|
74
83
|
#
|
|
75
84
|
# @param instance [Object] the instance to be represented as a JSI
|
|
76
|
-
# @param
|
|
85
|
+
# @param base_uri [#to_str, URI, nil]
|
|
86
|
+
# The base URI of the instance document. An absolute URI.
|
|
87
|
+
#
|
|
88
|
+
# It is rare that this needs to be specified. It is useful when the instance contains schemas,
|
|
89
|
+
# and schemas in the document use relative URIs for `$id` or `$ref` without an absolute id
|
|
90
|
+
# in an ancestor schema - those URIs will be resolved relative to `base_uri`.
|
|
77
91
|
#
|
|
78
|
-
#
|
|
79
|
-
#
|
|
92
|
+
# See also {Base::Conf conf} {Base::Conf#root_uri `root_uri`}. `base_uri` is not used to identify
|
|
93
|
+
# any resource, only to resolve relative URIs. `root_uri` does identify the root resource.
|
|
80
94
|
# @param register [Boolean] Whether schema resources in the instantiated JSI will be registered
|
|
81
|
-
# in the
|
|
95
|
+
# in the {Base::Conf configured} {Base::Conf#registry `registry`}.
|
|
82
96
|
# This is only useful when the JSI is a schema or contains schemas.
|
|
83
|
-
# The JSI's root will be registered with the `uri` param, if specified, whether or not the
|
|
84
|
-
# root is a schema.
|
|
85
|
-
# @param schema_registry [SchemaRegistry, nil] The registry to use for references to other schemas and,
|
|
86
|
-
# depending on `register` and `uri` params, to register this JSI and/or any contained schemas with
|
|
87
|
-
# declared URIs.
|
|
88
97
|
# @param stringify_symbol_keys [Boolean] Whether the instance content will have any Symbol keys of Hashes
|
|
89
98
|
# replaced with Strings (recursively through the document).
|
|
90
99
|
# Replacement is done on a copy; the given instance is not modified.
|
|
91
|
-
# @
|
|
92
|
-
#
|
|
100
|
+
# @param mutable [Boolean] Whether the instantiated JSI will be mutable.
|
|
101
|
+
# The instance content will be transformed with the {Base::Conf configured}
|
|
102
|
+
# {Base::Conf#to_immutable `to_immutable`} if the JSI will be immutable.
|
|
103
|
+
# @param conf_kw Additional keyword params are passed to initialize a {Base::Conf}, the JSI's {Base#jsi_conf}.
|
|
104
|
+
# @return [Base] a JSI whose content comes from the given instance and whose schemas are
|
|
105
|
+
# in-place applicators of the schemas in this set.
|
|
93
106
|
def new_jsi(instance,
|
|
94
|
-
|
|
107
|
+
base_uri: nil,
|
|
95
108
|
register: false,
|
|
96
|
-
|
|
97
|
-
|
|
109
|
+
stringify_symbol_keys: false,
|
|
110
|
+
mutable: false,
|
|
111
|
+
**conf_kw
|
|
98
112
|
)
|
|
99
|
-
if
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
raise(BlockGivenError) if block_given?
|
|
114
|
+
|
|
115
|
+
conf = Base::Conf.new(**conf_kw)
|
|
102
116
|
|
|
103
|
-
|
|
117
|
+
instance = Util.deep_stringify_symbol_keys(instance) if stringify_symbol_keys
|
|
104
118
|
|
|
105
|
-
if
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
unless uri.absolute? && !uri.fragment
|
|
111
|
-
raise(ArgumentError, "uri must be an absolute URI with no fragment; got: #{uri.inspect}")
|
|
112
|
-
end
|
|
119
|
+
instance = conf.to_immutable.call(instance) if !mutable && conf.to_immutable
|
|
120
|
+
|
|
121
|
+
applied_schemas = SchemaSet.build do |y|
|
|
122
|
+
c = y.method(:yield) # TODO drop c, just pass y, when all supported Enumerator::Yielder.method_defined?(:to_proc)
|
|
123
|
+
each { |is| is.each_inplace_applicator_schema(instance, &c) }
|
|
113
124
|
end
|
|
114
125
|
|
|
126
|
+
base_uri = Util.uri(base_uri, nnil: false, yabs: true) || conf.root_uri
|
|
127
|
+
|
|
115
128
|
jsi_class = JSI::SchemaClasses.class_for_schemas(applied_schemas,
|
|
116
129
|
includes: SchemaClasses.includes_for(instance),
|
|
130
|
+
mutable: mutable,
|
|
117
131
|
)
|
|
118
|
-
jsi = jsi_class.new(
|
|
132
|
+
jsi = jsi_class.new(
|
|
133
|
+
jsi_document: instance,
|
|
119
134
|
jsi_indicated_schemas: self,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
135
|
+
jsi_base_uri: base_uri,
|
|
136
|
+
jsi_conf: conf,
|
|
137
|
+
).send(:jsi_initialized)
|
|
123
138
|
|
|
124
|
-
if register &&
|
|
125
|
-
schema_registry.register(jsi)
|
|
126
|
-
end
|
|
139
|
+
conf.registry.register(jsi) if register && conf.registry
|
|
127
140
|
|
|
128
141
|
jsi
|
|
129
142
|
end
|
|
130
143
|
|
|
131
|
-
# a set of inplace applicator schemas of each schema in this set which apply to the given instance.
|
|
132
|
-
# (see {Schema#inplace_applicator_schemas})
|
|
133
|
-
#
|
|
134
|
-
# @param instance (see Schema#inplace_applicator_schemas)
|
|
135
|
-
# @return [JSI::SchemaSet]
|
|
136
|
-
def inplace_applicator_schemas(instance)
|
|
137
|
-
SchemaSet.new(each_inplace_applicator_schema(instance))
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
# yields each inplace applicator schema which applies to the given instance.
|
|
141
|
-
#
|
|
142
|
-
# @param instance (see Schema#inplace_applicator_schemas)
|
|
143
|
-
# @yield [JSI::Schema]
|
|
144
|
-
# @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
|
|
145
|
-
def each_inplace_applicator_schema(instance, &block)
|
|
146
|
-
return to_enum(__method__, instance) unless block
|
|
147
|
-
|
|
148
|
-
each do |schema|
|
|
149
|
-
schema.each_inplace_applicator_schema(instance, &block)
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
nil
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
# a set of child applicator subschemas of each schema in this set which apply to the child
|
|
156
|
-
# of the given instance on the given token.
|
|
157
|
-
# (see {Schema#child_applicator_schemas})
|
|
158
|
-
#
|
|
159
|
-
# @param instance (see Schema#child_applicator_schemas)
|
|
160
|
-
# @return [JSI::SchemaSet]
|
|
161
|
-
def child_applicator_schemas(token, instance)
|
|
162
|
-
SchemaSet.new(each_child_applicator_schema(token, instance))
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# yields each child applicator schema which applies to the child of
|
|
166
|
-
# the given instance on the given token.
|
|
167
|
-
#
|
|
168
|
-
# @param (see Schema#child_applicator_schemas)
|
|
169
|
-
# @yield [JSI::Schema]
|
|
170
|
-
# @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
|
|
171
|
-
def each_child_applicator_schema(token, instance, &block)
|
|
172
|
-
return to_enum(__method__, token, instance) unless block
|
|
173
|
-
|
|
174
|
-
each do |schema|
|
|
175
|
-
schema.each_child_applicator_schema(token, instance, &block)
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
nil
|
|
179
|
-
end
|
|
180
|
-
|
|
181
144
|
# validates the given instance against our schemas
|
|
182
145
|
#
|
|
183
146
|
# @param instance [Object] the instance to validate against our schemas
|
|
184
147
|
# @return [JSI::Validation::Result]
|
|
185
148
|
def instance_validate(instance)
|
|
186
|
-
|
|
187
|
-
|
|
149
|
+
inject(Validation::Result::Full.new) do |result, schema|
|
|
150
|
+
result.merge(schema.instance_validate(instance))
|
|
151
|
+
end.freeze
|
|
188
152
|
end
|
|
189
153
|
|
|
190
154
|
# whether the given instance is valid against our schemas
|
|
@@ -194,26 +158,19 @@ module JSI
|
|
|
194
158
|
all? { |schema| schema.instance_valid?(instance) }
|
|
195
159
|
end
|
|
196
160
|
|
|
197
|
-
# @return [
|
|
198
|
-
def
|
|
199
|
-
|
|
161
|
+
# @return [Set<SchemaModule>]
|
|
162
|
+
def jsi_schema_modules
|
|
163
|
+
Set.new(self, &:jsi_schema_module).freeze
|
|
200
164
|
end
|
|
201
165
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
q.seplist(self, nil, :each) { |e|
|
|
211
|
-
q.pp e
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
q.breakable ''
|
|
216
|
-
q.text ']'
|
|
166
|
+
# Builds a SchemaSet, yielding each schema and a callable to be called with each schema of the resulting SchemaSet.
|
|
167
|
+
# @yield [Schema, #to_proc]
|
|
168
|
+
# @return [SchemaSet]
|
|
169
|
+
def each_yield_set(&block)
|
|
170
|
+
self.class.new(Enumerator.new do |y|
|
|
171
|
+
c = y.method(:yield) # TODO drop c, just pass y, when all supported Enumerator::Yielder.method_defined?(:to_proc)
|
|
172
|
+
each { |schema| yield(schema, c) }
|
|
173
|
+
end)
|
|
217
174
|
end
|
|
218
175
|
end
|
|
219
176
|
end
|
data/lib/jsi/set.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
# @private
|
|
5
|
+
class Set < ::Set
|
|
6
|
+
include(Util::Pretty)
|
|
7
|
+
|
|
8
|
+
def pretty_print(q)
|
|
9
|
+
q.text(self.class.to_s)
|
|
10
|
+
q.text('[')
|
|
11
|
+
q.group do
|
|
12
|
+
q.nest(2) do
|
|
13
|
+
q.breakable('')
|
|
14
|
+
q.seplist(self) do |e|
|
|
15
|
+
q.pp(e)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
q.breakable('')
|
|
19
|
+
end
|
|
20
|
+
q.text(']')
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/jsi/simple_wrap.rb
CHANGED
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module JSI
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
dialect = Schema::Dialect.new(
|
|
5
|
+
vocabularies: [
|
|
6
|
+
Schema::Vocabulary.new(elements: [
|
|
7
|
+
Schema::Element.new do |element|
|
|
8
|
+
element.add_action(:inplace_applicate) { inplace_schema_applicate(schema) }
|
|
9
|
+
element.add_action(:child_applicate) { child_schema_applicate(schema) }
|
|
10
|
+
end,
|
|
11
|
+
]),
|
|
12
|
+
],
|
|
13
|
+
)
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def internal_validate_keywords(result_builder)
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
simple_wrap_metaschema = JSI.new_metaschema(nil, schema_implementation_modules: [simple_wrap_implementation])
|
|
18
|
-
SimpleWrap = simple_wrap_metaschema.new_schema_module({})
|
|
15
|
+
simple_wrap_metaschema = JSI.new_metaschema_node(nil, dialect: dialect)
|
|
16
|
+
SimpleWrap = simple_wrap_metaschema.new_schema_module(Util::EMPTY_HASH)
|
|
19
17
|
|
|
20
18
|
# SimpleWrap is a JSI schema module which recursively wraps nested structures
|
|
21
|
-
module SimpleWrap
|
|
22
|
-
end
|
|
19
|
+
module SimpleWrap end
|
|
23
20
|
|
|
24
|
-
SimpleWrap::
|
|
21
|
+
SimpleWrap::DIALECT = dialect
|
|
25
22
|
SimpleWrap::METASCHEMA = simple_wrap_metaschema
|
|
26
23
|
end
|
data/lib/jsi/struct.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
# JSI::Struct adds to Struct:
|
|
5
|
+
#
|
|
6
|
+
# - always initialized by keywords
|
|
7
|
+
# - .subclass enables hierarchical class inheritance with added members
|
|
8
|
+
# - better pretty/inspect
|
|
9
|
+
# @private
|
|
10
|
+
class Struct < ::Struct
|
|
11
|
+
include(Util::Pretty)
|
|
12
|
+
|
|
13
|
+
STRUCT_NEW = Struct.singleton_class.instance_method(:new)
|
|
14
|
+
private_constant(:STRUCT_NEW)
|
|
15
|
+
|
|
16
|
+
HAS_KEYWORD_INIT = Struct.new(:_, keyword_init: true) && true rescue false
|
|
17
|
+
private_constant(:HAS_KEYWORD_INIT)
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
# @return [Class]
|
|
21
|
+
def subclass(*members)
|
|
22
|
+
#chkbug fail if !members.all? { |m| m.is_a?(Symbol) }
|
|
23
|
+
self_members = self.members rescue [] # NoMethodError on mri, NameError on truffle
|
|
24
|
+
# Struct does not enable adding members to subclasses of its generated classes,
|
|
25
|
+
# but that is still possible by binding Struct.new to the class and calling
|
|
26
|
+
# that with both existing and new members.
|
|
27
|
+
if HAS_KEYWORD_INIT
|
|
28
|
+
STRUCT_NEW.bind(self).call(*self_members, *members, keyword_init: true)
|
|
29
|
+
else
|
|
30
|
+
STRUCT_NEW.bind(self).call(*self_members, *members)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if !HAS_KEYWORD_INIT
|
|
36
|
+
def initialize(h = {})
|
|
37
|
+
super(*members.map { |m| h.key?(m) ? h.delete(m) : nil })
|
|
38
|
+
raise(ArgumentError, "#{self.class} given non-members: #{h}") if !h.empty?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [self.class]
|
|
43
|
+
def merge(**h)
|
|
44
|
+
self.class.new(**to_h, **h)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def pretty_print(q)
|
|
48
|
+
jsi_pp_object_group(q) do
|
|
49
|
+
q.seplist(each_pair) do |k, v|
|
|
50
|
+
q.text(k.to_s)
|
|
51
|
+
q.text(': ')
|
|
52
|
+
q.pp(v)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/jsi/uri.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
# JSI::URI adds to Addressable::URI:
|
|
5
|
+
#
|
|
6
|
+
# - `JSI::URI["http://x"]` parses, and JSI::URI#inspect shows this form, copy/pastable
|
|
7
|
+
# - Immutable when instantiated with `.[]`, `.parse`, or modified-copy instance methods join, merge, or normalize.
|
|
8
|
+
# However `.new` and `#dup` do not freeze for compatibility with some libraries (Faraday) that dup and mutate URIs.
|
|
9
|
+
# @private
|
|
10
|
+
class URI < Addressable::URI
|
|
11
|
+
class << self
|
|
12
|
+
# @param uri [#to_str]
|
|
13
|
+
# @return [URI]
|
|
14
|
+
def [](uri)
|
|
15
|
+
parse(uri)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def parse(uri)
|
|
19
|
+
super.freeze
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def join(uri)
|
|
24
|
+
super.freeze
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def merge(hash)
|
|
28
|
+
super.freeze
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def normalize
|
|
32
|
+
super.freeze
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @return [String]
|
|
36
|
+
def inspect
|
|
37
|
+
-"#{self.class}[#{to_s.inspect}]"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -25,15 +25,6 @@ module JSI
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
class MemoMap::Mutable < MemoMap
|
|
28
|
-
Result = AttrStruct[*%w(
|
|
29
|
-
value
|
|
30
|
-
inputs
|
|
31
|
-
inputs_hash
|
|
32
|
-
)]
|
|
33
|
-
|
|
34
|
-
class Result
|
|
35
|
-
end
|
|
36
|
-
|
|
37
28
|
def [](**inputs)
|
|
38
29
|
key = key_for(inputs)
|
|
39
30
|
|
|
@@ -43,11 +34,12 @@ module JSI
|
|
|
43
34
|
|
|
44
35
|
result_mutex.synchronize do
|
|
45
36
|
inputs_hash = inputs.hash
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
result_value, result_inputs, result_inputs_hash = @results[key]
|
|
38
|
+
if inputs_hash == result_inputs_hash && inputs == result_inputs
|
|
39
|
+
result_value
|
|
48
40
|
else
|
|
49
41
|
value = @block.call(**inputs)
|
|
50
|
-
@results[key] =
|
|
42
|
+
@results[key] = [value, inputs, inputs_hash]
|
|
51
43
|
value
|
|
52
44
|
end
|
|
53
45
|
end
|
|
@@ -58,6 +50,8 @@ module JSI
|
|
|
58
50
|
def [](**inputs)
|
|
59
51
|
key = key_for(inputs)
|
|
60
52
|
|
|
53
|
+
return @results[key] if @results.key?(key)
|
|
54
|
+
|
|
61
55
|
result_mutex = @result_mutexes_mutex.synchronize do
|
|
62
56
|
@result_mutexes[key] ||= Mutex.new
|
|
63
57
|
end
|
|
@@ -66,7 +60,9 @@ module JSI
|
|
|
66
60
|
if @results.key?(key)
|
|
67
61
|
@results[key]
|
|
68
62
|
else
|
|
69
|
-
@results[key] = @block.call(**inputs)
|
|
63
|
+
result = @results[key] = @block.call(**inputs)
|
|
64
|
+
@result_mutexes.delete(key)
|
|
65
|
+
result
|
|
70
66
|
end
|
|
71
67
|
end
|
|
72
68
|
end
|
data/lib/jsi/util/private.rb
CHANGED
|
@@ -5,13 +5,14 @@ module JSI
|
|
|
5
5
|
#
|
|
6
6
|
# @api private
|
|
7
7
|
module Util::Private
|
|
8
|
-
autoload :AttrStruct, 'jsi/util/private/attr_struct'
|
|
9
8
|
autoload :MemoMap, 'jsi/util/private/memo_map'
|
|
10
9
|
|
|
11
10
|
extend self
|
|
12
11
|
|
|
13
12
|
EMPTY_ARY = [].freeze
|
|
14
13
|
|
|
14
|
+
EMPTY_HASH = {}.freeze
|
|
15
|
+
|
|
15
16
|
EMPTY_SET = Set[].freeze
|
|
16
17
|
|
|
17
18
|
CLASSES_ALWAYS_FROZEN = Set[TrueClass, FalseClass, NilClass, Integer, Float, BigDecimal, Rational, Symbol].freeze
|
|
@@ -67,10 +68,16 @@ module JSI
|
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
def const_name_from_parts(parts, join: '')
|
|
71
|
+
initAZ = false
|
|
70
72
|
parts = parts.map do |part|
|
|
71
|
-
part = part.dup
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
part = part.to_str.dup
|
|
74
|
+
if !initAZ
|
|
75
|
+
part[/\A[^a-zA-Z]*/] = ''
|
|
76
|
+
end
|
|
77
|
+
if part[0]
|
|
78
|
+
part[0] = part[0].upcase
|
|
79
|
+
initAZ = true
|
|
80
|
+
end
|
|
74
81
|
part.gsub!(RUBY_REJECT_NAME_RE, '_')
|
|
75
82
|
part
|
|
76
83
|
end
|
|
@@ -81,18 +88,46 @@ module JSI
|
|
|
81
88
|
end
|
|
82
89
|
end
|
|
83
90
|
|
|
91
|
+
if JSON.parse('[]', freeze: true).frozen?
|
|
92
|
+
def json_parse_freeze(json)
|
|
93
|
+
JSON.parse(json, freeze: true)
|
|
94
|
+
end
|
|
95
|
+
else
|
|
96
|
+
def json_parse_freeze(json)
|
|
97
|
+
Util.deep_to_frozen(JSON.parse(json))
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
84
101
|
# string or URI → frozen URI
|
|
85
|
-
# @
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
102
|
+
# @param nnil must not be nil
|
|
103
|
+
# @param yabs must be absolute
|
|
104
|
+
# @param ynorm must be normalized
|
|
105
|
+
# @param tonorm normalize returned URI
|
|
106
|
+
# @return [URI, nil]
|
|
107
|
+
def uri(uri, nnil: false, yabs: false, ynorm: false, tonorm: false)
|
|
108
|
+
return nil if !nnil && uri.nil?
|
|
109
|
+
if uri.is_a?(URI)
|
|
110
|
+
auri = uri
|
|
111
|
+
elsif uri.is_a?(String) || uri.respond_to?(:to_str)
|
|
112
|
+
auri = URI.parse(uri)
|
|
113
|
+
else
|
|
114
|
+
raise(URIError, "URI is not a string: #{uri.inspect}")
|
|
115
|
+
end
|
|
116
|
+
if yabs && !auri.scheme
|
|
117
|
+
raise(URIError, "URI must be an absolute URI. got: #{uri.inspect}")
|
|
118
|
+
end
|
|
119
|
+
if yabs && auri.fragment
|
|
120
|
+
if auri.fragment.empty?
|
|
121
|
+
auri = auri.merge(fragment: nil)
|
|
90
122
|
else
|
|
91
|
-
uri.
|
|
123
|
+
raise(URIError, "URI must have no fragment. got: #{uri.inspect}")
|
|
92
124
|
end
|
|
93
|
-
else
|
|
94
|
-
Addressable::URI.parse(uri).freeze
|
|
95
125
|
end
|
|
126
|
+
if ynorm && uri.to_str != auri.normalize.to_s
|
|
127
|
+
raise(URIError, "URI must be in normalized form. got: #{uri.inspect}; normalized: #{auri.normalize.to_s.inspect}")
|
|
128
|
+
end
|
|
129
|
+
auri = auri.normalize if tonorm
|
|
130
|
+
auri
|
|
96
131
|
end
|
|
97
132
|
|
|
98
133
|
# this is the Y-combinator, which allows anonymous recursive functions. for a simple example,
|
|
@@ -139,6 +174,18 @@ module JSI
|
|
|
139
174
|
nil
|
|
140
175
|
end
|
|
141
176
|
|
|
177
|
+
# @param visited_refs [Array<Schema::Ref>]
|
|
178
|
+
# @param ref [Schema::Ref, nil]
|
|
179
|
+
# @return [Array<Schema::Ref>]
|
|
180
|
+
def add_visited_ref(visited_refs, ref)
|
|
181
|
+
return(visited_refs) if ref.nil?
|
|
182
|
+
#chkbug fail unless ref.is_a?(Schema::Ref) && visited_refs.is_a?(Array) && visited_refs.frozen?
|
|
183
|
+
if visited_refs.include?(ref)
|
|
184
|
+
raise(ResolutionError, "cyclical ref application with refs: #{visited_refs}")
|
|
185
|
+
end
|
|
186
|
+
visited_refs.dup.push(ref).freeze
|
|
187
|
+
end
|
|
188
|
+
|
|
142
189
|
# Defines equality methods and #hash (for Hash / Set), based on a method #jsi_fingerprint
|
|
143
190
|
# implemented by the includer. #jsi_fingerprint is to include the class and any properties
|
|
144
191
|
# of the instance which constitute its identity.
|
|
@@ -179,24 +226,5 @@ module JSI
|
|
|
179
226
|
super
|
|
180
227
|
end
|
|
181
228
|
end
|
|
182
|
-
|
|
183
|
-
module Virtual
|
|
184
|
-
class InstantiationError < StandardError
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# this virtual class is not intended to be instantiated except by its subclasses, which override #initialize
|
|
188
|
-
def initialize
|
|
189
|
-
# :nocov:
|
|
190
|
-
raise(InstantiationError, "cannot instantiate virtual class #{self.class}")
|
|
191
|
-
# :nocov:
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
# virtual_method is used to indicate that the method calling it must be implemented on the (non-virtual) subclass
|
|
195
|
-
def virtual_method
|
|
196
|
-
# :nocov:
|
|
197
|
-
raise(Bug, "class #{self.class} must implement #{caller_locations.first.label}")
|
|
198
|
-
# :nocov:
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
229
|
end
|
|
202
230
|
end
|