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,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Base::Mutable
5
+ include(Util::FingerprintHash)
6
+
7
+ def jsi_node_content
8
+ jsi_ptr.evaluate(jsi_document)
9
+ end
10
+
11
+ def jsi_mutable?
12
+ true
13
+ end
14
+
15
+ private
16
+
17
+ def jsi_mutability_initialize
18
+ @child_node_by_token_map = method(:jsi_child_node_by_token_compute)
19
+ @child_node_map = jsi_memomap(key_by: Base::BY_TOKEN, &method(:jsi_child_node_compute))
20
+ end
21
+
22
+ def jsi_memomap_class
23
+ Util::MemoMap::Mutable
24
+ end
25
+ end
26
+
27
+ module Base::Immutable
28
+ include(Util::FingerprintHash::Immutable)
29
+
30
+ attr_reader(:jsi_node_content)
31
+
32
+ def jsi_mutable?
33
+ false
34
+ end
35
+
36
+ private
37
+
38
+ def jsi_mutability_initialize
39
+ @child_node_by_token_map = jsi_memomap(&method(:jsi_child_node_by_token_compute))
40
+ @child_node_map = method(:jsi_child_node_compute)
41
+ @jsi_node_content = @jsi_ptr.evaluate(@jsi_document)
42
+ end
43
+
44
+ def jsi_memomap_class
45
+ Util::MemoMap::Immutable
46
+ end
47
+ end
48
+ end
data/lib/jsi/base/node.rb CHANGED
@@ -1,32 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSI
4
- module Base::Enumerable
5
- include ::Enumerable
6
-
7
- # an Array containing each item in this JSI.
8
- #
9
- # @param kw keyword arguments are passed to {Base#[]} - see its keyword params
10
- # @return [Array]
11
- def to_a(**kw)
12
- # TODO remove eventually (keyword argument compatibility)
13
- # discard when all supported ruby versions Enumerable#to_a delegate keywords to #each (3.0.1 breaks; 2.7.x warns)
14
- # https://bugs.ruby-lang.org/issues/18289
15
- ary = []
16
- each(**kw) do |e|
17
- ary << e
18
- end
19
- ary.freeze
20
- end
21
-
22
- alias_method :entries, :to_a
23
- end
24
-
25
- # module extending a {JSI::Base} object when its instance (its {Base#jsi_node_content})
26
- # is a Hash (or responds to `#to_hash`)
4
+ # Included on {Base} subclasses for instances that are Hash or
5
+ # [#to_hash](https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects).
6
+ #
7
+ # Dynamically defines most methods of Hash to make the JSI duck-type like a Hash.
27
8
  module Base::HashNode
28
- include Base::Enumerable
29
-
30
9
  # instantiates and yields each property name (hash key) as a JSI described by any `propertyNames` schemas.
31
10
  #
32
11
  # @yield [JSI::Base]
@@ -34,12 +13,8 @@ module JSI
34
13
  def jsi_each_propertyName
35
14
  return to_enum(__method__) { jsi_node_content_hash_pubsend(:size) } unless block_given?
36
15
 
37
- property_schemas = SchemaSet.build do |schemas|
38
- jsi_schemas.each do |s|
39
- if s.keyword?('propertyNames') && s['propertyNames'].is_a?(Schema)
40
- schemas << s['propertyNames']
41
- end
42
- end
16
+ property_schemas = jsi_schemas.each_yield_set do |s, y|
17
+ s.dialect_invoke_each(:propertyNames, &y)
43
18
  end
44
19
  jsi_node_content_hash_pubsend(:each_key) do |key|
45
20
  yield property_schemas.new_jsi(key)
@@ -60,14 +35,14 @@ module JSI
60
35
  nil
61
36
  end
62
37
 
63
- # See {Base#jsi_child_token_in_range?}
64
- def jsi_child_token_in_range?(token)
38
+ # See {Base#jsi_child_token_present?}
39
+ def jsi_child_token_present?(token)
65
40
  jsi_node_content_hash_pubsend(:key?, token)
66
41
  end
67
42
 
68
43
  # See {Base#jsi_node_content_child}
69
44
  def jsi_node_content_child(token)
70
- # I could check token_in_range? and return nil here (as ArrayNode does).
45
+ # I could check token_present? and return nil here (as ArrayNode does).
71
46
  # without that check, if the instance defines Hash#default or #default_proc, that result is returned.
