[Zope-dev] registering adapters and subscribers for string labels
Chris Withers
chris at simplistix.co.uk
Fri Apr 25 07:13:10 EDT 2008
Hi All,
I bumped into hurry.workflow yesterday and was reminded again that we
often have the need to register components against string labels.
As an example, I want to register a subscriber to a particular workflow
transition.
Now, the hurry.workflow examples I've seen seem to revolve around a
generic subscriber with a load of if-then-else logic in them - exactly
the kind of stuff the component architecture is designed to avoid ;-)
(not only that, it's *really* inefficient too)
So, I dug up some code I wrote to get around this and thought I'd throw
it out to the list. I have a feeling this should ship somewhere, but I
don't know where. Any suggestions?
(if you look you'll see the code is all tested with a nice little
narrative doctest in actions.py and a selection of unit tests for the
edge cases in the test file, should keep even Marius happy ;-) )
cheers,
Chris
PS: I showed Kapil this code yesterday and he asked "why screw with
globals". The answer is to allow the registrations against names to be
made in zcml - as you can see in the doctest. I'm open to other
suggestions for this ;-)
--
Simplistix - Content Management, Zope & Python Consulting
- http://www.simplistix.co.uk
-------------- next part --------------
"""
This module contains class singletons for any actions
that we want to register adapters against.
>>> from actions import register,get
To create a new one:
>>> register('foo')
'foo'
To get hold of one once it's been registered:
>>> get('foo')
<Action:'foo'>
And why do we want to do all this? Well, so we can register
adapters against string constants like workflow actions:
>>> do_zcml('''
... <adapter
... for="test_actions.SomeClass actions.foo"
... provides="test_actions.ISomething"
... factory="test_actions.SomethingAdapter"
... />
... ''')
Now we can get an adapter as follows:
>>> from test_actions import SomeClass,ISomething
>>> from zope.component import getMultiAdapter
>>> o = SomeClass()
>>> getMultiAdapter((o,get('foo')),ISomething)
<SomethingAdapter for (<SomeClass instance>, <Action:'foo'>)>
"""
from new import classobj
registry = {}
class Action:
def name(self):
return self.__class__.__name__
def __repr__(self):
return '<Action:%r>' % self.name()
def get(name):
return registry[name]
def register(name):
if not registry.has_key(name):
mod_name = name.replace(' ','_')
if mod_name in globals():
raise ValueError(
'%r is not available for registration as an action name' % mod_name
)
k = classobj(name,(Action,),{})
registry[name]=k()
globals()[mod_name]=k
return name
-------------- next part --------------
from actions import get,register,Action
from unittest import TestCase,TestSuite,makeSuite
from zope.app import component
from zope.component import provideAdapter
from zope.configuration import xmlconfig
from zope.interface import Interface,implements
from zope.testing.doctest import DocTestSuite
class SomeClass:
def __repr__(self):
return '<SomeClass instance>'
class ISomething(Interface): pass
class SomethingAdapter:
implements(ISomething)
def __init__(self,*args):
self.args = args
def __repr__(self):
return '<SomethingAdapter for %s>' % repr(self.args)
def do_zcml(text):
xmlconfig.string(('''
<configure
xmlns="http://namespaces.zope.org/zope">
%s
</configure>
'''%text).strip(),xmlconfig.file('meta.zcml',component))
class Tests(TestCase):
def test_get(self):
# we test a bogus get here, a kosher one is tested
# in test_register
self.assertRaises(KeyError,get,'bogus')
def test_register(self):
name = register('foo')
self.assertEqual(name,'foo')
obj = get('foo')
self.failUnless(isinstance(obj,Action))
self.assertEqual(obj.name(),'foo')
self.assertEqual(repr(obj),"<Action:'foo'>")
def test_duplicate_register(self):
register('foo')
obj1 = get('foo')
register('foo')
obj2 = get('foo')
self.failUnless(obj1 is obj2)
def test_adapter(self):
register('foo bar')
xmlconfig.string('''
<configure
xmlns="http://namespaces.zope.org/zope">
<adapter
for="actions.foo_bar"
provides="test_actions.ISomething"
factory="test_actions.SomethingAdapter"
/>
</configure>
'''.strip(),xmlconfig.file('meta.zcml',component))
a = ISomething(get('foo bar'))
self.failUnless(isinstance(a,SomethingAdapter))
self.assertEqual(len(a.args),1)
self.failUnless(a.args[0] is get('foo bar'))
def test_register_bad(self):
self.assertRaises(ValueError,register,'Action')
self.assertRaises(ValueError,register,'get')
self.assertRaises(ValueError,register,'register')
self.assertRaises(ValueError,register,'registry')
def test_suite():
suite = TestSuite()
suite.addTest(makeSuite(Tests))
suite.addTest(DocTestSuite('actions',
globs={'do_zcml':do_zcml}))
return suite
More information about the Zope-Dev
mailing list