In the initial development of KARL3, we kept the “framework” very thin. Our “pay only for what you eat” mantra helped us see the virtue of creating just the scaffolding we needed.
Along the way, we saw that the CMF pattern of a formal “content type”, known system wide and with some extra metadata, was useful. The (very tiny) repoze.lemonade package grew to support these very few demands. The Lemonade docs attest to how focused the mini-framework was kept.
In particular, Lemonade lets the Zope Component Architecture do most of the work.
In this tutorial, we will convert our “content types” (content managed through-the-web and later, indexed in a catalog) into Lemonade content types. We will then leverage this facility to introduce event subscribers triggered by changes in content.
Before using Lemonade, we need to install it into our sandbox:
$ easy_install -i http://dist.repoze.org/lemonade/dev/simple repoze.lemonade
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <configure xmlns="http://namespaces.repoze.org/bfg"
xmlns:lemonade="http://namespaces.repoze.org/lemonade">
<include package="repoze.lemonade.includes" file="meta.zcml"/>
<lemonade:content
factory=".feed.Feed"
type="feedstool.models.interfaces.IFeed"
/>
<lemonade:content
factory=".feedentry.FeedEntry"
type=".interfaces.IFeedEntry"
/>
<subscriber
for="repoze.lemonade.interfaces.IContent
repoze.folder.interfaces.IObjectAddedEvent"
handler=".subscribers.index_content"/>
</configure>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | from repoze.bfg.chameleon_zpt import render_template_to_response
from repoze.bfg.chameleon_zpt import get_template
from webob.exc import HTTPFound
from repoze.bfg.url import model_url
from repoze.bfg.view import static
from repoze.lemonade.content import create_content
from feedstool.models.interfaces import IFeed
static_view = static('templates/static')
def list_feeds_view(context, request):
layout = get_template('templates/layout.pt')
feeds = []
for feed in context.values():
feeds.append({
'title': feed.title,
'model_url': model_url(feed, request),
})
return render_template_to_response(
'templates/list_feeds.pt',
request=request,
layout=layout,
page_title="List Feeds",
feeds=feeds)
def add_feed_view(context, request):
layout = get_template('templates/layout.pt')
is_submitted = request.POST.get('form.submitted', False)
if is_submitted is not False:
title = request.POST['title']
url = request.POST['url']
#feed = create_content(IFeed, title, url)
from feedstool.models.feed import Feed
feed = Feed(title, url)
name = title.replace(' ', '-').lower()
context[name] = feed
return HTTPFound(location=model_url(feed, request))
# Form was not submitted, return the page
return render_template_to_response(
'templates/add_feed.pt',
request=request,
layout=layout,
page_title='Add Feed',
is_submitted=is_submitted)
|
1 2 | def index_content(object, event):
print "Object added", object.title
|
Restart the server, visit http://localhost:6543/, and add a feed. In your server window you should see:
$ paster serve FeedsTool.ini --reload
Starting subprocess with file monitor
Starting server in PID 19498.
serving on 0.0.0.0:6543 view at http://127.0.0.1:6543
No handlers could be found for logger "ZODB.FileStorage"
Object added My First Feed
As you can see on the last line, our event subscriber was run when the IObjectAddedEvent event was triggered, as configured in the models/configure.zcml subscriber.
Subscribers make a nice way for external packages to get plugged-in and watch for things that are of interest to them. This further re-inforces the utility of ZCML as a configuration language.