jsi 0.7.0 → 0.8.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -1
  3. data/CHANGELOG.md +15 -0
  4. data/README.md +19 -18
  5. data/jsi.gemspec +2 -3
  6. data/lib/jsi/base/mutability.rb +44 -0
  7. data/lib/jsi/base/node.rb +199 -34
  8. data/lib/jsi/base.rb +412 -228
  9. data/lib/jsi/jsi_coder.rb +18 -16
  10. data/lib/jsi/metaschema_node/bootstrap_schema.rb +57 -23
  11. data/lib/jsi/metaschema_node.rb +138 -107
  12. data/lib/jsi/ptr.rb +59 -37
  13. data/lib/jsi/schema/application/child_application/draft04.rb +0 -1
  14. data/lib/jsi/schema/application/child_application/draft06.rb +0 -1
  15. data/lib/jsi/schema/application/child_application/draft07.rb +0 -1
  16. data/lib/jsi/schema/application/child_application.rb +0 -25
  17. data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -1
  18. data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -1
  19. data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -1
  20. data/lib/jsi/schema/application/inplace_application/ref.rb +1 -1
  21. data/lib/jsi/schema/application/inplace_application/someof.rb +1 -1
  22. data/lib/jsi/schema/application/inplace_application.rb +0 -27
  23. data/lib/jsi/schema/draft04.rb +0 -1
  24. data/lib/jsi/schema/draft06.rb +0 -1
  25. data/lib/jsi/schema/draft07.rb +0 -1
  26. data/lib/jsi/schema/ref.rb +44 -18
  27. data/lib/jsi/schema/schema_ancestor_node.rb +65 -56
  28. data/lib/jsi/schema/validation/contains.rb +1 -1
  29. data/lib/jsi/schema/validation/draft04/minmax.rb +2 -0
  30. data/lib/jsi/schema/validation/draft04.rb +0 -2
  31. data/lib/jsi/schema/validation/draft06.rb +0 -2
  32. data/lib/jsi/schema/validation/draft07.rb +0 -2
  33. data/lib/jsi/schema/validation/items.rb +3 -3
  34. data/lib/jsi/schema/validation/pattern.rb +1 -1
  35. data/lib/jsi/schema/validation/properties.rb +4 -4
  36. data/lib/jsi/schema/validation/ref.rb +1 -1
  37. data/lib/jsi/schema/validation.rb +0 -2
  38. data/lib/jsi/schema.rb +405 -194
  39. data/lib/jsi/schema_classes.rb +196 -127
  40. data/lib/jsi/schema_registry.rb +66 -17
  41. data/lib/jsi/schema_set.rb +76 -30
  42. data/lib/jsi/simple_wrap.rb +2 -7
  43. data/lib/jsi/util/private/attr_struct.rb +28 -14
  44. data/lib/jsi/util/private/memo_map.rb +75 -0
  45. data/lib/jsi/util/private.rb +73 -92
  46. data/lib/jsi/util/typelike.rb +28 -28
  47. data/lib/jsi/util.rb +120 -36
  48. data/lib/jsi/validation/error.rb +4 -0
  49. data/lib/jsi/validation/result.rb +18 -32
  50. data/lib/jsi/version.rb +1 -1
  51. data/lib/jsi.rb +67 -25
  52. data/lib/schemas/json-schema.org/draft-04/schema.rb +159 -4
  53. data/lib/schemas/json-schema.org/draft-06/schema.rb +161 -4
  54. data/lib/schemas/json-schema.org/draft-07/schema.rb +188 -4
  55. metadata +19 -5
  56. data/lib/jsi/metaschema.rb +0 -6
  57. data/lib/jsi/schema/validation/core.rb +0 -39
data/lib/jsi/ptr.rb CHANGED
@@ -16,12 +16,17 @@ module JSI
16
16
  class ResolutionError < Error
17
17
  end
18
18
 
19
+ POS_INT_RE = /\A[1-9]\d*\z/
20
+ private_constant :POS_INT_RE
21
+
19
22
  # instantiates a pointer or returns the given pointer
20
23
  # @param ary_ptr [#to_ary, JSI::Ptr] an array of tokens, or a pointer
21
24
  # @return [JSI::Ptr]