72
47
  # the preferred mechanism for a JSI's default value should be its schema.
73
48
  # but there's no compelling reason not to support both, so I'll return what #[] returns.
@@ -76,6 +51,8 @@ module JSI
76
51
 
77
52
  # See {Base#[]}
78
53
  def [](token, as_jsi: jsi_child_as_jsi_default, use_default: jsi_child_use_default_default)
54
+ raise(BlockGivenError) if block_given?
55
+ token = token.jsi_node_content if token.is_a?(Schema::SchemaAncestorNode)
79
56
  if jsi_node_content_hash_pubsend(:key?, token)
80
57
  jsi_child(token, as_jsi: as_jsi)
81
58
  else
@@ -87,19 +64,45 @@ module JSI
87
64
  end
88
65
  end
89
66
 
90
- # yields each hash key and value of this node.
67
+ # See [Hash#store](https://ruby-doc.org/current/Hash.html#method-i-store)
68
+ def store(key, value)
69
+ self[key] = value
70
+ end
71
+
72
+ # See {Base#jsi_as_child_default_as_jsi}. true for HashNode.
73
+ def jsi_as_child_default_as_jsi
74
+ true
75
+ end
76
+
77
+ # yields each Hash key (JSON object property name) and value of this node.
91
78
  #
92
79
  # each yielded key is a key of the instance hash, and each yielded value is the result of {Base#[]}.
93
80
  #
81
+ # @param key_as_jsi (see #each_key)
94
82
  # @param kw keyword arguments are passed to {Base#[]}
95
83
  # @yield [Object, Object] each key and value of this hash node
96
84
  # @return [self, Enumerator] an Enumerator if invoked without a block; otherwise self
97
- def each(**kw, &block)
98
- return to_enum(__method__, **kw) { jsi_node_content_hash_pubsend(:size) } unless block
85
+ def each(key_as_jsi: false, **kw, &block)
86
+ return to_enum(__method__, key_as_jsi: key_as_jsi, **kw) { jsi_node_content_hash_pubsend(:size) } unless block
99
87
  if block.arity > 1
100
- jsi_node_content_hash_pubsend(:each_key) { |k| yield k, self[k, **kw] }
88
+ each_key(key_as_jsi: key_as_jsi) { |k| yield(k, self[k, **kw]) }
101
89
  else
102
- jsi_node_content_hash_pubsend(:each_key) { |k| yield [k, self[k, **kw]] }
90
+ each_key(key_as_jsi: key_as_jsi) { |k| yield([k, self[k, **kw]]) }
91
+ end
92
+ self
93
+ end
94
+
95
+ alias_method(:each_pair, :each)
96
+
97
+ # Yields each key (property name)
98
+ # @param key_as_jsi [Boolean] Yield each key as a JSI instance, per {#jsi_each_propertyName}
99
+ # @yield [String, Base]
100
+ def each_key(key_as_jsi: false, &block)
101
+ return to_enum(__method__, key_as_jsi: key_as_jsi) { size } unless block
102
+ if key_as_jsi
103
+ jsi_each_propertyName(&block)
104
+ else
105
+ jsi_node_content_hash_pubsend(:each_key, &block)
103
106
  end
104
107
  self
105
108
  end
@@ -109,7 +112,7 @@ module JSI
109
112
  # @return [Hash]
110
113
  def to_hash(**kw)
111
114
  hash = {}
112
- jsi_node_content_hash_pubsend(:each_key) { |k| hash[k] = self[k, **kw] }
115
+ each_key { |k| hash[k] = self[k, **kw] }
113
116
  hash.freeze
114
117
  end
115
118
 
@@ -121,7 +124,7 @@ module JSI
121
124
  k.is_a?(Symbol) ? k.to_s :
122
125
  k.respond_to?(:to_str) && (kstr = k.to_str).is_a?(String) ? kstr :
123
126
  raise(TypeError, "JSON object (Hash) cannot be keyed with: #{k.pretty_inspect.chomp}")
124
- hash[ks] = jsi_child(k, as_jsi: true).as_json(**options)
127
+ hash[ks] = jsi_child_node(k).as_json(**options)
125
128
  end
126
129
  hash
127
130
  end
@@ -158,7 +161,7 @@ module JSI
158
161
  end
159
162
 
160
163
  # methods that don't look at the value; can skip the overhead of #[] (invoked by #to_hash)
