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
""" 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
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
_______________________________________________ Zope-Dev maillist - Zope-Dev@zope.org http://mail.zope.org/mailman/listinfo/zope-dev ** No cross posts or HTML encoding! ** (Related lists - http://mail.zope.org/mailman/listinfo/zope-announce http://mail.zope.org/mailman/listinfo/zope )