22
25
  def self.ary_ptr(ary_ptr)
23
26
  if ary_ptr.is_a?(Ptr)
24
27
  ary_ptr
28
+ elsif ary_ptr == Util::EMPTY_ARY
29
+ EMPTY
25
30
  else
26
31
  new(ary_ptr)
27
32
  end
@@ -42,7 +47,7 @@ module JSI
42
47
  # @param tokens any number of tokens
43
48
  # @return [JSI::Ptr]
44
49
  def self.[](*tokens)
45
- new(tokens)
50
+ tokens.empty? ? EMPTY : new(tokens.freeze)
46
51
  end
47
52
 
48
53
  # parse a URI-escaped fragment and instantiate as a JSI::Ptr
@@ -55,6 +60,12 @@ module JSI
55
60
  # JSI::Ptr.from_fragment('/foo%20bar')
56
61
  # => JSI::Ptr["foo bar"]
57
62
  #
63
+ # Note: A fragment does not include a leading '#'. The string "#/foo" is a URI containing the
64
+ # fragment "/foo", which should be parsed by `Addressable::URI` before passing to this method, e.g.:
65
+ #
66
+ # JSI::Ptr.from_fragment(Addressable::URI.parse("#/foo").fragment)
67
+ # => JSI::Ptr["foo"]
68
+ #
58
69
  # @param fragment [String] a fragment containing a pointer
59
70
  # @return [JSI::Ptr]
60
71
  # @raise [JSI::Ptr::PointerSyntaxError] when the fragment does not contain a pointer with
@@ -75,13 +86,17 @@ module JSI
75
86
  # @return [JSI::Ptr]
76
87
  # @raise [JSI::Ptr::PointerSyntaxError] when the pointer_string does not have valid pointer syntax
77
88
  def self.from_pointer(pointer_string)
78
- tokens = pointer_string.split('/', -1).map! do |piece|
79
- piece.gsub('~1', '/').gsub('~0', '~')
80
- end
81
- if tokens[0] == ''
82
- new(tokens[1..-1])
83
- elsif tokens.empty?
84
- new(tokens)
89
+ pointer_string = pointer_string.to_str
90
+ if pointer_string[0] == ?/
91
+ tokens = pointer_string.split('/', -1).map! do |piece|
92
+ piece.gsub!('~1', '/')
93
+ piece.gsub!('~0', '~')
94
+ piece.freeze
95
+ end
96
+ tokens.shift
97
+ new(tokens.freeze)
98
+ elsif pointer_string.empty?
99
+ EMPTY
85
100
  else
86
101
  raise(PointerSyntaxError, "Invalid pointer syntax in #{pointer_string.inspect}: pointer must begin with /")
87
102
  end
@@ -94,7 +109,7 @@ module JSI
94
109
  unless tokens.respond_to?(:to_ary)
95
110
  raise(TypeError, "tokens must be an array. got: #{tokens.inspect}")
96
111
  end
97
- @tokens = tokens.to_ary.map(&:freeze).freeze
112
+ @tokens = Util.deep_to_frozen(tokens.to_ary, not_implemented: proc { |o| o })
98
113
  end
99
114
 
100
115
  attr_reader :tokens
@@ -117,19 +132,19 @@ module JSI
117
132
  # the pointer string representation of this pointer
118
133
  # @return [String]
119
134
  def pointer
120
- tokens.map { |t| '/' + t.to_s.gsub('~', '~0').gsub('/', '~1') }.join('')
135
+ tokens.map { |t| '/' + t.to_s.gsub('~', '~0').gsub('/', '~1') }.join('').freeze
121
136
  end
122
137
 
123
138
  # the fragment string representation of this pointer
124
139
  # @return [String]
125
140
  def fragment
126
- Addressable::URI.escape(pointer)
141
+ Addressable::URI.escape(pointer).freeze
127
142
  end
128
143
 
129
144
  # a URI consisting of a fragment containing this pointer's fragment string representation
130
145
  # @return [Addressable::URI]
131
146
  def uri
132
- Addressable::URI.new(fragment: fragment)
147
+ Addressable::URI.new(fragment: fragment).freeze
133
148
  end
134
149
 
135
150
  # whether this pointer is empty, i.e. it has no tokens