161
- SAFE_KEY_ONLY_METHODS.each do |method_name|
164
+ SAFE_KEY_ONLY_METHODS.reject { |m| instance_method(m).owner == self }.each do |method_name|
162
165
  if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
163
166
  define_method(method_name) do |*a, &b|
164
167
  jsi_node_content_hash_pubsend(method_name, *a, &b)
@@ -171,11 +174,11 @@ module JSI
171
174
  end
172
175
  end
173
176
 
174
- # module extending a {JSI::Base} object when its instance (its {Base#jsi_node_content})
175
- # is an Array (or responds to `#to_ary`)
177
+ # Included on {Base} subclasses for instances that are Array or
178
+ # [#to_ary](https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html#label-Array-Convertible+Objects).
179
+ #
180
+ # Dynamically defines most methods of Array to make the JSI duck-type like an Array.
176
181
  module Base::ArrayNode
177
- include Base::Enumerable
178
-
179
182
  # See {Base#jsi_array?}. Always true for ArrayNode.
180
183
  def jsi_array?
181
184
  true
@@ -188,16 +191,16 @@ module JSI
188
191
  nil
189
192
  end
190
193
 
191
- # See {Base#jsi_child_token_in_range?}
192
- def jsi_child_token_in_range?(token)
194
+ # See {Base#jsi_child_token_present?}
195
+ def jsi_child_token_present?(token)
193
196
  token.is_a?(Integer) && token >= 0 && token < jsi_node_content_ary_pubsend(:size)
194
197
  end
195
198
 
196
199
  # See {Base#jsi_node_content_child}
197
200
  def jsi_node_content_child(token)
198
- # we check token_in_range? here (unlike HashNode) because we do not want to pass
201
+ # we check token_present? here (unlike HashNode) because we do not want to pass
199
202
  # negative indices, Ranges, or non-Integers to Array#[]
200
- if jsi_child_token_in_range?(token)
203
+ if jsi_child_token_present?(token)
201
204
  jsi_node_content_ary_pubsend(:[], token)
202
205
  else
203
206
  nil
@@ -206,6 +209,8 @@ module JSI
206
209
 
207
210
  # See {Base#[]}
208
211
  def [](token, as_jsi: jsi_child_as_jsi_default, use_default: jsi_child_use_default_default)
212
+ raise(BlockGivenError) if block_given?
213
+ token = token.jsi_node_content if token.is_a?(Schema::SchemaAncestorNode)
209
214
  size = jsi_node_content_ary_pubsend(:size)
210
215
  if token.is_a?(Integer)
211
216
  if token < 0
@@ -265,6 +270,11 @@ module JSI
265
270
  end
266
271
  end
267
272
 
273
+ # See {Base#jsi_as_child_default_as_jsi}. true for ArrayNode.
274
+ def jsi_as_child_default_as_jsi
275
+ true
276
+ end
277
+
268
278
  # yields each array element of this node.
269
279
  #
270
280
  # each yielded element is the result of {Base#[]} for each index of the instance array.
@@ -288,7 +298,7 @@ module JSI
288
298
 
289
299
  # See {Base#as_json}
290
300
  def as_json(options = {})
291
- each_index.map { |i| jsi_child(i, as_jsi: true).as_json(**options) }
301
+ each_index.map { |i| jsi_child_node(i).as_json(**options) }
292
302
  end
293
303
 
294
304
  include Util::Arraylike
@@ -324,7 +334,7 @@ module JSI
324
334
 
325
335
  # methods that don't look at the value; can skip the overhead of #[] (invoked by #to_a).
326
336
  # we override these methods from Arraylike
327
- SAFE_INDEX_ONLY_METHODS.each do |method_name|
337
+ SAFE_INDEX_ONLY_METHODS.reject { |m| instance_method(m).owner == self }.each do |method_name|
328
338
  if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
329
339
  define_method(method_name) do |*a, &b|
330
340
  jsi_node_content_ary_pubsend(method_name, *a, &b)
@@ -337,6 +347,10 @@ module JSI
337
347
  end
338
348
  end
339
349
 
350
+ # Included on {Base} subclasses for instances that are String or
351
+ # [#to_str](https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html#label-String-Convertible+Objects).
352
+ #
353
+ # Dynamically defines most methods of String to make the JSI duck-type like a String.
340
354
  module Base::StringNode
341
355
  delegate_methods = %w(% * + << =~ [] []=
342
356
  ascii_only? b byteindex byterindex bytes bytesize byteslice bytesplice capitalize capitalize!