jsi 0.8.1 → 0.9.0

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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -2
  3. data/CHANGELOG.md +9 -0
  4. data/LICENSE.md +2 -3
  5. data/README.md +68 -31
  6. data/docs/Glossary.md +313 -0
  7. data/jsi.gemspec +1 -0
  8. data/lib/jsi/base/mutability.rb +4 -0
  9. data/lib/jsi/base/node.rb +63 -24
  10. data/lib/jsi/base.rb +556 -173
  11. data/lib/jsi/metaschema_node/bootstrap_schema.rb +106 -56
  12. data/lib/jsi/metaschema_node.rb +227 -160
  13. data/lib/jsi/ptr.rb +32 -15
  14. data/lib/jsi/ref.rb +197 -0
  15. data/lib/jsi/registry.rb +311 -0
  16. data/lib/jsi/schema/cxt/child_application.rb +35 -0
  17. data/lib/jsi/schema/cxt/inplace_application.rb +37 -0
  18. data/lib/jsi/schema/cxt.rb +80 -0
  19. data/lib/jsi/schema/dialect.rb +137 -0
  20. data/lib/jsi/schema/draft04.rb +113 -5
  21. data/lib/jsi/schema/draft06.rb +123 -5
  22. data/lib/jsi/schema/draft07.rb +157 -5
  23. data/lib/jsi/schema/draft202012.rb +303 -0
  24. data/lib/jsi/schema/dynamic_anchor_map.rb +63 -0
  25. data/lib/jsi/schema/element.rb +69 -0
  26. data/lib/jsi/schema/elements/anchor.rb +13 -0
  27. data/lib/jsi/schema/elements/array_validation.rb +82 -0
  28. data/lib/jsi/schema/elements/comment.rb +10 -0
  29. data/lib/jsi/schema/{validation → elements}/const.rb +11 -7
  30. data/lib/jsi/schema/elements/contains.rb +59 -0
  31. data/lib/jsi/schema/elements/contains_minmax.rb +91 -0
  32. data/lib/jsi/schema/elements/content_encoding.rb +10 -0
  33. data/lib/jsi/schema/elements/content_media_type.rb +10 -0
  34. data/lib/jsi/schema/elements/content_schema.rb +16 -0
  35. data/lib/jsi/schema/elements/default.rb +11 -0
  36. data/lib/jsi/schema/elements/definitions.rb +19 -0
  37. data/lib/jsi/schema/elements/dependencies.rb +99 -0
  38. data/lib/jsi/schema/elements/dependent_required.rb +49 -0
  39. data/lib/jsi/schema/elements/dependent_schemas.rb +69 -0
  40. data/lib/jsi/schema/elements/dynamic_ref.rb +69 -0
  41. data/lib/jsi/schema/elements/enum.rb +26 -0
  42. data/lib/jsi/schema/elements/examples.rb +10 -0
  43. data/lib/jsi/schema/elements/format.rb +10 -0
  44. data/lib/jsi/schema/elements/id.rb +30 -0
  45. data/lib/jsi/schema/elements/if_then_else.rb +82 -0
  46. data/lib/jsi/schema/elements/info_bool.rb +10 -0
  47. data/lib/jsi/schema/elements/info_string.rb +10 -0
  48. data/lib/jsi/schema/elements/items.rb +93 -0
  49. data/lib/jsi/schema/elements/items_prefixed.rb +96 -0
  50. data/lib/jsi/schema/elements/not.rb +31 -0
  51. data/lib/jsi/schema/elements/numeric.rb +137 -0
  52. data/lib/jsi/schema/elements/numeric_draft04.rb +77 -0
  53. data/lib/jsi/schema/elements/object_validation.rb +55 -0
  54. data/lib/jsi/schema/elements/pattern.rb +35 -0
  55. data/lib/jsi/schema/elements/properties.rb +145 -0
  56. data/lib/jsi/schema/elements/property_names.rb +48 -0
  57. data/lib/jsi/schema/elements/ref.rb +62 -0
  58. data/lib/jsi/schema/elements/required.rb +34 -0
  59. data/lib/jsi/schema/elements/self.rb +24 -0
  60. data/lib/jsi/schema/elements/some_of.rb +180 -0
  61. data/lib/jsi/schema/elements/string_validation.rb +57 -0
  62. data/lib/jsi/schema/elements/type.rb +43 -0
  63. data/lib/jsi/schema/elements/unevaluated_items.rb +54 -0
  64. data/lib/jsi/schema/elements/unevaluated_properties.rb +54 -0
  65. data/lib/jsi/schema/elements/xschema.rb +10 -0
  66. data/lib/jsi/schema/elements/xvocabulary.rb +10 -0
  67. data/lib/jsi/schema/elements.rb +101 -0
  68. data/lib/jsi/schema/issue.rb +3 -4
  69. data/lib/jsi/schema/schema_ancestor_node.rb +103 -50
  70. data/lib/jsi/schema/vocabulary.rb +36 -0
  71. data/lib/jsi/schema.rb +520 -338
  72. data/lib/jsi/schema_classes.rb +168 -124
  73. data/lib/jsi/schema_set.rb +67 -126
  74. data/lib/jsi/set.rb +23 -0
  75. data/lib/jsi/simple_wrap.rb +13 -16
  76. data/lib/jsi/struct.rb +57 -0
  77. data/lib/jsi/uri.rb +40 -0
  78. data/lib/jsi/util/private/memo_map.rb +9 -13
  79. data/lib/jsi/util/private.rb +57 -12
  80. data/lib/jsi/util/typelike.rb +19 -64
  81. data/lib/jsi/util.rb +52 -34
  82. data/lib/jsi/validation/error.rb +41 -2
  83. data/lib/jsi/validation/result.rb +118 -71
  84. data/lib/jsi/validation.rb +1 -6
  85. data/lib/jsi/version.rb +1 -1
  86. data/lib/jsi.rb +158 -41
  87. data/lib/schemas/json-schema.org/draft/2020-12/schema.rb +67 -0
  88. data/lib/schemas/json-schema.org/draft-04/schema.rb +63 -106
  89. data/lib/schemas/json-schema.org/draft-06/schema.rb +56 -105
  90. data/lib/schemas/json-schema.org/draft-07/schema.rb +67 -124
  91. data/readme.rb +3 -3
  92. data/{resources}/schemas/2020-12_strict.json +19 -0
  93. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/applicator.json +48 -0
  94. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/content.json +17 -0
  95. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/core.json +51 -0
  96. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-annotation.json +14 -0
  97. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-assertion.json +14 -0
  98. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/meta-data.json +37 -0
  99. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/unevaluated.json +15 -0
  100. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/validation.json +98 -0
  101. data/{resources}/schemas/json-schema.org/draft/2020-12/schema.json +58 -0
  102. metadata +70 -48
  103. data/lib/jsi/schema/application/child_application/contains.rb +0 -25
  104. data/lib/jsi/schema/application/child_application/draft04.rb +0 -21
  105. data/lib/jsi/schema/application/child_application/draft06.rb +0 -28
  106. data/lib/jsi/schema/application/child_application/draft07.rb +0 -28
  107. data/lib/jsi/schema/application/child_application/items.rb +0 -18
  108. data/lib/jsi/schema/application/child_application/properties.rb +0 -25
  109. data/lib/jsi/schema/application/child_application.rb +0 -13
  110. data/lib/jsi/schema/application/draft04.rb +0 -8
  111. data/lib/jsi/schema/application/draft06.rb +0 -8
  112. data/lib/jsi/schema/application/draft07.rb +0 -8
  113. data/lib/jsi/schema/application/inplace_application/dependencies.rb +0 -28
  114. data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -25
  115. data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -26
  116. data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -32
  117. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +0 -20
  118. data/lib/jsi/schema/application/inplace_application/ref.rb +0 -18
  119. data/lib/jsi/schema/application/inplace_application/someof.rb +0 -44
  120. data/lib/jsi/schema/application/inplace_application.rb +0 -14
  121. data/lib/jsi/schema/application.rb +0 -12
  122. data/lib/jsi/schema/ref.rb +0 -186
  123. data/lib/jsi/schema/validation/array.rb +0 -69
  124. data/lib/jsi/schema/validation/contains.rb +0 -25
  125. data/lib/jsi/schema/validation/dependencies.rb +0 -49
  126. data/lib/jsi/schema/validation/draft04/minmax.rb +0 -93
  127. data/lib/jsi/schema/validation/draft04.rb +0 -110
  128. data/lib/jsi/schema/validation/draft06.rb +0 -120
  129. data/lib/jsi/schema/validation/draft07.rb +0 -157
  130. data/lib/jsi/schema/validation/enum.rb +0 -25
  131. data/lib/jsi/schema/validation/ifthenelse.rb +0 -46
  132. data/lib/jsi/schema/validation/items.rb +0 -54
  133. data/lib/jsi/schema/validation/not.rb +0 -20
  134. data/lib/jsi/schema/validation/numeric.rb +0 -121
  135. data/lib/jsi/schema/validation/object.rb +0 -45
  136. data/lib/jsi/schema/validation/pattern.rb +0 -34
  137. data/lib/jsi/schema/validation/properties.rb +0 -101
  138. data/lib/jsi/schema/validation/property_names.rb +0 -32
  139. data/lib/jsi/schema/validation/ref.rb +0 -40
  140. data/lib/jsi/schema/validation/required.rb +0 -27
  141. data/lib/jsi/schema/validation/someof.rb +0 -90
  142. data/lib/jsi/schema/validation/string.rb +0 -47
  143. data/lib/jsi/schema/validation/type.rb +0 -49
  144. data/lib/jsi/schema/validation.rb +0 -49
  145. data/lib/jsi/schema_registry.rb +0 -200
  146. data/lib/jsi/util/private/attr_struct.rb +0 -141