@@ -149,29 +164,33 @@ module JSI
149
164
  if root?
150
165
  raise(Ptr::Error, "cannot access parent of root pointer: #{pretty_inspect.chomp}")
151
166
  end
152
- Ptr.new(tokens[0...-1])
167
+ tokens.size == 1 ? EMPTY : Ptr.new(tokens[0...-1].freeze)
153
168
  end
154
169
 
155
- # whether this pointer contains the other_ptr - that is, whether this pointer is an ancestor
156
- # of `other_ptr`, a descendent pointer. `contains?` is inclusive; a pointer does contain itself.
170
+ # whether this pointer is an ancestor of `other_ptr`, a descendent pointer.
171
+ # `ancestor_of?` is inclusive; a pointer is an ancestor of itself.
172
+ #
157
173
  # @return [Boolean]
158
- def contains?(other_ptr)
174
+ def ancestor_of?(other_ptr)
159
175
  tokens == other_ptr.tokens[0...tokens.size]
160
176
  end
161
177
 
178
+ # @deprecated
179
+ def contains?(other_ptr)
180
+ ancestor_of?(other_ptr)
181
+ end
182
+
162
183
  # part of this pointer relative to the given ancestor_ptr
163
184
  # @return [JSI::Ptr]
164
185
  # @raise [JSI::Ptr::Error] if the given ancestor_ptr is not an ancestor of this pointer
165
186
  def relative_to(ancestor_ptr)
166
- unless ancestor_ptr.contains?(self)
187
+ return self if ancestor_ptr.empty?
188
+ unless ancestor_ptr.ancestor_of?(self)
167
189
  raise(Error, "ancestor_ptr #{ancestor_ptr.inspect} is not ancestor of #{inspect}")
168
190
  end
169
- Ptr.new(tokens[ancestor_ptr.tokens.size..-1])
191
+ ancestor_ptr.tokens.size == tokens.size ? EMPTY : Ptr.new(tokens[ancestor_ptr.tokens.size..-1].freeze)
170
192
  end
171
193
 
172
- # @deprecated after v0.6
173
- alias_method :ptr_relative_to, :relative_to
174
-
175
194
  # a pointer with the tokens of this one plus the given `ptr`'s.
176
195
  # @param ptr [JSI::Ptr, #to_ary]
177
196
  # @return [JSI::Ptr]
@@ -183,7 +202,7 @@ module JSI
183
202
  else
184
203
  raise(TypeError, "ptr must be a #{Ptr} or Array of tokens; got: #{ptr.inspect}")
185
204
  end
186
- Ptr.new(tokens + ptr_tokens)
205
+ ptr_tokens.empty? ? self : Ptr.new((tokens + ptr_tokens).freeze)
187
206
  end
188
207
 
189
208
  # a pointer consisting of the first `n` of our tokens
@@ -191,10 +210,10 @@ module JSI
191
210
  # @return [JSI::Ptr]
192
211
  # @raise [ArgumentError] if n is not between 0 and the size of our tokens
193
212
  def take(n)
194
- unless (0..tokens.size).include?(n)
213
+ unless n.is_a?(Integer) && n >= 0 && n <= tokens.size
195
214
  raise(ArgumentError, "n not in range (0..#{tokens.size}): #{n.inspect}")
196
215
  end
197
- Ptr.new(tokens.take(n))
216
+ n == tokens.size ? self : Ptr.new(tokens.take(n).freeze)
198
217
  end
199
218
 
200
219
  # appends the given token to this pointer's tokens and returns the result
@@ -202,7 +221,7 @@ module JSI
202
221
  # @param token [Object]
203
222
  # @return [JSI::Ptr] pointer to a child node of this pointer with the given token
204
223
  def [](token)
205
- Ptr.new(tokens + [token])
224
+ Ptr.new(tokens.dup.push(token).freeze)
206
225
  end
207
226
 
208
227
  # takes a document and a block. the block is yielded the content of the given document at this
@@ -227,7 +246,7 @@ module JSI
227
246
  Util.modified_copy(document, &block)
228
247
  else
229
248
  car = tokens[0]
230
- cdr = Ptr.new(tokens[1..-1])
249
+ cdr = tokens.size == 1 ? EMPTY : Ptr.new(tokens[1..-1].freeze)
231
250
  token, document_child = node_subscript_token_child(document, car)
