In Part 1 we have seen how to define interfaces. Now we want to see how
we can actually use them.
So let's look at the Document example. What we maybe need is a way to
create new objects. Usually it's rather easy as you simply instantiate
the class:
document = Document("title","body")
But imagine you have a lot of such content types and you want some
registry of available content types. You could e.g. also have Image or
Survey.
To do this we can define a new interface which all those types have in
common: IContentType
This interface can be the superclass of e.g. IDocument:
class IContentType(Interface):
"""a content type"""
title = Attribute("""the title of the object""")
def render():
"""render this object"""
class IDocument(IContentType):
"""a documen content type"""
body = Attribute("""the body of the object""")
As you can see we moved title and render() into the interface definition
of IContentType as we defined that every content type should at least
have a title and a render method. We simply add those type specific
fields to those more specific content types (Image could have an image
attribute).
Now we probably have some core Content Management component and we might
also have additional (optional) packages which define additional content
types. The core component needs to know about all of them so it can e.g.
show them in a list. So what we need is a registry.
What we can do is to register a utility for each specific content type
which knows how to create an instance. This could look as follows:
class DocumentFactory(object):
"""create a new document"""
def create(self, title):
return Document(title)
Let's turn this into a content type factory interface:
class IContentTypeFactory(Interface)
"""lots of documentation"""
def create(title):
"""create a new instance of the content type and return it.
Take title as only parameter.
"""
and we can add the implements(IContentTypeFactory) to the above class.
To make it a utility we register it:
from zope.component import getGlobalSiteManager
gsm = getGlobalSiteManager()
document_factory = DocumentFactory()
gsm.registerUtility(document_factory, IContentTypeFactory)
(This is one way of registering it in pure python. There are better and
simpler ways to do that and I will describe them in a separate part).
Now we can retrieve the utility again:
from zope.component import getUtility
factory = getUtility(IContentTypeFactory)
document = factory.create("my title")
Now we have a document.
The only problem we still have is that we can only register one utility
with that interface. What we want is many utilities (one for each
content type) but with a different name. We call them named utilities.
We do the registration instead like this:
gsm.registerUtility(document_factory, IContentTypeFactory, 'document')
We simply add the name to the registration call and can then retrieve it
like this:
factory = getUtility(IContentTypeFactory, 'document')
This was we tell the registry we want a utility which implements
IContentTypeFactory (means we can call create()) and with the name
"document" (so that we know that we get a Document back).
We can also retrieve a list of all utilities registered for this
interface (because we of course don't know all the names in advance):
from zope.component import getUtilitiesFor
factories = getUtilitiesFor(IContentTypeFactory)
What we get is a list of tuples, consisting of the name and the actual
utility, e.g.
[
('document', <DocumentFactory at ...>),
....
]
We can then iterate over these and maybe display the available content
types.
In OGP this pattern can be useful to register handlers for each
individual UDP packet we receive. It could work as follows:
1. receive packet
2. decode header and esp. the packet type (an numeric id)
3. look up the handler which knows what to do with that packet by the
packet id:
handler = getUtility(IPacketHandler, str(id))
4. call the handler's handle() method:
result = handler.handle()
That way we can even have separate packages implementing additional
handlers. We can also use queryUtility() instead of getUtility() which
returns None instead of an exception of the handler is not found
(because it might not be yet implemented).
Each handler then just needs to implement the handle() method and add a
line
implements(IPacketHandler)
It also needs to register itself with the component architecture as
shown above (registerUtility) but as said I will show easier ways to do
that later (but it's still only one line).
Each handler could then be tested on it's own (e.g. by passing some
premade packet into it) and it could even contain the doctest which
tests it itself.
class SomePacketHandler(PacketHandler):
"""handle some packet
>>> packet=test_packets.some_packet
>>> handler = SomePacketHandler()
>>> result = handler.handle(packet)
>>> len(result)==155
True
>>> some other tests with results
"""
implement(IPacketHandler)
def handle(self, packet):
"""handle the packet"""
...
So all in all utilities are just some general functions which do a
simple job and are registered globally so that every package/component
can ask for them.
Another nice thing with named utilities (those which have a name
additionally) is that you can define a default for those if no name if
given.
E.g. if you register a utility like this
registerUtility(IUtility,utility)
then it has the name ""
If you then add named utilities like this:
registerUtility(IUtility,utility,"somename")
they are registered under "somename". So unnamed utilities are really
just the same but their name is "".
This can be useful e.g. for the networking layer. You might want to have
a utility which handles networking. The default one might simply use
urllib2. But you can add additional named utilities which use eventlet
etc. If you omit the name, you get the default, if you give one you get
the named one.
I hope this is somewhat clear. The concept is actually rather simple:
Remember a class instance with an interface and optionally a name and
return it again if asked for it.
-- Christian
--
Christian Scholz video blog: http://comlounge.tv
COM.lounge blog: http://mrtopf.de/blog
Luetticher Strasse 10 Skype: HerrTopf
52064 Aachen Homepage: http://comlounge.net
Tel: +49 241 400 730 0 E-Mail [EMAIL PROTECTED]
Fax: +49 241 979 00 850 IRC: MrTopf, Tao_T
neue Show: TOPFtäglich (http://mrtopf.de/blog/category/topf-taglich/)
_______________________________________________
Click here to unsubscribe or manage your list subscription:
https://lists.secondlife.com/cgi-bin/mailman/listinfo/pyogp