[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/dublincore/ add a
way to store some Dublin Core fields on content objects
Fred L. Drake, Jr.
fdrake at gmail.com
Tue May 10 03:29:43 EDT 2005
Log message for revision 30318:
add a way to store some Dublin Core fields on content objects
Changed:
U Zope3/trunk/src/zope/app/dublincore/annotatableadapter.py
A Zope3/trunk/src/zope/app/dublincore/tests/partial.txt
A Zope3/trunk/src/zope/app/dublincore/tests/test_partialannotatable.py
-=-
Modified: Zope3/trunk/src/zope/app/dublincore/annotatableadapter.py
===================================================================
--- Zope3/trunk/src/zope/app/dublincore/annotatableadapter.py 2005-05-09 18:15:54 UTC (rev 30317)
+++ Zope3/trunk/src/zope/app/dublincore/annotatableadapter.py 2005-05-10 07:29:43 UTC (rev 30318)
@@ -21,6 +21,7 @@
from zope.app.annotation.interfaces import IAnnotations, IAnnotatable
from zope.app.dublincore.zopedublincore import ZopeDublinCore
+from zope.app.dublincore.zopedublincore import DateProperty, ScalarProperty
from zope.app.location import Location
@@ -48,9 +49,7 @@
self.annotations[DCkey] = self._mapping
self.annotations = None
-__doc__ = ZDCAnnotatableAdapter.__doc__ + __doc__
-
class ZDCAnnotationData(PersistentDict):
"""Data for a Dublin Core annotation.
@@ -58,3 +57,64 @@
serialization to be registered. See the
zope.app.dublincore.fssync package.
"""
+
+
+# Hybrid adapters.
+#
+# Adapter factories created using this support the Dublin Core using a
+# mixture of annotations and data on the context object.
+
+def partialAnnotatableAdapterFactory(direct_fields):
+ if not direct_fields:
+ raise ValueError("only use partialAnnotatableAdapterFactory()"
+ " if at least one DC field is implemented directly")
+ fieldmap = {}
+ try:
+ # is direct_fields a sequence or a mapping?
+ direct_fields[0]
+ except KeyError:
+ # direct_fields: { dc_name: attribute_name }
+ fieldmap.update(direct_fields)
+ else:
+ for attrname in direct_fields:
+ fieldmap[attrname] = attrname
+
+ class ZDCPartialAnnotatableAdapter(ZDCAnnotatableAdapter):
+
+ def __init__(self, context):
+ self.__context = context
+ # can't use super() since this isn't a globally available class
+ ZDCAnnotatableAdapter.__init__(self, context)
+
+ for dcname, attrname in fieldmap.iteritems():
+ oldprop = ZopeDublinCore.__dict__.get(dcname)
+ if oldprop is None:
+ raise ValueError("%r is not a valid DC field" % dcname)
+ if (isinstance(oldprop, DateProperty)
+ or not isinstance(oldprop, ScalarProperty)):
+ raise ValueError("%r is not a supported DC field" % dcname)
+ prop = DirectProperty(dcname, attrname)
+ setattr(ZDCPartialAnnotatableAdapter, dcname, prop)
+
+ return ZDCPartialAnnotatableAdapter
+
+
+class DirectProperty(object):
+
+ def __init__(self, name, attrname):
+ self.__name__ = name
+ self.__attrname = attrname
+
+ def __get__(self, inst, klass):
+ if inst is None:
+ return self
+ context = inst._ZDCPartialAnnotatableAdapter__context
+ return getattr(context, self.__attrname, u"")
+
+ def __set__(self, inst, value):
+ if not isinstance(value, unicode):
+ raise TypeError("Element must be unicode")
+ context = inst._ZDCPartialAnnotatableAdapter__context
+ oldvalue = getattr(context, self.__attrname, None)
+ if oldvalue != value:
+ setattr(context, self.__attrname, value)
Added: Zope3/trunk/src/zope/app/dublincore/tests/partial.txt
===================================================================
--- Zope3/trunk/src/zope/app/dublincore/tests/partial.txt 2005-05-09 18:15:54 UTC (rev 30317)
+++ Zope3/trunk/src/zope/app/dublincore/tests/partial.txt 2005-05-10 07:29:43 UTC (rev 30318)
@@ -0,0 +1,139 @@
+====================================
+Dublin Core metadata as content data
+====================================
+
+Sometimes we want to include data in content objects which mirrors one
+or more Dublin Core fields. In these cases, we want the Dublin Core
+structures to use the data in the content object rather than keeping a
+separate value in the annotations typically used. What fields we want
+to do this with can vary, however, and we may not want the Dublin Core
+APIs to constrain our choices of field names for our content objects.
+
+To deal with this, we can use speciallized adapter implementations
+tailored to specific content objects. To make this a bit easier,
+there is a factory for such adapters.
+
+Let's take a look at the simplest case of this to start with. We have
+some content object with a `title` attribute that should mirror the
+Dublin Core `title` field::
+
+ >>> import zope.interface
+
+ >>> import zope.app.annotation.interfaces
+
+ >>> class Content(object):
+ ...
+ ... zope.interface.implements(
+ ... zope.app.annotation.interfaces.IAttributeAnnotatable)
+ ...
+ ... title = u""
+ ... description = u""
+
+To avoid having a discrepency between the `title` attribute of our
+content object and the equivalent Dublin Core field, we can provide a
+specific adapter for our object::
+
+ >>> from zope.app.dublincore import annotatableadapter
+
+ >>> factory = annotatableadapter.partialAnnotatableAdapterFactory(
+ ... ["title"])
+
+This creates an adapter factory that maps the Dublin Core `title`
+field to the `title` attribute on instances of our `Content` class.
+Multiple mappings may be specified by naming the additional fields in
+the sequence passed to `partialAnnotatableAdapterFactory()`. (We'll
+see later how to use different attribute names for Dublin Core
+fields.)
+
+Let's see what happens when we use the adapter.
+
+When using the adapter to retrieve a field set to use the content
+object, the value stored on the content object is used::
+
+ >>> content = Content()
+ >>> adapter = factory(content)
+
+ >>> adapter.title
+ u''
+
+ >>> content.title = u"New Title"
+ >>> adapter.title
+ u'New Title'
+
+If we set the relevant Dublin Core field using the adapter, the
+content object is updated::
+
+ >>> adapter.title = u"Adapted Title"
+ >>> content.title
+ u'Adapted Title'
+
+Dublin Core fields which are not specifically mapped to the content
+object do not affect the content object::
+
+ >>> adapter.description = u"Some long description."
+ >>> content.description
+ u''
+ >>> adapter.description
+ u'Some long description.'
+
+
+Using arbitrary field names
+---------------------------
+
+We've seen the simple approach, allowing a Dublin Core field to be
+stored on the content object using an attribute of the same name as
+the DC field. However, we may want to use a different name for some
+reason. The `partialAnnotatableAdapterFactory()` supports this as
+well.
+
+If we call `partialAnnotatableAdapterFactory()` with a mapping instead
+of a sequence, the mapping is used to map Dublin Core field names to
+attribute names on the content object.
+
+Let's look at an example where we want the `abstract` attribute on the
+content object to be used for the `description` Dublin Core field::
+
+ >>> class Content(object):
+ ...
+ ... zope.interface.implements(
+ ... zope.app.annotation.interfaces.IAttributeAnnotatable)
+ ...
+ ... abstract = u""
+
+We can create the adapter factory by passing a mapping to
+`partialAnnotatableAdapterFactory()`::
+
+ >>> factory = annotatableadapter.partialAnnotatableAdapterFactory(
+ ... {"description": "abstract"})
+
+We can check the effects of the adapter as before::
+
+ >>> content = Content()
+ >>> adapter = factory(content)
+
+ >>> adapter.description
+ u''
+
+ >>> content.abstract = u"What it's about."
+ >>> adapter.description
+ u"What it's about."
+
+ >>> adapter.description = u"Change of plans."
+ >>> content.abstract
+ u'Change of plans.'
+
+
+Limitations
+-----------
+
+The current implementation has a number of limitations to be aware of;
+hopefully these can be removed in the future.
+
+- Only simple string properties, like `title`, are supported. This is
+ largely because other field types have not been given sufficient
+ thought. Attempting to use this for other fields will cause a
+ `ValueError` to be raised by `partialAnnotatableAdapterFactory()`.
+
+- The CMF-like APIs are not supported in the generated adapters. It
+ is not clear that these APIs are used, but content object
+ implementations should be aware of this limitation.
Property changes on: Zope3/trunk/src/zope/app/dublincore/tests/partial.txt
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Added: Zope3/trunk/src/zope/app/dublincore/tests/test_partialannotatable.py
===================================================================
--- Zope3/trunk/src/zope/app/dublincore/tests/test_partialannotatable.py 2005-05-09 18:15:54 UTC (rev 30317)
+++ Zope3/trunk/src/zope/app/dublincore/tests/test_partialannotatable.py 2005-05-10 07:29:43 UTC (rev 30318)
@@ -0,0 +1,27 @@
+"""Tests of the 'partial' annotatable adapter.
+
+"""
+__docformat__ = "reStructuredText"
+
+import zope.testing.doctest
+
+import zope.app.annotation.attribute
+import zope.app.annotation.interfaces
+import zope.app.testing.placelesssetup
+import zope.app.testing.ztapi
+
+
+def setUp(test):
+ zope.app.testing.placelesssetup.setUp(test)
+ zope.app.testing.ztapi.provideAdapter(
+ zope.app.annotation.interfaces.IAttributeAnnotatable,
+ zope.app.annotation.interfaces.IAnnotations,
+ zope.app.annotation.attribute.AttributeAnnotations)
+
+def tearDown(test):
+ zope.app.testing.placelesssetup.tearDown(test)
+
+
+def test_suite():
+ return zope.testing.doctest.DocFileSuite(
+ "partial.txt", setUp=setUp, tearDown=tearDown)
Property changes on: Zope3/trunk/src/zope/app/dublincore/tests/test_partialannotatable.py
___________________________________________________________________
Name: svn:mime-type
+ text/x-python
Name: svn:eol-style
+ native
More information about the Zope3-Checkins
mailing list