232
251
  modified_document_child = cdr.modified_document_copy(document_child, &block)
233
252
  if modified_document_child.object_id == document_child.object_id
@@ -246,33 +265,36 @@ module JSI
246
265
  # a string representation of this pointer
247
266
  # @return [String]
248
267
  def inspect
249
- "#{self.class.name}[#{tokens.map(&:inspect).join(", ")}]"
268
+ -"#{self.class.name}[#{tokens.map(&:inspect).join(", ")}]"
250
269
  end
251
270
 
252
- alias_method :to_s, :inspect
271
+ def to_s
272
+ inspect
273
+ end
253
274
 
254
- # pointers are equal if the tokens are equal
275
+ # see {Util::Private::FingerprintHash}
276
+ # @api private
255
277
  def jsi_fingerprint
256
- {class: Ptr, tokens: tokens}
278
+ {class: Ptr, tokens: tokens}.freeze
257
279
  end
258
- include Util::FingerprintHash
280
+ include Util::FingerprintHash::Immutable
281
+
282
+ EMPTY = new(Util::EMPTY_ARY)
259
283
 
260
284
  private
261
285
 
262
286
  def node_subscript_token_child(value, token, *a, **kw)
263
287
  if value.respond_to?(:to_ary)
264
- if token.is_a?(String) && token =~ /\A\d|[1-9]\d+\z/
288
+ if token.is_a?(String) && (token == '0' || token =~ POS_INT_RE)
265
289
  token = token.to_i
266
290
  elsif token == '-'
267
291
  # per rfc6901, - refers "to the (nonexistent) member after the last array element" and is
268
292
  # expected to raise an error condition.
269
293
  raise(ResolutionError, "Invalid resolution: #{token.inspect} refers to a nonexistent element in array #{value.inspect}")
270
294
  end
271
- unless token.is_a?(Integer)
272
- raise(ResolutionError, "Invalid resolution: #{token.inspect} is not an integer and cannot be resolved in array #{value.inspect}")
273
- end
274
- unless (0...(value.respond_to?(:size) ? value : value.to_ary).size).include?(token)
275
- raise(ResolutionError, "Invalid resolution: #{token.inspect} is not a valid index of #{value.inspect}")
295
+ size = (value.respond_to?(:size) ? value : value.to_ary).size
296
+ unless token.is_a?(Integer) && token >= 0 && token < size
297
+ raise(ResolutionError, "Invalid resolution: #{token.inspect} is not a valid array index of #{value.inspect}")
276
298
  end
277
299
 
278
300
  ary = (value.respond_to?(:[]) ? value : value.to_ary)
@@ -2,7 +2,6 @@
2
2
 
3
3
  module JSI
4
4
  module Schema::Application::ChildApplication::Draft04
5
- include Schema::Application::ChildApplication
6
5
  include Schema::Application::ChildApplication::Items
7
6
  include Schema::Application::ChildApplication::Properties
8
7
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  module JSI
4
4
  module Schema::Application::ChildApplication::Draft06
5
- include Schema::Application::ChildApplication
6
5
  include Schema::Application::ChildApplication::Items
7
6
  include Schema::Application::ChildApplication::Contains
8
7
  include Schema::Application::ChildApplication::Properties
@@ -2,7 +2,6 @@
2
2
 
3
3
  module JSI
4
4
  module Schema::Application::ChildApplication::Draft07
5
- include Schema::Application::ChildApplication
6
5
  include Schema::Application::ChildApplication::Items
7
6
  include Schema::Application::ChildApplication::Contains
8
7
  include Schema::Application::ChildApplication::Properties
@@ -9,30 +9,5 @@ module JSI
9
9
  autoload :Items, 'jsi/schema/application/child_application/items'
10
10
  autoload :Contains, 'jsi/schema/application/child_application/contains'
11
11
  autoload :Properties, 'jsi/schema/application/child_application/properties'
