Skip to content

Commit

Permalink
Merge 1aaa24f into f23b4f9
Browse files Browse the repository at this point in the history
  • Loading branch information
notEthan committed Sep 4, 2019
2 parents f23b4f9 + 1aaa24f commit 5dfc3ba
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 51 deletions.
2 changes: 1 addition & 1 deletion lib/jsi/json-schema-fragments.rb
Expand Up @@ -43,7 +43,7 @@ def initialize(schema_data, data, opts={})
def schema_from_fragment(base_schema, fragment)
schema_uri = base_schema.uri

pointer = JSI::JSON::Pointer.new(:fragment, fragment)
pointer = JSI::JSON::Pointer.from_fragment(fragment)

base_schema = JSON::Schema.new(pointer.evaluate(base_schema.schema), schema_uri, @options[:version])

Expand Down
4 changes: 2 additions & 2 deletions lib/jsi/json/node.rb
Expand Up @@ -52,7 +52,7 @@ def initialize(document, path)
end
@document = document
@path = path.to_ary.dup.freeze
@pointer = JSI::JSON::Pointer.new(:reference_tokens, path)
@pointer = JSI::JSON::Pointer.new(path)
end

# the path of this Node within its document
Expand Down Expand Up @@ -129,7 +129,7 @@ def deref
return self unless ref.is_a?(String)

