[Checkins] SVN: grok/trunk/doc/grok_ Rename this from rules to overview.

Martijn Faassen faassen at infrae.com
Mon Apr 21 12:50:19 EDT 2008


Log message for revision 85542:
  Rename this from rules to overview.
  

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

-=-
Copied: grok/trunk/doc/grok_overview.txt (from rev 85541, grok/trunk/doc/grok_rules.txt)
===================================================================
--- grok/trunk/doc/grok_overview.txt	                        (rev 0)
+++ grok/trunk/doc/grok_overview.txt	2008-04-21 16:50:18 UTC (rev 85542)
@@ -0,0 +1,715 @@
+A Quick Overview of Grok
+========================
+
+This document intends to descibe the rules and APIs of Grok
+briefly. It is not intended to be a beginner's tutorial. It's also not
+a reference. It's something in between. 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 used when
+an object 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.
+
+``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)
+
+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 ``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.

Deleted: grok/trunk/doc/grok_rules.txt
===================================================================
--- grok/trunk/doc/grok_rules.txt	2008-04-21 16:47:47 UTC (rev 85541)
+++ grok/trunk/doc/grok_rules.txt	2008-04-21 16:50:18 UTC (rev 85542)
@@ -1,715 +0,0 @@
-The rules of Grok
-=================
-
-This document intends to descibe the rules of Grok briefly. It is not
-intended to be a beginner's tutorial. It's also not a reference. It's
-something in between. 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 used when
-an object 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.
-
-``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)
-
-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 ``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.



More information about the Checkins mailing list