Class: JSI::JSON::Pointer

Inherits:
Object
  • Object
show all
Defined in:
lib/jsi/json/pointer.rb

Overview

a JSON Pointer, as described by RFC 6901 https://tools.ietf.org/html/rfc6901

Defined Under Namespace

Classes: Error, PointerSyntaxError, ReferenceError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type, representation) ⇒ Pointer

initializes a JSI::JSON::Pointer from the given representation.

type may be one of:

  • :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


62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/jsi/json/pointer.rb', line 62

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}"
  end
  @reference_tokens = reference_tokens.map(&:freeze).freeze
end

Instance Attribute Details

#reference_tokensObject (readonly)

Returns the value of attribute reference_tokens.



76
77
78
# File 'lib/jsi/json/pointer.rb', line 76

def reference_tokens
  @reference_tokens
end

Class Method Details

.parse_fragment(fragment) ⇒ Object

parse a fragment to an array of reference tokens

/foo/bar

=> ['foo', 'bar']

/foo%20bar

=> ['foo bar']



23
24
25
26
27
28
29
30
31
# File 'lib/jsi/json/pointer.rb', line 23

def self.parse_fragment(fragment)
  fragment = Addressable::URI.unescape(fragment)
  match = fragment.match(/\A#/)
  if match
    parse_pointer(match.post_match)
  else
    raise(PointerSyntaxError, "Invalid fragment syntax in #{fragment.inspect}: fragment must begin with #")
  end
end

.parse_pointer(pointer_string) ⇒ Object

parse a pointer to an array of reference tokens

/foo

=> ['foo']

/foo~0bar/baz~1qux

=> ['foo~bar', 'baz/qux']



42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/jsi/json/pointer.rb', line 42

def self.parse_pointer(pointer_string)
  tokens = pointer_string.split('/', -1).map! do |piece|
    piece.gsub('~1', '/').gsub('~0', '~')
  end
  if tokens[0] == ''
    tokens[1..-1]
  elsif tokens.empty?
    tokens
  else
    raise(PointerSyntaxError, "Invalid pointer syntax in #{pointer_string.inspect}: pointer must begin with /")
  end
end

Instance Method Details

#evaluate(document) ⇒ Object

takes a root json document and evaluates this pointer through the document, returning the value pointed to by this pointer.

Parameters:

  • document (#to_ary, #to_hash)

    the document against which we will evaluate this pointer

Returns:

  • (Object)

    the content of the document pointed to by this pointer

Raises:



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/jsi/json/pointer.rb', line 84

def evaluate(document)
  res = reference_tokens.inject(document) do |value, token|
    if value.respond_to?(:to_ary)
      if token.is_a?(String) && token =~ /\A\d|[1-9]\d+\z/
        token = token.to_i
      end
      unless token.is_a?(Integer)
        raise(ReferenceError, "Invalid resolution for #{to_s}: #{token.inspect} is not an integer and cannot be resolved in array #{value.inspect}")
      end
      unless (0...(value.respond_to?(:size) ? value : value.to_ary).size).include?(token)
        raise(ReferenceError, "Invalid resolution for #{to_s}: #{token.inspect} is not a valid index of #{value.inspect}")
      end
      (value.respond_to?(:[]) ? value : value.to_ary)[token]
    elsif value.respond_to?(:to_hash)
      unless (value.respond_to?(:key?) ? value : value.to_hash).key?(token)
        raise(ReferenceError, "Invalid resolution for #{to_s}: #{token.inspect} is not a valid key of #{value.inspect}")
      end
      (value.respond_to?(:[]) ? value : value.to_hash)[token]
    else
      raise(ReferenceError, "Invalid resolution for #{to_s}: #{token.inspect} cannot be resolved in #{value.inspect}")
    end
  end
  res
end

#fragmentObject

the fragment string representation of this Pointer



115
116
117
# File 'lib/jsi/json/pointer.rb', line 115

def fragment
  '#' + Addressable::URI.escape(pointer)
end

#pointerObject

the pointer string representation of this Pointer



110
111
112
# File 'lib/jsi/json/pointer.rb', line 110

def pointer
  reference_tokens.map { |t| '/' + t.to_s.gsub('~', '~0').gsub('/', '~1') }.join('')
end

#to_sObject



119
120
121
# File 'lib/jsi/json/pointer.rb', line 119

def to_s
  "#<#{self.class.inspect} #{@type} = #{representation_s}>"
end