Skip to content

Commit d7aa909

Browse files
committed
add JSON::Schema::Pointer class to abstract handling of JSON pointer, dealing with pointers in fragments or in plain pointer syntax. use this in #schema_from_fragment.
1 parent 775a380 commit d7aa909

File tree

2 files changed

+99
-28
lines changed

2 files changed

+99
-28
lines changed

lib/json-schema/pointer.rb

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
require 'addressable/uri'
2+
3+
module JSON
4+
class Schema
5+
class Pointer
6+
class Error < JSON::Schema::SchemaError
7+
end
8+
class PointerSyntaxError < Error
9+
end
10+
class ReferenceError < Error
11+
end
12+
13+
def self.parse_fragment(fragment)
14+
fragment = Addressable::URI.unescape(fragment)
15+
match = fragment.match(/\A#/)
16+
if match
17+
parse_pointer(match.post_match)
18+
else
19+
raise(PointerSyntaxError, "Invalid fragment syntax in #{fragment.inspect}: fragment must begin with #")
20+
end
21+
end
22+
23+
def self.parse_pointer(pointer_string)
24+
tokens = pointer_string.split('/', -1).map! do |piece|
25+
piece.gsub('~1', '/').gsub('~0', '~')
26+
end
27+
if tokens[0] == ''
28+
tokens[1..-1]
29+
else
30+
raise(PointerSyntaxError, "Invalid pointer syntax in #{pointer_string.inspect}: pointer must begin with /")
31+
end
32+
end
33+
34+
def initialize(type, representation)
35+
@type = type
36+
if type == :reference_tokens
37+
reference_tokens = representation
38+
elsif type == :fragment
39+
reference_tokens = self.class.parse_fragment(representation)
40+
elsif type == :pointer
41+
reference_tokens = self.class.parse_pointer(representation)
42+
else
43+
raise ArgumentError, "invalid initialization type: #{type.inspect} with representation #{representation.inspect}"
44+
end
45+
@reference_tokens = reference_tokens.map(&:freeze).freeze
46+
end
47+
48+
attr_reader :reference_tokens
49+
50+
def evaluate(document)
51+
reference_tokens.each.inject(document) do |value, token|
52+
if value.is_a?(Array)
53+
if token.is_a?(String) && token =~ /\A\d|[1-9]\d+\z/
54+
token = token.to_i
55+
end
56+
unless token.is_a?(Integer)
57+
raise(ReferenceError, "Invalid resolution for #{to_s}: #{token.inspect} is not an integer and cannot be resolved in array #{value.inspect}")
58+
end
59+
unless (0...value.size).include?(token)
60+
raise(ReferenceError, "Invalid resolution for #{to_s}: #{token.inspect} is not a valid index of #{value.inspect}")
61+
end
62+
elsif value.is_a?(Hash)
63+
unless value.key?(token)
64+
raise(ReferenceError, "Invalid resolution for #{to_s}: #{token.inspect} is not a valid key of #{value.inspect}")
65+
end
66+
else
67+
raise(ReferenceError, "Invalid resolution for #{to_s}: #{token.inspect} cannot be resolved in #{value.inspect}")
68+
end
69+
value[token]
70+
end
71+
end
72+
73+
def to_s
74+
"#<#{self.class.name} #{@type} = #{representation_s}>"
75+
end
76+
77+
def representation_s
78+
if @type == :fragment
79+
fragment
80+
elsif @type == :pointer
81+
pointer
82+
else
83+
reference_tokens.inspect
84+
end
85+
end
86+
87+
def pointer
88+
reference_tokens.map { |t| '/' + t.gsub('~', '~0').gsub('/', '~1') }.join('')
89+
end
90+
91+
def fragment
92+
'#' + Addressable::URI.escape(pointer)
93+
end
94+
end
95+
end
96+
end

lib/json-schema/validator.rb

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
require 'json-schema/errors/json_load_error'
1313
require 'json-schema/errors/json_parse_error'
1414
require 'json-schema/util/uri'
15+
require 'json-schema/pointer'
1516

1617
module JSON
1718

@@ -73,36 +74,10 @@ def initialize(schema_data, data, opts={})
7374

7475
def schema_from_fragment(base_schema, fragment)
7576
schema_uri = base_schema.uri
76-
fragments = fragment.split("/")
7777

78-
# ensure the first element was a hash, per the fragment spec
79-
if fragments.shift != "#"
80-
raise JSON::Schema::SchemaError.new("Invalid fragment syntax in :fragment option")
81-
end
78+
pointer = JSON::Schema::Pointer.new(:fragment, fragment)
8279

83-
fragments.each do |f|
84-
if base_schema.is_a?(JSON::Schema) #test if fragment is a JSON:Schema instance
85-
if !base_schema.schema.has_key?(f)
86-
raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
87-
end
88-
base_schema = base_schema.schema[f]
89-
elsif base_schema.is_a?(Hash)
90-
if !base_schema.has_key?(f)
91-
raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
92-
end
93-
base_schema = base_schema[f]
94-
elsif base_schema.is_a?(Array)
95-
if base_schema[f.to_i].nil?
96-
raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
97-
end
98-
base_schema = base_schema[f.to_i]
99-
else
100-
raise JSON::Schema::SchemaError.new("Invalid schema encountered when resolving :fragment option")
101-
end
102-
end
103-
unless base_schema.is_a?(JSON::Schema)
104-
base_schema = JSON::Schema.new(base_schema,schema_uri,@options[:version])
105-
end
80+
base_schema = JSON::Schema.new(pointer.evaluate(base_schema.schema), schema_uri, @options[:version])
10681

10782
if @options[:list]
10883
base_schema.to_array_schema

0 commit comments

Comments
 (0)