Import of working code base

Signed-off-by: martin f. krafft <madduck@madduck.net>
diff --git a/mergers/dict/__init__.py b/mergers/dict/__init__.py
new file mode 100644
index 0000000..8346017
--- /dev/null
+++ b/mergers/dict/__init__.py
@@ -0,0 +1,11 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from update import DictUpdate
+from recursive_update import DictRecursiveUpdate
+from recursive_policy_update import DictRecursivePolicyUpdate
diff --git a/mergers/dict/base.py b/mergers/dict/base.py
new file mode 100644
index 0000000..92fdd1b
--- /dev/null
+++ b/mergers/dict/base.py
@@ -0,0 +1,12 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from mergers.base import BaseMerger
+
+class BaseDictMerger(BaseMerger):
+    pass
diff --git a/mergers/dict/recursive_policy_update.py b/mergers/dict/recursive_policy_update.py
new file mode 100644
index 0000000..f3bcb3e
--- /dev/null
+++ b/mergers/dict/recursive_policy_update.py
@@ -0,0 +1,32 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from base import BaseDictMerger
+
+class DictRecursivePolicyUpdate(BaseDictMerger):
+
+    def __init__(self, policy=None):
+        super(DictRecursivePolicyUpdate, self).__init__()
+        if policy is None:
+            policy = {(dict,dict) : self.merge,
+                      (list,list) : lambda x,y: x+y,
+                      (dict,list) : lambda x,y: self.merge(x, dict(y)),
+                      None        : lambda x,y: y
+                     }
+        self._policy = policy
+
+    def merge(self, first, second):
+        ret = first.copy()
+        for k,v in second.iteritems():
+            if k in ret:
+                pfn = self._policy.get((type(ret[k]), type(v)),
+                                       self._policy.get(None))
+                ret[k] = pfn(ret[k], v)
+            else:
+                ret[k] = v
+        return ret
diff --git a/mergers/dict/recursive_update.py b/mergers/dict/recursive_update.py
new file mode 100644
index 0000000..ba94c1f
--- /dev/null
+++ b/mergers/dict/recursive_update.py
@@ -0,0 +1,25 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from base import BaseDictMerger
+
+class DictRecursiveUpdate(BaseDictMerger):
+
+    def merge(self, first, second):
+        ret = first.copy()
+        for k,v in second.iteritems():
+            if k in ret:
+                if isinstance(ret[k], dict):
+                    if isinstance(v, (list, tuple)):
+                        v = dict(v)
+                    ret[k] = self.merge(ret[k], v)
+                else:
+                    ret[k] = v
+            else:
+                ret[k] = v
+        return ret
diff --git a/mergers/dict/tests/test_recursive_policy_update.py b/mergers/dict/tests/test_recursive_policy_update.py
new file mode 100644
index 0000000..f44ada6
--- /dev/null
+++ b/mergers/dict/tests/test_recursive_policy_update.py
@@ -0,0 +1,26 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from test_recursive_update import TestDictRecursiveUpdate
+from mergers.dict import DictRecursivePolicyUpdate
+
+class TestDictRecursivePolicyUpdate(TestDictRecursiveUpdate):
+
+    def setUp(self):
+        self.merger = DictRecursivePolicyUpdate()
+
+    def test_nested_lists_extend(self):
+        first = {'one': [1,2],
+                 'two': {'one': [1,2]}}
+        second = {'one': [3,4], 
+                  'two': {'one': [3,4]}}
+        ret = self.merger.merge(first, second)
+        assert len(ret['one']) == 4
+        assert ret['one'][2] == 3
+        assert len(ret['two']['one']) == 4
+        assert ret['two']['one'][3] == 4
diff --git a/mergers/dict/tests/test_recursive_update.py b/mergers/dict/tests/test_recursive_update.py
new file mode 100644
index 0000000..c1e8501
--- /dev/null
+++ b/mergers/dict/tests/test_recursive_update.py
@@ -0,0 +1,45 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from test_update import TestDictUpdate
+from mergers.dict import DictRecursiveUpdate
+
+class TestDictRecursiveUpdate(TestDictUpdate):
+
+    def setUp(self):
+        self.merger = DictRecursiveUpdate()
+
+    def test_simple_recursive_dict_update(self):
+        first = {'one':{1:1,2:3,3:2}}
+        second = {'one':{2:2,3:3,4:4}}
+        ret = self.merger.merge(first, second)
+        assert len(ret) == 1
+        for k,v in ret['one'].iteritems():
+            assert k == v
+
+    def test_complex_recursive_dict_update(self):
+        first = {'one': 1,
+                 'two': {'a':92,'b':94},
+                 'three': {'third':0.33,'two thirds':0.67},
+                 'four': {1:{1:1},2:{2:2},3:{3:4}}
+                }
+        second = {'five': 5,
+                  'one': 1,
+                  'two': {'b':93,'c':94},
+                  'four': {4:{4:4}, 3:{3:3}},
+                 }
+        ret = self.merger.merge(first, second)
+        assert ret['one'] == 1
+        assert len(ret['two']) == 3
+        assert ret['two']['b'] == 93
+        assert len(ret['three']) == 2
+        assert len(ret['four']) == 4
+        for i in range(1,4):
+            assert len(ret['four'][i]) == 1
+            for k,v in ret['four'][i].iteritems():
+                assert k == v
diff --git a/mergers/dict/tests/test_update.py b/mergers/dict/tests/test_update.py
new file mode 100644
index 0000000..8e35c53
--- /dev/null
+++ b/mergers/dict/tests/test_update.py
@@ -0,0 +1,22 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from mergers.dict import DictUpdate
+
+class TestDictUpdate:
+
+    def setUp(self):
+        self.merger = DictUpdate()
+
+    def test_dict_update(self):
+        first = {1:1,2:3,3:2}
+        second = {2:2,3:3,4:4}
+        ret = self.merger.merge(first, second)
+        assert len(ret) == 4
+        for k,v in ret.iteritems():
+            assert k == v
diff --git a/mergers/dict/update.py b/mergers/dict/update.py
new file mode 100644
index 0000000..dd834f0
--- /dev/null
+++ b/mergers/dict/update.py
@@ -0,0 +1,16 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from base import BaseDictMerger
+
+class DictUpdate(BaseDictMerger):
+
+    def merge(self, first, second):
+        ret = first.copy()
+        ret.update(second)
+        return ret