12
-
13
- # a set of child applicator subschemas of this schema which apply to the child of the given instance
14
- # on the given token.
15
- #
16
- # @param token [Object] the array index or object property name for the child instance
17
- # @param instance [Object] the instance to check any child applicators against
18
- # @return [JSI::SchemaSet] child applicator subschemas of this schema for the given token
19
- # of the instance
20
- def child_applicator_schemas(token, instance)
21
- SchemaSet.new(each_child_applicator_schema(token, instance))
22
- end
23
-
24
- # yields each child applicator subschema (from properties, items, etc.) which applies to the child of
25
- # the given instance on the given token.
26
- #
27
- # @param (see #child_applicator_schemas)
28
- # @yield [JSI::Schema]
29
- # @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
30
- def each_child_applicator_schema(token, instance, &block)
31
- return to_enum(__method__, token, instance) unless block
32
-
33
- internal_child_applicate_keywords(token, instance, &block)
34
-
35
- nil
36
- end
37
12
  end
38
13
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  module JSI
4
4
  module Schema::Application::InplaceApplication::Draft04
5
- include Schema::Application::InplaceApplication
6
5
  include Schema::Application::InplaceApplication::Ref
7
6
  include Schema::Application::InplaceApplication::Dependencies
8
7
  include Schema::Application::InplaceApplication::SomeOf
@@ -2,7 +2,6 @@
2
2
 
3
3
  module JSI
4
4
  module Schema::Application::InplaceApplication::Draft06
5
- include Schema::Application::InplaceApplication
6
5
  include Schema::Application::InplaceApplication::Ref
7
6
  include Schema::Application::InplaceApplication::Dependencies
8
7
  include Schema::Application::InplaceApplication::SomeOf
@@ -2,7 +2,6 @@
2
2
 
3
3
  module JSI
4
4
  module Schema::Application::InplaceApplication::Draft07
5
- include Schema::Application::InplaceApplication
6
5
  include Schema::Application::InplaceApplication::Ref
7
6
  include Schema::Application::InplaceApplication::Dependencies
8
7
  include Schema::Application::InplaceApplication::IfThenElse
@@ -5,7 +5,7 @@ module JSI
5
5
  # @private
6
6
  def internal_applicate_ref(instance, visited_refs, throw_done: false, &block)
7
7
  if keyword?('$ref') && schema_content['$ref'].respond_to?(:to_str)
8
- ref = jsi_memoize(:ref) { Schema::Ref.new(schema_content['$ref'], self) }
8
+ ref = schema_ref('$ref')
9
9
  unless visited_refs.include?(ref)
10
10
  ref.deref_schema.each_inplace_applicator_schema(instance, visited_refs: visited_refs + [ref], &block)
11
11
  if throw_done
@@ -27,7 +27,7 @@ module JSI
27
27
  oneOf_idxs = schema_content['oneOf'].each_index
28
28
  subschema_idx_valid = Hash.new { |h, i| h[i] = subschema(['oneOf', i]).instance_valid?(instance) }
29
29
  # count up to 2 `oneOf` subschemas which `instance` validates against
30
- nvalid = oneOf_idxs.inject(0) { |n, i| n > 1 ? n : subschema_idx_valid[i] ? n + 1 : n }
30
+ nvalid = oneOf_idxs.inject(0) { |n, i| n <= 1 && subschema_idx_valid[i] ? n + 1 : n }
31
31
  if nvalid == 1
32
32
  applicator_idxs = oneOf_idxs.select { |i| subschema_idx_valid[i] }
33
33
  else
@@ -10,32 +10,5 @@ module JSI
10
10
  autoload :SomeOf, 'jsi/schema/application/inplace_application/someof'
11
11
  autoload :IfThenElse, 'jsi/schema/application/inplace_application/ifthenelse'
12
12
  autoload :Dependencies, 'jsi/schema/application/inplace_application/dependencies'
13
-
14
- # a set of inplace applicator schemas of this schema (from $ref, allOf, etc.) which apply to the
15
- # given instance.
16
- #
17
- # the returned set will contain this schema itself, unless this schema contains a $ref keyword.
18
- #
19
- # @param instance [Object] the instance to check any applicators against
20
- # @return [JSI::SchemaSet] matched applicator schemas
21
- def inplace_applicator_schemas(instance)
22
- SchemaSet.new(each_inplace_applicator_schema(instance))
23
- end
24
-
25
- # yields each inplace applicator schema which applies to the given instance.
26
- #
27
- # @param instance (see #inplace_applicator_schemas)
28
- # @param visited_refs [Enumerable<JSI::Schema::Ref>]
29
- # @yield [JSI::Schema]
30
- # @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
31
- def each_inplace_applicator_schema(instance, visited_refs: [], &block)
32
- return to_enum(__method__, instance, visited_refs: visited_refs) unless block
33
-
34
- catch(:jsi_application_done) do
35
- internal_inplace_applicate_keywords(instance, visited_refs, &block)
36
- end
37
-
38
- nil
39
- end
40
13
  end
