module Abstractivator::Trees
Constants
- SetMask
Public Instance Methods
recursive_delete!(hash, keys)
click to toggle source
recursively deletes the specified keys
# File lib/abstractivator/trees/recursive_delete.rb, line 9 def recursive_delete!(hash, keys) x = hash # hash is named 'hash' for documentation purposes but may be anything case x when Hash keys.each{|k| x.delete(k)} x.each_value{|v| recursive_delete!(v, keys)} when Array x.each{|v| recursive_delete!(v, keys)} end x end
set_mask(items, get_key)
click to toggle source
# File lib/abstractivator/trees/tree_compare.rb, line 11 def set_mask(items, get_key) SetMask.new(items, get_key) end
tree_compare(tree, mask, path=[], index=nil)
click to toggle source
Compares a tree to a mask. Returns a diff of where the tree differs from the mask. Ignores parts of the tree not specified in the mask.
# File lib/abstractivator/trees/tree_compare.rb, line 18 def tree_compare(tree, mask, path=[], index=nil) if mask == [:*] && tree.is_a?(Enumerable) [] elsif mask == :+ && tree != :__missing__ [] elsif mask == :- && tree != :__missing__ [diff(path, tree, :__absent__)] elsif mask.callable? are_equivalent = mask.call(tree) are_equivalent ? [] : [diff(path, tree, mask)] else case mask when Hash if tree.is_a?(Hash) mask.each_pair.flat_map do |k, v| tree_compare(tree.fetch(k, :__missing__), v, push_path(path, k)) end else [diff(path, tree, mask)] end when SetMask # must check this before Enumerable because Structs are enumerable if tree.is_a?(Enumerable) # convert the enumerables to hashes, then compare those hashes tree_items = tree mask_items = mask.items.dup get_key = mask.get_key be_strict = !mask_items.delete(:*) new_tree = hashify_set(tree_items, get_key) new_mask = hashify_set(mask_items, get_key) tree_keys = Set.new(new_tree.keys) mask_keys = Set.new(new_mask.keys) tree_only = tree_keys - mask_keys # report duplicate keys if new_tree.size < tree_items.size diff(path, [:__duplicate_keys__, duplicates(tree_items.map(&get_key))], nil) elsif new_mask.size < mask_items.size diff(path, nil, [:__duplicate_keys__, duplicates(mask_items.map(&get_key))]) # hash comparison allows extra values in the tree. # report extra values in the tree unless there was a :* in the mask elsif be_strict && tree_only.any? tree_only.map{|k| diff(push_path(path, k), new_tree[k], :__absent__)} else # compare as hashes tree_compare(new_tree, new_mask, path, index) end else [diff(path, tree, mask.items)] end when Enumerable if tree.is_a?(Enumerable) index ||= 0 if !tree.any? && !mask.any? [] elsif !tree.any? [diff(push_path(path, index.to_s), :__missing__, mask)] elsif !mask.any? [diff(push_path(path, index.to_s), tree, :__absent__)] else # if the mask is programmatically generated (unlikely), then # the mask might be really big and this could blow the stack. # don't support this case for now. tree_compare(tree.first, mask.first, push_path(path, index.to_s)) + tree_compare(tree.drop(1), mask.drop(1), path, index + 1) end else [diff(path, tree, mask)] end else tree == mask ? [] : [diff(path, tree, mask)] end end end
tree_map(h) { |config| ... }
click to toggle source
Transforms a tree at certain paths. The transform is non-destructive and reuses untouched substructure. For efficiency, it first builds a “path_tree” that describes which paths to transform. This path_tree is then used as input for a data-driven algorithm.
# File lib/abstractivator/trees/tree_map.rb, line 14 def tree_map(h) raise ArgumentError.new('Must provide a transformer block') unless block_given? config = BlockCollector.new yield(config) TransformTreeClosure.new(config).do_obj(h, config.get_path_tree) end
Private Instance Methods
diff(path, tree, mask)
click to toggle source
# File lib/abstractivator/trees/tree_compare.rb, line 110 def diff(path, tree, mask) {path: path_string(path), tree: tree, mask: massage_mask_for_diff(mask)} end
duplicates(xs)
click to toggle source
# File lib/abstractivator/trees/tree_compare.rb, line 98 def duplicates(xs) xs.group_by{|x| x}.each_pair.select{|_k, v| v.size > 1}.map(&:first) end
hashify_set(items, get_key)
click to toggle source
# File lib/abstractivator/trees/tree_compare.rb, line 94 def hashify_set(items, get_key) Hash[items.map{|x| [get_key.call(x), x] }] end
massage_mask_for_diff(mask)
click to toggle source
# File lib/abstractivator/trees/tree_compare.rb, line 114 def massage_mask_for_diff(mask) if mask.callable? :__predicate__ else mask end end
path_string(path)
click to toggle source
# File lib/abstractivator/trees/tree_compare.rb, line 106 def path_string(path) path.join('/') end
push_path(path, name)
click to toggle source
# File lib/abstractivator/trees/tree_compare.rb, line 102 def push_path(path, name) path + [name] end