===================
Referencing objects
===================


Simple references
=================

For working with references you have to have a located site set:

>>> import zope.app.component.hooks
>>> root = getRootFolder()
>>> zope.app.component.hooks.setSite(root)

>>> from gocept.reference.tests import Address, City

>>> root['dessau'] = City('Dessau', '06844')
>>> root['halle'] = City('Halle', '06110')
>>> root['jena'] = City('Jena', '07743')

>>> theuni = Address('Christian Theune', root['dessau'])

>>> theuni.city
<gocept.reference.tests.City object at 0x...>

>>> theuni.city = None
>>> print theuni.city
None

>>> del theuni.city
>>> theuni.city
Traceback (most recent call last):
AttributeError: city

>>> theuni.city = 12
Traceback (most recent call last):
TypeError: ...


Integrity-ensured references
============================

>>> from gocept.reference.tests import Monument

Located source
--------------

Referential integrity can be ensured iff the source of the reference is
located:

>>> root['fuchsturm'] = Monument('Fuchsturm', root['dessau'])
>>> root['fuchsturm'].city is root['dessau']
True

>>> import transaction
>>> transaction.commit()

>>> del root['dessau']
Traceback (most recent call last):
IntegrityError: Can't delete or move <gocept.reference.tests.City object at 0x...>.
It is still being referenced.

>>> transaction.commit()
Traceback (most recent call last):
DoomedTransaction

>>> transaction.abort()
>>> 'dessau' in root
True

>>> from gocept.reference.interfaces import IReferenceTarget

>>> IReferenceTarget(root['dessau']).is_referenced()
True

>>> root['fuchsturm'].city = None
>>> IReferenceTarget(root['dessau']).is_referenced()
False

>>> del root['dessau']
>>> 'dessau' in root
False

XXX References will also be correctly cancelled when the attribute or the
source is deleted.

>>> del root['fuchsturm']

Non-located source
------------------

If the source of a reference is not located, we can do anything we want with
references, including breaking them:

>>> fuchsturm = Monument('Fuchsturm', root['jena'])
>>> fuchsturm.city is root['jena']
True

>>> del fuchsturm.city
>>> fuchsturm.city
Traceback (most recent call last):
AttributeError: city

>>> fuchsturm.city = root['jena']
>>> fuchsturm.city is root['jena']
True

>>> del root['jena']
>>> fuchsturm.city
Traceback (most recent call last):
IntegrityError: Target u'/jena' of reference 'city' no longer exists.


Changing the location state of the source
-----------------------------------------

We cannot put an object with a broken reference back into containment since
referential integrity is not given:

>>> transaction.commit()

>>> root['fuchsturm'] = fuchsturm
Traceback (most recent call last):
IntegrityError: Target u'/jena' of reference 'city' no longer exists.

The transaction was doomed, let's recover the last working state:

>>> transaction.commit()
Traceback (most recent call last):
DoomedTransaction

>>> transaction.abort()

We have to repair the fuchsturm object by hand as it was not part of the
transaction:

>>> fuchsturm.__parent__ = fuchsturm.__name__ = None

>>> from gocept.reference.interfaces import IReferenceSource
>>> IReferenceSource(fuchsturm).verify_integrity()
False

>>> IReferenceTarget(root['halle']).is_referenced()
False
>>> fuchsturm.city = root['halle']
>>> IReferenceSource(fuchsturm).verify_integrity()
True
>>> IReferenceTarget(root['halle']).is_referenced()
False

>>> root['fuchsturm'] = fuchsturm
>>> IReferenceTarget(root['halle']).is_referenced()
True

>>> fuchsturm = root['fuchsturm']
>>> del root['fuchsturm']
>>> fuchsturm.city is root['halle']
True

>>> del root['halle']
>>> 'halle' in root
False


Upgrading from unconstrained to constrained references
------------------------------------------------------

Downgrading from integrity ensured references to unensured
----------------------------------------------------------