@@ -4,7 +4,7 @@ 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 < ::Set
7
+ class SchemaSet < Set
8
8
  class << self
9
9
  # Builds a SchemaSet, yielding a yielder to be called with each schema of the SchemaSet.
10
10
  #
@@ -13,21 +13,6 @@ module JSI
13
13
  def build(&block)
14
14
  new(Enumerator.new(&block))
15
15
  end
16
-
17
- # ensures the given param becomes a SchemaSet. returns the param if it is already SchemaSet, otherwise
18
- # initializes a SchemaSet from it.
19
- #
20
- # @param schemas [SchemaSet, Enumerable] the object to ensure becomes a SchemaSet
21
- # @return [SchemaSet] the given SchemaSet, or a SchemaSet initialized from the given Enumerable
22
- # @raise [ArgumentError] when the schemas param is not an Enumerable
23
- # @raise [Schema::NotASchemaError] when the schemas param contains objects which are not Schemas
24
- def ensure_schema_set(schemas)
25
- if schemas.is_a?(SchemaSet)
26
- schemas
27
- else
28
- new(schemas)
29
- end
30
- end
31
16
  end
32
17
 
33
18
  # initializes a SchemaSet from the given enum and freezes it.
@@ -36,7 +21,7 @@ module JSI
36
21
  # if no block is given, the enum must contain only Schemas.
