Zope2 sucks! It's too difficult to develop big applications! The products model is not very flexible and it's difficult to extend.
The view logic in the same place as the data. Registering templates on the same class. Security - again.. on the class. Routing directly on zope objects not on views.
There should be another way!
More abstraction! How java/c++ people are doing it? They use design patterns. Can we use those?
Better yet: can we use just the part that makes sense in a big web application?
The birth of zope3 framework.
A collection of packages which are loosely connected via interfaces.
Remember those zope.* namespaced packages all other PYPI? Yeah.. it's the zope3 framework.
It seems that not all good ideas make sense in the real world.
zope3 was designed to be a non-framework that allows you to choose whichever packages you like on your own. It was the answer to the monolithic approach of Zope2.
Think of gentoo's stage1 installation.
It turned out to be even more esoteric that Zope2.
And it was hard to bootstrap a new application.
Combine the important parts of zope3 with Zope2 (using Five) to create a Zope2 framework that is better than Zope2
Plone/Grok has been using components successfully for years and they don't want to switch back.
Allows sane code reuse - without sub-classing or import loops.
A decoupled architecture is always easier to refactor and improve upon.
Provide new functionality to class/collection of objects without actually modifying any code in the target classes.
Does not add a performance overhead. Looking up a component is equal to looking up a dictionary key.
Less monkey patches! Yeay!
Interfaces for python (yeah, like Java/C++).
Contains component registries, functions to manipulate components.
A simple event system that uses interfaces. This one is optional, but usually comes with the other 2.
This is how an application structure can look like.
##########################################################################
# #
# Global component registry #
# #
# ################################################################## #
# # # #
# # Component registry 1 Component registry 2 # #
# # # #
# # ############################################################ # #
# # # # # # #
# # # Component registry 1.1 # # # #
# # # # # # #
# # # ########################## # # # #
# # # # # # # # #
# # # # Events, Adapters, # # Events, Utilities, # # #
# # # # Utilities. # # Adapters, MultiAdapters # # #
# # # # # # # # #
# # # ########################## # # # #
# # # # # # #
# # ############################################################ # #
# # # #
# ################################################################## #
# #
# #
##########################################################################
We'll use the global site manager as our component registry.
from zope.component import getGlobalSiteManager
from zope.interface import Interface, implements
class IUtility(Interface):
""" A generic utility interface """
def do_stuff(param):
""" A utility does things """
class SomeUtility(object):
implements(IUtility)
def do_stuff(self, name):
print "Hello", name
gsm = getGlobalSiteManager()
gsm.registerUtility(SomeUtility(), IUtility)
now let's use it
from zope.component import getGlobalSiteManager
gsm = getGlobalSiteManager()
my_utility = gsm.getUtility(IUtility)
my_utility.do_stuff("Medved")
Let's see how we can provide additional functionality to an object using adapters.
from zope.interface import implements, Interface
from zope.component import adapts
from zope.component import getGlobalSiteManager
class IDriver(Interface):
""" """
class Driver(object):
""" """
implements(IDriver)
def __init__(self, name):
self.name = name
class ICar(Interface):
""" """
class SuperCar(object):
implements(ICar)
adapts(IDriver)
def __init__(self, driver):
self.driver = driver # here is where we can connect the dots
def super_drive(self):
""" """
print "%s, warp 9! Engage!" % self.driver.name
gsm = getGlobalSiteManager()
gsm.registerAdapter(SuperCar)
now let's call the adapter and see what happens.
>> driver = Driver("Data")
>> gsm.getAdapter(driver, ICar).super_drive()
Data, warp 9! Engage!
Create a document object and throw a created event when the document is created.
import datetime
from zope.interface import Interface, implements
from zope.component import adapter, handle, getGlobalSiteManager
class Document(object):
def __init__(self):
handle(DocumentCreated(self))
class IDocumentCreated(Interface):
""" Marker interface for the event"""
class DocumentCreated(object):
""" This class acts just like an adapter"""
implements(IDocumentCreated)
def __init__(self, doc):
self.doc = doc
@adapter(IDocumentCreated)
def document_created_handler(event):
event.doc.created = datetime.datetime.utcnow()
gsm = getGlobalSiteManager()
gsm.registerHandler(document_created_handler)
now when we create a dcoument and event will be thrown
>> doc = Document()
>> print doc.created
1999-01-01 14:28:10.282545
/
#