[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