37
22
  #
38
23
  # @param enum [#each] the schemas to be included in the SchemaSet, or items to be passed to the block
39
- # @yieldparam yields each element of enum for preprocessing into a Schema
24
+ # @yieldparam yields each element of `enum` for preprocessing into a Schema
40
25
  # @yieldreturn [JSI::Schema]
41
26
  # @raise [JSI::Schema::NotASchemaError]
42
27
  def initialize(enum, &block)
@@ -53,7 +38,15 @@ module JSI
53
38
  raise(ArgumentError, "#{SchemaSet} initialized with non-Enumerable: #{enum.pretty_inspect.chomp}")
54
39
  end
55
40
 
56
- super
41
+ super(&nil) # note super() does implicitly pass block without &nil
42
+
43
+ compare_by_identity
44
+
45
+ if block
46
+ enum.each_entry { |o| add(block[o]) }
47
+ else
48
+ merge(enum)
49
+ end
57
50
 
58
51
  not_schemas = reject { |s| s.is_a?(Schema) }
59
52
  if !not_schemas.empty?
@@ -67,130 +60,85 @@ module JSI
67
60
  end
68
61
 
69
62
  # Instantiates a new JSI whose content comes from the given `instance` param.
70
- # This SchemaSet indicates the schemas of the JSI - its schemas are inplace
63
+ #
64
+ # The schemas of the JSI (its {Base#jsi_schemas}) are in-place
71
65
  # applicators of this set's schemas which apply to the given instance.
