Bound data (reconfigure.items.bound.BoundData) is a special class that can be subclassed and stuffed with properties, which will act as proxies to an underlying Node tree. This can be confusing, so let’s go with an example:
>>> from reconfigure.nodes import Node, PropertyNode
>>> from reconfigure.items.bound import BoundData
>>>
>>> node = Node('test')
>>> node.append(PropertyNode('name', 'Alice'))
>>> node.append(PropertyNode('age', '25'))
>>> node.append(PropertyNode('gender', 'f'))
>>> print node
(test)
name = Alice
age = 25
gender = f
Here we have a very simple Node tree. Note that all values are str and the gender is coded in a single character (we have probably parsed this tree from some .ini file). Now let’s define a BoundData class:
>>> class HumanData (BoundData):
... pass
...
>>> HumanData.bind_property('name', 'name')
>>> HumanData.bind_property('age', 'age', getter=int, setter=str)
>>> HumanData.bind_property('gender', 'gender',
... getter=lambda x: 'Male' if x == 'm' else 'Female',
... setter=lambda x: 'm' if x == 'Male' else 'f')
>>> human = HumanData(node)
>>> human
<__main__.MyData object at 0x114ddd0>
>>> print human
{
"gender": "Female",
"age": 25,
"name": "Alice"
}
First, we’ve defined our BoundData subclass. Then, we have defined three properties in it:
- name is the simplest property, it’s directly bound to “name” child PropertyNode
- age also has a getter and setter. These are invoked when the property is read or written. In this case, we use int() to parse a number from the node tree and str() to stringify it when writing back.
- gender is similar to age but has more complex getter and setter that transform “m” and “f” to a human-readable description.
When the properties are mutated, the modifications are applied to Node tree immediately and vice versa:
>>> human.age
25
>>> human.age = 30
>>> node.get('age').value
'30'
>>> node.get('age').value = 27
>>> human.age
27
Let’s try a more complex node tree:
>>> nodes = Node('',
... Node('Alice',
... PropertyNode('Phone', '1234-56-78')
... ),
... Node('Bob',
... PropertyNode('Phone', '8765-43-21')
... )
... )
>>> print nodes
()
(Alice)
Phone = 1234-56-78
(Bob)
Phone = 8765-43-21
Bound data classes:
>>> class PersonData (BoundData):
... def template(self, name, phone):
... return Node(name,
... PropertyNode('Phone', phone)
... )
...
>>> class PhonebookData (BoundData):
... pass
...
>>> PersonData.bind_property('Phone', 'phone')
>>> PersonData.bind_name('name')
>>>
>>> PhonebookData.bind_collection('entries', item_class=PersonData)
>>>
>>> phonebook = PhonebookData(nodes)
>>> print phonebook
{
"entries": [
{
"phone": "1234-56-78",
"name": "Alice"
},
{
"phone": "8765-43-21",
"name": "Bob"
}
]
}
Here, bind_collection method is used to create a collection property from child nodes. item_class class will be used to wrap these nodes.
Alternatively, you can employ reconfigure.items.bound.BoundDictionary class to create a dict-like property:
>>> PhonebookData.bind_collection('entries', collection_class=BoundDictionary, item_class=PersonData, key=lambda x: x.name)
>>> print phonebook
{
"entries": {
"Bob": {
"phone": "8765-43-21",
"name": "Bob"
},
"Alice": {
"phone": "1234-56-78",
"name": "Alice"
}
}
}