[Checkins] SVN: grok/trunk/doc/grok_overview. Rename into a rst extension.

Jan-Wijbrand Kolman janwijbrand at gmail.com
Thu May 1 07:32:17 EDT 2008


Log message for revision 85990:
  Rename into a rst extension.

Changed:
  A   grok/trunk/doc/grok_overview.rst
  D   grok/trunk/doc/grok_overview.txt

-=-
Copied: grok/trunk/doc/grok_overview.rst (from rev 85987, grok/trunk/doc/grok_overview.txt)
===================================================================
--- grok/trunk/doc/grok_overview.rst	                        (rev 0)
+++ grok/trunk/doc/grok_overview.rst	2008-05-01 11:32:17 UTC (rev 85990)
@@ -0,0 +1,1316 @@
+Grok Developer's Notes 
+======================
+
+This document is a developer's overview of Grok. It is not intended to
+be a beginner's tutorial. It's also not a reference. It gives a
+succinct an overview of *what's there* in Grok, with a brief idea on
+how to use it, so you can try it out and learn more about it. This
+includes rules, common APIs and patterns.
+
+Models
+------
+
+A Grok-based application is composed out of one or more *models*. We
+also call these *content objects*, or just *objects*. The objects are
+just Python objects, instantiated from a class. Models can be stored
+in the object database (ZODB), created by an object relational mapper,
+or created on the fly by your code.
+
+Grok comes with two kinds of models: ``grok.Model`` and
+``grok.Container``. ``grok.Model`` is the most basic one and doesn't
+really do much for you. You can subclass from ``grok.Model``, like
+this::
+
+  class Document(grok.Model):
+      pass
+
+The main thing subclassing from ``grok.Model`` does is make it
+possible (but not required) to store instances in the ZODB.
+
+You can also subclass from ``grok.Container``, like this::
+
+  class Folder(grok.Container):
+      pass
+
+A container is like a model, but also acts much like a Python
+dictionary. The main difference with Python dictionaries is that its
+methods, like ``keys`` and ``items``, are iterator-like. They also do
+more, like send events, but we can forget about that for now.
+
+In order to be able to install an application, you need to mix in
+``grok.Application`` into a class::
+
+  class Application(grok.Application, grok.Container):
+      pass
+
+Instances of this class can now be installable in the Grok web UI.
+
+Let's make a structure with some folders and documents::
+
+  app = Application()
+  app['a'] = a = Container()
+  a['b'] = Document()
+  a['c'] = Container()
+  a['c']['d'] = Document()
+
+Grok publishes these objects to the web: this is called object
+publishing. What this means in essence is that objects can be
+addressed with URLs. When you access a URL of a Grok application with
+your web browser, Grok uses this URL to find an object.
+
+An example: if ``app`` were installed under
+``http://localhost:8080/app``, the following URLs will exist in your
+application::
+
+  http://localhost:8080/app
+  http://localhost:8080/app/a
+  http://localhost:8080/app/a/b
+  http://localhost:8080/app/a/c
+  http://localhost:8080/app/a/c/d
+
+``__parent__`` and ``__name__``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Models in Grok are automatically supplied with a ``__parent__`` and a
+``__name__`` attribute. 
+
+* ``__parent__`` points to object this object is in. If the object is in 
+  a container, this is the container.
+
+* ``__name__`` is the name this object has in a URL (and in its
+  container, if it is in a container).
+
+These attributes are used for navigation through content space, and
+Grok also uses them to construct URLs automatically (see below). The
+``__parent__`` and ``__name__`` attributes are automatically added to
+an object when it is placed in a container, or when it is being
+traversed through using ``traverse``.
+
+Custom traversing
+~~~~~~~~~~~~~~~~~
+
+Grok resolves URLs to objects by *traversing* through the containers
+and models in question. What if you want to customize the way this
+traversal works? Perhaps you want to traverse through objects you
+create yourself, or objects created by an object relational
+mapper. Grok offers a handy way to do so: the ``traverse`` method on
+models.
+
+A math example: imagine you want to create an application that
+represents integer numbers, and you want to traverse to each
+individual number, like this::
+
+  http://localhost:8080/integers/0
+  http://localhost:8080/integers/1
+  http://localhost:8080/integers/2
+  http://localhost:8080/integers/3
+  ...
+
+and so on. How would we implement this? We cannot create a container
+and fill it with all integers possible, as there are an infinite
+number of them. Okay, so we are in a math example, so let's be exact:
+this is true if we ignore memory limitations and URL length
+limitations. Storing all possible integers in a container is just not
+*practical*.
+
+We use the ``traverse`` method::
+
+  class ParticularInteger(grok.Model):
+      def __init__(self, number):
+          self.number = number
+
+  class Integers(grok.Application, grok.Model):
+      def traverse(self, name):
+          try:
+               value = int(name)
+          except ValueError:
+               return None # not an integer
+          return ParticularInteger(value)
+
+Now all URLs for numbers are available. What's more, other URLs like
+this are not::
+
+  http://localhost:8080/integers/foo
+
+The ``traverse`` method works by trying to convert the path element
+that comes in as ``name`` to an integer. If it fails, we return
+``None``, telling Grok that the ``traverse()`` method didn't find the
+object asked for. Grok then falls back on default behavior, which in
+this case would mean a ``404 Not Found`` error.
+
+Views
+-----
+
+Now that we have models and can build structures of them, we will need
+to look at ways to actually present them to the user: views. So what
+is a view? A view is a class that represents a model in some way. It
+creates a user interface of some sort (typically HTML) for a model. A
+single model can have more than one view. It looks like this::
+ 
+  class Index(grok.View):
+      grok.context(Application)
+     
+      def render(self):
+          return "This is the application"
+
+The ``grok.context`` bit in the class is an example of using a *Grok
+directive*. If you use ``grok.context`` on a view class, it connects
+the view to the class we give it. So in this case, ``Index`` is a view
+for ``Application``. Note that if there is only a single model in the
+module and you want your view to be associated with it, you can leave
+out ``grok.context`` and the view will be associated with that model
+by default. Many directives have such default behavior, allowing you
+to leave them out of your code if you organize your code in a certain
+way.
+
+The default view for a model is called ``index``. You can specify
+``index`` at the end of the URL, like this::
+
+  http://localhost:8080/app/index
+
+What happens when you go to this URL is that Grok instantiates the
+``Index`` class, creating a ``Index`` instance. View instances have
+a number of attributes by default: 
+
+  * ``context``, the model instance that the view is presenting.
+
+  * ``request``, the current web request.
+
+  * ``response``, an object representing the response sent to the
+                  user.  Used less often.
+
+``index`` views are special, as it's also fine not to add ``index`` at
+the end, because the name ``index`` is the default::
+
+  http://localhost:8080/app
+
+You can also create views with different names::
+
+  class Edit(grok.View):
+      grok.context(Application)
+    
+      def render(self):
+          return "This is the edit screen for the application"
+
+Now you can go to this URL::
+
+   http://localhost:8080/app/edit
+
+The name of the view is the name of the view class, lowercased. This
+is the default behavior: you can override this using the ``grok.name``
+directive::
+
+  class SomeImpossiblyLongClassName(grok.View):
+      grok.context(Application)
+      grok.name('edit')
+ 
+      def render(self):
+          return "This is the edit screen for the application"
+
+Templates
+~~~~~~~~~
+
+In the previous examples, we used the ``render`` method to determine
+what you actually see on a web page. For most views we don't want to
+do that: we want to use a template to prepare presentation. Using a
+template with a view is easy.  First create a directory
+``<name>_templates``, where ``<name>`` is the the module that contains
+the views. So, if you are developing in a module ``app.py``, you need
+to create a subdirectory ``app_templates`` for templates in the same
+directory as the ``app.py`` module.
+
+You can then add templates to that directory with the same name as the
+view class name (lowercase), with the ``.pt`` extension
+appended. These templates follow the Zope Page Template (ZPT) rules,
+though Grok can also be extended to support other template languages.
+
+You could for instance have this view::
+
+  class Index(grok.View):
+      grok.context(Application)
+
+and a file ``index.pt`` in the module's templates directory containing
+template code.
+
+These are the defaults. If for some reason you want the name of the
+template directory not to be based on the name of module, you can
+manually set the name of the template directory used by a module by
+using the ``grok.templatedir`` directive in the module. If you want
+the name of the template not to be based on the name of the class, you
+use the ``grok.template`` directive in the view class.
+
+The template can access attributes and methods on the view through the
+special ``view`` name available in the template. The template can
+access attributes and methods on the model through the special
+``context`` name available in the template. The template has the
+following special names available::
+
+* ``view`` - the view that this template is associated with
+
+* ``context`` - the model that is being viewed
+
+* ``request`` - the current request object
+
+* ``static`` - to make URLs to static content made available by this module
+
+and any names you also make available using the ``namespace`` method.
+
+static content
+~~~~~~~~~~~~~~
+
+A typical web page references one or more CSS files, javascript files
+and images: static content that is part of the layout.
+
+To make available static content to your template create a directory
+in your package called ``static``. Put ``.css`` files, ``.js`` files,
+image and whatever else is needed in there.
+
+You can now refer to these static files in your template using the
+special name ``static``, like this (ZPT example)::
+
+  <img tal:attributes="src static/my_image.png" />
+
+This will automatically create a URL to the place where Grok published
+that image.
+
+You can create subdirectories in ``static`` and refer to them as you'd
+expect::
+
+  <image tal:attributes="src static/images/some_image.gif" />
+
+``update``
+~~~~~~~~~~
+
+You can define an ``update`` method in a view to prepare a view just
+before it is accessed. You can use this to process information in the
+request (URL parameters or form variables) or in the context, and set
+attributes on the view that can be used in the template::
+
+  def update(self):
+      self.total = int(self.request.form['a']) + int(self.request.form['b'])
+
+The template now has access to ``view.total``.
+
+You can define parameters in the update view. These will be
+automatically bound to parameters (or form values) in the request::
+
+  def update(self, a, b):
+      self.total = int(a) + int(b)
+
+``namespace``
+~~~~~~~~~~~~~
+
+If you just want a variable to become available in the top-level of
+your template (much like ``view`` and ``model``), you can also define
+the ``namespace`` method on the view::
+
+  def namespace(self):
+      return {'foo': "Some value"}
+
+You can now refer to ``foo`` in your template and have available to
+this value.
+
+the ``url`` method
+~~~~~~~~~~~~~~~~~~
+
+Views have a special method called ``url()`` that can be used to
+create URLs to objects. The ``url`` method takes zero, one or two 
+arguments::
+
+* self.url() - URL to this view.
+
+* self.url(object) - URL to the provided object.
+
+* self.url(u"name") - URL to the context object, with ``/name`` appended,
+                   to point to a view or subobject of the context.
+
+* self.url(object, u"name") - URL to the provided object, with
+  		   ``/name`` appended, to point to a view or subobject
+  		   of the provided object.
+
+From the view, this is accessed through ``self.url()``. From the
+template, this method can be accessed using ``view.url()``.
+
+the ``application_url`` method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When using views it is sometimes desirable to be able to construct a
+URL to the application object. ``application_url`` is a quick way to
+do it.  It takes a single optional argument, name, which is the name
+of a view of the application.
+
+the ``redirect`` method
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``redirect`` method on views can be used to redirect the browser
+to another URL. Example::
+
+   def render(self):
+       self.redirect(self.url(self.context.__parent__))
+       # return empty body as we are going to redirect anyway
+       return ''
+
+``__parent__`` and ``__name__`` on views
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Like models, views also get supplied with a ``__parent__`` and
+``__name__`` object when they are instantiated for a particular model.
+
+``__parent__`` points to the model being viewed (and is the same as
+``context``, which should normally be used).
+
+``__name__`` is the name of the view in the URL.
+ 
+The ``@@`` thing
+~~~~~~~~~~~~~~~~
+
+Supposing you have a view called ``edit``, whenever you write this::
+
+  http://localhost:8080/app/edit
+
+you can also write this::
+
+  http://localhost:8080/app/@@edit
+
+Why the ugly ``@@`` syntax? Imagine that ``app`` is a container, and
+that your user interface lets the user add objects to it with a name
+of their own choosing. The user could decide to add an object called
+``index``. In that case Grok wouldn't know whether the
+``http://localhost:8080/app/index`` index is to get to a view or a
+subobject. ``@@`` tells the system to look up a view definitely. If
+``@@`` is not provided, subobjects take precedence over views in case
+of name collision.
+
+Request
+-------
+
+Some useful things to know about the request object (accessible as an
+attribute on the view):
+
+Information on the ``request`` object can be accessed using mapping
+access (``request[`foo`]``). You can access request form variables and
+cookies and headers (including `environment variables`_).
+
+.. _`environment variables`: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
+
+To access form variables in particular use: ``request.form['foo']``.
+
+To access cookies in particular use: ``request.cookies['foo']``.
+
+To access headers (and environment variables) in particular use:
+``request.headers['foo']``. You can also use ``request.getHeader()``,
+with the header name as the argument, and an optional second default
+argument.
+
+Instead of the mapping access, the ``get`` methods work as well, as on
+normal Python dictionaries.
+
+More can be found in the ``IHTTPRequest`` interface documentation
+in ``zope.publisher.interfaces.http``.
+
+Response
+--------
+
+Some useful things to know about the response object (accessible as
+an attribute on the view):
+
+``setStatus(name, reason)`` sets the HTTP status code. The argument
+may either be an integer representing the status code (such as ``200``
+or ``400``), or a string (``OK``, ``NotFound``). The optional second
+argument can be be used to pass the human-readable representation
+(``Not Found``).
+
+``setHeader(name, value)`` can be used to set HTTP response headers. The first
+argument is the header name, the second the value.
+
+``addHeader(name, value)`` can be used to add a HTTP header, while
+retaining any previously set headers with the same name.
+
+``setCookie(name, value, **kw)`` can be used to set a cookie. The first
+argument is the cookie name, the second the value. Optional keyword
+arguments can be used to set up further cookie properties (such as
+``max_age`` and ``expires``).
+
+``expireCookie(name, value)`` can be used to immediately expire a
+cookie.
+
+More can be found in the ``IHTTPResponse`` interface documentation
+in ``zope.publisher.interfaces.http``.
+
+Adapters
+--------
+
+An adapter is much like a view, but is aimed towards developers, not
+end users. It presents an interface to an object, but an interface for
+developers, not an user interface for end-users.
+
+The section on adapters will of necessity be rather abstract. Feel
+free to skip it until you want to know what is going on up with
+interfaces and adapters - it is an important foundation to Grok, but one
+you do not know much about when you get started.
+
+An adapter can be used to add new methods to an object without
+changing the object. To demonstrate the principle, we will construct
+adapters entirely by hand first. At the end we will show how Groks
+helps in constructing adapters and using them.
+
+Imagine we are developing a content management system and we want to
+get information about the size (in, say, bytes, approximately) of
+content objects stored in our CMS, for instance in order to display it
+in our UI or to calculate the total size of all objects in a
+container. The simplest approach would be to add a ``size()`` method
+to all our content objects::
+
+  class Document(grok.Model):
+       def __init__(self, text):
+           self.text = text
+
+       def size(self):
+           return len(self.text.encode('UTF-8'))
+
+  class Image(grok.Model):
+       def __init__(self, data):
+            self.data = data
+
+       def size(self):
+            return len(self.data)
+
+  class Container(grok.Container):
+        def size(self):
+            total = 0
+            for obj in self.values():
+                total += obj.size() 
+            return total
+
+For simple cases this is fine, but for larger applications this can
+become a problem. Our ``Document`` model needs a ``size`` method, and
+does our ``Image`` model, and our ``Container``, and our ``News Item``
+model, and so on. Given the requirements of a typical CMS, content
+objects would soon end up with a very large number of methods, for all
+sorts of functionality, from getting the size of objects to offering a
+commenting facility. It would be nicer to separate things out and keep
+the underlying models clean.
+
+To do this, we can use the adaptation pattern. As said, we will do it
+by hand at first. An adapter is an object that adds an API to another
+object (typically stored as the ``context`` attribute of the
+adapter)::
+
+  class DocumentSized(object):
+      def __init__(self, context):
+          self.context = context
+
+      def size(self):
+          return len(self.context.text.encode('UTF-8'))
+
+We would use it like this::
+
+   DocumentSized(document).size()
+
+We could extend this same adapter to work for different kinds of
+content objects, but that isn't very extensible when new adapters need
+to be made::
+
+  class Sized(object):
+      def __init__(self, context):
+          self.context = context
+
+      def size(self):
+          if isinstance(self.context, Document):
+               return len(self.context.text.encode('UTF-8'))
+          elif isinstance(self.context, Image):
+               return len(self.context.data)
+          elif isintance(self.context, Container):
+               total = 0
+               for obj in self.context.values():
+                   total += Sized(obj).size() 
+               return total
+               
+Instead, we can create a smart ``sized`` factory that does this
+switch-on-type behavior instead, keeping our adapters clean::
+
+  class DocumentSized(object):
+      def __init__(self, context):
+          self.context = context
+ 
+      def sized(self):
+          return len(self.context.text.encode('UTF-8'))
+
+  class ImageSized(object):
+      def __init__(self, context):
+          self.context = context
+ 
+      def sized(self):
+          return len(self.context.data)
+
+  class ContainerSized(object):
+      def __init__(self, context):
+          self.context = context
+ 
+      def sized(self):
+          total = 0
+          for obj in self.context.values():
+              total += sized(obj).size() 
+          return total
+   
+  def sized(context):
+      if isinstance(context, Document):
+          return DocumentedSized(context)
+      elif isinstance(context, Image):
+          return ImageSized(context)
+      elif isinstance(context, Container):
+          return ContainerSized(context)
+       
+We can now call ``sized`` for a content object and get an object back
+that implements the "sized API"::
+
+   s = sized(my_content_object)
+   print s.size()
+
+It's good to spell out the APIs of your application explicitly, as
+documentation so that other developers can work with them and also
+implement them for their own content objects. Grok lets you do this
+using an *interface* specification, using the ``zope.interface``
+package::
+
+  from zope.interface import Interface
+
+  class ISized(Interface):
+      def size():
+           "Return the size of the object"
+
+We can now make this ``ISized`` interface into the adapter factory
+(like ``sized`` above), without actually having to implement it
+directly. Let's do that now by subclassing from ``grok.Adapter`` and
+using a few grok directives::
+
+  class DocumentSized(grok.Adapter):
+      grok.context(Document)
+      grok.provides(ISized)
+ 
+      def sized(self):
+          return len(self.context.text.encode('UTF-8'))
+
+  class ImageSized(grok.Adapter):
+      grok.context(Image)
+      grok.provides(ISized)
+ 
+      def sized(self):
+          return len(self.context.data)
+
+  class ContainerSized(grok.Adapter):
+      grok.context(Container)
+      grok.provides(ISized)
+ 
+      def sized(self):
+          total = 0
+          for obj in self.context.values():
+              total += ISized(obj).size()
+          return total
+
+We can now use ``ISized`` like we used ``sized`` above::
+
+   s = ISized(my_content_object)
+   print s.size()
+
+When new content objects were to be created for this CMS, ``ISized``
+adapters can be registered for them anywhere. Using this pattern,
+existing objects implemented by someone else can be made to conform
+with the ``ISized`` API without having to modify them.
+
+``grok.context`` works as for views. It is useful to point it to any
+class however, not just that of models. ``grok.provides`` has to be
+pointed to an interface (the interface that the adapter *adapts to*).
+
+Interfaces
+~~~~~~~~~~
+
+Classes can also be made to *implement* an interface. This means that
+instances of that class *provide* that interface::
+
+  from zope.interface import Interface, Attribute
+
+  class IAnimal(Interface):
+      name = Attribute("The name of the animal")
+
+      def makeSound():
+          "The sound the animal makes."
+
+  class Cow(object):
+      grok.implements(IAnimal)
+ 
+      def __init__(self, name):
+          self.name = name
+
+      def makeSound(self):
+          return "Mooo"
+
+We can ask the interface machinery whether an object provides an interface::
+
+  >>> cow = Cow()
+  >>> IAnimal.providedBy(cow)
+  True
+
+If you use an interface to adapt an object, and this object already
+provides the interface, you get back the object itself::
+
+  >>> IAnimal(cow) is cow
+  True
+
+``grok.context`` can always point to an interface instead of a class
+directly. This indirection can be useful to make a view or adapter
+work for a whole set of classes that all implement the same interface.
+
+``ComponentLookupError``
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+What if an adapter cannot be found for a particular object? Perhaps no
+adapter has been registered for a particular object or a particular
+interface. The system will raise a ``ComponentLookupError``::
+
+  >>> ISized(cow)
+  Traceback (most recent call last):
+    ...
+  ComponentLookupError
+
+If you want to catch this exception, you can import it from
+``zope.component.interfaces``::
+
+  from zope.component.interfaces import ComponentLookupError
+
+Named adapters
+~~~~~~~~~~~~~~
+
+It is possible to give an adapter a name, making it a *named
+adapter*. This way it is possible to have more than one adapter
+registered for a single object that all provide the same interface,
+each with a different name. This feature is rarely used directly,
+but internally it is used for views, as we will see later. The
+``grok.name()`` directive can be used to give an adapter a name::
+
+  class Adapter(object):
+      grok.name('somename')
+      grok.context(SomeClass)
+      grok.provides(ISomeInterface)
+ 
+Actually all adapters are named: by default the name of an adapter is
+the empty string.
+
+You cannot call the interface directly to get a named adapter for an
+object.  Instead, you need to use the APIs provided by the
+``zope.component`` package, in particular ``getAdapter``::
+
+  from zope import component
+  
+  my_adapter = component.getAdapter(some_object, ISomeInterface, 
+                                   name='somename')
+
+``getAdapter`` can also be used to look up unnamed adapters, as an
+alternative to using the interface directly::
+
+  myadapter = component.getAdapter(some_object, ISomeInterface)
+
+Multi adapters
+~~~~~~~~~~~~~~
+
+Another feature of adapters is that you can adapt multiple objects at
+once using a *multi adapter*. Again this feature is rarely used in
+practice, except internally to implement views and events.
+
+You can construct a multi adapter by subclassing from
+``grok.MultiAdapter``::
+
+  class MyMultiAdapter(grok.MultiAdapter):
+      grok.adapts(SomeClass, AnotherClass)
+      grok.provides(ISomeInterface)
+   
+      def __init__(some_instance, another_instance):
+          self.some_interface = some_instance
+          self.another_instance = another_instance
+
+The multi-adapter receives as many arguments as what it was registered
+for using ``grok.adapts``.
+
+A multi adapter also cannot be looked up directly by calling the
+interface. Instead, we need to use the ``zope.component`` package
+again::
+
+  from zope import component
+
+  my_multi_adapter = component.getMultiAdapter((some_object, another_object),
+                                               ISomeInterface)
+
+``getMultiAdapter`` receives as the first argument a tuple with the
+combination of objects to adapt.
+
+It can also optionally be named using ``grok.name`` and then looked up
+using a name argument::
+
+  my_named_multi_adapter = component.getMultiAdapter(
+      (some_object, another_object), ISomeInterface, name="foo")
+
+Views as adapters
+~~~~~~~~~~~~~~~~~
+
+A view in Grok is in fact a named multi adapter, providing the base
+interface (``Interface``). This means that a view in Grok can be
+looked up in code by the following call::
+ 
+  from zope.interface import Interface
+
+  view = component.getMultiAdapter((object, request), Interface, name="index")
+
+Since the default for the second argument is in fact ``Interface``, this
+call can be shorted to this::
+
+  view = component.getMultiAdapter((object, request), name="index")
+
+Being able to do this in code is sometimes useful. It is also what
+Grok does internally when it looks up a view.
+
+Events
+------
+
+Grok lets you write handlers for *events*. Using event handlers you
+can hook into code that you do not control. Events allow decoupling: a
+framework can send events without worrying who is interested in it,
+and similarly you can send events to work with existing bits of
+framework that expects them. You can also define new types of events
+if you are designing a framework yourself.
+
+You write an event handler by writing a function that *subscribes* to
+the event, and marking it with a python decorator::
+
+  @grok.subscribe(Document, grok.IObjectAddedEvent)
+  def handle(obj, event):
+      print "Object %s was added." % obj
+
+Whenever an instance of a model of class ``Document`` (or subclasses)
+is added to a container, this code will be run. You can then take some
+action. Any ``grok.Container`` subclass will take care of sending
+these events automatically.  You can have as many subscribers for a
+particular event as you like.  The order in which they are run is not
+guaranteed by the system, so cannot be relied on.
+
+The event handler takes two arguments: the object for which the event
+was fired, and the event instance. The event instance has attributes,
+depending on the type of event.
+
+Events defined by Grok
+~~~~~~~~~~~~~~~~~~~~~~
+
+Here we describe the standard events defined by Grok. Described are
+the interfaces which you would use in a subscriber, and how you can
+send this event yourself. Other events may be defined by libraries or
+by you.
+
+``IObjectMovedEvent``
++++++++++++++++++++++
+
+Will be fired whenever an object is moved from container to container,
+renamed, added or removed.
+
+The event object has these attributes:
+
+* ``object`` - the object being moved
+
+* ``oldParent`` - the parent (container) from which the object was moved
+                  or removed, or ``None`` if this object is newly added.
+
+* ``oldName`` - the previous name of the object in its container,
+                before renaming if renaming took place, or ``None`` if
+                this object is newly added.
+
+* ``newParent`` - the parent (container) to this object was moved or
+                added. ``None`` if this object was removed.
+
+* ``newName`` - the name the object has in the new container, or ``None``
+                if this object was removed.
+
+Containers take care of sending this event, but should you want to
+send it yourself, use::
+
+  grok.notify(grok.ObjectMovedEvent(obj, oldParent, oldName, newParent, newName))
+
+``IObjectAddedEvent``
++++++++++++++++++++++
+
+Fired when an object is added to a container. Specialization of
+``IObjectMovedEvent``, and shares the attributes as described.
+
+Containers take care of sending this event, but should you want to send it
+yourself, use::
+
+  grok.notify(grok.ObjectAddedEvent(obj))
+
+or::
+
+  grok.notify(grok.ObjectAddedEvent(obj, newParent, newName))
+
+``IObjectRemovedEvent``
++++++++++++++++++++++++
+
+Fired when an object is removed from a container (and not re-added
+elsewhere). Specialization of ``IObjectMovedEvent``, and shares the
+attributes as described.
+
+Containers take care of sending this event, but should you want to send it
+yourself, use::
+
+  grok.notify(grok.ObjectRemovedEvent(obj)
+
+or::
+
+  grok.notify(grok.ObjectRemovedEvent(obj, oldparent, oldName))
+
+``IObjectModifiedEvent``
+++++++++++++++++++++++++
+
+Fired when an object is modified by the system, such as when a form is
+saved. If you modify the object in code, the system won't know about
+this, and you will have to remember to send it yourself.
+
+This event has a single attribute, ``object``, which is the object
+that was modified.
+
+To send this event yourself, use::
+
+  grok.notify(grok.ObjectModifiedEvent(obj))
+
+``IContainerModifiedEvent``
++++++++++++++++++++++++++++
+
+A specialization of ``IObjectModifiedEvent`` that fires when the
+container was modified by adding something to it or removing from it.
+
+Containers take care of sending this event, but if you want to send it
+yourself, use::
+
+  grok.notify(grok.ContainerModifiedEvent(obj))
+
+``IObjectCreatedEvent``
++++++++++++++++++++++++
+
+Fired when an object is created. When you create your own objects the
+system won't know about this, and you will have to remember to send it
+yourself if you care about listing to ``IObjectCreatedEvent``. This is
+fairly rare - usually you're better of looking at
+``IObjectAddedEvent`` if you can.
+
+This event has a single attribute, ``object``, which is the object
+that was created.
+
+To send this event yourself::
+
+  grok.notify(grok.ObjectCreatedEvent(obj))
+
+``IObjectCopiedEvent``
+++++++++++++++++++++++
+
+Fired when an object was copied. It is a specialization of
+``IObjectCreatedEvent`` that is fired by the system if you use the
+``zope.copypastemove`` functionality. 
+
+Besides the ``object`` attribute it shares with
+``IObjectCreattedEvent``, it has also has the ``original`` attribute,
+which was the object that iwas copied from.
+
+To send this event yourself::
+
+  grok.notify(grok.ObjectCopiedEvent(copy, original))
+
+Creating and sending your own events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you are going to send an object that pertains to a particular object,
+subclass ``zope.component.interfaces.ObjectEvent``::
+
+  from zope.component.interfaces import ObjectEvent
+
+  class MyEvent(ObjectEvent):
+      pass
+
+You can then send it like this::
+
+  grok.notify(MyEvent(some_obj))
+
+And listen for it like this::
+
+  grok.subscribe(SomeClass, MyEvent)
+  def handle_my_event(obj, event):
+      pass
+
+This subclassing from ``ObjectEvent`` is not required; if your event
+isn't about an object, you can choose to design your event class
+entirely yourself. See ``zope.sendmail`` for the construction of mail sending
+events for an example. 
+
+Interfaces for events
+~~~~~~~~~~~~~~~~~~~~~
+
+For documentation purposes it can be a good idea to to define an
+interface for your event. You can then also allow for multiple
+implementations of the same event interface. When you have an
+interface for your event, you can then listen for the interface in the
+subscribers as well::
+
+  from zope.interface import Interface
+
+  class IMyEvent(zope.component.interfaces.IObjectEvent):
+      "My special event"
+
+  class MyEvent(zope.component.interfaces.ObjectEvent):
+      grok.implements(IMyEvent)
+
+  grok.subscribe(SomeClass, IMyEvent):
+  def handle_my_event(obj, event):
+      pass
+
+More about interfaces
+---------------------
+
+We have seen small examples of interfaces before, but here we will go
+a bit more into them, and why they are useful.
+
+An *interface* is a description of the API of a class (or more rarely,
+module or object). Interfaces are useful because:
+
+* They are API documentation.
+
+* They can describe how a framework expects you to implement classes
+  that fit into it.
+
+* The system can inspect the interfaces a particular object provides,
+  and treat them as an abstract form of classes for registration
+  purposes. 
+
+Interfaces make it possible to use a generic framework's pluggability
+points with confidence: you can clearly see what you are supposed to
+implement to plug into it. You can define very generic frameworks
+yourself by defining them in terms of interfaces.
+
+Some interface features
+~~~~~~~~~~~~~~~~~~~~~~~
+
+A summary of interface features we've seen:
+
+* To create an interface, subclass from ``zope.interface.Interface``.
+
+* To state that implementors of the interface must have a method, supply
+  the method with arguments. Don't use ``self`` as the first
+  arguments, as this is an implementation detail not important to the
+  interface. Instead, describe the methods as they look to the caller.
+
+* To state that implementors of the interface must have an attribute, use::
+
+    some_attribute = zope.interface.Attribute("Description of attribute")
+
+* To state a class *implements* an interface, use ``grok.implements``.
+
+* Instances of a class are said to *provide* the interface that the
+  class *implements*.
+
+* You can check whether an instance provides a certain interface by using
+  ``some_interface.providedBy``::
+
+     IObjectEvent.providedBy(NonSubclassEvent(some_obj))
+
+Interfaces and events
+~~~~~~~~~~~~~~~~~~~~~
+
+Let's study interfaces some more in connection with
+``IObjectModifiedEvent``. The ``IObjectModifiedEvent`` interface looks
+like this::
+
+  class IObjectModifiedEvent(zope.component.interfaces.IObjectEvent):
+      """An object has been modified"""
+
+This refers us to the ``IObjectEvent`` interface, which looks like
+this::
+
+  from zope import interface
+
+  class IObjectEvent(interface.Interface):
+      """An event related to an object.
+      """
+
+      object = interface.Attribute("The subject of the event.")
+
+We therefore know that if we implement ``IObjectModifiedEvent``, we
+must supply a single attribute, ``object``.
+
+The following event handler for instances of ``SomeClass`` subscribes
+to *any* event that provides ``IModifiedObjectEvent``::
+
+   @grok.subscribe(SomeClass, IObjectModifiedEvent):
+   def handle_event(obj, event):
+       "Called when there is an IObjectModifiedEvent for SomeClass instances."
+
+This handler will be called not only for subclasses of the
+``grok.ObjectModifiedEvent`` class, but also for other, otherwise
+unrelated classes that implement ``IObjectEvent``, such as this one::
+
+  class NonSubclassObjectEvent(object):
+      grok.implements(IObjectEvent)
+ 
+      def __init__(self, object):
+           self.object = object
+
+So far we have only used interfaces for the second argument of the
+event handler registration, but the principle also works for the first
+argument. For example, to handle ``IObjectModifiedEvent`` events for
+all kinds of containers, you can subscribe to
+``zope.app.container.interfaces.IContainer`` objects::
+
+  @grok.subscribe(IContainer, IObjectModifiedEvent):
+  def handle_event(obj, event):
+      "Called whenever any container is modified"
+
+``zope.app.container.interfaces.IContainer`` defines the abstract
+container API that all containers must provide, no matter how they are
+implemented internally.
+
+Interfaces and adapters
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The same principle also works for adapters and ``grok.context``. You
+can use ``grok.context`` with interfaces as well as with concrete
+classes. To write an adapter that works for any kind of container, you
+can write::
+
+  from zope.app.container.interfaces import IContainer
+
+  class SortedKeysAdapter(grok.Adapter):
+      grok.context(IContainer)
+      grok.provides(ISortedKeys)
+
+      def sortedKeys(self):
+          return sorted(self.context.keys())     
+
+Interfaces and views
+~~~~~~~~~~~~~~~~~~~~
+
+The same principle can also be used with ``grok.context`` in other
+places, such as in views. This view is registered for all containers::
+
+  from zope.app.container.interfaces import IContainer
+
+  class Keys(grok.View):
+     grok.context(IContainer)
+     
+     def render(self):
+         return ', '.join(ISortedKeysAdapter(self.context).sortedKeys())
+
+The view ``keys`` exists for all containers, no matter how they are
+implement, where they are implemented or who implemented them, as long
+as they provide ``IContainer``.
+
+Using the fact ``Interface`` is the base of all interfaces, you can
+even register a view for *all* objects. This can be useful to register
+ZPT macros, which will then be available on all contexts::
+
+  class Layout(grok.View):
+      grok.context(Interface)
+ 
+with a template ``layout.pt`` associated to it.
+
+You can then use these macros in any page template anywhere by
+referring to them like this::
+
+  <html metal:use-macro="context/@@layout/macros/page">
+
+Forms
+-----
+
+Grok can autogenerate web forms from descriptions called *schema*. A
+schema is a special kind of interface. We already saw ``Attribute``,
+which can be used to specify that something that provides that
+interface should have that attribute. The ``zope.schema`` package adds
+a lot more specific field descriptions. Here is an example of a
+schema::
+
+  from zope.interface import Interface
+  from zope import schema
+  
+  class ISpecies(Interface):
+      name = schema.TextLine(u"Animal species name")
+      scientific_name = schema.TextLine(u"Scientific name")
+      legs = schema.Int(u"Number of legs")
+
+Let's also look at a simple implementation of this interface::
+
+  class Species(grok.Model):
+      grok.implements(ISpecies)
+
+Note how we aren't even creating an ``__init__`` to set the
+attributes; we could, but we'll see below that Grok's ``applyData``
+can take care of this automatically.
+
+The ``ISpecies`` schema can be turned into a form. Grok does this by
+looking up a *widget* for each schema field to display it. A widget is
+very much like a view. Let's look at a form for this schema::
+
+  class Species(grok.Form):
+      form_fields = grok.Fields(ISpecies)
+  
+      @grok.action(u"Save form")
+      def handle_save(self, **data):
+          print data['name']
+          print data['scientific_name']
+          print data['legs']
+
+What is going on here? Firstly we use a special base class called
+``grok.Form``. A form is a special kind of ``grok.View``, and
+associates the same way (using ``grok.context``). A form expects two
+things::
+
+* a ``form_fields`` attribute. Above we see the most common way to construct
+  this attribute, using ``grok.Fields`` on the interface.
+
+* one or more actions. Actions are specified by using the
+  ``@grok.action`` decorator. An action gets the fields filled in the
+  form as keyword parameters, so ``**data`` in this case. We could
+  also have specified the arguments we expected specifically.
+
+Form widgets translate the raw HTML form input to Python objects, such
+as (unicode) strings, integers and datetime objects, as specified by
+schema fields. The schema fields can then be used to validate this
+input further. Forms are self-submitting, and in case of a validation
+error the form can render them in-line next to the fields.
+
+We'll look at a lot of form features next.
+
+``grok.AddForm``
+~~~~~~~~~~~~~~~~
+
+An add form is used to create a new object. Most forms are views of
+the object that they are representing, but an add form is typically
+associated a view of the container in which new objects are to be
+added. Let's look at an example::
+
+  class SpeciesContainer(grok.Container):
+      pass
+
+  class Add(grok.AddForm):
+      grok.context(SpeciesContainer)
+
+      form_fields = grok.Fields(ISpecies)
+      
+      @grok.action(u"Add species")
+      def add_species(self, **data):
+          # create a species instance
+          species = Species()
+          # assign the right attributes to fulfill ISpecies schema with
+          # the form data
+          self.applyData(species, **data)
+          # stores the instance into the SpeciesContainer
+          name = data['name']
+          self.context[name] = species
+          # redirect to the newly created object
+          self.redirect(self.url(species))
+          # we don't want to display anything, as we redirect
+          return ''
+
+The user can now go to ``myspeciescontainer/add`` to add a species,
+where ``myspeciescontainer`` is any instance of ``SpeciesContainer``.
+
+``grok.EditForm``
+~~~~~~~~~~~~~~~~~
+
+Now that we can create species objects, let's create a form so you can
+easily edit them. This *is* a view of the ``Species`` model::
+
+  class Edit(grok.EditForm):
+     grok.context(Species)
+    
+     form_fields = grok.Fields(ISpecies)
+
+     @grok.action(u"Edit species")
+     def edit_species(self, **data):
+          self.applyData(species, **data)
+     
+Forms are self-submitting, so this will show the edit form again. If
+you want to display another page, you can redirect the browser as we
+showed for the add form previously.
+
+The user can now go to ``myspecies/edit`` to edit the species.
+
+``grok.DisplayForm``
+~~~~~~~~~~~~~~~~~~~~
+
+Sometimes you just want to display an object, and not actually edit
+it. If the object is schema-based, an easy way to do this is to use
+display forms. Let's look at an example::
+
+  class Display(grok.DisplayForm):
+     grok.context(Species)
+    
+     form_fields = grok.Fields(ISpecies)
+
+The user can now go to ``myspecies/display`` to look at the species.
+
+Associating a template for a form
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By default, Grok supplies some templates for forms. They work, but
+they are not very pretty and don't fit into your application's
+layout. You can instead use your own form rendering logic in a
+template you associate with the form just like you associate templates
+with views. You can also abstract form rendering logic you keep
+reusing into a ZPT macro. Below is an example of form rendering logic
+to help you get started. The example doesn't have any consideration
+for layouting to make the logic clear. As a result, the form will be
+very ugly if you use this - you will want to use CSS or table HTML to
+layout things::
+
+  <!-- render the form tag -->
+  <form action="." tal:attributes="action request/URL" method="post"
+        class="edit-form" enctype="multipart/form-data">
+    <!-- render any validation errors on top -->
+    <ul class="errors" tal:condition="view/errors">
+      <li tal:repeat="error view/error_views">
+         <span tal:replace="structure error">Error Type</span>
+      </li>
+    </ul>
+
+    <!-- render the widgets -->
+    <tal:block repeat="widget view/widgets">
+      <label tal:attributes="for widget/name">
+        <!-- a * when the widget is required -->
+        <span class="required" tal:condition="widget/required">*</span>
+        <!-- the title of the field -->
+        <span i18n:translate="" tal:content="widget/label">label</span>
+      </label>
+     
+      <!-- render the HTML widget -->
+      <div class="widget" tal:content="structure widget">
+        <input type="text" />
+      </div>
+      
+      <!-- render any field specific validation error from a previous
+           form submit next to the field -->
+      <div class="error" tal:condition="widget/error">
+        <span tal:replace="structure widget/error">error</span>
+      </div>
+    </tal:block>
+
+    <!-- render all the action submit buttons -->
+    <span class="actionButtons" tal:condition="view/availableActions">
+      <input tal:repeat="action view/actions"
+             tal:replace="structure action/render" />
+    </span>
+  </form>
+
+The template for a display form a lot simpler::
+
+  <tal:block repeat="widget view/widgets">
+    <tal:block content="widget/label" />
+    <input tal:replace="structure widget" />
+  </tal:block>
+   
+  <!-- render all the action submit buttons -->
+  <span class="actionButtons" tal:condition="view/availableActions">
+    <input tal:repeat="action view/actions"
+           tal:replace="structure action/render" />
+  </span>
+

Deleted: grok/trunk/doc/grok_overview.txt
===================================================================
--- grok/trunk/doc/grok_overview.txt	2008-05-01 11:29:50 UTC (rev 85989)
+++ grok/trunk/doc/grok_overview.txt	2008-05-01 11:32:17 UTC (rev 85990)
@@ -1,1316 +0,0 @@
-Grok Developer's Notes 
-======================
-
-This document is a developer's overview of Grok. It is not intended to
-be a beginner's tutorial. It's also not a reference. It gives a
-succinct an overview of *what's there* in Grok, with a brief idea on
-how to use it, so you can try it out and learn more about it. This
-includes rules, common APIs and patterns.
-
-Models
-------
-
-A Grok-based application is composed out of one or more *models*. We
-also call these *content objects*, or just *objects*. The objects are
-just Python objects, instantiated from a class. Models can be stored
-in the object database (ZODB), created by an object relational mapper,
-or created on the fly by your code.
-
-Grok comes with two kinds of models: ``grok.Model`` and
-``grok.Container``. ``grok.Model`` is the most basic one and doesn't
-really do much for you. You can subclass from ``grok.Model``, like
-this::
-
-  class Document(grok.Model):
-      pass
-
-The main thing subclassing from ``grok.Model`` does is make it
-possible (but not required) to store instances in the ZODB.
-
-You can also subclass from ``grok.Container``, like this::
-
-  class Folder(grok.Container):
-      pass
-
-A container is like a model, but also acts much like a Python
-dictionary. The main difference with Python dictionaries is that its
-methods, like ``keys`` and ``items``, are iterator-like. They also do
-more, like send events, but we can forget about that for now.
-
-In order to be able to install an application, you need to mix in
-``grok.Application`` into a class::
-
-  class Application(grok.Application, grok.Container):
-      pass
-
-Instances of this class can now be installable in the Grok web UI.
-
-Let's make a structure with some folders and documents::
-
-  app = Application()
-  app['a'] = a = Container()
-  a['b'] = Document()
-  a['c'] = Container()
-  a['c']['d'] = Document()
-
-Grok publishes these objects to the web: this is called object
-publishing. What this means in essence is that objects can be
-addressed with URLs. When you access a URL of a Grok application with
-your web browser, Grok uses this URL to find an object.
-
-An example: if ``app`` were installed under
-``http://localhost:8080/app``, the following URLs will exist in your
-application::
-
-  http://localhost:8080/app
-  http://localhost:8080/app/a
-  http://localhost:8080/app/a/b
-  http://localhost:8080/app/a/c
-  http://localhost:8080/app/a/c/d
-
-``__parent__`` and ``__name__``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Models in Grok are automatically supplied with a ``__parent__`` and a
-``__name__`` attribute. 
-
-* ``__parent__`` points to object this object is in. If the object is in 
-  a container, this is the container.
-
-* ``__name__`` is the name this object has in a URL (and in its
-  container, if it is in a container).
-
-These attributes are used for navigation through content space, and
-Grok also uses them to construct URLs automatically (see below). The
-``__parent__`` and ``__name__`` attributes are automatically added to
-an object when it is placed in a container, or when it is being
-traversed through using ``traverse``.
-
-Custom traversing
-~~~~~~~~~~~~~~~~~
-
-Grok resolves URLs to objects by *traversing* through the containers
-and models in question. What if you want to customize the way this
-traversal works? Perhaps you want to traverse through objects you
-create yourself, or objects created by an object relational
-mapper. Grok offers a handy way to do so: the ``traverse`` method on
-models.
-
-A math example: imagine you want to create an application that
-represents integer numbers, and you want to traverse to each
-individual number, like this::
-
-  http://localhost:8080/integers/0
-  http://localhost:8080/integers/1
-  http://localhost:8080/integers/2
-  http://localhost:8080/integers/3
-  ...
-
-and so on. How would we implement this? We cannot create a container
-and fill it with all integers possible, as there are an infinite
-number of them. Okay, so we are in a math example, so let's be exact:
-this is true if we ignore memory limitations and URL length
-limitations. Storing all possible integers in a container is just not
-*practical*.
-
-We use the ``traverse`` method::
-
-  class ParticularInteger(grok.Model):
-      def __init__(self, number):
-          self.number = number
-
-  class Integers(grok.Application, grok.Model):
-      def traverse(self, name):
-          try:
-               value = int(name)
-          except ValueError:
-               return None # not an integer
-          return ParticularInteger(value)
-
-Now all URLs for numbers are available. What's more, other URLs like
-this are not::
-
-  http://localhost:8080/integers/foo
-
-The ``traverse`` method works by trying to convert the path element
-that comes in as ``name`` to an integer. If it fails, we return
-``None``, telling Grok that the ``traverse()`` method didn't find the
-object asked for. Grok then falls back on default behavior, which in
-this case would mean a ``404 Not Found`` error.
-
-Views
------
-
-Now that we have models and can build structures of them, we will need
-to look at ways to actually present them to the user: views. So what
-is a view? A view is a class that represents a model in some way. It
-creates a user interface of some sort (typically HTML) for a model. A
-single model can have more than one view. It looks like this::
- 
-  class Index(grok.View):
-      grok.context(Application)
-     
-      def render(self):
-          return "This is the application"
-
-The ``grok.context`` bit in the class is an example of using a *Grok
-directive*. If you use ``grok.context`` on a view class, it connects
-the view to the class we give it. So in this case, ``Index`` is a view
-for ``Application``. Note that if there is only a single model in the
-module and you want your view to be associated with it, you can leave
-out ``grok.context`` and the view will be associated with that model
-by default. Many directives have such default behavior, allowing you
-to leave them out of your code if you organize your code in a certain
-way.
-
-The default view for a model is called ``index``. You can specify
-``index`` at the end of the URL, like this::
-
-  http://localhost:8080/app/index
-
-What happens when you go to this URL is that Grok instantiates the
-``Index`` class, creating a ``Index`` instance. View instances have
-a number of attributes by default: 
-
-  * ``context``, the model instance that the view is presenting.
-
-  * ``request``, the current web request.
-
-  * ``response``, an object representing the response sent to the
-                  user.  Used less often.
-
-``index`` views are special, as it's also fine not to add ``index`` at
-the end, because the name ``index`` is the default::
-
-  http://localhost:8080/app
-
-You can also create views with different names::
-
-  class Edit(grok.View):
-      grok.context(Application)
-    
-      def render(self):
-          return "This is the edit screen for the application"
-
-Now you can go to this URL::
-
-   http://localhost:8080/app/edit
-
-The name of the view is the name of the view class, lowercased. This
-is the default behavior: you can override this using the ``grok.name``
-directive::
-
-  class SomeImpossiblyLongClassName(grok.View):
-      grok.context(Application)
-      grok.name('edit')
- 
-      def render(self):
-          return "This is the edit screen for the application"
-
-Templates
-~~~~~~~~~
-
-In the previous examples, we used the ``render`` method to determine
-what you actually see on a web page. For most views we don't want to
-do that: we want to use a template to prepare presentation. Using a
-template with a view is easy.  First create a directory
-``<name>_templates``, where ``<name>`` is the the module that contains
-the views. So, if you are developing in a module ``app.py``, you need
-to create a subdirectory ``app_templates`` for templates in the same
-directory as the ``app.py`` module.
-
-You can then add templates to that directory with the same name as the
-view class name (lowercase), with the ``.pt`` extension
-appended. These templates follow the Zope Page Template (ZPT) rules,
-though Grok can also be extended to support other template languages.
-
-You could for instance have this view::
-
-  class Index(grok.View):
-      grok.context(Application)
-
-and a file ``index.pt`` in the module's templates directory containing
-template code.
-
-These are the defaults. If for some reason you want the name of the
-template directory not to be based on the name of module, you can
-manually set the name of the template directory used by a module by
-using the ``grok.templatedir`` directive in the module. If you want
-the name of the template not to be based on the name of the class, you
-use the ``grok.template`` directive in the view class.
-
-The template can access attributes and methods on the view through the
-special ``view`` name available in the template. The template can
-access attributes and methods on the model through the special
-``context`` name available in the template. The template has the
-following special names available::
-
-* ``view`` - the view that this template is associated with
-
-* ``context`` - the model that is being viewed
-
-* ``request`` - the current request object
-
-* ``static`` - to make URLs to static content made available by this module
-
-and any names you also make available using the ``namespace`` method.
-
-static content
-~~~~~~~~~~~~~~
-
-A typical web page references one or more CSS files, javascript files
-and images: static content that is part of the layout.
-
-To make available static content to your template create a directory
-in your package called ``static``. Put ``.css`` files, ``.js`` files,
-image and whatever else is needed in there.
-
-You can now refer to these static files in your template using the
-special name ``static``, like this (ZPT example)::
-
-  <img tal:attributes="src static/my_image.png" />
-
-This will automatically create a URL to the place where Grok published
-that image.
-
-You can create subdirectories in ``static`` and refer to them as you'd
-expect::
-
-  <image tal:attributes="src static/images/some_image.gif" />
-
-``update``
-~~~~~~~~~~
-
-You can define an ``update`` method in a view to prepare a view just
-before it is accessed. You can use this to process information in the
-request (URL parameters or form variables) or in the context, and set
-attributes on the view that can be used in the template::
-
-  def update(self):
-      self.total = int(self.request.form['a']) + int(self.request.form['b'])
-
-The template now has access to ``view.total``.
-
-You can define parameters in the update view. These will be
-automatically bound to parameters (or form values) in the request::
-
-  def update(self, a, b):
-      self.total = int(a) + int(b)
-
-``namespace``
-~~~~~~~~~~~~~
-
-If you just want a variable to become available in the top-level of
-your template (much like ``view`` and ``model``), you can also define
-the ``namespace`` method on the view::
-
-  def namespace(self):
-      return {'foo': "Some value"}
-
-You can now refer to ``foo`` in your template and have available to
-this value.
-
-the ``url`` method
-~~~~~~~~~~~~~~~~~~
-
-Views have a special method called ``url()`` that can be used to
-create URLs to objects. The ``url`` method takes zero, one or two 
-arguments::
-
-* self.url() - URL to this view.
-
-* self.url(object) - URL to the provided object.
-
-* self.url(u"name") - URL to the context object, with ``/name`` appended,
-                   to point to a view or subobject of the context.
-
-* self.url(object, u"name") - URL to the provided object, with
-  		   ``/name`` appended, to point to a view or subobject
-  		   of the provided object.
-
-From the view, this is accessed through ``self.url()``. From the
-template, this method can be accessed using ``view.url()``.
-
-the ``application_url`` method
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When using views it is sometimes desirable to be able to construct a
-URL to the application object. ``application_url`` is a quick way to
-do it.  It takes a single optional argument, name, which is the name
-of a view of the application.
-
-the ``redirect`` method
-~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``redirect`` method on views can be used to redirect the browser
-to another URL. Example::
-
-   def render(self):
-       self.redirect(self.url(self.context.__parent__))
-       # return empty body as we are going to redirect anyway
-       return ''
-
-``__parent__`` and ``__name__`` on views
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Like models, views also get supplied with a ``__parent__`` and
-``__name__`` object when they are instantiated for a particular model.
-
-``__parent__`` points to the model being viewed (and is the same as
-``context``, which should normally be used).
-
-``__name__`` is the name of the view in the URL.
- 
-The ``@@`` thing
-~~~~~~~~~~~~~~~~
-
-Supposing you have a view called ``edit``, whenever you write this::
-
-  http://localhost:8080/app/edit
-
-you can also write this::
-
-  http://localhost:8080/app/@@edit
-
-Why the ugly ``@@`` syntax? Imagine that ``app`` is a container, and
-that your user interface lets the user add objects to it with a name
-of their own choosing. The user could decide to add an object called
-``index``. In that case Grok wouldn't know whether the
-``http://localhost:8080/app/index`` index is to get to a view or a
-subobject. ``@@`` tells the system to look up a view definitely. If
-``@@`` is not provided, subobjects take precedence over views in case
-of name collision.
-
-Request
--------
-
-Some useful things to know about the request object (accessible as an
-attribute on the view):
-
-Information on the ``request`` object can be accessed using mapping
-access (``request[`foo`]``). You can access request form variables and
-cookies and headers (including `environment variables`_).
-
-.. _`environment variables`: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
-
-To access form variables in particular use: ``request.form['foo']``.
-
-To access cookies in particular use: ``request.cookies['foo']``.
-
-To access headers (and environment variables) in particular use:
-``request.headers['foo']``. You can also use ``request.getHeader()``,
-with the header name as the argument, and an optional second default
-argument.
-
-Instead of the mapping access, the ``get`` methods work as well, as on
-normal Python dictionaries.
-
-More can be found in the ``IHTTPRequest`` interface documentation
-in ``zope.publisher.interfaces.http``.
-
-Response
---------
-
-Some useful things to know about the response object (accessible as
-an attribute on the view):
-
-``setStatus(name, reason)`` sets the HTTP status code. The argument
-may either be an integer representing the status code (such as ``200``
-or ``400``), or a string (``OK``, ``NotFound``). The optional second
-argument can be be used to pass the human-readable representation
-(``Not Found``).
-
-``setHeader(name, value)`` can be used to set HTTP response headers. The first
-argument is the header name, the second the value.
-
-``addHeader(name, value)`` can be used to add a HTTP header, while
-retaining any previously set headers with the same name.
-
-``setCookie(name, value, **kw)`` can be used to set a cookie. The first
-argument is the cookie name, the second the value. Optional keyword
-arguments can be used to set up further cookie properties (such as
-``max_age`` and ``expires``).
-
-``expireCookie(name, value)`` can be used to immediately expire a
-cookie.
-
-More can be found in the ``IHTTPResponse`` interface documentation
-in ``zope.publisher.interfaces.http``.
-
-Adapters
---------
-
-An adapter is much like a view, but is aimed towards developers, not
-end users. It presents an interface to an object, but an interface for
-developers, not an user interface for end-users.
-
-The section on adapters will of necessity be rather abstract. Feel
-free to skip it until you want to know what is going on up with
-interfaces and adapters - it is an important foundation to Grok, but one
-you do not know much about when you get started.
-
-An adapter can be used to add new methods to an object without
-changing the object. To demonstrate the principle, we will construct
-adapters entirely by hand first. At the end we will show how Groks
-helps in constructing adapters and using them.
-
-Imagine we are developing a content management system and we want to
-get information about the size (in, say, bytes, approximately) of
-content objects stored in our CMS, for instance in order to display it
-in our UI or to calculate the total size of all objects in a
-container. The simplest approach would be to add a ``size()`` method
-to all our content objects::
-
-  class Document(grok.Model):
-       def __init__(self, text):
-           self.text = text
-
-       def size(self):
-           return len(self.text.encode('UTF-8'))
-
-  class Image(grok.Model):
-       def __init__(self, data):
-            self.data = data
-
-       def size(self):
-            return len(self.data)
-
-  class Container(grok.Container):
-        def size(self):
-            total = 0
-            for obj in self.values():
-                total += obj.size() 
-            return total
-
-For simple cases this is fine, but for larger applications this can
-become a problem. Our ``Document`` model needs a ``size`` method, and
-does our ``Image`` model, and our ``Container``, and our ``News Item``
-model, and so on. Given the requirements of a typical CMS, content
-objects would soon end up with a very large number of methods, for all
-sorts of functionality, from getting the size of objects to offering a
-commenting facility. It would be nicer to separate things out and keep
-the underlying models clean.
-
-To do this, we can use the adaptation pattern. As said, we will do it
-by hand at first. An adapter is an object that adds an API to another
-object (typically stored as the ``context`` attribute of the
-adapter)::
-
-  class DocumentSized(object):
-      def __init__(self, context):
-          self.context = context
-
-      def size(self):
-          return len(self.context.text.encode('UTF-8'))
-
-We would use it like this::
-
-   DocumentSized(document).size()
-
-We could extend this same adapter to work for different kinds of
-content objects, but that isn't very extensible when new adapters need
-to be made::
-
-  class Sized(object):
-      def __init__(self, context):
-          self.context = context
-
-      def size(self):
-          if isinstance(self.context, Document):
-               return len(self.context.text.encode('UTF-8'))
-          elif isinstance(self.context, Image):
-               return len(self.context.data)
-          elif isintance(self.context, Container):
-               total = 0
-               for obj in self.context.values():
-                   total += Sized(obj).size() 
-               return total
-               
-Instead, we can create a smart ``sized`` factory that does this
-switch-on-type behavior instead, keeping our adapters clean::
-
-  class DocumentSized(object):
-      def __init__(self, context):
-          self.context = context
- 
-      def sized(self):
-          return len(self.context.text.encode('UTF-8'))
-
-  class ImageSized(object):
-      def __init__(self, context):
-          self.context = context
- 
-      def sized(self):
-          return len(self.context.data)
-
-  class ContainerSized(object):
-      def __init__(self, context):
-          self.context = context
- 
-      def sized(self):
-          total = 0
-          for obj in self.context.values():
-              total += sized(obj).size() 
-          return total
-   
-  def sized(context):
-      if isinstance(context, Document):
-          return DocumentedSized(context)
-      elif isinstance(context, Image):
-          return ImageSized(context)
-      elif isinstance(context, Container):
-          return ContainerSized(context)
-       
-We can now call ``sized`` for a content object and get an object back
-that implements the "sized API"::
-
-   s = sized(my_content_object)
-   print s.size()
-
-It's good to spell out the APIs of your application explicitly, as
-documentation so that other developers can work with them and also
-implement them for their own content objects. Grok lets you do this
-using an *interface* specification, using the ``zope.interface``
-package::
-
-  from zope.interface import Interface
-
-  class ISized(Interface):
-      def size():
-           "Return the size of the object"
-
-We can now make this ``ISized`` interface into the adapter factory
-(like ``sized`` above), without actually having to implement it
-directly. Let's do that now by subclassing from ``grok.Adapter`` and
-using a few grok directives::
-
-  class DocumentSized(grok.Adapter):
-      grok.context(Document)
-      grok.provides(ISized)
- 
-      def sized(self):
-          return len(self.context.text.encode('UTF-8'))
-
-  class ImageSized(grok.Adapter):
-      grok.context(Image)
-      grok.provides(ISized)
- 
-      def sized(self):
-          return len(self.context.data)
-
-  class ContainerSized(grok.Adapter):
-      grok.context(Container)
-      grok.provides(ISized)
- 
-      def sized(self):
-          total = 0
-          for obj in self.context.values():
-              total += ISized(obj).size()
-          return total
-
-We can now use ``ISized`` like we used ``sized`` above::
-
-   s = ISized(my_content_object)
-   print s.size()
-
-When new content objects were to be created for this CMS, ``ISized``
-adapters can be registered for them anywhere. Using this pattern,
-existing objects implemented by someone else can be made to conform
-with the ``ISized`` API without having to modify them.
-
-``grok.context`` works as for views. It is useful to point it to any
-class however, not just that of models. ``grok.provides`` has to be
-pointed to an interface (the interface that the adapter *adapts to*).
-
-Interfaces
-~~~~~~~~~~
-
-Classes can also be made to *implement* an interface. This means that
-instances of that class *provide* that interface::
-
-  from zope.interface import Interface, Attribute
-
-  class IAnimal(Interface):
-      name = Attribute("The name of the animal")
-
-      def makeSound():
-          "The sound the animal makes."
-
-  class Cow(object):
-      grok.implements(IAnimal)
- 
-      def __init__(self, name):
-          self.name = name
-
-      def makeSound(self):
-          return "Mooo"
-
-We can ask the interface machinery whether an object provides an interface::
-
-  >>> cow = Cow()
-  >>> IAnimal.providedBy(cow)
-  True
-
-If you use an interface to adapt an object, and this object already
-provides the interface, you get back the object itself::
-
-  >>> IAnimal(cow) is cow
-  True
-
-``grok.context`` can always point to an interface instead of a class
-directly. This indirection can be useful to make a view or adapter
-work for a whole set of classes that all implement the same interface.
-
-``ComponentLookupError``
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-What if an adapter cannot be found for a particular object? Perhaps no
-adapter has been registered for a particular object or a particular
-interface. The system will raise a ``ComponentLookupError``::
-
-  >>> ISized(cow)
-  Traceback (most recent call last):
-    ...
-  ComponentLookupError
-
-If you want to catch this exception, you can import it from
-``zope.component.interfaces``::
-
-  from zope.component.interfaces import ComponentLookupError
-
-Named adapters
-~~~~~~~~~~~~~~
-
-It is possible to give an adapter a name, making it a *named
-adapter*. This way it is possible to have more than one adapter
-registered for a single object that all provide the same interface,
-each with a different name. This feature is rarely used directly,
-but internally it is used for views, as we will see later. The
-``grok.name()`` directive can be used to give an adapter a name::
-
-  class Adapter(object):
-      grok.name('somename')
-      grok.context(SomeClass)
-      grok.provides(ISomeInterface)
- 
-Actually all adapters are named: by default the name of an adapter is
-the empty string.
-
-You cannot call the interface directly to get a named adapter for an
-object.  Instead, you need to use the APIs provided by the
-``zope.component`` package, in particular ``getAdapter``::
-
-  from zope import component
-  
-  my_adapter = component.getAdapter(some_object, ISomeInterface, 
-                                   name='somename')
-
-``getAdapter`` can also be used to look up unnamed adapters, as an
-alternative to using the interface directly::
-
-  myadapter = component.getAdapter(some_object, ISomeInterface)
-
-Multi adapters
-~~~~~~~~~~~~~~
-
-Another feature of adapters is that you can adapt multiple objects at
-once using a *multi adapter*. Again this feature is rarely used in
-practice, except internally to implement views and events.
-
-You can construct a multi adapter by subclassing from
-``grok.MultiAdapter``::
-
-  class MyMultiAdapter(grok.MultiAdapter):
-      grok.adapts(SomeClass, AnotherClass)
-      grok.provides(ISomeInterface)
-   
-      def __init__(some_instance, another_instance):
-          self.some_interface = some_instance
-          self.another_instance = another_instance
-
-The multi-adapter receives as many arguments as what it was registered
-for using ``grok.adapts``.
-
-A multi adapter also cannot be looked up directly by calling the
-interface. Instead, we need to use the ``zope.component`` package
-again::
-
-  from zope import component
-
-  my_multi_adapter = component.getMultiAdapter((some_object, another_object),
-                                               ISomeInterface)
-
-``getMultiAdapter`` receives as the first argument a tuple with the
-combination of objects to adapt.
-
-It can also optionally be named using ``grok.name`` and then looked up
-using a name argument::
-
-  my_named_multi_adapter = component.getMultiAdapter(
-      (some_object, another_object), ISomeInterface, name="foo")
-
-Views as adapters
-~~~~~~~~~~~~~~~~~
-
-A view in Grok is in fact a named multi adapter, providing the base
-interface (``Interface``). This means that a view in Grok can be
-looked up in code by the following call::
- 
-  from zope.interface import Interface
-
-  view = component.getMultiAdapter((object, request), Interface, name="index")
-
-Since the default for the second argument is in fact ``Interface``, this
-call can be shorted to this::
-
-  view = component.getMultiAdapter((object, request), name="index")
-
-Being able to do this in code is sometimes useful. It is also what
-Grok does internally when it looks up a view.
-
-Events
-------
-
-Grok lets you write handlers for *events*. Using event handlers you
-can hook into code that you do not control. Events allow decoupling: a
-framework can send events without worrying who is interested in it,
-and similarly you can send events to work with existing bits of
-framework that expects them. You can also define new types of events
-if you are designing a framework yourself.
-
-You write an event handler by writing a function that *subscribes* to
-the event, and marking it with a python decorator::
-
-  @grok.subscribe(Document, grok.IObjectAddedEvent)
-  def handle(obj, event):
-      print "Object %s was added." % obj
-
-Whenever an instance of a model of class ``Document`` (or subclasses)
-is added to a container, this code will be run. You can then take some
-action. Any ``grok.Container`` subclass will take care of sending
-these events automatically.  You can have as many subscribers for a
-particular event as you like.  The order in which they are run is not
-guaranteed by the system, so cannot be relied on.
-
-The event handler takes two arguments: the object for which the event
-was fired, and the event instance. The event instance has attributes,
-depending on the type of event.
-
-Events defined by Grok
-~~~~~~~~~~~~~~~~~~~~~~
-
-Here we describe the standard events defined by Grok. Described are
-the interfaces which you would use in a subscriber, and how you can
-send this event yourself. Other events may be defined by libraries or
-by you.
-
-``IObjectMovedEvent``
-+++++++++++++++++++++
-
-Will be fired whenever an object is moved from container to container,
-renamed, added or removed.
-
-The event object has these attributes:
-
-* ``object`` - the object being moved
-
-* ``oldParent`` - the parent (container) from which the object was moved
-                  or removed, or ``None`` if this object is newly added.
-
-* ``oldName`` - the previous name of the object in its container,
-                before renaming if renaming took place, or ``None`` if
-                this object is newly added.
-
-* ``newParent`` - the parent (container) to this object was moved or
-                added. ``None`` if this object was removed.
-
-* ``newName`` - the name the object has in the new container, or ``None``
-                if this object was removed.
-
-Containers take care of sending this event, but should you want to
-send it yourself, use::
-
-  grok.notify(grok.ObjectMovedEvent(obj, oldParent, oldName, newParent, newName))
-
-``IObjectAddedEvent``
-+++++++++++++++++++++
-
-Fired when an object is added to a container. Specialization of
-``IObjectMovedEvent``, and shares the attributes as described.
-
-Containers take care of sending this event, but should you want to send it
-yourself, use::
-
-  grok.notify(grok.ObjectAddedEvent(obj))
-
-or::
-
-  grok.notify(grok.ObjectAddedEvent(obj, newParent, newName))
-
-``IObjectRemovedEvent``
-+++++++++++++++++++++++
-
-Fired when an object is removed from a container (and not re-added
-elsewhere). Specialization of ``IObjectMovedEvent``, and shares the
-attributes as described.
-
-Containers take care of sending this event, but should you want to send it
-yourself, use::
-
-  grok.notify(grok.ObjectRemovedEvent(obj)
-
-or::
-
-  grok.notify(grok.ObjectRemovedEvent(obj, oldparent, oldName))
-
-``IObjectModifiedEvent``
-++++++++++++++++++++++++
-
-Fired when an object is modified by the system, such as when a form is
-saved. If you modify the object in code, the system won't know about
-this, and you will have to remember to send it yourself.
-
-This event has a single attribute, ``object``, which is the object
-that was modified.
-
-To send this event yourself, use::
-
-  grok.notify(grok.ObjectModifiedEvent(obj))
-
-``IContainerModifiedEvent``
-+++++++++++++++++++++++++++
-
-A specialization of ``IObjectModifiedEvent`` that fires when the
-container was modified by adding something to it or removing from it.
-
-Containers take care of sending this event, but if you want to send it
-yourself, use::
-
-  grok.notify(grok.ContainerModifiedEvent(obj))
-
-``IObjectCreatedEvent``
-+++++++++++++++++++++++
-
-Fired when an object is created. When you create your own objects the
-system won't know about this, and you will have to remember to send it
-yourself if you care about listing to ``IObjectCreatedEvent``. This is
-fairly rare - usually you're better of looking at
-``IObjectAddedEvent`` if you can.
-
-This event has a single attribute, ``object``, which is the object
-that was created.
-
-To send this event yourself::
-
-  grok.notify(grok.ObjectCreatedEvent(obj))
-
-``IObjectCopiedEvent``
-++++++++++++++++++++++
-
-Fired when an object was copied. It is a specialization of
-``IObjectCreatedEvent`` that is fired by the system if you use the
-``zope.copypastemove`` functionality. 
-
-Besides the ``object`` attribute it shares with
-``IObjectCreattedEvent``, it has also has the ``original`` attribute,
-which was the object that iwas copied from.
-
-To send this event yourself::
-
-  grok.notify(grok.ObjectCopiedEvent(copy, original))
-
-Creating and sending your own events
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you are going to send an object that pertains to a particular object,
-subclass ``zope.component.interfaces.ObjectEvent``::
-
-  from zope.component.interfaces import ObjectEvent
-
-  class MyEvent(ObjectEvent):
-      pass
-
-You can then send it like this::
-
-  grok.notify(MyEvent(some_obj))
-
-And listen for it like this::
-
-  grok.subscribe(SomeClass, MyEvent)
-  def handle_my_event(obj, event):
-      pass
-
-This subclassing from ``ObjectEvent`` is not required; if your event
-isn't about an object, you can choose to design your event class
-entirely yourself. See ``zope.sendmail`` for the construction of mail sending
-events for an example. 
-
-Interfaces for events
-~~~~~~~~~~~~~~~~~~~~~
-
-For documentation purposes it can be a good idea to to define an
-interface for your event. You can then also allow for multiple
-implementations of the same event interface. When you have an
-interface for your event, you can then listen for the interface in the
-subscribers as well::
-
-  from zope.interface import Interface
-
-  class IMyEvent(zope.component.interfaces.IObjectEvent):
-      "My special event"
-
-  class MyEvent(zope.component.interfaces.ObjectEvent):
-      grok.implements(IMyEvent)
-
-  grok.subscribe(SomeClass, IMyEvent):
-  def handle_my_event(obj, event):
-      pass
-
-More about interfaces
----------------------
-
-We have seen small examples of interfaces before, but here we will go
-a bit more into them, and why they are useful.
-
-An *interface* is a description of the API of a class (or more rarely,
-module or object). Interfaces are useful because:
-
-* They are API documentation.
-
-* They can describe how a framework expects you to implement classes
-  that fit into it.
-
-* The system can inspect the interfaces a particular object provides,
-  and treat them as an abstract form of classes for registration
-  purposes. 
-
-Interfaces make it possible to use a generic framework's pluggability
-points with confidence: you can clearly see what you are supposed to
-implement to plug into it. You can define very generic frameworks
-yourself by defining them in terms of interfaces.
-
-Some interface features
-~~~~~~~~~~~~~~~~~~~~~~~
-
-A summary of interface features we've seen:
-
-* To create an interface, subclass from ``zope.interface.Interface``.
-
-* To state that implementors of the interface must have a method, supply
-  the method with arguments. Don't use ``self`` as the first
-  arguments, as this is an implementation detail not important to the
-  interface. Instead, describe the methods as they look to the caller.
-
-* To state that implementors of the interface must have an attribute, use::
-
-    some_attribute = zope.interface.Attribute("Description of attribute")
-
-* To state a class *implements* an interface, use ``grok.implements``.
-
-* Instances of a class are said to *provide* the interface that the
-  class *implements*.
-
-* You can check whether an instance provides a certain interface by using
-  ``some_interface.providedBy``::
-
-     IObjectEvent.providedBy(NonSubclassEvent(some_obj))
-
-Interfaces and events
-~~~~~~~~~~~~~~~~~~~~~
-
-Let's study interfaces some more in connection with
-``IObjectModifiedEvent``. The ``IObjectModifiedEvent`` interface looks
-like this::
-
-  class IObjectModifiedEvent(zope.component.interfaces.IObjectEvent):
-      """An object has been modified"""
-
-This refers us to the ``IObjectEvent`` interface, which looks like
-this::
-
-  from zope import interface
-
-  class IObjectEvent(interface.Interface):
-      """An event related to an object.
-      """
-
-      object = interface.Attribute("The subject of the event.")
-
-We therefore know that if we implement ``IObjectModifiedEvent``, we
-must supply a single attribute, ``object``.
-
-The following event handler for instances of ``SomeClass`` subscribes
-to *any* event that provides ``IModifiedObjectEvent``::
-
-   @grok.subscribe(SomeClass, IObjectModifiedEvent):
-   def handle_event(obj, event):
-       "Called when there is an IObjectModifiedEvent for SomeClass instances."
-
-This handler will be called not only for subclasses of the
-``grok.ObjectModifiedEvent`` class, but also for other, otherwise
-unrelated classes that implement ``IObjectEvent``, such as this one::
-
-  class NonSubclassObjectEvent(object):
-      grok.implements(IObjectEvent)
- 
-      def __init__(self, object):
-           self.object = object
-
-So far we have only used interfaces for the second argument of the
-event handler registration, but the principle also works for the first
-argument. For example, to handle ``IObjectModifiedEvent`` events for
-all kinds of containers, you can subscribe to
-``zope.app.container.interfaces.IContainer`` objects::
-
-  @grok.subscribe(IContainer, IObjectModifiedEvent):
-  def handle_event(obj, event):
-      "Called whenever any container is modified"
-
-``zope.app.container.interfaces.IContainer`` defines the abstract
-container API that all containers must provide, no matter how they are
-implemented internally.
-
-Interfaces and adapters
-~~~~~~~~~~~~~~~~~~~~~~~
-
-The same principle also works for adapters and ``grok.context``. You
-can use ``grok.context`` with interfaces as well as with concrete
-classes. To write an adapter that works for any kind of container, you
-can write::
-
-  from zope.app.container.interfaces import IContainer
-
-  class SortedKeysAdapter(grok.Adapter):
-      grok.context(IContainer)
-      grok.provides(ISortedKeys)
-
-      def sortedKeys(self):
-          return sorted(self.context.keys())     
-
-Interfaces and views
-~~~~~~~~~~~~~~~~~~~~
-
-The same principle can also be used with ``grok.context`` in other
-places, such as in views. This view is registered for all containers::
-
-  from zope.app.container.interfaces import IContainer
-
-  class Keys(grok.View):
-     grok.context(IContainer)
-     
-     def render(self):
-         return ', '.join(ISortedKeysAdapter(self.context).sortedKeys())
-
-The view ``keys`` exists for all containers, no matter how they are
-implement, where they are implemented or who implemented them, as long
-as they provide ``IContainer``.
-
-Using the fact ``Interface`` is the base of all interfaces, you can
-even register a view for *all* objects. This can be useful to register
-ZPT macros, which will then be available on all contexts::
-
-  class Layout(grok.View):
-      grok.context(Interface)
- 
-with a template ``layout.pt`` associated to it.
-
-You can then use these macros in any page template anywhere by
-referring to them like this::
-
-  <html metal:use-macro="context/@@layout/macros/page">
-
-Forms
------
-
-Grok can autogenerate web forms from descriptions called *schema*. A
-schema is a special kind of interface. We already saw ``Attribute``,
-which can be used to specify that something that provides that
-interface should have that attribute. The ``zope.schema`` package adds
-a lot more specific field descriptions. Here is an example of a
-schema::
-
-  from zope.interface import Interface
-  from zope import schema
-  
-  class ISpecies(Interface):
-      name = schema.TextLine(u"Animal species name")
-      scientific_name = schema.TextLine(u"Scientific name")
-      legs = schema.Int(u"Number of legs")
-
-Let's also look at a simple implementation of this interface::
-
-  class Species(grok.Model):
-      grok.implements(ISpecies)
-
-Note how we aren't even creating an ``__init__`` to set the
-attributes; we could, but we'll see below that Grok's ``applyData``
-can take care of this automatically.
-
-The ``ISpecies`` schema can be turned into a form. Grok does this by
-looking up a *widget* for each schema field to display it. A widget is
-very much like a view. Let's look at a form for this schema::
-
-  class Species(grok.Form):
-      form_fields = grok.Fields(ISpecies)
-  
-      @grok.action(u"Save form")
-      def handle_save(self, **data):
-          print data['name']
-          print data['scientific_name']
-          print data['legs']
-
-What is going on here? Firstly we use a special base class called
-``grok.Form``. A form is a special kind of ``grok.View``, and
-associates the same way (using ``grok.context``). A form expects two
-things::
-
-* a ``form_fields`` attribute. Above we see the most common way to construct
-  this attribute, using ``grok.Fields`` on the interface.
-
-* one or more actions. Actions are specified by using the
-  ``@grok.action`` decorator. An action gets the fields filled in the
-  form as keyword parameters, so ``**data`` in this case. We could
-  also have specified the arguments we expected specifically.
-
-Form widgets translate the raw HTML form input to Python objects, such
-as (unicode) strings, integers and datetime objects, as specified by
-schema fields. The schema fields can then be used to validate this
-input further. Forms are self-submitting, and in case of a validation
-error the form can render them in-line next to the fields.
-
-We'll look at a lot of form features next.
-
-``grok.AddForm``
-~~~~~~~~~~~~~~~~
-
-An add form is used to create a new object. Most forms are views of
-the object that they are representing, but an add form is typically
-associated a view of the container in which new objects are to be
-added. Let's look at an example::
-
-  class SpeciesContainer(grok.Container):
-      pass
-
-  class Add(grok.AddForm):
-      grok.context(SpeciesContainer)
-
-      form_fields = grok.Fields(ISpecies)
-      
-      @grok.action(u"Add species")
-      def add_species(self, **data):
-          # create a species instance
-          species = Species()
-          # assign the right attributes to fulfill ISpecies schema with
-          # the form data
-          self.applyData(species, **data)
-          # stores the instance into the SpeciesContainer
-          name = data['name']
-          self.context[name] = species
-          # redirect to the newly created object
-          self.redirect(self.url(species))
-          # we don't want to display anything, as we redirect
-          return ''
-
-The user can now go to ``myspeciescontainer/add`` to add a species,
-where ``myspeciescontainer`` is any instance of ``SpeciesContainer``.
-
-``grok.EditForm``
-~~~~~~~~~~~~~~~~~
-
-Now that we can create species objects, let's create a form so you can
-easily edit them. This *is* a view of the ``Species`` model::
-
-  class Edit(grok.EditForm):
-     grok.context(Species)
-    
-     form_fields = grok.Fields(ISpecies)
-
-     @grok.action(u"Edit species")
-     def edit_species(self, **data):
-          self.applyData(species, **data)
-     
-Forms are self-submitting, so this will show the edit form again. If
-you want to display another page, you can redirect the browser as we
-showed for the add form previously.
-
-The user can now go to ``myspecies/edit`` to edit the species.
-
-``grok.DisplayForm``
-~~~~~~~~~~~~~~~~~~~~
-
-Sometimes you just want to display an object, and not actually edit
-it. If the object is schema-based, an easy way to do this is to use
-display forms. Let's look at an example::
-
-  class Display(grok.DisplayForm):
-     grok.context(Species)
-    
-     form_fields = grok.Fields(ISpecies)
-
-The user can now go to ``myspecies/display`` to look at the species.
-
-Associating a template for a form
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-By default, Grok supplies some templates for forms. They work, but
-they are not very pretty and don't fit into your application's
-layout. You can instead use your own form rendering logic in a
-template you associate with the form just like you associate templates
-with views. You can also abstract form rendering logic you keep
-reusing into a ZPT macro. Below is an example of form rendering logic
-to help you get started. The example doesn't have any consideration
-for layouting to make the logic clear. As a result, the form will be
-very ugly if you use this - you will want to use CSS or table HTML to
-layout things::
-
-  <!-- render the form tag -->
-  <form action="." tal:attributes="action request/URL" method="post"
-        class="edit-form" enctype="multipart/form-data">
-    <!-- render any validation errors on top -->
-    <ul class="errors" tal:condition="view/errors">
-      <li tal:repeat="error view/error_views">
-         <span tal:replace="structure error">Error Type</span>
-      </li>
-    </ul>
-
-    <!-- render the widgets -->
-    <tal:block repeat="widget view/widgets">
-      <label tal:attributes="for widget/name">
-        <!-- a * when the widget is required -->
-        <span class="required" tal:condition="widget/required">*</span>
-        <!-- the title of the field -->
-        <span i18n:translate="" tal:content="widget/label">label</span>
-      </label>
-     
-      <!-- render the HTML widget -->
-      <div class="widget" tal:content="structure widget">
-        <input type="text" />
-      </div>
-      
-      <!-- render any field specific validation error from a previous
-           form submit next to the field -->
-      <div class="error" tal:condition="widget/error">
-        <span tal:replace="structure widget/error">error</span>
-      </div>
-    </tal:block>
-
-    <!-- render all the action submit buttons -->
-    <span class="actionButtons" tal:condition="view/availableActions">
-      <input tal:repeat="action view/actions"
-             tal:replace="structure action/render" />
-    </span>
-  </form>
-
-The template for a display form a lot simpler::
-
-  <tal:block repeat="widget view/widgets">
-    <tal:block content="widget/label" />
-    <input tal:replace="structure widget" />
-  </tal:block>
-   
-  <!-- render all the action submit buttons -->
-  <span class="actionButtons" tal:condition="view/availableActions">
-    <input tal:repeat="action view/actions"
-           tal:replace="structure action/render" />
-  </span>
-



More information about the Checkins mailing list