66
+ # The JSI's {Base#jsi_indicated_schemas} set is this set.
67
+ #
68
+ # The resulting JSI is an instance of a number of modules:
69
+ #
70
+ # - The {SchemaModule JSI schema module} of each applicator schema.
71
+ # - {Base::HashNode}, {Base::ArrayNode}, or {Base::StringNode} if the instance is
72
+ # a hash/object, array, or string.
73
+ # - A module defining readers for properties described by applicator schemas.
74
+ # If the instance is mutable, writers as well.
72
75
  #
73
76
  # @param instance [Object] the instance to be represented as a JSI
74
- # @param uri [#to_str, Addressable::URI] The retrieval URI of the instance.
77
+ # @param base_uri [#to_str, URI, nil]
78
+ # The base URI of the instance document. An absolute URI.
75
79
  #
76
- # It is rare that this needs to be specified, and only useful for instances which contain schemas.
77
- # See {Schema::MetaSchema#new_schema}'s `uri` param documentation.
80
+ # It is rare that this needs to be specified. It is useful when the instance contains schemas,
81
+ # and schemas in the document use relative URIs for `$id` or `$ref` without an absolute id
82
+ # in an ancestor schema - those URIs will be resolved relative to `base_uri`.
83
+ #
84
+ # See also {Base::Conf conf} {Base::Conf#root_uri `root_uri`}. `base_uri` is not used to identify
85
+ # any resource, only to resolve relative URIs. `root_uri` does identify the root resource.
78
86
  # @param register [Boolean] Whether schema resources in the instantiated JSI will be registered
79
- # in the schema registry indicated by param `schema_registry`.
87
+ # in the {Base::Conf configured} {Base::Conf#registry `registry`}.
80
88
  # This is only useful when the JSI is a schema or contains schemas.
81
- # The JSI's root will be registered with the `uri` param, if specified, whether or not the
82
- # root is a schema.
83
- # @param schema_registry [SchemaRegistry, nil] The registry to use for references to other schemas and,
84
- # depending on `register` and `uri` params, to register this JSI and/or any contained schemas with
85
- # declared URIs.
86
89
  # @param stringify_symbol_keys [Boolean] Whether the instance content will have any Symbol keys of Hashes
87
90
  # replaced with Strings (recursively through the document).
88
91
  # Replacement is done on a copy; the given instance is not modified.
89
- # @param to_immutable [#call, nil] A proc/callable which takes given instance content
90
- # and results in an immutable (i.e. deeply frozen) object equal to that.
91
- # If the instantiated JSI will be mutable, this is not used.
92
- # Though not recommended, this may be nil with immutable JSIs if the instance content is otherwise
93
- # guaranteed to be immutable, as well as any modified copies of the instance.
94
92
  # @param mutable [Boolean] Whether the instantiated JSI will be mutable.
95
- # The instance content will be transformed with `to_immutable` if the JSI will be immutable.
96
- # @return [JSI::Base subclass] a JSI whose content comes from the given instance and whose schemas are
97
- # inplace applicators of the schemas in this set.
93
+ # The instance content will be transformed with the {Base::Conf configured}
94
+ # {Base::Conf#to_immutable `to_immutable`} if the JSI will be immutable.
95
+ # @param conf_kw Additional keyword params are passed to initialize a {Base::Conf}, the JSI's {Base#jsi_conf}.
96
+ # @return [Base] a JSI whose content comes from the given instance and whose schemas are
97
+ # in-place applicators of the schemas in this set.
98
98
  def new_jsi(instance,
99
- uri: nil,
99
+ base_uri: nil,
100
100
  register: false,
101
- schema_registry: JSI.schema_registry,
102
101
  stringify_symbol_keys: false,
103
- to_immutable: DEFAULT_CONTENT_TO_IMMUTABLE,
104
- mutable: false
102
+ mutable: false,
103
+ **conf_kw
105
104
  )