if ref[/\A#/]
return self.class.new_by_type(document, JSI::JSON::Pointer.parse_fragment(ref)).deref
return self.class.new_by_type(document, JSI::JSON::Pointer.from_fragment(ref).reference_tokens).deref
end

# HAX for how google does refs and ids
Expand Down
93 changes: 52 additions & 41 deletions lib/jsi/json/pointer.rb
Expand Up @@ -11,66 +11,70 @@ class PointerSyntaxError < Error
class ReferenceError < Error
end

# parse a fragment to an array of reference tokens
# parse a URI-escaped fragment and instantiate as a JSI::JSON::Pointer
#
# #/foo/bar
# ptr = JSI::JSON::Pointer.from_fragment('#/foo/bar')
# => #<JSI::JSON::Pointer fragment: #/foo/bar>
# ptr.reference_tokens
# => ["foo", "bar"]
#
# => ['foo', 'bar']
# with URI escaping:
#
# #/foo%20bar
# ptr = JSI::JSON::Pointer.from_fragment('#/foo%20bar')
# => #<JSI::JSON::Pointer fragment: #/foo%20bar>
# ptr.reference_tokens
# => ["foo bar"]
#
# => ['foo bar']
def self.parse_fragment(fragment)
# @param fragment [String] a fragment containing a pointer (starting with #)
# @return [JSI::JSON::Pointer]
def self.from_fragment(fragment)
fragment = Addressable::URI.unescape(fragment)
match = fragment.match(/\A#/)
if match
parse_pointer(match.post_match)
from_pointer(match.post_match, type: 'fragment')
else
raise(PointerSyntaxError, "Invalid fragment syntax in #{fragment.inspect}: fragment must begin with #")
end
end

# parse a pointer to an array of reference tokens
# parse a pointer string and instantiate as a JSI::JSON::Pointer
#
# /foo
# ptr1 = JSI::JSON::Pointer.from_pointer('/foo')
# => #<JSI::JSON::Pointer pointer: /foo>
# ptr1.reference_tokens
# => ["foo"]
#
# => ['foo']
# ptr2 = JSI::JSON::Pointer.from_pointer('/foo~0bar/baz~1qux')
# => #<JSI::JSON::Pointer pointer: /foo~0bar/baz~1qux>
# ptr2.reference_tokens
# => ["foo~bar", "baz/qux"]
#
# /foo~0bar/baz~1qux
#
# => ['foo~bar', 'baz/qux']
def self.parse_pointer(pointer_string)
# @param pointer_string [String] a pointer string
# @param type (for internal use) indicates the original representation of the pointer
# @return [JSI::JSON::Pointer]
def self.from_pointer(pointer_string, type: 'pointer')
tokens = pointer_string.split('/', -1).map! do |piece|
piece.gsub('~1', '/').gsub('~0', '~')
end
if tokens[0] == ''
tokens[1..-1]
new(tokens[1..-1], type: type)
elsif tokens.empty?
tokens
new(tokens, type: type)
else
raise(PointerSyntaxError, "Invalid pointer syntax in #{pointer_string.inspect}: pointer must begin with /")
end
end

# initializes a JSI::JSON::Pointer from the given representation.
#
# type may be one of:
# initializes a JSI::JSON::Pointer from the given reference_tokens.
#
# - :fragment - the representation is a fragment containing a pointer (starting with #)
# - :pointer - the representation is a pointer (starting with /)
# - :reference_tokens - the representation is an array of tokens referencing a path in a document
def initialize(type, representation)
@type = type
if type == :reference_tokens
reference_tokens = representation
elsif type == :fragment
reference_tokens = self.class.parse_fragment(representation)
elsif type == :pointer
reference_tokens = self.class.parse_pointer(representation)
else
raise ArgumentError, "invalid initialization type: #{type.inspect} with representation #{representation.inspect}"
# @param reference_tokens [Array<Object>]
# @param type [String, Symbol] one of 'pointer' or 'fragment'
def initialize(reference_tokens, type: nil)
unless reference_tokens.respond_to?(:to_ary)
raise(TypeError, "reference_tokens must be an array. got: #{reference_tokens.inspect}")
end
@reference_tokens = reference_tokens.map(&:freeze).freeze
@reference_tokens = reference_tokens.to_ary.map(&:freeze).freeze
@type = type.is_a?(Symbol) ? type.to_s : type
end

attr_reader :reference_tokens
Expand Down Expand Up @@ -106,29 +110,36 @@ def evaluate(document)
res
end

# the pointer string representation of this Pointer
# @return [String] the pointer string representation of this Pointer
def pointer
reference_tokens.map { |t| '/' + t.to_s.gsub('~', '~0').gsub('/', '~1') }.join('')
end

# the fragment string representation of this Pointer
# @return [String] the fragment string representation of this Pointer
def fragment
'#' + Addressable::URI.escape(pointer)
end

# @return [String] string representation of this Pointer
def inspect
"#<#{self.class.inspect} #{representation_s}>"
end

# @return [String] string representation of this Pointer
def to_s
"#<#{self.class.inspect} #{@type} = #{representation_s}>"
inspect
end

private

# @return [String] a representation of this pointer based on @type
def representation_s
if @type == :fragment
fragment
elsif @type == :pointer
pointer
if @type == 'fragment'
"fragment: #{fragment}"
elsif @type == 'pointer'
"pointer: #{pointer}"
else
reference_tokens.inspect
"reference_tokens: #{reference_tokens.inspect}"
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/jsi/schema.rb
Expand Up @@ -97,13 +97,13 @@ def schema_id
end
if parent_auri.fragment
# add onto the fragment
parent_id_path = JSI::JSON::Pointer.new(:fragment, '#' + parent_auri.fragment).reference_tokens
parent_id_path = JSI::JSON::Pointer.from_fragment('#' + parent_auri.fragment).reference_tokens
path_from_id_node = parent_id_path + path_from_id_node
parent_auri.fragment = nil
#else: no fragment so parent_id good as is
end

fragment = JSI::JSON::Pointer.new(:reference_tokens, path_from_id_node).fragment
fragment = JSI::JSON::Pointer.new(path_from_id_node).fragment
schema_id = parent_auri.to_s + fragment

schema_id
Expand Down
18 changes: 13 additions & 5 deletions test/jsi_json_pointer_test.rb
Expand Up @@ -35,13 +35,13 @@
"/m~0n" , 8,
]
evaluations.each_slice(2) do |pointer, value|
assert_equal(value, JSI::JSON::Pointer.new(:pointer, pointer).evaluate(document))
assert_equal(value, JSI::JSON::Pointer.from_pointer(pointer).evaluate(document))
end
end

it 'raises for invalid syntax' do
err = assert_raises(JSI::JSON::Pointer::PointerSyntaxError) do
JSI::JSON::Pointer.new(:pointer, "this does not begin with slash").evaluate(document)
JSI::JSON::Pointer.from_pointer("this does not begin with slash").evaluate(document)
end
assert_equal("Invalid pointer syntax in \"this does not begin with slash\": pointer must begin with /", err.message)
end
Expand Down Expand Up @@ -80,19 +80,27 @@
'#/m~0n', 8,
]
evaluations.each_slice(2) do |fragment, value|
assert_equal(value, JSI::JSON::Pointer.new(:fragment, fragment).evaluate(document))
assert_equal(value, JSI::JSON::Pointer.from_fragment(fragment).evaluate(document))
end
end

it 'raises for invalid syntax' do
err = assert_raises(JSI::JSON::Pointer::PointerSyntaxError) do
JSI::JSON::Pointer.new(:fragment, "this does not begin with #").evaluate(document)
JSI::JSON::Pointer.from_fragment("this does not begin with #").evaluate(document)
end
assert_equal("Invalid fragment syntax in \"this does not begin with #\": fragment must begin with #", err.message)
err = assert_raises(JSI::JSON::Pointer::PointerSyntaxError) do
JSI::JSON::Pointer.new(:fragment, "#this does not begin with slash").evaluate(document)
JSI::JSON::Pointer.from_fragment("#this does not begin with slash").evaluate(document)
end
assert_equal("Invalid pointer syntax in \"this does not begin with slash\": pointer must begin with /", err.message)
end
end
describe 'initialize' do
describe 'invalid reference_tokens' do
it 'raises' do
err = assert_raises(TypeError) { JSI::JSON::Pointer.new({}) }
assert_equal("reference_tokens must be an array. got: {}", err.message)
end
end
end
end

0 comments on commit 5dfc3ba

Please sign in to comment.