Method: Abstractivator::Trees#tree_compare
- Defined in:
- lib/abstractivator/trees/tree_compare.rb
#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 |