106
- instance = Util.deep_stringify_symbol_keys(instance) if stringify_symbol_keys
105
+ raise(BlockGivenError) if block_given?
107
106
 
108
- instance = to_immutable.call(instance) if !mutable && to_immutable
107
+ conf = Base::Conf.new(**conf_kw)
108
+
109
+ instance = Util.deep_stringify_symbol_keys(instance) if stringify_symbol_keys
109
110
 
110
- applied_schemas = inplace_applicator_schemas(instance)
111
+ instance = conf.to_immutable.call(instance) if !mutable && conf.to_immutable
111
112
 
112
- if uri
113
- unless uri.respond_to?(:to_str)
114
- raise(TypeError, "uri must be string or Addressable::URI; got: #{uri.inspect}")
115
- end
116
- uri = Util.uri(uri)
117
- unless uri.absolute? && !uri.fragment
118
- raise(ArgumentError, "uri must be an absolute URI with no fragment; got: #{uri.inspect}")
119
- end
113
+ applied_schemas = SchemaSet.build do |y|
114
+ c = y.method(:yield) # TODO drop c, just pass y, when all supported Enumerator::Yielder.method_defined?(:to_proc)
115
+ each { |is| is.each_inplace_applicator_schema(instance, &c) }
120
116
  end
121
117
 
118
+ base_uri = Util.uri(base_uri, nnil: false, yabs: true) || conf.root_uri
119
+
122
120
  jsi_class = JSI::SchemaClasses.class_for_schemas(applied_schemas,
123
121
  includes: SchemaClasses.includes_for(instance),
124
122
  mutable: mutable,
125
123
  )
126
- jsi = jsi_class.new(instance,
124
+ jsi = jsi_class.new(
125
+ jsi_document: instance,
127
126
  jsi_indicated_schemas: self,
128
- jsi_schema_base_uri: uri,
129
- jsi_schema_registry: schema_registry,
130
- jsi_content_to_immutable: to_immutable,
131
- )
127
+ jsi_base_uri: base_uri,
128
+ jsi_conf: conf,
129
+ ).send(:jsi_initialized)
132
130
 
133
- schema_registry.register(jsi) if register && schema_registry
131
+ conf.registry.register(jsi) if register && conf.registry
134
132
 
135
133
  jsi
136
134
  end
137
135
 
138
- # a set of inplace applicator schemas of each schema in this set which apply to the given instance.
139
- # (see {Schema#inplace_applicator_schemas})
140
- #
141
- # @param instance (see Schema#inplace_applicator_schemas)
142
- # @return [JSI::SchemaSet]
143
- def inplace_applicator_schemas(instance)
144
- SchemaSet.new(each_inplace_applicator_schema(instance))
145
- end
146
-
147
- # yields each inplace applicator schema which applies to the given instance.
148
- #
149
- # @param instance (see Schema#inplace_applicator_schemas)
150
- # @yield [JSI::Schema]
151
- # @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
152
- def each_inplace_applicator_schema(instance, &block)
153
- return to_enum(__method__, instance) unless block
154
-
155
- each do |schema|
156
- schema.each_inplace_applicator_schema(instance, &block)
157
- end
158
-
159
- nil
160
- end
161
-
162
- # a set of child applicator subschemas of each schema in this set which apply to the child
163
- # of the given instance on the given token.
164
- # (see {Schema#child_applicator_schemas})
165
- #
166
- # @param instance (see Schema#child_applicator_schemas)
167
- # @return [JSI::SchemaSet]
168
- def child_applicator_schemas(token, instance)
169
- SchemaSet.new(each_child_applicator_schema(token, instance))
170
- end
171
-
172
- # yields each child applicator schema which applies to the child of
173
- # the given instance on the given token.
174
- #
175
- # @param (see Schema#child_applicator_schemas)
176
- # @yield [JSI::Schema]
177
- # @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
178
- def each_child_applicator_schema(token, instance, &block)
179
- return to_enum(__method__, token, instance) unless block
180
-
181
- each do |schema|
182
- schema.each_child_applicator_schema(token, instance, &block)
183
- end
184
-
185
- nil
186
- end
187
-
188
136
  # validates the given instance against our schemas