41
14
  end
@@ -3,7 +3,6 @@
3
3
  module JSI
4
4
  module Schema
5
5
  module Draft04
6
- include Schema
7
6
  include OldId
8
7
  include IdWithAnchor
9
8
  include IntegerDisallows0Fraction
@@ -3,7 +3,6 @@
3
3
  module JSI
4
4
  module Schema
5
5
  module Draft06
6
- include Schema
7
6
  include BigMoneyId
8
7
  include IdWithAnchor
9
8
  include IntegerAllows0Fraction
@@ -3,7 +3,6 @@
3
3
  module JSI
4
4
  module Schema
5
5
  module Draft07
6
- include Schema
7
6
  include BigMoneyId
8
7
  include IdWithAnchor
9
8
  include IntegerAllows0Fraction
@@ -1,30 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSI
4
- # JSI::Schema::Ref is a reference to another schema (the result of #deref_schema), resolved using a ref URI
5
- # from a ref schema (the ref URI typically the contents of the ref_schema's "$ref" keyword)
4
+ # A JSI::Schema::Ref is a reference to a schema identified by a URI, typically from
5
+ # a `$ref` keyword of a schema.
6
6
  class Schema::Ref
7
- # @param ref [String] a reference URI
8
- # @param ref_schema [JSI::Schema] a schema from which the reference originated
9
- def initialize(ref, ref_schema = nil)
7
+ # @param ref [String] A reference URI - typically the `$ref` value of the ref_schema
8
+ # @param ref_schema [JSI::Schema] A schema from which the reference originated.
9
+ #
10
+ # If the ref URI consists of only a fragment, it is resolved from the `ref_schema`'s
11
+ # {Schema#schema_resource_root}. Otherwise the resource is found in the `ref_schema`'s
12
+ # {SchemaAncestorNode#jsi_schema_registry #jsi_schema_registry} (and any fragment is resolved from there).
13
+ # @param schema_registry [SchemaRegistry] The registry in which the resource this ref refers to will be found.
14
+ # This should only be specified in the absence of a `ref_schema`.
15
+ # If neither is specified, {JSI.schema_registry} is used.
16
+ def initialize(ref, ref_schema: nil, schema_registry: nil)
10
17
  raise(ArgumentError, "ref is not a string") unless ref.respond_to?(:to_str)
11
18
  @ref = ref
12
- @ref_uri = Addressable::URI.parse(ref)
19
+ @ref_uri = Util.uri(ref)
13
20
  @ref_schema = ref_schema ? Schema.ensure_schema(ref_schema) : nil
21
+ @schema_registry = schema_registry || (ref_schema ? ref_schema.jsi_schema_registry : JSI.schema_registry)
22
+ @deref_schema = nil
14
23
  end
15
24
 
25
+ # @return [String]
16
26
  attr_reader :ref
17
27
 
28
+ # @return [Addressable::URI]
18
29
  attr_reader :ref_uri
19
30
 
31
+ # @return [Schema, nil]
20
32
  attr_reader :ref_schema
21
33
 
34
+ # @return [SchemaRegistry, nil]
35
+ attr_reader(:schema_registry)
36
+
22
37
  # finds the schema this ref points to
23
38
  # @return [JSI::Schema]
24
39
  # @raise [JSI::Schema::NotASchemaError] when the thing this ref points to is not a schema
25
40
  # @raise [JSI::Schema::ReferenceError] when this reference cannot be resolved
26
41
  def deref_schema
27
- return @deref_schema if instance_variable_defined?(:@deref_schema)
42
+ return @deref_schema if @deref_schema
28
43
 
29
44
  schema_resource_root = nil
