Class: JSI::Ptr
- Inherits:
-
Object
- Object
- JSI::Ptr
- Includes:
- Util::FingerprintHash
- Defined in:
- lib/jsi/ptr.rb
Overview
a representation to work with JSON Pointer, as described by RFC 6901 https://tools.ietf.org/html/rfc6901
a pointer is a sequence of tokens pointing to a node in a document.
Defined Under Namespace
Classes: Error, PointerSyntaxError, ResolutionError
Instance Attribute Summary collapse
-
#tokens ⇒ Object
(also: #reference_tokens)
readonly
Returns the value of attribute tokens.
Class Method Summary collapse
-
.[](*tokens) ⇒ JSI::Ptr
instantiates a pointer from the given tokens.
-
.ary_ptr(ary_ptr) ⇒ JSI::Ptr
instantiates a pointer or returns the given pointer.
-
.from_fragment(fragment) ⇒ JSI::Ptr
parse a URI-escaped fragment and instantiate as a JSI::Ptr.
-
.from_pointer(pointer_string) ⇒ JSI::Ptr
parse a pointer string and instantiate as a JSI::Ptr.
Instance Method Summary collapse
-
#+(ptr) ⇒ JSI::Ptr
a pointer with the tokens of this one plus the given
ptr
's. -
#[](token) ⇒ JSI::Ptr
appends the given token to this pointer's tokens and returns the result.
-
#contains?(other_ptr) ⇒ Boolean
whether this pointer contains the other_ptr - that is, whether this pointer is an ancestor of
other_ptr
, a child pointer. -
#empty? ⇒ Boolean
(also: #root?)
whether this pointer is empty, i.e.
-
#evaluate(document, *a) ⇒ Object
takes a root json document and evaluates this pointer through the document, returning the value pointed to by this pointer.
-
#fragment ⇒ String
the fragment string representation of this pointer.
-
#initialize(tokens) ⇒ Ptr
constructor
initializes a JSI::Ptr from the given tokens.
-
#inspect ⇒ String
(also: #to_s)
a string representation of this pointer.
-
#jsi_fingerprint ⇒ Object
pointers are equal if the tokens are equal.
-
#modified_document_copy(document) {|Object| ... } ⇒ Object
takes a document and a block.
-
#parent ⇒ JSI::Ptr
pointer to the parent of where this pointer points.
-
#pointer ⇒ String
the pointer string representation of this pointer.
-
#ptr_relative_to(ancestor_ptr) ⇒ JSI::Ptr
part of this pointer relative to the given ancestor_ptr.
-
#take(n) ⇒ JSI::Ptr
a pointer consisting of the first
n
of our tokens. -
#uri ⇒ Addressable::URI
a URI consisting of a fragment containing this pointer's fragment string representation.
Methods included from Util::FingerprintHash
Constructor Details
#initialize(tokens) ⇒ Ptr
initializes a JSI::Ptr from the given tokens.
93 94 95 96 97 98 |
# File 'lib/jsi/ptr.rb', line 93 def initialize(tokens) unless tokens.respond_to?(:to_ary) raise(TypeError, "tokens must be an array. got: #{tokens.inspect}") end @tokens = tokens.to_ary.map(&:freeze).freeze end |
Instance Attribute Details
#tokens ⇒ Object (readonly) Also known as: reference_tokens
Returns the value of attribute tokens.
100 101 102 |
# File 'lib/jsi/ptr.rb', line 100 def tokens @tokens end |
Class Method Details
.[](*tokens) ⇒ JSI::Ptr
44 45 46 |
# File 'lib/jsi/ptr.rb', line 44 def self.[](*tokens) new(tokens) end |
.ary_ptr(ary_ptr) ⇒ JSI::Ptr
instantiates a pointer or returns the given pointer
22 23 24 25 26 27 28 |
# File 'lib/jsi/ptr.rb', line 22 def self.ary_ptr(ary_ptr) if ary_ptr.is_a?(Ptr) ary_ptr else new(ary_ptr) end end |
.from_fragment(fragment) ⇒ JSI::Ptr
parse a URI-escaped fragment and instantiate as a JSI::Ptr
JSI::Ptr.from_fragment('/foo/bar')
=> JSI::Ptr["foo", "bar"]
with URI escaping:
JSI::Ptr.from_fragment('/foo%20bar')
=> JSI::Ptr["foo bar"]
62 63 64 |
# File 'lib/jsi/ptr.rb', line 62 def self.from_fragment(fragment) from_pointer(Addressable::URI.unescape(fragment)) end |
.from_pointer(pointer_string) ⇒ JSI::Ptr
parse a pointer string and instantiate as a JSI::Ptr
JSI::Ptr.from_pointer('/foo')
=> JSI::Ptr["foo"]
JSI::Ptr.from_pointer('/foo~0bar/baz~1qux')
=> JSI::Ptr["foo~bar", "baz/qux"]
77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/jsi/ptr.rb', line 77 def self.from_pointer(pointer_string) tokens = pointer_string.split('/', -1).map! do |piece| piece.gsub('~1', '/').gsub('~0', '~') end if tokens[0] == '' new(tokens[1..-1]) elsif tokens.empty? new(tokens) else raise(PointerSyntaxError, "Invalid pointer syntax in #{pointer_string.inspect}: pointer must begin with /") end end |
Instance Method Details
#+(ptr) ⇒ JSI::Ptr
a pointer with the tokens of this one plus the given ptr
's.
179 180 181 182 183 184 185 186 187 188 |
# File 'lib/jsi/ptr.rb', line 179 def +(ptr) if ptr.is_a?(Ptr) ptr_tokens = ptr.tokens elsif ptr.respond_to?(:to_ary) ptr_tokens = ptr else raise(TypeError, "ptr must be a JSI::Ptr or Array of tokens; got: #{ptr.inspect}") end Ptr.new(self.tokens + ptr_tokens) end |
#[](token) ⇒ JSI::Ptr
appends the given token to this pointer's tokens and returns the result
205 206 207 |
# File 'lib/jsi/ptr.rb', line 205 def [](token) Ptr.new(tokens + [token]) end |
#contains?(other_ptr) ⇒ Boolean
whether this pointer contains the other_ptr - that is, whether this pointer is an ancestor
of other_ptr
, a child pointer. contains?
is inclusive; a pointer does contain itself.
162 163 164 |
# File 'lib/jsi/ptr.rb', line 162 def contains?(other_ptr) self.tokens == other_ptr.tokens[0...self.tokens.size] end |
#empty? ⇒ Boolean Also known as: root?
whether this pointer is empty, i.e. it has no tokens
140 141 142 |
# File 'lib/jsi/ptr.rb', line 140 def empty? tokens.empty? end |
#evaluate(document, *a) ⇒ Object
takes a root json document and evaluates this pointer through the document, returning the value pointed to by this pointer.
112 113 114 115 116 117 118 |
# File 'lib/jsi/ptr.rb', line 112 def evaluate(document, *a) res = tokens.inject(document) do |value, token| _, child = node_subscript_token_child(value, token, *a) child end res end |
#fragment ⇒ String
the fragment string representation of this pointer
128 129 130 |
# File 'lib/jsi/ptr.rb', line 128 def fragment Addressable::URI.escape(pointer) end |
#inspect ⇒ String Also known as: to_s
a string representation of this pointer
249 250 251 |
# File 'lib/jsi/ptr.rb', line 249 def inspect "#{self.class.name}[#{tokens.map(&:inspect).join(", ")}]" end |
#jsi_fingerprint ⇒ Object
pointers are equal if the tokens are equal
256 257 258 |
# File 'lib/jsi/ptr.rb', line 256 def jsi_fingerprint {class: Ptr, tokens: tokens} end |
#modified_document_copy(document) {|Object| ... } ⇒ Object
takes a document and a block. the block is yielded the content of the given document at this pointer's location. the block must result a modified copy of that content (and MUST NOT modify the object it is given). this modified copy of that content is incorporated into a modified copy of the given document, which is then returned. the structure and contents of the document outside the path pointed to by this pointer is not modified.
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/jsi/ptr.rb', line 220 def modified_document_copy(document, &block) # we need to preserve the rest of the document, but modify the content at our path. # # this is actually a bit tricky. we can't modify the original document, obviously. # we could do a deep copy, but that's expensive. instead, we make a copy of each array # or hash in the path above the node we point to. this node's content is modified by the # caller, and that is recursively merged up to the document root. if empty? Typelike.modified_copy(document, &block) else car = tokens[0] cdr = Ptr.new(tokens[1..-1]) token, document_child = node_subscript_token_child(document, car) modified_document_child = cdr.modified_document_copy(document_child, &block) if modified_document_child.object_id == document_child.object_id document else modified_document = document.respond_to?(:[]=) ? document.dup : document.respond_to?(:to_hash) ? document.to_hash.dup : document.respond_to?(:to_ary) ? document.to_ary.dup : raise(Bug) # not possible; node_subscript_token_child would have raised modified_document[token] = modified_document_child modified_document end end end |
#parent ⇒ JSI::Ptr
pointer to the parent of where this pointer points
151 152 153 154 155 156 157 |
# File 'lib/jsi/ptr.rb', line 151 def parent if root? raise(Ptr::Error, "cannot access parent of root pointer: #{pretty_inspect.chomp}") else Ptr.new(tokens[0...-1]) end end |
#pointer ⇒ String
the pointer string representation of this pointer
122 123 124 |
# File 'lib/jsi/ptr.rb', line 122 def pointer tokens.map { |t| '/' + t.to_s.gsub('~', '~0').gsub('/', '~1') }.join('') end |
#ptr_relative_to(ancestor_ptr) ⇒ JSI::Ptr
part of this pointer relative to the given ancestor_ptr
169 170 171 172 173 174 |
# File 'lib/jsi/ptr.rb', line 169 def ptr_relative_to(ancestor_ptr) unless ancestor_ptr.contains?(self) raise(Error, "ancestor_ptr #{ancestor_ptr.inspect} is not ancestor of #{inspect}") end Ptr.new(tokens[ancestor_ptr.tokens.size..-1]) end |
#take(n) ⇒ JSI::Ptr
a pointer consisting of the first n
of our tokens
194 195 196 197 198 199 |
# File 'lib/jsi/ptr.rb', line 194 def take(n) unless (0..tokens.size).include?(n) raise(ArgumentError, "n not in range (0..#{tokens.size}): #{n.inspect}") end Ptr.new(tokens.take(n)) end |
#uri ⇒ Addressable::URI
a URI consisting of a fragment containing this pointer's fragment string representation
134 135 136 |
# File 'lib/jsi/ptr.rb', line 134 def uri Addressable::URI.new(fragment: fragment) end |