189
137
  #
190
138
  # @param instance [Object] the instance to validate against our schemas
191
139
  # @return [JSI::Validation::Result]
192
140
  def instance_validate(instance)
193
- inject(Validation::FullResult.new) do |result, schema|
141
+ inject(Validation::Result::Full.new) do |result, schema|
194
142
  result.merge(schema.instance_validate(instance))
195
143
  end.freeze
196
144
  end
@@ -202,26 +150,19 @@ module JSI
202
150
  all? { |schema| schema.instance_valid?(instance) }
203
151
  end
204
152
 
205
- # @return [String]
206
- def inspect
207
- -"#{self.class}[#{map(&:inspect).join(", ")}]"
208
- end
209
-
210
- def to_s
211
- inspect
153
+ # @return [Set<SchemaModule>]
154
+ def jsi_schema_modules
155
+ Set.new(self, &:jsi_schema_module).freeze
212
156
  end
213
157
 
214
- def pretty_print(q)
215
- q.text self.class.to_s
216
- q.text '['
217
- q.group(2) {
218
- q.breakable('')
219
- q.seplist(self, nil, :each) { |e|
220
- q.pp e
221
- }
222
- }
223
- q.breakable ''
224
- q.text ']'
158
+ # Builds a SchemaSet, yielding each schema and a callable to be called with each schema of the resulting SchemaSet.
159
+ # @yield [Schema, #to_proc]
160
+ # @return [SchemaSet]
161
+ def each_yield_set(&block)
162
+ self.class.new(Enumerator.new do |y|
163
+ c = y.method(:yield) # TODO drop c, just pass y, when all supported Enumerator::Yielder.method_defined?(:to_proc)
164
+ each { |schema| yield(schema, c) }
165
+ end)
225
166
  end
226
167
  end
227
168
  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
@@ -1,26 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSI
4
- simple_wrap_implementation = Module.new do
5
- def internal_child_applicate_keywords(token, instance)
6
- yield self
7
- end
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
- def internal_inplace_applicate_keywords(instance, visited_refs)
10
- yield self
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])
15
+ simple_wrap_metaschema = JSI.new_metaschema_node(nil, dialect: dialect)
18
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::Implementation = simple_wrap_implementation
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
- if @results.key?(key) && inputs_hash == @results[key].inputs_hash && inputs == @results[key].inputs
47
- @results[key].value
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] = Result.new(value: value, inputs: inputs, inputs_hash: inputs_hash)
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
@@ -5,7 +5,6 @@ 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
@@ -69,10 +68,16 @@ module JSI
69
68
  end
70
69
 
71
70
  def const_name_from_parts(parts, join: '')
71
+ initAZ = false
72
72
  parts = parts.map do |part|
73
- part = part.dup
74
- part[/\A[^a-zA-Z]*/] = ''
75
- part[0] = part[0].upcase if part[0]
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
76
81
  part.gsub!(RUBY_REJECT_NAME_RE, '_')
77
82
  part
78
83
  end
@@ -83,18 +88,46 @@ module JSI
83
88
  end
84
89
  end
85
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
+
86
101
  # string or URI → frozen URI
87
- # @return [Addressable::URI]
88
- def uri(uri)
89
- if uri.is_a?(Addressable::URI)
90
- if uri.frozen?
91
- uri
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)
92
122
  else
93
- uri.dup.freeze
123
+ raise(URIError, "URI must have no fragment. got: #{uri.inspect}")
94
124
  end
95
- else
96
- Addressable::URI.parse(uri).freeze
97
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
98
131
  end
99
132
 
100
133
  # this is the Y-combinator, which allows anonymous recursive functions. for a simple example,
@@ -141,6 +174,18 @@ module JSI
141
174
  nil
142
175
  end
143
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
+
144
189
  # Defines equality methods and #hash (for Hash / Set), based on a method #jsi_fingerprint
145
190
  # implemented by the includer. #jsi_fingerprint is to include the class and any properties
146
191
  # of the instance which constitute its identity.