class Xf::Trace
A more abstract version of a Scope
in which the lead key is treated as if it could be anywhere in a hash tree. A Trace
will dive until it finds all matching nodes.
Note that this can potentially be slow, but is also very difficult to emulate succinctly inline in vanilla Ruby.
@author baweaver @since 0.1.0
Public Class Methods
Creates a Scope
@param trace_path [Any]
What node to try and locate
@return [Xf::Trace]
# File lib/xf/trace.rb, line 19 def initialize(trace_path) @trace_path = trace_path end
Public Instance Methods
Gets a value from a Hash
@param &fn [Proc]
Block to yield the hash, key, and value from any matching element into. Used to transform returned values upon retrieval
@return [Proc -> Array]
Array containing matching values, optionally transformed
# File lib/xf/trace.rb, line 31 def get(&fn) Proc.new { |target| get_value(target, &fn) } end
# File lib/xf/trace.rb, line 35 def get_value(hash, &fn) retrieved_values = [] recursing_dive(hash) { |h, k, v| retrieved_values.push(block_given? ? fn[h, k, v] : v) } retrieved_values end
Sets a value in a Hash
@param value = nil [Any]
Value to set at the target
@param &fn [Proc]
Block to yield target value to. Returned value will be set as the new value at the target.
@return [Proc -> Hash]
New Hash with transformation applied
# File lib/xf/trace.rb, line 56 def set(value = nil, &fn) Proc.new { |hash| set_value(hash, value, &fn) } end
Mutates a value in a Hash
@see set
@note
This method does the same thing as `#set`, except that it mutates the target value instead of creating a clone first.
# File lib/xf/trace.rb, line 68 def set!(value = nil, &fn) Proc.new { |hash| set_value!(hash, value, &fn) } end
# File lib/xf/trace.rb, line 72 def set_value(hash, value = nil, &fn) set_value!(deep_clone(hash), value, &fn) end
# File lib/xf/trace.rb, line 76 def set_value!(hash, value = nil, &fn) recursing_dive(hash) { |h, k, v| h[k] = block_given? ? yield(v) : value } hash end
Private Instance Methods
# File lib/xf/trace.rb, line 104 def deep_clone(hash) Marshal.load(Marshal.dump(hash)) end
Match is defined as having complete access to a hash, key, and value to make it easier to override for other future fun tasks.
In the base case of trace we only really care about the key matching for speed reasons. If you really want power though you could rig this up to work with Qo to query things.
@param hash [Hash] [description] @param key [Any] [description] @param value [Any] [description] @param matcher [#===] [description]
@return [type] [description]
# File lib/xf/trace.rb, line 100 def match?(hash, key, value, matcher) matcher === key end
# File lib/xf/trace.rb, line 108 def recursing_dive(target_hash, &fn) target_hash.each { |k, v| yield(target_hash, k, v) if match?(target_hash, k, v, @trace_path) next unless target_hash[k].is_a?(Hash) recursing_dive(target_hash[k], &fn) } end