30
45
  check_schema_resource_root = -> {
@@ -36,7 +51,7 @@ module JSI
36
51
  end
37
52
  }
38
53
 
39
- ref_uri_nofrag = ref_uri.merge(fragment: nil)
54
+ ref_uri_nofrag = ref_uri.merge(fragment: nil).freeze
40
55
 
41
56
  if ref_uri_nofrag.empty?
42
57
  unless ref_schema
@@ -49,7 +64,7 @@ module JSI
49
64
  # the URI only consists of a fragment (or is empty).
50
65
  # for a fragment pointer, resolve using Schema#resource_root_subschema on the ref_schema.
51
66
  # for a fragment anchor, bootstrap does not support anchors; otherwise use the ref_schema's schema_resource_root.
52
- schema_resource_root = ref_schema.is_a?(MetaschemaNode::BootstrapSchema) ? nil : ref_schema.schema_resource_root
67
+ schema_resource_root = ref_schema.is_a?(MetaSchemaNode::BootstrapSchema) ? nil : ref_schema.schema_resource_root
53
68
  resolve_fragment_ptr = ref_schema.method(:resource_root_subschema)
54
69
  else
55
70
  # find the schema_resource_root from the non-fragment URI. we will resolve any fragment, either pointer or anchor, from there.
@@ -57,19 +72,26 @@ module JSI
57
72
  if ref_uri_nofrag.absolute?
58
73
  ref_abs_uri = ref_uri_nofrag
59
74
  elsif ref_schema && ref_schema.jsi_resource_ancestor_uri
60
- ref_abs_uri = ref_schema.jsi_resource_ancestor_uri.join(ref_uri_nofrag)
75
+ ref_abs_uri = ref_schema.jsi_resource_ancestor_uri.join(ref_uri_nofrag).freeze
61
76
  else
62
77
  ref_abs_uri = nil
63
78
  end
64
79
  if ref_abs_uri
65
- schema_resource_root = JSI.schema_registry.find(ref_abs_uri)
80
+ unless schema_registry
81
+ raise(Schema::ReferenceError, [
82
+ "could not resolve remote ref with no schema_registry specified",
83
+ "ref URI: #{ref_uri.to_s}",
84
+ ("from: #{ref_schema.pretty_inspect.chomp}" if ref_schema),
85
+ ].compact.join("\n"))
86
+ end
87
+ schema_resource_root = schema_registry.find(ref_abs_uri)
66
88
  end
67
89
 
68
90
  unless schema_resource_root
69
91
  # HAX for how google does refs and ids
70
92
  if ref_schema && ref_schema.jsi_document.respond_to?(:to_hash) && ref_schema.jsi_document['schemas'].respond_to?(:to_hash)
71
- ref_schema.jsi_document['schemas'].each_key do |k|
72
- if Addressable::URI.parse(ref_schema.jsi_document['schemas'][k]['id']) == ref_uri_nofrag
93
+ ref_schema.jsi_document['schemas'].each do |k, v|
94
+ if Addressable::URI.parse(v['id']) == ref_uri_nofrag
73
95
  schema_resource_root = ref_schema.resource_root_subschema(['schemas', k])
74
96
  end
75
97
  end
@@ -136,10 +158,12 @@ module JSI
136
158
 
137
159
  # @return [String]
138
160
  def inspect
139
- %Q(\#<#{self.class.name} #{ref}>)
161
+ -%Q(\#<#{self.class.name} #{ref}>)
140
162
  end
141
163
 
142
- alias_method :to_s, :inspect
164
+ def to_s
165
+ inspect
166
+ end
143
167
 
144
168
  # pretty-prints a representation of self to the given printer
145
169
  # @return [void]
@@ -151,10 +175,12 @@ module JSI
151
175
  q.text '>'
152
176
  end
153
177
 
154
- # @private
178
+ # see {Util::Private::FingerprintHash}
179
+ # @api private
155
180
  def jsi_fingerprint
156
- {class: self.class, ref: ref, ref_schema: ref_schema}
181
+ {class: self.class, ref: ref, ref_schema: ref_schema}.freeze
157
182
  end
158
- include Util::FingerprintHash
183
+
184
+ include(Util::FingerprintHash::Immutable)
159
185
  end
160
186
  end