Module: ObjectPatch::Pointer
- Defined in:
- lib/object_patch/pointer.rb
Overview
This module contains the code to convert between an JSON pointer path representation and the keys required to traverse an array. It can make use of an a path and evaluate it against a provided (potentially deeply nested) array or hash.
This is mostly compliant with RFC6901, however, a few small exceptions have been made, though they shouldn’t break compatibility with pure implementations.
Class Method Summary collapse
-
.encode(ary_path) ⇒ String
Given an array of keys this will provide a properly escaped JSONPointer path.
-
.escape(str) ⇒ String
Escapes reserved characters as defined by RFC6901.
-
.eval(path, obj) ⇒ Object
Given a parsed path and an object, get the nested value within the object.
-
.parse(path) ⇒ Array<String,Fixnum>
Convert a JSON pointer into an array of keys that can be used to traverse a parsed JSON document.
-
.unescape(str) ⇒ String
Unescapes any reserved characters within a JSON pointer segment.
Instance Method Summary collapse
-
#encode(ary_path) ⇒ String
private
Given an array of keys this will provide a properly escaped JSONPointer path.
-
#escape(str) ⇒ String
private
Escapes reserved characters as defined by RFC6901.
-
#eval(path, obj) ⇒ Object
private
Given a parsed path and an object, get the nested value within the object.
-
#parse(path) ⇒ Array<String,Fixnum>
private
Convert a JSON pointer into an array of keys that can be used to traverse a parsed JSON document.
-
#unescape(str) ⇒ String
private
Unescapes any reserved characters within a JSON pointer segment.
Class Method Details
.encode(ary_path) ⇒ String
Given an array of keys this will provide a properly escaped JSONPointer path.
47 48 49 50 |
# File 'lib/object_patch/pointer.rb', line 47 def encode(ary_path) ary_path = Array(ary_path).map { |p| p.is_a?(String) ? escape(p) : p } "/" << ary_path.join("/") end |
.escape(str) ⇒ String
Escapes reserved characters as defined by RFC6901. This is intended to escape individual segments of the pointer and thus should not be run on an already generated path.
59 60 61 |
# File 'lib/object_patch/pointer.rb', line 59 def escape(str) str.gsub(/~|\//, { '~' => '~0', '/' => '~1' }) end |
.eval(path, obj) ⇒ Object
Given a parsed path and an object, get the nested value within the object.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/object_patch/pointer.rb', line 19 def eval(path, obj) path.inject(obj) do |o, p| if o.is_a?(Hash) raise MissingTargetException unless o.keys.include?(p) o[p] elsif o.is_a?(Array) # The last element +1 is technically how this is interpretted. This # will always trigger the index error so it may not be valuable to # set... p = o.size if p == "-1" # Technically a violation of the RFC to allow reverse access to the # array but I'll allow it... raise ObjectOperationOnArrayException unless p.to_s.match(/\A-?\d+\Z/) raise InvalidIndexError unless p.to_i.abs < o.size o[p.to_i] else # We received a Scalar value from the prior iteration... we can't do # anything with this... raise TraverseScalarException end end end |
.parse(path) ⇒ Array<String,Fixnum>
Convert a JSON pointer into an array of keys that can be used to traverse a parsed JSON document.
68 69 70 71 72 73 74 75 76 |
# File 'lib/object_patch/pointer.rb', line 68 def parse(path) # I'm pretty sure this isn't quite valid but it's a holdover from # tenderlove's code. Once the operations are refactored I believe this # won't be necessary. return [""] if path == "/" # Strip off the leading slash path = path.sub(/^\//, '') path.split("/").map { |p| unescape(p) } end |
.unescape(str) ⇒ String
Unescapes any reserved characters within a JSON pointer segment.
83 84 85 |
# File 'lib/object_patch/pointer.rb', line 83 def unescape(str) str.gsub(/~[01]/, { '~0' => '~', '~1' => '/' }) end |
Instance Method Details
#encode(ary_path) ⇒ String (private)
Given an array of keys this will provide a properly escaped JSONPointer path.
47 48 49 50 |
# File 'lib/object_patch/pointer.rb', line 47 def encode(ary_path) ary_path = Array(ary_path).map { |p| p.is_a?(String) ? escape(p) : p } "/" << ary_path.join("/") end |
#escape(str) ⇒ String (private)
Escapes reserved characters as defined by RFC6901. This is intended to escape individual segments of the pointer and thus should not be run on an already generated path.
59 60 61 |
# File 'lib/object_patch/pointer.rb', line 59 def escape(str) str.gsub(/~|\//, { '~' => '~0', '/' => '~1' }) end |
#eval(path, obj) ⇒ Object (private)
Given a parsed path and an object, get the nested value within the object.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/object_patch/pointer.rb', line 19 def eval(path, obj) path.inject(obj) do |o, p| if o.is_a?(Hash) raise MissingTargetException unless o.keys.include?(p) o[p] elsif o.is_a?(Array) # The last element +1 is technically how this is interpretted. This # will always trigger the index error so it may not be valuable to # set... p = o.size if p == "-1" # Technically a violation of the RFC to allow reverse access to the # array but I'll allow it... raise ObjectOperationOnArrayException unless p.to_s.match(/\A-?\d+\Z/) raise InvalidIndexError unless p.to_i.abs < o.size o[p.to_i] else # We received a Scalar value from the prior iteration... we can't do # anything with this... raise TraverseScalarException end end end |
#parse(path) ⇒ Array<String,Fixnum> (private)
Convert a JSON pointer into an array of keys that can be used to traverse a parsed JSON document.
68 69 70 71 72 73 74 75 76 |
# File 'lib/object_patch/pointer.rb', line 68 def parse(path) # I'm pretty sure this isn't quite valid but it's a holdover from # tenderlove's code. Once the operations are refactored I believe this # won't be necessary. return [""] if path == "/" # Strip off the leading slash path = path.sub(/^\//, '') path.split("/").map { |p| unescape(p) } end |
#unescape(str) ⇒ String (private)
Unescapes any reserved characters within a JSON pointer segment.
83 84 85 |
# File 'lib/object_patch/pointer.rb', line 83 def unescape(str) str.gsub(/~[01]/, { '~0' => '~', '~1' => '/' }) end |