Class: JSI::JSON::Node
- Inherits:
-
Object
- Object
- JSI::JSON::Node
- Includes:
- FingerprintHash, PathedNode
- Defined in:
- lib/jsi/json/node.rb
Overview
JSI::JSON::Node is an abstraction of a node within a JSON document. it aims to act like the underlying data type of the node's content (generally Hash or Array-like) in most cases.
the main advantage offered by using a Node over the underlying data is in dereferencing. if a Node consists of a hash with a $ref property pointing within the same document, then the Node will transparently follow the ref and return the referenced data.
in most other respects, a Node aims to act like a Hash when the content is Hash-like, an Array when the content is Array-like. methods of Hash and Array are defined and delegated to the node's content.
however, destructive methods are for the most part not implemented. at the moment only #[]= is implemented. since Node thinly wraps the underlying data, you can change the data and it will be reflected in the node. implementations of destructive methods are planned.
methods that return a modified copy such as #merge are defined, and return a copy of the document with the content of the node modified. the original node's document and content are untouched.
Instance Attribute Summary collapse
-
#document ⇒ Object
(also: #node_document)
readonly
the document containing this Node at our pointer.
-
#pointer ⇒ Object
(also: #node_ptr)
readonly
JSI::JSON::Pointer pointing to this node within its document.
Class Method Summary collapse
-
.new_by_type(document, pointer) ⇒ Object
if the content of the document at the given pointer is Hash-like, returns a HashNode; if Array-like, returns ArrayNode.
- .new_doc(document) ⇒ Object
Instance Method Summary collapse
-
#[](subscript) ⇒ Object
returns content at the given subscript - call this the subcontent.
-
#[]=(subscript, value) ⇒ Object
assigns the given subscript of the content to the given value.
-
#as_json(*opt) ⇒ Object
returns a jsonifiable representation of this node's content.
-
#deref {|Node| ... } ⇒ JSI::JSON::Node
returns a Node, dereferencing a $ref attribute if possible.
-
#document_node ⇒ Object
(also: #document_root_node)
a Node at the root of the document.
- #dup ⇒ Object
-
#fingerprint ⇒ Object
fingerprint for equality (see FingerprintHash).
-
#fragment ⇒ Object
the pointer fragment to this node within the document, per RFC 6901 https://tools.ietf.org/html/rfc6901.
-
#initialize(document, pointer) ⇒ Node
constructor
a Node represents the content of a document at a given pointer.
-
#inspect ⇒ Object
a string representing this node.
-
#modified_copy(&block) ⇒ Object
takes a block.
-
#object_group_text ⇒ Array<String>
meta-information about the object, outside the content.
-
#parent_node ⇒ Object
the parent of this node.
-
#path ⇒ Array<Object>
The path of this node; an array of reference_tokens of the pointer.
-
#pointer_path ⇒ Object
the pointer path to this node within the document, per RFC 6901 https://tools.ietf.org/html/rfc6901.
-
#pretty_print(q) ⇒ Object
pretty-prints a representation this node to the given printer.
-
#root_node? ⇒ Boolean
Whether this node is the root of its document.
Methods included from FingerprintHash
Methods included from PathedNode
#node_content, #node_ptr_deref
Constructor Details
#initialize(document, pointer) ⇒ Node
a Node represents the content of a document at a given pointer.
47 48 49 50 51 52 53 54 55 56 |
# File 'lib/jsi/json/node.rb', line 47 def initialize(document, pointer) unless pointer.is_a?(JSI::JSON::Pointer) raise(TypeError, "pointer must be a JSI::JSON::Pointer. got: #{pointer.pretty_inspect.chomp} (#{pointer.class})") end if document.is_a?(JSI::JSON::Node) raise(TypeError, "document of a Node should not be another JSI::JSON::Node: #{document.inspect}") end @document = document @pointer = pointer end |
Instance Attribute Details
#document ⇒ Object (readonly) Also known as: node_document
the document containing this Node at our pointer
59 60 61 |
# File 'lib/jsi/json/node.rb', line 59 def document @document end |
#pointer ⇒ Object (readonly) Also known as: node_ptr
JSI::JSON::Pointer pointing to this node within its document
62 63 64 |
# File 'lib/jsi/json/node.rb', line 62 def pointer @pointer end |
Class Method Details
.new_by_type(document, pointer) ⇒ Object
if the content of the document at the given pointer is Hash-like, returns a HashNode; if Array-like, returns ArrayNode. otherwise returns a regular Node, although Nodes are for the most part instantiated from Hash or Array-like content.
35 36 37 38 39 40 41 42 43 44 |
# File 'lib/jsi/json/node.rb', line 35 def self.new_by_type(document, pointer) content = pointer.evaluate(document) if content.respond_to?(:to_hash) HashNode.new(document, pointer) elsif content.respond_to?(:to_ary) ArrayNode.new(document, pointer) else Node.new(document, pointer) end end |
.new_doc(document) ⇒ Object
27 28 29 |
# File 'lib/jsi/json/node.rb', line 27 def self.new_doc(document) new_by_type(document, JSI::JSON::Pointer.new([])) end |
Instance Method Details
#[](subscript) ⇒ Object
returns content at the given subscript - call this the subcontent.
if the content cannot be subscripted, raises TypeError.
if the subcontent is Hash-like, it is wrapped as a JSI::JSON::HashNode before being returned. if the subcontent is Array-like, it is wrapped as a JSI::JSON::ArrayNode before being returned.
if this node's content is a $ref - that is, a hash with a $ref attribute - and the subscript is not a key of the hash, then the $ref is followed before returning the subcontent.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/jsi/json/node.rb', line 84 def [](subscript) ptr = self.pointer content = self.content if content.respond_to?(:to_hash) && !(content.respond_to?(:key?) ? content : content.to_hash).key?(subscript) pointer.deref(document) do |deref_ptr| ptr = deref_ptr content = ptr.evaluate(document) end end unless content.respond_to?(:[]) if content.respond_to?(:to_hash) content = content.to_hash elsif content.respond_to?(:to_ary) content = content.to_ary else raise(NoMethodError, "undefined method `[]`\nsubscripting with #{subscript.pretty_inspect.chomp} (#{subscript.class}) from #{content.class.inspect}. content is: #{content.pretty_inspect.chomp}") end end begin subcontent = content[subscript] rescue TypeError => e raise(e.class, e. + "\nsubscripting with #{subscript.pretty_inspect.chomp} (#{subscript.class}) from #{content.class.inspect}. content is: #{content.pretty_inspect.chomp}", e.backtrace) end if subcontent.respond_to?(:to_hash) HashNode.new(document, ptr[subscript]) elsif subcontent.respond_to?(:to_ary) ArrayNode.new(document, ptr[subscript]) else subcontent end end |
#[]=(subscript, value) ⇒ Object
assigns the given subscript of the content to the given value. the document is modified in place.
117 118 119 120 121 122 123 |
# File 'lib/jsi/json/node.rb', line 117 def []=(subscript, value) if value.is_a?(Node) content[subscript] = value.content else content[subscript] = value end end |
#as_json(*opt) ⇒ Object
returns a jsonifiable representation of this node's content
170 171 172 |
# File 'lib/jsi/json/node.rb', line 170 def as_json(*opt) Typelike.as_json(content, *opt) end |
#deref {|Node| ... } ⇒ JSI::JSON::Node
returns a Node, dereferencing a $ref attribute if possible. if this node is not hash-like, does not have a $ref, or if what its $ref cannot be found, this node is returned.
currently only $refs pointing within the same document are followed.
134 135 136 137 138 139 |
# File 'lib/jsi/json/node.rb', line 134 def deref(&block) pointer.deref(document) do |deref_ptr| return Node.new_by_type(document, deref_ptr).tap(&(block || Util::NOOP)) end return self end |
#document_node ⇒ Object Also known as: document_root_node
a Node at the root of the document
142 143 144 |
# File 'lib/jsi/json/node.rb', line 142 def document_node Node.new_doc(document) end |
#dup ⇒ Object
180 181 182 |
# File 'lib/jsi/json/node.rb', line 180 def dup modified_copy(&:dup) end |
#fingerprint ⇒ Object
fingerprint for equality (see FingerprintHash). two nodes are equal if they are both nodes (regardless of type, e.g. one may be a Node and the other may be a HashNode) within equal documents at equal pointers. note that this means two nodes with the same content may not be considered equal.
216 217 218 |
# File 'lib/jsi/json/node.rb', line 216 def fingerprint {class: JSI::JSON::Node, document: document, pointer: pointer} end |
#fragment ⇒ Object
the pointer fragment to this node within the document, per RFC 6901 https://tools.ietf.org/html/rfc6901
165 166 167 |
# File 'lib/jsi/json/node.rb', line 165 def fragment pointer.fragment end |
#inspect ⇒ Object
a string representing this node
193 194 195 |
# File 'lib/jsi/json/node.rb', line 193 def inspect "\#<#{self.class.inspect}#{JSI.object_group_str(object_group_text)} #{node_content.inspect}>" end |
#modified_copy(&block) ⇒ Object
takes a block. the block is yielded the content of this node. the block MUST return a modified copy of that content (and NOT modify the object it is given).
176 177 178 |
# File 'lib/jsi/json/node.rb', line 176 def modified_copy(&block) Node.new_by_type(pointer.modified_document_copy(document, &block), pointer) end |
#object_group_text ⇒ Array<String>
meta-information about the object, outside the content. used by #inspect / #pretty_print
186 187 188 189 190 |
# File 'lib/jsi/json/node.rb', line 186 def object_group_text [ "fragment=#{node_ptr.fragment.inspect}", ] + (node_content.respond_to?(:object_group_text) ? node_content.object_group_text : []) end |
#parent_node ⇒ Object
the parent of this node. if this node is the document root, raises JSI::JSON::Pointer::ReferenceError.
155 156 157 |
# File 'lib/jsi/json/node.rb', line 155 def parent_node Node.new_by_type(document, pointer.parent) end |
#path ⇒ Array<Object>
Returns the path of this node; an array of reference_tokens of the pointer.
65 66 67 |
# File 'lib/jsi/json/node.rb', line 65 def path pointer.reference_tokens end |
#pointer_path ⇒ Object
the pointer path to this node within the document, per RFC 6901 https://tools.ietf.org/html/rfc6901
160 161 162 |
# File 'lib/jsi/json/node.rb', line 160 def pointer_path pointer.pointer end |
#pretty_print(q) ⇒ Object
pretty-prints a representation this node to the given printer
198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/jsi/json/node.rb', line 198 def pretty_print(q) q.instance_exec(self) do |obj| text "\#<#{obj.class.inspect}#{JSI.object_group_str(obj.object_group_text)}" group_sub { nest(2) { breakable ' ' pp obj.content } } breakable '' text '>' end end |
#root_node? ⇒ Boolean
Returns whether this node is the root of its document.
149 150 151 |
# File 'lib/jsi/json/node.rb', line 149 def root_node? pointer.root? end |