
class Hash

  # Weaves two hashes producing a new hash. The two hashes need
  # to be compatible according to the following rules for each node:
  #
  #   <tt>
  #   hash, hash => hash (recursive +)
  #   hash, array => error
  #   hash, value => error
  #   array, hash => error
  #   array, array => array + array
  #   array, value => array << value
  #   value, hash => error
  #   value, array => array.unshift(valueB)
  #   valueA, valueB => valueB
  #   </tt>
  #
  # Example:
  #
  #   # to do
  #
  def weave(h)
    raise ArgumentError, "Hash expected" unless h.kind_of?(Hash)
    s = self.clone
    h.each { |k,node|
      node_is_hash = node.kind_of?(Hash)
      node_is_array = node.kind_of?(Array)
      if s.has_key?(k)
        self_node_is_hash = s[k].kind_of?(Hash)
        self_node_is_array = s[k].kind_of?(Array)
        if self_node_is_hash
          if node_is_hash
            s[k] = s[k].weave(node)
          elsif node_is_array
            raise ArgumentError, 'Incompatible hash addition' #self[k] = node
          else
            raise ArgumentError, 'Incompatible hash addition' #self[k] = node
          end
        elsif self_node_is_array
          if node_is_hash
            raise ArgumentError, 'Incompatible hash addition' #self[k] = node
          elsif node_is_array
            s[k] += node
          else
            s[k] << node
          end
        else
          if node_is_hash
            raise ArgumentError, 'Incompatible hash addition' #self[k] = node
          elsif node_is_array
            s[k].unshift( node )
          else
            s[k] = node
          end
        end
      else
        s[k] = node
      end
    }
    s
  end

end


#  _____         _
# |_   _|__  ___| |_
#   | |/ _ \/ __| __|
#   | |  __/\__ \ |_
#   |_|\___||___/\__|
#
=begin test

  require 'test/unit'

  class TCHash < Test::Unit::TestCase

    def test_weave
      b = { :a=>1, :b=>[1,2,3], :c=>{ :x=>'X' } }
      c = { :a=>2, :b=>[4,5,6], :c=>{ :x=>'A', :y => 'B' } }
      r = { :a=>2, :b=>[1,2,3,4,5,6], :c=>{ :x => 'A', :y => 'B' } }
      assert_equal( r, b.weave(c) )
    end

  end

=end
