Common Parts
============

node.parts.Adopt
----------------

General imports.::

    >>> from plumber import plumber
    >>> from node.tests.env import MockupNode
    >>> from node.tests.env import NoNode

A dictionary is used as end point.::

    >>> from node.parts import Adopt
    >>> class AdoptingDict(dict):
    ...     __metaclass__ = plumber
    ...     __plumbing__ = Adopt

    >>> ad = AdoptingDict()

The mockup node is adopted.::

    >>> node = MockupNode()
    >>> ad['foo'] = node
    >>> ad['foo'] is node
    True
    >>> node.__name__
    'foo'
    >>> node.__parent__ is ad
    True

The non-node object is not adopted.::

    >>> nonode = NoNode()
    >>> ad['bar'] = nonode
    >>> ad['bar'] is nonode
    True
    >>> hasattr(nonode, '__name__')
    False
    >>> hasattr(nonode, '__parent__')
    False

If something goes wrong, the adoption does not happen.  See plumbing.Adopt for
exceptions that are handled.

XXX: In case this should be configurable, it would be nice if a plumbing
element could be instatiated which is currently not possible. It would be
possible by defining the plumbing __init__ method with a different name.
Maybe it is also possible to have two __init__ one decorated one not, if the
plumbing decorator could influence that all plumbing functions are stored under
a different name. If the decorator cannot do that a Plumbing metaclass will
work for sure, however, it is questionable whether it justifies a metaclass
instead of just naming the plumbing init eg plumbing__init__.
::

    >>> class FakeDict(object):
    ...     def __setitem__(self, key, val):
    ...         raise KeyError(key)
    ...     def setdefault(self, key, default=None):
    ...         pass

    >>> class FailingAD(FakeDict):
    ...     __metaclass__ = plumber
    ...     __plumbing__ = Adopt

    >>> fail = FailingAD()
    >>> node = MockupNode()
    >>> fail['foo'] = node
    Traceback (most recent call last):
    ...
    KeyError: 'foo'
    >>> node.__name__ is None
    True
    >>> node.__parent__ is None
    True

node.parts.NodeChildValidate
----------------------------

::

    >>> from plumber import plumber
    >>> from node.parts import (
    ...     NodeChildValidate,
    ...     DefaultInit,
    ...     Nodify,
    ...     OdictStorage,
    ... )
    
    >>> class NodeChildValidateNode(object):
    ...     __metaclass__ = plumber
    ...     __plumbing__ = NodeChildValidate, DefaultInit, Nodify, OdictStorage
    
    >>> node = NodeChildValidateNode()
    >>> node.allow_non_node_childs
    False
    
    >>> node['child'] = 1
    Traceback (most recent call last):
      ...
    ValueError: Non-node childs are not allowed.
    
    >>> class SomeClass(object): pass
    
    >>> node['aclasshere'] = SomeClass
    Traceback (most recent call last):
      ...
    ValueError: It isn't allowed to use classes as values.
    
    >>> node.allow_non_node_childs = True
    
    >>> node['child'] = 1
    >>> node['child']
    1
