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 )

Reply via email to