-
Notifications
You must be signed in to change notification settings - Fork 5
/
README.txt
229 lines (169 loc) · 8.56 KB
/
README.txt
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
This package provides base classes of basic component types for the
Zope Component Architecture, as well as means for configuring and
registering them directly in Python (without ZCML).
.. contents::
How to set up ``grokcore.component``
====================================
In the following we assume you're writing or extending an application
that does bootstrap configuration using ZCML. There's always a single
ZCML file that is executed when the application is started, which then
includes everything else. Let's assume this file is called
``site.zcml`` (that's what it's called in Zope), so that file is what
we'll be editing.
In order to register the components that you wrote using the base
classes and directives available from ``grokcore.component``, we'll
use the ``<grok:grok />`` ZCML directive. But before we can use it,
we need to make sure it's available to the ZCML machinery. We do this
by including the meta configuration from ``grokcore.component``::
<include package="grokcore.component" file="meta.zcml" />
Put this line somewhere to the top of ``site.zcml``, next to other
meta configuration includes. Now, further down the line, we can tell
the machinery in ``grokcore.component`` to register all components in
your package (let's say it's called ``helloworld``)::
<grok:grok package="helloworld" />
To sum up, your ``site.zcml`` file should look like something like this::
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:grok="http://namespaces.zope.org/grok">
<!-- do the meta configuration to make the ZCML directives available -->
<include package="zope.foobar" file="meta.zcml" />
<include package="zope.frobnaz" file="meta.zcml" />
<include package="grokcore.component" file="meta.zcml" />
<!-- now load the configuration of packages that we depend on -->
<include package="zope.barfoo" />
<include package="zope.somethingorother" />
<!-- finally load my components which are based on grokcore.component -->
<grok:grok package="helloworld" />
</configure>
There is an optional ``exclude`` on the `grok` directive. It allows to specify
names of packages or modules that if encountered won't be grokked. These
names might contain unix shell-style wildcards.
Examples
========
Adapter
-------
Here's a simple adapter that may be useful in Zope. It extracts the
languages that a user prefers from the request::
import grokcore.component
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.i18n.interfaces import IUserPreferredLanguages
class CookieLanguage(grokcore.component.Adapter):
"""Extract the preferred language from a cookie"""
grokcore.component.context(IBrowserRequest)
grokcore.component.implements(IUserPreferredLanguages)
# No need to implement __init__, it's already provided by the base class.
def getPreferredLanguages(self):
# This an adapter for the request, so self.context is the request.
request = self.context
# Extract the preferred language from a cookie:
lang = request.cookies.get('language', 'en')
# According to IUserPreferredLanguages, we must return a list.
return [lang]
Multi-adapter
-------------
Here's a multi-adapter that functions as a content provider as known
from the ``zope.contentprovider`` library. Content providers are
components that return snippets of HTML. They're multi-adapters for
the content object (model), the request and the view that they're
supposed to be a part of::
import grokcore.component
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.publisher.interfaces.browser import IBrowserPage
from zope.contentprovider.interfaces import IContentProvider
class HelloWorldProvider(grokcore.component.MultiAdapter):
"""Display Hello World!"""
grokcore.component.adapts(Interface, IBrowserRequest, IBrowserPage)
grokcore.component.implements(IContentProvider)
def __init__(self, context, request, view):
pass
def update(self):
pass
def render(self):
return u'<p>Hello World!</p>'
Global utility
--------------
Here's a simple named utility, again from the Zope world. It's a
translation domain. In other words, it contains translations of user
messages and is invoked when the i18n machinery needs to translate
something::
import grokcore.component
from zope.i18n.interfaces import ITranslationDomain
class HelloWorldTranslationDomain(grokcore.component.GlobalUtility):
grokcore.component.implements(ITranslationDomain)
grokcore.component.name('helloworld')
domain = u'helloworld'
def translate(self, msgid, mapping=None, context=None,
target_language=None, default=None):
if target_language is None:
preferred = IUserPreferredLanguages(context)
target_language = preferred.getPreferredLanguages()[0]
translations = {'de': u'Hallo Welt',
'nl': u'Hallo Wereld'}
return translations.get(target_language, u'Hello World')
Of course, it's silly to implement your own translation domain utility
if there are already implementations available in ``zope.i18n`` (one
that reads translations from a GNU gettext message catalog and a
simple implementation for tests). Let's try to reuse that
implementation and register an instance::
import grokcore.component
from zope.i18n.interfaces import ITranslationDomain
from zope.i18n.simpletranslationdomain import SimpleTranslationDomain
messages = {('de', u'Hello World'): u'Hallo Welt',
('nl', u'Hello World'): u'Hallo Wereld'}
helloworld_domain = SimpleTranslationDomain(u'helloworld', messages)
grokcore.component.global_utility(helloworld_domain,
provides=ITranslationDomain,
name='helloworld',
direct=True)
Global adapter
--------------
Sometimes, you may have an object that should be registered as an adapter
factory. It may have come from some other framework that configured that
adapter for you, say, or you may have a class that you instantiate many
times to get different variations on a particular adapter factory. In these
cases, subclassing grokcore.component.Adapter or MultiAdapter is not
possible. Instead, you can use the global_adapter() directive. Here is an
example drawing on the ``z3c.form`` library, which provides an adapter factory
factory for named widget attributes::
import zope.interface
import zope.schema
import grokcore.component
import z3c.form.widget import ComputedWidgetAttribute
class ISchema(Interface):
"""This schema will be used to power a z3c.form form"""
field = zope.schema.TextLine(title=u"Sample field")
...
label_override = z3c.form.widget.StaticWidgetAttribute(
u"Override label", field=ISchema['field'])
grokcore.component.global_adapter(label_override, name=u"label")
In the example above, the provided and adapted interfaces are deduced from the
object returned by the ``StaticWidgetAttribute`` factory. The full syntax
for global_adapter is::
global_adapter(factory, (IAdapted1, IAdapted2,), IProvided, name=u"name")
The factory must be a callable (the adapter factory). Adapted interfaces are
given as a tuple. You may use a single interface instead of a one-element
tuple for single adapters. The provided interface is given as shown. The name
defaults to u"" (an unnamed adapter).
Handling events
---------------
Here we see an event handler much like it occurs within Zope itself. It
subscribes to the modified event for all annotatable objects (in other words,
objects that can have metadata associated with them). When invoked, it updates
the Dublin Core 'Modified' property accordingly::
import datetime
import grokcore.component
from zope.annotation.interfaces import IAnnotatable
from zope.lifecycleevent.interfaces import IObjectModifiedEvent
from zope.dublincore.interfaces import IZopeDublinCore
@grokcore.component.subscribe(IAnnotatable, IObjectModifiedEvent)
def updateDublinCoreAfterModification(obj, event):
"""Updated the Dublin Core 'Modified' property when a modified
event is sent for an object."""
IZopeDublinCore(obj).modified = datetime.datetime.utcnow()
Subscriptions
-------------
Subscriptions look similar to Adapter, however, unlike regular adapters,
subscription adapters are used when we want all of the adapters that adapt an
object to a particular adapter.
Analogous to MultiAdapter, there is a MultiSubscription component that "adapts"
multiple objects.