Module: Abstractivator::Trees
- Defined in:
- lib/abstractivator/trees/tree_map.rb,
lib/abstractivator/trees/tree_compare.rb,
lib/abstractivator/trees/block_collector.rb,
lib/abstractivator/trees/recursive_delete.rb
Defined Under Namespace
Classes: BlockCollector, SetMask, TransformTreeClosure
Instance Method Summary collapse
-
#recursive_delete!(hash, keys) ⇒ Object
recursively deletes the specified keys.
- #set_mask(items, get_key) ⇒ Object
-
#tree_compare(tree, mask, path = [], index = nil) ⇒ Object
Compares a tree to a mask.
-
#tree_map(h) {|config| ... } ⇒ Object
Transforms a tree at certain paths.
Instance Method Details
#recursive_delete!(hash, keys) ⇒ Object
recursively deletes the specified keys
9 10 11 12 13 14 15 16 17 18 19 |
# 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) ⇒ Object
11 12 13 |
# 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) ⇒ Object
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.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# 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| ... } ⇒ Object
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.
14 15 16 17 18 19 |
# 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 |