jsi 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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