[Checkins] SVN: megrok.cherry/ Initial import of megrok.cherry. This is:

Martijn Faassen faassen at infrae.com
Tue Nov 28 17:38:06 EST 2006


Log message for revision 71322:
  Initial import of megrok.cherry. This is:
  
  * an experimental integration of Zope 3 with the CherryPy WSGI server.
  
  * an attempt to see how much of Zope needs to be loaded in order to
    run Grok.
  
  These two aims are combined as running Zope 3 in a completely different
  server context makes it very easy to test a barebones Zope 3 installation
  at the same time.
  
  The experiment is far from finished yet: I've barely scratched the
  surface of testing Grok, and I hope that we can get away with loading
  less of Zope 3 on startup, as I'd like to speed up startup/shutdown further.
  
  

Changed:
  A   megrok.cherry/
  A   megrok.cherry/trunk/
  A   megrok.cherry/trunk/README.txt
  A   megrok.cherry/trunk/bootstrap.py
  A   megrok.cherry/trunk/buildout.cfg
  A   megrok.cherry/trunk/grok/
  A   megrok.cherry/trunk/grok/COPYRIGHT.txt
  A   megrok.cherry/trunk/grok/CREDITS.txt
  A   megrok.cherry/trunk/grok/INSTALL.txt
  A   megrok.cherry/trunk/grok/LICENSE.txt
  A   megrok.cherry/trunk/grok/README.txt
  A   megrok.cherry/trunk/grok/TODO.txt
  A   megrok.cherry/trunk/grok/bootstrap/
  A   megrok.cherry/trunk/grok/bootstrap/bootstrap.py
  A   megrok.cherry/trunk/grok/buildout.cfg
  A   megrok.cherry/trunk/grok/doc/
  A   megrok.cherry/trunk/grok/doc/design/
  A   megrok.cherry/trunk/grok/doc/design/adapters.py
  A   megrok.cherry/trunk/grok/doc/design/annotations.py
  A   megrok.cherry/trunk/grok/doc/design/container.py
  A   megrok.cherry/trunk/grok/doc/design/grok_beginner.txt
  A   megrok.cherry/trunk/grok/doc/design/grok_dev.txt
  A   megrok.cherry/trunk/grok/doc/design/model.py
  A   megrok.cherry/trunk/grok/doc/design/permission.py
  A   megrok.cherry/trunk/grok/doc/design/skin-minimal.py
  A   megrok.cherry/trunk/grok/doc/design/skin.py
  A   megrok.cherry/trunk/grok/doc/design/subscriber.py
  A   megrok.cherry/trunk/grok/doc/design/traversal.py
  A   megrok.cherry/trunk/grok/doc/design/utility.py
  A   megrok.cherry/trunk/grok/doc/design/views.py
  A   megrok.cherry/trunk/grok/doc/reference/
  A   megrok.cherry/trunk/grok/doc/reference/README.txt
  A   megrok.cherry/trunk/grok/doc/reference/components.tex
  A   megrok.cherry/trunk/grok/doc/reference/core.tex
  A   megrok.cherry/trunk/grok/doc/reference/decorators.tex
  A   megrok.cherry/trunk/grok/doc/reference/directives.tex
  A   megrok.cherry/trunk/grok/doc/reference/events.tex
  A   megrok.cherry/trunk/grok/doc/reference/exceptions.tex
  A   megrok.cherry/trunk/grok/doc/reference/model.tex
  A   megrok.cherry/trunk/grok/doc/reference/reference.tex
  A   megrok.cherry/trunk/grok/doc/tutorial.txt
  A   megrok.cherry/trunk/grok/grokblog/
  A   megrok.cherry/trunk/grok/grokblog/setup.py
  A   megrok.cherry/trunk/grok/grokblog/src/
  A   megrok.cherry/trunk/grok/grokblog/src/grokblog/
  A   megrok.cherry/trunk/grok/grokblog/src/grokblog/__init__.py
  A   megrok.cherry/trunk/grok/grokblog/src/grokblog/blog.py
  A   megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/
  A   megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/addentry.pt
  A   megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/blogindex.pt
  A   megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/dateindex.pt
  A   megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/entryedit.pt
  A   megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/entryindex.pt
  A   megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/entryitem.pt
  A   megrok.cherry/trunk/grok/grokblog/src/grokblog/configure.zcml
  A   megrok.cherry/trunk/grok/grokwiki/
  A   megrok.cherry/trunk/grok/grokwiki/README.txt
  A   megrok.cherry/trunk/grok/grokwiki/setup.py
  A   megrok.cherry/trunk/grok/grokwiki/src/
  A   megrok.cherry/trunk/grok/grokwiki/src/grokwiki/
  A   megrok.cherry/trunk/grok/grokwiki/src/grokwiki/__init__.py
  A   megrok.cherry/trunk/grok/grokwiki/src/grokwiki/configure.zcml
  A   megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page.py
  A   megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/
  A   megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/edit.pt
  A   megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/index.pt
  A   megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/layout.pt
  A   megrok.cherry/trunk/grok/grokwiki/src/grokwiki/static/
  A   megrok.cherry/trunk/grok/grokwiki/src/grokwiki/static/wiki.css
  A   megrok.cherry/trunk/grok/grokwiki/src/grokwiki/wiki.py
  A   megrok.cherry/trunk/grok/grokwiki/src/grokwiki/xmlrpc.py
  A   megrok.cherry/trunk/grok/ldapaddressbook/
  A   megrok.cherry/trunk/grok/ldapaddressbook/README.txt
  A   megrok.cherry/trunk/grok/ldapaddressbook/setup.py
  A   megrok.cherry/trunk/grok/ldapaddressbook/src/
  A   megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/
  A   megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/__init__.py
  A   megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/addressbook.py
  A   megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/configure.zcml
  A   megrok.cherry/trunk/grok/setup.py
  A   megrok.cherry/trunk/grok/src/
  A   megrok.cherry/trunk/grok/src/grok/
  A   megrok.cherry/trunk/grok/src/grok/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/_grok.py
  A   megrok.cherry/trunk/grok/src/grok/components.py
  A   megrok.cherry/trunk/grok/src/grok/configure.zcml
  A   megrok.cherry/trunk/grok/src/grok/directive.py
  A   megrok.cherry/trunk/grok/src/grok/error.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/
  A   megrok.cherry/trunk/grok/src/grok/ftests/README.txt
  A   megrok.cherry/trunk/grok/src/grok/ftests/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/form/
  A   megrok.cherry/trunk/grok/src/grok/ftests/form/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/form/actions.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/form/form.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/static/
  A   megrok.cherry/trunk/grok/src/grok/ftests/static/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/static/simple.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/
  A   megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/ellie.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/static/
  A   megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/static/file.txt
  A   megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/static/static.pt
  A   megrok.cherry/trunk/grok/src/grok/ftests/test_grok_functional.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/traversal/
  A   megrok.cherry/trunk/grok/src/grok/ftests/traversal/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/traversal/containertraverse.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/traversal/modeltraverse.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/traversal/traverser.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/url/
  A   megrok.cherry/trunk/grok/src/grok/ftests/url/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/url/redirect.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/url/url.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/view/
  A   megrok.cherry/trunk/grok/src/grok/ftests/view/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/view/index.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/view/macros.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/view/view.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc/
  A   megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc/xmlrpc.py
  A   megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc_helper.py
  A   megrok.cherry/trunk/grok/src/grok/interfaces.py
  A   megrok.cherry/trunk/grok/src/grok/meta.zcml
  A   megrok.cherry/trunk/grok/src/grok/scan.py
  A   megrok.cherry/trunk/grok/src/grok/security.py
  A   megrok.cherry/trunk/grok/src/grok/tests/
  A   megrok.cherry/trunk/grok/src/grok/tests/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/adapter.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/alphabetical.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontext.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontextimported.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontextmultiple.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontextmultiple_fixture.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/classorinterface.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/functioncontext.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/implementsmany.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/implementsnone.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/implementsnonemulti.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/importedmodel.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/importedmodel2.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/interface.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/interfacemodule.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontext.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontextimported.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontextmultiple.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontextmultiple_fixture.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/multiadapter.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/multiadaptsnone.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/multiple.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/namedadapter.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/nomodel.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/oldstyleclass.py
  A   megrok.cherry/trunk/grok/src/grok/tests/adapter/order.py
  A   megrok.cherry/trunk/grok/src/grok/tests/container/
  A   megrok.cherry/trunk/grok/src/grok/tests/container/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/container/container.py
  A   megrok.cherry/trunk/grok/src/grok/tests/container/container_model.py
  A   megrok.cherry/trunk/grok/src/grok/tests/error/
  A   megrok.cherry/trunk/grok/src/grok/tests/error/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/error/error.py
  A   megrok.cherry/trunk/grok/src/grok/tests/error/filesystemtemplate.py
  A   megrok.cherry/trunk/grok/src/grok/tests/error/filesystemtemplate_templates/
  A   megrok.cherry/trunk/grok/src/grok/tests/error/filesystemtemplate_templates/nocontext.pt
  A   megrok.cherry/trunk/grok/src/grok/tests/event/
  A   megrok.cherry/trunk/grok/src/grok/tests/event/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/event/errorconditions.py
  A   megrok.cherry/trunk/grok/src/grok/tests/event/errorconditions_fixture.py
  A   megrok.cherry/trunk/grok/src/grok/tests/event/subscriber.py
  A   megrok.cherry/trunk/grok/src/grok/tests/form/
  A   megrok.cherry/trunk/grok/src/grok/tests/form/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/form/form.py
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/package.py
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/scan.py
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/cave.py
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/mammoth.py
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/mammoth_templates/
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/mammoth_templates/index.pt
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/notpackage/
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/notpackage/dummy.py
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/painting/
  A   megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/painting/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/security/
  A   megrok.cherry/trunk/grok/src/grok/tests/security/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/security/modeldefaultpublic.py
  A   megrok.cherry/trunk/grok/src/grok/tests/security/viewdefaultpublic.py
  A   megrok.cherry/trunk/grok/src/grok/tests/site/
  A   megrok.cherry/trunk/grok/src/grok/tests/site/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/site/site.py
  A   megrok.cherry/trunk/grok/src/grok/tests/static/
  A   megrok.cherry/trunk/grok/src/grok/tests/static/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy.py
  A   megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy_fixture/
  A   megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy_fixture/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy_fixture/static/
  A   megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy_fixture/static.py
  A   megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage.py
  A   megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage_fixture/
  A   megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage_fixture/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage_fixture/static/
  A   megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage_fixture/static/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/test_grok.py
  A   megrok.cherry/trunk/grok/src/grok/tests/traversal/
  A   megrok.cherry/trunk/grok/src/grok/tests/traversal/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/utility/
  A   megrok.cherry/trunk/grok/src/grok/tests/utility/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/utility/implementsnone.py
  A   megrok.cherry/trunk/grok/src/grok/tests/utility/utility.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/
  A   megrok.cherry/trunk/grok/src/grok/tests/view/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/ambiguouscontext.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/before.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirandinlinetemplate.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirandinlinetemplate_templates/
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirandinlinetemplate_templates/cavepainting.pt
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate_templates/
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate_templates/cavepainting.pt
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate_templates/food.pt
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplateandrender.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplateandrender_templates/
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplateandrender_templates/cavepainting.pt
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly_templates/
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly_templates/index.pt
  A   megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly_templates/invalid.txt
  A   megrok.cherry/trunk/grok/src/grok/tests/view/eithertemplateorrender.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/explicitimplicittemplate.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/inline.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/inlinebogus.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/missingcontext.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/namemultiple.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/namemultiple_fixture.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/nameunicode.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/nomodulename.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/nomodulename_fixture.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/notemplateorrender.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/template.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/templatedirectory.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/templatedirectoryname/
  A   megrok.cherry/trunk/grok/src/grok/tests/view/templatedirectoryname/food.pt
  A   megrok.cherry/trunk/grok/src/grok/tests/view/templatenotfound.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/twoviewsusetemplate.py
  A   megrok.cherry/trunk/grok/src/grok/tests/view/view.py
  A   megrok.cherry/trunk/grok/src/grok/tests/xmlrpc/
  A   megrok.cherry/trunk/grok/src/grok/tests/xmlrpc/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/xmlrpc/nocontext.py
  A   megrok.cherry/trunk/grok/src/grok/tests/zcml/
  A   megrok.cherry/trunk/grok/src/grok/tests/zcml/__init__.py
  A   megrok.cherry/trunk/grok/src/grok/tests/zcml/directiveerror.py
  A   megrok.cherry/trunk/grok/src/grok/tests/zcml/directiveimporterror.py
  A   megrok.cherry/trunk/grok/src/grok/tests/zcml/directiveimporterror_fixture.py
  A   megrok.cherry/trunk/grok/src/grok/tests/zcml/directivemodule.py
  A   megrok.cherry/trunk/grok/src/grok/tests/zcml/directivepackage.py
  A   megrok.cherry/trunk/grok/src/grok/util.py
  A   megrok.cherry/trunk/grok/src/grok/zcml.py
  A   megrok.cherry/trunk/grok/src/grok.egg-info/
  A   megrok.cherry/trunk/grok/src/grok.egg-info/PKG-INFO
  A   megrok.cherry/trunk/grok/src/grok.egg-info/SOURCES.txt
  A   megrok.cherry/trunk/grok/src/grok.egg-info/dependency_links.txt
  A   megrok.cherry/trunk/grok/src/grok.egg-info/not-zip-safe
  A   megrok.cherry/trunk/grok/src/grok.egg-info/requires.txt
  A   megrok.cherry/trunk/grok/src/grok.egg-info/top_level.txt
  A   megrok.cherry/trunk/setup.py
  A   megrok.cherry/trunk/site.zcml
  A   megrok.cherry/trunk/src/
  A   megrok.cherry/trunk/src/megrok/
  A   megrok.cherry/trunk/src/megrok/__init__.py
  A   megrok.cherry/trunk/src/megrok/cherry/
  A   megrok.cherry/trunk/src/megrok/cherry/__init__.py
  A   megrok.cherry/trunk/src/megrok/cherry/configure.zcml
  A   megrok.cherry/trunk/src/megrok/cherry/demo.py
  A   megrok.cherry/trunk/src/megrok/cherry/error.py
  A   megrok.cherry/trunk/src/megrok/cherry/server.py

-=-
Added: megrok.cherry/trunk/README.txt
===================================================================
--- megrok.cherry/trunk/README.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/README.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,15 @@
+This is:
+
+* an experimental integration of Zope 3 with the CherryPy WSGI server.
+
+* an attempt to see how much of Zope needs to be loaded in order to
+  run Grok.
+
+These two aims are combined as running Zope 3 in a completely different
+server context makes it very easy to test a barebones Zope 3 installation
+at the same time.
+
+The experiment is far from finished yet: I've barely scratched the
+surface of testing Grok, and I hope that we can get away with loading
+less of Zope 3 on startup, as I'd like to speed up startup/shutdown further.
+

Added: megrok.cherry/trunk/bootstrap.py
===================================================================
--- megrok.cherry/trunk/bootstrap.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/bootstrap.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 69908 2006-08-31 21:53:00Z jim $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)

Added: megrok.cherry/trunk/buildout.cfg
===================================================================
--- megrok.cherry/trunk/buildout.cfg	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/buildout.cfg	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,40 @@
+[buildout]
+develop = . grok
+parts = zope3 data devpython
+
+[zope3]
+recipe = zc.recipe.zope3checkout
+url = svn://svn.zope.org/repos/main/Zope3/branches/3.3
+
+[devpython]
+recipe = zc.recipe.egg
+interpreter = devpython
+eggs = megrok.cherry
+extra-paths = ${buildout:directory}/parts/zope3/src
+
+[instance]
+recipe = zc.recipe.zope3instance
+database = testdata
+user = grok:grok
+eggs = setuptools
+       grok
+zcml = zope.annotation
+       zope.copypastemove
+       zope.formlib
+       zope.i18n-meta
+       zope.i18n.locales
+       zope.publisher
+       zope.security-meta
+       zope.size
+       zope.traversing
+       zope.traversing.browser
+       zope.app    
+       zope.app-meta
+       zope.app.securitypolicy
+       zope.app.securitypolicy-meta
+       zope.app.authentication
+       grok
+       grok-meta
+
+[data]
+recipe = zc.recipe.filestorage

Added: megrok.cherry/trunk/grok/COPYRIGHT.txt
===================================================================
--- megrok.cherry/trunk/grok/COPYRIGHT.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/COPYRIGHT.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,10 @@
+Copyright (c) 2006 gocept gmbh & co. kg, Martijn Faassen and Philipp von
+Weitershausen
+All Rights Reserved.
+
+This software is subject to the provisions of the Zope Public License,
+Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+FOR A PARTICULAR PURPOSE. 

Added: megrok.cherry/trunk/grok/CREDITS.txt
===================================================================
--- megrok.cherry/trunk/grok/CREDITS.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/CREDITS.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,22 @@
+CREDITS
+=======
+
+* Martijn Faassen
+
+* Wolfgang Schnerring
+
+* Christian Theune
+
+* Philipp von Weitershausen
+
+* Christian Zagrodnick
+
+* ME GROK
+
+Thank you
+---------
+
+* gocept for hosting the first grok sprint
+
+* Felicia Faassen Wong, Christian Zagrodnick and Magda Motyka for the
+  delicious food at the first grok sprint

Added: megrok.cherry/trunk/grok/INSTALL.txt
===================================================================
--- megrok.cherry/trunk/grok/INSTALL.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/INSTALL.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,50 @@
+Preparing for grok development
+------------------------------
+
+Grok is installed via buildout <http://cheeseshop.python.org/pypi/zc.buildout>.
+
+You may have setuptools already installed for your system Python. In
+that case, you may need to upgrade it first because buildout requires
+a very recent version::
+
+    $ sudo easy_install -U setuptools
+
+If this command fails because easy_install is not available, there is
+a good chance you do not have setuptools available for your system
+Python. If so, there is no problem because setuptools will be
+installed locally by buildout.
+
+Bootstrap the buildout environment::
+
+    $ python bootstrap/bootstrap.py
+
+and run the buildout command::
+
+    $ bin/buildout
+    [lots of stuff will be downloaded and installed locally here]
+
+Running the demo applications
+-----------------------------
+
+Start the instance:
+
+   $ cd parts/instance
+   $ bin/zopectl fg
+
+If you now connect to port 8080 and log in with username 'grok',
+password 'grok', you should be able to add the grok-based applications
+(such as grokwiki) from the menu.
+
+Running the tests
+-----------------
+
+Due to a bug in Zope, we need to run the tests this way, from a
+special instance directory:
+
+    $ cd parts/testinstance
+    $ ../../bin/test
+
+otherwise you will get an error about not finding
+'ftesting.zcml'. We've set up a special test instance just for running
+the test from, which includes the minimum needed ZCML (so the tests
+run faster).

Added: megrok.cherry/trunk/grok/LICENSE.txt
===================================================================
--- megrok.cherry/trunk/grok/LICENSE.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/LICENSE.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,54 @@
+Zope Public License (ZPL) Version 2.1
+-------------------------------------
+
+A copyright notice accompanies this license document that
+identifies the copyright holders.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the
+   accompanying copyright notice, this list of conditions,
+   and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the accompanying
+   copyright notice, this list of conditions, and the
+   following disclaimer in the documentation and/or other
+   materials provided with the distribution.
+
+3. Names of the copyright holders must not be used to
+   endorse or promote products derived from this software
+   without prior written permission from the copyright
+   holders.
+
+4. The right to distribute this software or to use it for
+   any purpose does not give you the right to use
+   Servicemarks (sm) or Trademarks (tm) of the copyright
+   holders. Use of them is covered by separate agreement
+   with the copyright holders.
+
+5. If any files are modified, you must cause the modified
+   files to carry prominent notices stating that you changed
+   the files and the date of any change.
+
+Disclaimer
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS''
+  AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+  NO EVENT SHALL THE COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.

Added: megrok.cherry/trunk/grok/README.txt
===================================================================
--- megrok.cherry/trunk/grok/README.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/README.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,20 @@
+What is grok?
+=============
+
+Grok makes it easier to get started with a Zope 3 web application.
+
+Grok uses the Component Architecture and builds on Zope 3 concepts
+like content objects (models), views, and adapters.  Its simplicity
+lies in using **convention over configuration** and **sensible
+defaults** when wiring components together.  That means neither a
+configuration language like ZCML nor a lot of repitition are needed to
+create a web application with grok.
+
+Who is grok?
+============
+
+Grok is a friendly caveman from the Stone Age.  He has a big club that
+he hunts mammoths with.  He will also use this club to smash anything
+he doesn't like.
+
+"ME GROK SMASH ZCML!"

Added: megrok.cherry/trunk/grok/TODO.txt
===================================================================
--- megrok.cherry/trunk/grok/TODO.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/TODO.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,127 @@
+====
+TODO
+====
+
+Meta
+====
+
+- setup homepage (grok.zope.org or zope.org/grok, wiki or rest) (faassen)
+
+- tutorial (faassen)
+
+- reference (theuni)
+
+- Check demo applications for issues with patterns (grok wiki, ldap address
+  book)
+
+Core
+====
+
+- choice fields / sources (theuni)
+
+- annotations (faassen)
+
+- testing strategy for the tutorial (faassen)
+
+- security: grok.permission, grok.require, ... and the needed additions
+  to the security policy (philikon, theuni)
+
+- traversal - not megrok.routes (philikon)
+
+- local registrations (philikon)
+
+- make it easier to write tests (wosc, faassen)
+
+- grok.App (philikon)
+
+- forms / schema driven development (faassen, wosc)
+  * list form for grok.Container (w/ zc.table?)
+  * delete action on list form
+  * add form
+  
+  * custom templates for forms (make formlib macros available in some form?)
+  * support nested class 'fields' directly on a view (do we really want this?)
+
+- provide custom (better looking) template and styles for formlib 
+
+- error reporting during grokking (provide file/line-number information
+  on our extrinsically generated errors) (philikon)
+
+- FooBase convention + grok.baseclass to not grok certain classes
+
+- maybe: transform register_foo() functions that take lists into functions
+  that operate on a single entity and provide a decorator (@iterate) to 
+  call the function for each item (theuni)
+
+- grok.utility() needs to work on module level so we can register imported
+  objects as global utilities.
+
+Need to discuss
+---------------
+
+- TALES path expressions to generate URLs?
+
+- fall back to a static resource that is defined in a package on a higher
+  level if no static resource directory is defined locally in a package?
+
+- grok buildout integration
+
+- grok.grokkable (to allow grokking of imported things)
+
+- skins
+
+- form redirect
+
+- grok.resourcedir (multiple? per package? ...?)
+
+- authentication (pau integration) (faassen)
+
+- sessions (get the session information for something, similar to
+  annotations?)
+
+- menus - define a menu, associate a view with a menu (module-level,
+  class-level)
+
+- making new widgets (faassen, philikon)
+
+- local persistent utilities (e.g. catalog) (faassen, wosc)
+
+- catalog (faassen, wosc)
+
+- Admin UI (faassen, jw)
+
+- IMPORTANT: different strategies: grok.definefoo() versus n =
+  grok.Foo(), watch out for consistency/symmetry/...
+
+- have a custom security policy that is easier to understand than the
+  default Zope 3 policy
+
+- use ZCML's conflict resolution machinery
+
+- do not accept automatic template directory guessing convention for
+  __init__.py, bail out with grok error instead?
+
+- grok.name, grok.template class restrictions (e.g. grok.template
+  should only be usable from grok.View subclasses)
+
+- support grok.template(template) in addition to
+  grok.template('name_of_template')?
+
+- support grok.resource on view class level?
+
+- should grok.context and grok.Model be order-dependent?
+  (so their meaning becomes "below here, this is the context")
+
+- page template reloads without having to restart
+
+Punt
+----
+
+- making new fields
+
+- viewlets / content providers (LATER)
+
+- RDB - via extension: megrok.sqlalchemy for example - make it easy to
+  go between the different schema implementations
+
+- containment constraints (wait for zope 3 to do them right)

Added: megrok.cherry/trunk/grok/bootstrap/bootstrap.py
===================================================================
--- megrok.cherry/trunk/grok/bootstrap/bootstrap.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/bootstrap/bootstrap.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 69908 2006-08-31 21:53:00Z jim $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)

Added: megrok.cherry/trunk/grok/buildout.cfg
===================================================================
--- megrok.cherry/trunk/grok/buildout.cfg	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/buildout.cfg	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,76 @@
+[buildout]
+develop = . grokwiki ldapaddressbook grokblog
+parts = zope3 data instance testdata testinstance test
+
+[zope3]
+recipe = zc.recipe.zope3checkout
+url = svn://svn.zope.org/repos/main/Zope3/branches/3.3
+
+[data]
+recipe = zc.recipe.filestorage
+
+[testdata]
+recipe = zc.recipe.filestorage
+
+[testinstance]
+recipe = zc.recipe.zope3instance
+database = testdata
+user = grok:grok
+eggs = setuptools
+       grok
+zcml = zope.annotation
+       zope.copypastemove
+       zope.formlib
+       zope.i18n-meta
+       zope.i18n.locales
+       zope.publisher
+       zope.security-meta
+       zope.size
+       zope.traversing
+       zope.traversing.browser
+       zope.app    
+       zope.app-meta
+       zope.app.securitypolicy
+       zope.app.securitypolicy-meta
+       zope.app.authentication
+       grok
+       grok-meta
+
+[instance]
+recipe = zc.recipe.zope3instance
+database = data
+user = grok:grok
+eggs = setuptools
+       grok
+       grokwiki
+       grokblog
+
+zcml = zope.annotation
+       zope.copypastemove
+       zope.formlib
+       zope.i18n-meta
+       zope.i18n.locales
+       zope.publisher
+       zope.security-meta
+       zope.size
+       zope.traversing
+       zope.traversing.browser
+       zope.app    
+       zope.app-meta
+       zope.app.securitypolicy
+       zope.app.securitypolicy-meta
+       zope.app.authentication
+       zope.app.twisted
+
+       grok
+       grok-meta
+       grokwiki
+       grokblog
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = grok
+extra-paths = parts/zope3/src
+defaults = ['--tests-pattern', '^f?tests$',
+            '-v'
+           ]

Added: megrok.cherry/trunk/grok/doc/design/adapters.py
===================================================================
--- megrok.cherry/trunk/grok/doc/design/adapters.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/adapters.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,26 @@
+import grok
+from zope.publisher.interfaces.browser import IBrowserRequest, IBrowserView
+from zope.contentprovider.interfaces import IContentProvider
+from calc import Calculator
+
+class SingleAdapter(grok.Adapter):
+    grok.context(Calculator)
+    grok.adapts(Calculator)  # generally allowed, but not in this case, because there's already grok.context
+    grok.implements(ISomething)  # if this is not specified, app breaks
+    grok.name('')  # this is actually the default
+    grok.register()  # this is actually the default
+
+    def something(self):
+        """self.context is automatically provided"""
+        return self.context.foo
+
+class CalculatorContentProvider(grok.MultiAdapter)
+    grok.adapts(Calculator, IBrowserRequest, IBrowserView)
+    grok.implements(IContentProvider)
+
+    def __init__(self, context, request, view):
+        self.context = context
+        self.request = request
+        self.view = view
+
+    # ...

Added: megrok.cherry/trunk/grok/doc/design/annotations.py
===================================================================
--- megrok.cherry/trunk/grok/doc/design/annotations.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/annotations.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,18 @@
+import grok
+
+class Article(grok.Model):
+    pass
+
+class Comments(grok.Annotation):
+    grok.context(Article)  # this is actually the default
+    grok.implements(IComments)
+
+    def __init__(self): 
+        # XXX need super?!?
+        self.comments = OOTreeSet()
+
+    def addComment(self, text):
+        self.comments.insert(text)
+
+    def getComments(self):
+        return list(self.comments)

Added: megrok.cherry/trunk/grok/doc/design/container.py
===================================================================
--- megrok.cherry/trunk/grok/doc/design/container.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/container.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,8 @@
+import grok
+
+class Contact(grok.Model):
+    pass
+
+class AddressBook(grok.App, grok.Container):
+    pass
+

Added: megrok.cherry/trunk/grok/doc/design/grok_beginner.txt
===================================================================
--- megrok.cherry/trunk/grok/doc/design/grok_beginner.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/grok_beginner.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,404 @@
+==========================
+Grok: Making Zope 3 Easier
+==========================
+
+Publishing web pages
+====================
+
+You want to publish a web site that is in a directory. This includes
+any pictures and javascript and CSS you may have in that directory::
+
+  import grok
+
+  class Website(grok.View):
+      grok.files('mydirectory')
+
+XXX use htdocs here, familiar to some people
+
+Once you do this, you can browse into these from the Zope 3 root site.
+Files and assets in any subdirectories will also be published. (XXX
+how?)
+
+Dynamic pages
+=============
+
+Often you want your web pages not to be static but *dynamic*. A web
+page is dynamic when, before the web page is served to your user for
+display in a browser, you want to generate part or all of the
+information of the web page automatically. 
+
+Zope reads your HTML files as Zope Page Templates (ZPT), meaning you
+can use TAL (Template Attribute Language) to script your
+templates. TAL is added to HTML as attributes, meaning your HTML still
+looks very familiar. For an example of very simple TAL, consider this
+HTML snippet::
+
+  <p tal:content="python:1 + 1">Result here</p>
+
+This will generate the following::
+
+  <p>2</p>
+
+This means that the result of the Python expression `1 + 1` is
+dynamically inserted as the content of this `p` tag, replacing what
+was already there.
+
+You can add TAL to any of your HTML pages to make them dynamic.
+
+For more about TAL see XXX (TAL tutorial in here?)
+
+Using Python
+============
+
+Just TAL by itself, even when you use `python:` to embed snippets of
+Python, is limited. The idea of good application design is to use TAL
+just for fairly simple templating purposes, and to do anything a bit
+more complicated in Python code. Using TAL with Python code is easy:
+you just add methods to your view class and use them from your
+template. 
+
+Here we add a method that formats the current date and time::
+
+  from zope import grok
+  from datetime import datetime
+
+  class Website(grok.View):
+      grok.files('mydirectory')
+      
+      def currentDatetime(self):
+          return datetime.now().strftime('%Y-%m-%d %H:%M')
+
+We can then use this with TAL to display the current date and time::
+
+  <p tal:content="view/currentDatetime">Datetime goes here</p>
+
+All the methods you add to the class of your site are automatically
+available on the special `view` name from within your
+templates. `view` is one of the few names that are available in views
+by default.
+
+Note that we have used TAL's built-in `path` language here; this can
+be used for simple method calls instead of spelling it out explicitly.
+For the same effect, you can also use it using Python directly::
+
+  <p tal:content="python:view.currentDatetime()">Datetime goes here</p>
+
+This can be useful when you want to pass parameters to your methods.
+
+Generating HTML from Python
+===========================
+
+Sometimes you want to generate complicated HTML in Python and then
+include it in an existing web page. For reasons of security against
+cross-site scripting attacks, TAL will automatically escape any HTML
+into `&gt;` and `&lt;`. With the `structure` directive, you can tell
+TAL explicitly to not escape HTML this way, so it is passed literally
+into the template::
+
+  from zope import grok
+
+  class Website(grok.View):
+      grok.files('mydirectory')
+    
+      def someHTML(self):
+           return '<strong>%s</strong>' % (1 + 1)
+
+And then with TAL in one of your templates::
+
+  <p tal:content="structure view/someHTML">Result goes here</p>
+
+Generating the whole page from Python
+=====================================
+
+If for some reason you do not want to use a HTML template but just
+want to generate HTML directly from Python, this is also possible::
+
+  class Foo(grok.View):
+      @grok.page('serverload.html')
+      def serverLoad(self):
+          return '<html>..</html>'
+
+XXX a word on unicode
+
+Simple forms
+============
+
+Typical web applications have forms. Let's make a web form and use
+it. First we'll add the following form to one of our templates::
+
+  <form action="hello.html" method="post">
+    <p>
+    Please type your name: <input type="text" name="name" value="" /><br/>
+    <input type="submit" value="Submit" />
+    </p>
+  </form>
+
+As you can see, this form submits to the template named
+`hello.html`. Create such a template in your site and put the
+following TAL in there::
+
+  <html><body>
+  <p>Hello, <strong tal:content="request/form/name">name</strong></p>
+  </body></html>
+
+Now when you go to the form and submit it with the name `John`, you
+should see a web page that says:
+
+  Hello **John**
+
+Simple forms with Python
+========================
+
+Let's make a simple calculator to demonstrate how we combine this with
+Python::
+
+  <form action="sum.html" method="post">
+    <p>
+    First value: <input type="text" name="first" value="" /><br />
+    Second value: <input type="text" name="second" value="" /><br />
+    <input type="submit" value="Get the sum" />
+    </p>
+  </form>
+
+And create the following template `sum.html`::
+
+  <html><body>
+  <p>The sum is: <strong tal:content="data/sum">the sum goes here</strong></p>
+  </body></html>
+
+We've referred to a method `sum` here that does not exist yet, so let's
+implement it. It takes the raw values in the request and adds them to
+together, returning the sum::
+
+  from zope import grok
+
+  from ... import TemplateFile
+
+  class Website(grok.View):
+      grok.files('mydirectory')
+
+      @grok.page('sum.html')
+      def sum(self):
+          # before
+          self.sendEmail()
+          # call template (pull calculateSum2)
+          result = self.renderTemplate('sum.html', sum=self.calculateSum())
+          # post processing
+          result = result.replace('foo', 'bar')
+          return result
+
+
+      def sum(self):
+          self.before()
+          result = self.render('sum.html', **self.push())
+          result = self.after(result)
+          return result
+
+      def before(self):
+          pass
+
+class Website(grok.View):
+      grok.files('mydirectory')
+  
+      class sum(object):
+          """corresponds to sum.html"""
+ 
+          @grok.before()
+          def sendEmail(self):
+            ...
+
+          @grok.after()
+          def barify(self, result):
+              return result.replace('foo', 'bar')
+
+          def calculateSum(self):
+              return ...
+
+          def sum2(self):
+              return ...
+
+      class fancysum(sum):
+          """corresponds to fancysum.html"""
+
+           def calculateSum(self):
+                ...
+
+     
+      @grok.data('sum.html', 'sum')      
+      def sum(self):
+          # get hold of the form, from the request object that
+          # we can get from self
+          form = self.request.form
+          # now get first and second from the form; if no value found
+          # assume it's 0
+          first = form.get('first', 0)
+          second = form.get('second', 0)
+          # convert the input which was text to the numbers
+          # note that we don't handle any errors yet in case someone fills in
+          # something that's not a number
+          first = int(first)
+          second = int(second)
+          # now add the numbers and return this result
+          return first + second
+
+      @grok.page('sum.html')
+      def sum(self):
+          return "<html>...</html>"
+
+
+Form side effects
+=================
+
+Often you don't just want to see a result page when a user submits a
+form, but you want the let the system do some work just before we show
+the result. An example of this is to send an email. Another common
+example is to store the data that's in the form in a database
+somewhere. 
+
+This can be easily accomplished using the `@grok.before` decorator,
+which allows us to execute some Python code just before the template
+is rendered::
+
+  class Website(grok.View):
+      grok.templates('mydirectory')
+    
+      @grok.before('email_sent.html')
+      def email(self):
+          ... send the email XXX ... 
+
+
+Storing data
+============
+
+Instead of emailing the data, what if we wanted to record what the
+user entered instead? Zope offers a number of options for storing
+data, such as making a connection to relational databases (XXX which
+we'll handle later?), but Zope also ships with a powerful database of
+its own: the Zope Object Database (ZODB).
+
+The ZODB is a database of Python objects. You can store any Python object
+in it, and there are just a few things you need to be aware of initially:
+
+* You should subclass your own data classes from persistent.Persistent
+  so it's easy to store them in the ZODB.
+
+* To make sure the ZODB knows you changed a mutable attribute in the
+  instance, set the special `_p_changed` attribute on that instance to
+  `True`. This is only necessary if that attribute is not `Persistent`
+  itself. It's not necessary when you assign to an attribute directly
+  using `=`.
+
+This may sound complicated but it's not. Don't worry for now - most
+things work as normal in Python, and the rules just described are not
+difficult.
+
+So how do we get a space in the database to store our data in?
+`zope.grok` supports a special area where you can store your data.
+
+To get to the database in Python code, call `grok.database()`. This
+gives us access to a dictionary-like object, in which we can store our
+own sub objects::
+
+  from zope import grok
+  from persistent import Persistent
+
+  class NamesStorage(Persistent):
+     def __init__(self):
+        self.names = []
+
+    def addName(self, name):
+        self.names.append(name)
+        self._p_changed = True
+
+  class Website(grok.View):
+     grok.templates('mydirectory')
+   
+     def getNamesStorage(self):
+         """Get the names storage, or create it if it's not there yet.
+         """
+         db = grok.database()
+         storage = db.get('myapp.names_storage')
+         if storage is None:
+             db['myapp.names_storage'] = NamesStorage()
+         return storage
+
+     def storeName(self):
+         """
+         Retrieve the name from request and store it in
+         the names section.
+         """
+         storage = self.getNamesStorage()
+         storage.addName(self.request['name'])
+         
+XXX should following be separate section?
+
+Note that storing names in a list is not very efficient if the list of
+names grows to a larger size, as the object database will need to load
+the whole list of names into memory each time it is changed. We can
+avoid this using BTrees. XXX BTree explanation
+
+XXX showing the names in a web page
+
+Self-posting forms 
+==================
+
+It's a good design for many forms to be *self-posting*, that is, the
+result of a form submission shows the original form again. This way,
+mistakes by the user can be easily shown in the context of the form,
+and the user can correct them. When the form submission does succeed,
+the user is commonly redirected to another page.
+
+Let's first make a form that posts to itself, we'll call it just
+`form.html`::
+
+  <html><body>
+    <form action="." method="post">
+      <input type="text" name="number" value="" />
+    </form>
+  </body></html>
+
+We expect the user to enter an integer number, and we also want it the
+number to be required. Only if those conditions are true 
+....
+
+XXX can't use . perhaps, need to do absolute trick and explain it: request/URL
+
+n. .. self posting form ..
+
+Powerful forms using formlib
+============================
+
+class Foo(grok.View):
+
+    class Form(grok.Form):
+       grok.name('entry.html')
+       # would really be step 7 to control the template
+       # that renders the form. (this template needs to be
+       # moved)
+       template = grok.Template('entry.html')
+       
+       form_fields = grok.Fields(
+          name=schema.TextLine(title='Name'),
+          )
+
+       @grok.action('Submit')
+       def handle_submit(self, action, data):
+           .. send email ..
+           self.message = "The mail has successfully been sent."
+
+
+
+class MeetingView(grok.view):
+
+    grok.context(IMeeting, id=getfromrequestpath)
+
+n. You want to associate your application to a specific site
+
+n. You want to create a site content object
+
+n. You want to create a content object and associate views to that.
+
+n. You want to index content objects to search them.
+
+
+Some basic explanation of what's going on with unicode in Zope 3.

Added: megrok.cherry/trunk/grok/doc/design/grok_dev.txt
===================================================================
--- megrok.cherry/trunk/grok/doc/design/grok_dev.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/grok_dev.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,387 @@
+==========================
+Grok: Making Zope 3 Easier
+==========================
+
+Introduction
+============
+
+The Grok project tries to it easier to develop Zope 3
+applications. Zope 3 is typically configured using ZCML, the Zope
+Configuration Markup Language. ZCML is non-Python and very explicit.
+
+ZCML was in part inspired by the difficulties surrounding
+configuration in Zope 2. Zope 2 is heavy on implicit configuration
+("no docstring means no public access", "a permission used is a
+permission defined"). In addition, lots of the configuration happens
+in Python code, such as in the `__init__.py` of a product. These
+techniques can lead to problems: hard to understand configuration code
+interweaved with non-configuration code. Such code tends to be
+difficult to maintain, extend, and evolve.
+
+ZCML aims to separate configuration strongly from software to avoid
+these problems. The ZCML model in combination with the notion of
+explicit interfaces offers a strong model for evolving software over a
+longer period of time.
+
+ZCML has drawbacks however. Zope 3's setup for modularity, components
+and evolution results in more entities for a programmer to worry
+about: explicit interfaces, a multiplication of files and modules, a
+large list of places to import from, a series of ZCML directives to
+remember. Even though individual entities may be easier to understand,
+the combination of such can make the whole more difficult to
+understand, and there is just a lot to learn overall.
+
+This is especially off-putting to developers who are trying to start
+learning Zope 3. Even experienced programmers can be frustrated by so
+much need to state obvious things explicitly - the multiplication of
+entities in Zope 3 hurts agility of development. Finally, ZCML can be
+intimidating to a Python programmer merely by being non-Python, and
+XML in particular. While the latter is at least in part a superficial
+concern, it is one that stops people from trying Zope 3.
+
+Grok aims to reduce the visibility of ZCML to the Zope 3
+programmer. Grok also aims to reduce the entities a programmer needs
+to worry about: ideally a Zope 3 application should be able to fit
+completely inside a single module. Grok does not aim to do away with
+ZCML altogether: explicit configuration is valuable. Grok merely aims
+to keep the advanced concepts out of the programmer's face when not
+needed.
+
+How does Grok aim accomplish these goals? We will try to follow the
+guidelines that work for Ruby on Rails:
+
+* DRY: Don't Repeat Yourself
+
+* Convention over configuration
+
+Grok is specified as autogenerated ZCML. This is for convenience of
+specification: Grok does not need to be implemented this way but could
+be calling underlying component architecture APIs directly where
+needed. In fact, one of the aims of Grok is to produce more readable
+errors that guide programmers into the right direction to solve their
+problem.
+
+Grok does not aim to make the programmer do correct security - an
+application written with Grok without the programmer taking any
+particular actions will be entirely public by default. Convenience of
+development trumps security here. The idea of Grok is that the
+programmer should have the easiest time possible to get an application
+running first, and then can gradually tighten the application in
+various ways, including security, later on.
+
+Views
+-----
+
+To declare a browser view, a new class-level declaration `views` is
+added, with as argument the class or interface the view is being
+declared for::
+
+  from zope import grok
+
+  class FooView(grok.View):
+     grok.views(Foo)
+
+As you can see, Grok views are inherited from `grok.View`.
+
+This is equivalent to the following ZCML::
+
+  <browser:page
+    name="FooView"
+    for="Foo"
+    class="FooView" 
+    permission="zope.Public"
+    />
+
+Note that the name is deduced entirely from the name of the view
+class. Where this is not desired, the view name can be controlled
+using the `name` declaration on the view:
+
+  from zope import grok
+
+  class FooView(grok.View):
+      grok.name("info.html")
+      grok.views(Foo)
+
+      @grok.page('info.info')
+      ...
+
+which translates to::
+
+  <browser:page
+     name="info.html"
+     for="Foo"
+     class="FooView"
+     permission="zope.Public"
+   />
+
+It's also possible to set the skin the view is in using ``skin``
+declaration::
+
+  from zope import grok
+  from myskin import IMySkin
+
+  class FooView(grok.View):
+     grok.skin(IMySkin)
+
+XXX can we somehow stop the concept of interface from being introduced
+here? Class-based skins?
+
+Views that do not specify *any* declarations but inherit from
+grok.View are also automatically registered, for everything (`*`)::
+
+  from zope import grok
+
+  class MyView(grok.View):
+     def getData(self):
+         return fadfkjdlkfjd
+     __call__ = template('foo.pt')
+
+is equivalent to the following ZCML::
+
+  <browser:page
+     name="MyView"
+     for="*"
+     class="MyView"
+     permission="zope.Public"
+     />
+
+View Security 
+-------------
+
+The default permission on views generated by Grok is `zope.Public`. At
+first glance, this seems to be going back to the bad old insecure days
+of Zope 2. Zope 3 has a different security model though: content
+objects can declare their own security. Since Grok declares content
+objects entirely public by default as well however, that is not a very
+strong argument.
+
+The more important argument for this "wide-open" default is that
+dealing with security issues early on in application development tends
+to be very painful and distracting for most application developers,
+breaking their flow. The temptation is therefore large to just give
+any permissions that will permit the developer to continue. Security
+issues are typically ignored until a later stage of development. We
+recognize this pattern of development, and we can't really enforce
+developers doing security right anyway, so Grok doesn't aim to try.
+
+Of course it is possible to override the default, using a class
+declaration on the view called `permission`::
+
+  from zope import grok
+
+  class MyView:
+     grok.views(SomeContent)
+     grok.permission('zope.ManageContent')
+
+Non-existing permissions are not created automatically - new
+permissions have to be declared explicitly, using ZCML.
+
+Finally, Grok also offers a "security testing" mode, where the default
+permission of all views in a package is specified by the
+developer. This can be used at a later stage during development to
+flush out any security problems.
+
+XXX explain spelling for that
+
+Class security
+--------------
+
+Attributes, both reading and writing on content are protected with the
+`zope.Public` permission by default. The aim of Grok is to get out of
+the programmers hair.
+
+How does Grok determine that something is a content class and thus
+should have its attributes made public?
+
+Grok determines something is a content class if:
+
+* it has views that directly view it
+
+* it is a subclass of a class that has views
+
+* it has an interface that has views registered for it
+
+For the purposes of security, views registered for `*` do *not* count
+as views registered for a class or interface.
+
+To restrict the permission of a content class explicitly, the same
+`permission` class declaration can be used as the one defined for
+views. This declaration sets the default access permission for *all*
+attributes of the class::
+
+  from zope import grok
+
+  class FooContent:
+     grok.permission('zope.ManageContent')
+
+It is often desirable to make an exception for some attributes, however::
+
+  from zope ipmort grok
+
+  class FooContent:
+     grok.permission('myapp.View')
+
+     @@grok.protected('myapp.Edit')
+     def protectedMethod(self):
+         pass
+
+     @@grok.private()
+     def privateMethod(self):
+         pass
+
+     def _alsoPrivate():
+         pass
+
+As soon as specific declarations are used to restrict the access to
+various methods, the default view permission of `zope.Public` does not
+apply anymore for that class.
+
+Importing 
+---------
+
+As you've seen from the examples, every import in a simple Grok
+application will be from the `zope.grok` package. This means that the
+developer does not need to remember a complicated set of imports from
+a wide-ranging set of packages just to build a simple package.
+
+This is the opposite of the current trend in Zope 3 development:
+imports from `zope.app.zapi`, intended to be an easy location to get
+important stuff are replaced with the imports from the real location.
+
+`zope.app.zapi` as an approach to simplify things failed for a number
+of reasons: it not very coherent, so hard to predict what ended up
+there, and it was incomplete: to build a real Zope 3 application you
+always needed to import from places beyond `zope.app.zapi`. In
+contrast, `zope.grok` aims to provide a relatively small number of
+names to use, and that should be *all* needed to produce a simple
+`zope.grok`-based application.
+
+Of course as a grok-based application grows developers will find
+themselves importing from other package. This should be a voyage of
+gradual discovery for the developer. There should be no need to go the
+whole way right away.
+
+XXX possible exceptions: zope.schema, zope.formlib
+
+Schema-classes
+--------------
+
+It is possible to define the schema of a class directly::
+
+  from zope import grok
+
+  class MyContent(grok.Content):
+     class data:
+         foo = TextLine(u'Foo')
+
+Fields defined are automatically created as attributes on instances
+using the defaults specified by the fields (the default default if no
+default is specified).
+
+XXX schema really should accept pure ascii strings in all cases
+    instead of unicode strings, and not *require* a u'' in front of
+    strings and give an obscure error. If you need non-ascii, *then*
+    you worry about unicode. Yet another thing less to explain to
+    beginners however.
+
+XXX we cannot use the name `schema` for the schema inner class as that
+    would conflict with `from zope import schema`...
+
+By default the schema is readable and writeable by all.
+
+The schema read and write permissions for schema fields can be set
+using special class declarations::
+
+  from zope import grok
+
+  class MyContent(grok.Content):
+     class data:
+        read_permission('myapp.View')
+        write_permission('myapp.Edit')
+
+        foo = TextLine(u'Foo')
+
+XXX want to handle the usecase of a class with multiple related
+    schemas that have an inheritance relationship to each other?
+
+No interfaces necessary
+-----------------------
+
+A Grok application can be written without a single interface in
+sight. No interfaces means less entities for the developer to care
+about, and the goal of grok is to avoid having to worry about
+interfaces when building an application. Interfaces can always be
+added later during the evolution of an application.
+
+XXX exception for skins at present..
+
+As an application grows a developer can start introducing interfaces:
+grok does not *stop* the use of interfaces.
+
+Adapters
+--------
+
+XXX is this something to be treated by grok or is this an "advanced
+topic" outside the purview of grok? It should be easy to recognize
+adapters that use `adapts()` and automatically register them.
+
+Forms
+-----
+
+Let's see how `zope.formlib` fits in with grok::
+
+  from zope import schema
+  from zope import grok
+
+  class MyContent(grok.Content):
+     class data:
+         name = schema.TextLine("Name")
+         age = schema.Int("Age")
+
+  class MyContentView(grok.EditForm):
+     grok.name('edit.html')
+     grok.views(MyContent)
+
+     # XXX can it be automatically deduced where the fields come from
+     # at this point?
+     form_fields = grok.Fields()
+
+     grok.action('Ok')
+     def handle_input(self, action, data):
+        pass
+
+XXX should be really be covering up formlib.form? ``grok.Fields()``
+    seems to imply we might have to.
+
+Templates
+---------
+
+This sets up a template to use::
+
+  from zope import grok
+
+  class MyView(grok.View):
+     grok.template('mytemplate.pt')
+
+XXX Sensible default behavior:
+
+  * use template for __call__ if no __call__ defined
+
+  * template/update pattern?
+
+Menus
+-----
+
+XXX How to make things show up in menus? When we create a new content
+object we at least want an easy way to create it, though not
+necessarily through the ZMI. Menu class declaration?
+
+TDB
+---
+
+* Easy way to generate add views
+
+* easy local utility registration?
+
+* bread - easy creation of tables, catalog-based search
+

Added: megrok.cherry/trunk/grok/doc/design/model.py
===================================================================
--- megrok.cherry/trunk/grok/doc/design/model.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/model.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,56 @@
+"""
+Schema-driven development with grok
+"""
+import grok
+
+grok.directory('contact')
+
+class Contact(grok.SchemaModel):
+    """
+    This works now:
+
+      >>> c = Contact(name=u'Martijn', city=u'Rotterdam')
+      >>> c.name
+      u'Martijn'
+
+    Also w/o kw:
+
+      >>> c = Contact(u'Martijn', u'Rotterdam')
+      >>> c.name
+      u'Martijn'
+
+    """
+    class fields:
+        name = schema.TextLine()
+        city = schema.TextLine()
+
+
+class Edit(grok.EditForm):
+    pass
+    # this will automatically render an edit form, and use an
+    # 'edit.html' template if available
+
+class Index(grok.DisplayForm):
+    pass
+    # this will automatically render an display form, and use an
+    # 'index.html' template if available
+
+class Add(grok.AddForm):
+    grok.context(Contact)  # this is actually the default
+    grok.container(IContainer)  # this is actually the default
+
+class FancyEdit(grok.EditForm):
+    """ use cases:
+
+    * (actions with permissions)
+
+    *
+
+    """
+
+    @grok.action("Save")
+    def save(self, **data):
+        """
+        this overrides any actions defined on the base class
+        """
+        self.applyChanges(data)

Added: megrok.cherry/trunk/grok/doc/design/permission.py
===================================================================
--- megrok.cherry/trunk/grok/doc/design/permission.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/permission.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,36 @@
+import grok
+
+grok.definepermission('grok.Complex')  # define permission first
+
+class Complex(grok.Model):
+    # this sets the default for all methods
+    grok.require('zope.Public')  # this is actually the default
+
+    # this sets the default for reading all attributes that are not methods
+    grok.readable('zope.Public')  # this is actually the default
+
+    # this sets the default for writing all attributes that are not methods
+    grok.writable('zope.Public')  # this is actually the default
+
+    # this overrides the above
+    grok.readable('grok.Complex', 'attr1') # override default
+    grok.readable('zope.ManageServices', 'attr2') # override default
+    grok.writable('zope.ManageContent', 'attr1', 'attr2') # override default
+
+    def __init__(self):
+        self.attr1 = 1
+        self.attr2 = 2
+
+    @grok.require('some.permission')
+    def doSomethingVerySecret(self):
+        pass
+
+    @grok.require('zope.Public')  # this is actually the default
+    def imPublic(self):
+        pass
+
+class SubClass(Complex):
+    # it's all inherited
+
+    grok.readable('zope.Public', 'attr1')
+

Added: megrok.cherry/trunk/grok/doc/design/skin-minimal.py
===================================================================
--- megrok.cherry/trunk/grok/doc/design/skin-minimal.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/skin-minimal.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,18 @@
+import grok
+from zope import interface
+
+
+grok.definelayer('my')
+grok.defineskin('my')                 # Picks up the layer 'my' if it exists
+
+grok.layer('my')                      # If there is only a single layer defined
+                                      # in a module, it will be the default
+
+
+class Painting(grok.View):
+    pass
+
+
+fireplace = grok.PageTemplate("""\
+<html><body></body></html>
+""")

Added: megrok.cherry/trunk/grok/doc/design/skin.py
===================================================================
--- megrok.cherry/trunk/grok/doc/design/skin.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/skin.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,25 @@
+import grok
+from zope import interface
+
+
+grok.definelayer('my')
+grok.definelayer('admin')
+
+grok.layer('my')
+
+grok.defineskin('my', ['my'])         # this is the default
+grok.defineskin('my')                 # does the same as the line above
+grok.defineskin('admin', ['admin', 'my'])
+
+
+class Painting(grok.View):
+    pass
+
+
+fireplace = grok.PageTemplate("""\
+<html><body></body></html>
+""")
+
+
+class AdminPainting(grok.View):
+    grok.layer('adminlayer')

Added: megrok.cherry/trunk/grok/doc/design/subscriber.py
===================================================================
--- megrok.cherry/trunk/grok/doc/design/subscriber.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/subscriber.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,11 @@
+from zope.lifecycleevent.interfaces import IObjectModifiedEvent
+from calc import Calculator
+import grok
+
+ at grok.subscriber(Calculator, IObjectModifiedEvent)
+def calculatorChanged(calc, event):
+    pass
+
+
+# perhaps alias zope.event.notify to grok.notify???
+

Added: megrok.cherry/trunk/grok/doc/design/traversal.py
===================================================================
--- megrok.cherry/trunk/grok/doc/design/traversal.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/traversal.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,50 @@
+import grok
+
+class Appointment(grok.Model):
+    pass
+
+class Day(grok.Model):
+
+    def getAppointment(self, number):
+        if number in self.appointments:
+            return Appointment(number)
+        return None # try to look up views then
+
+    def traverse(self, name):
+        return self.getAppointment(int(number))
+    
+class Calendar(grok.Model):
+    def getYear(self, year):
+        return Year(year)
+
+    def traverse(self, name):
+        return self.getYear(int(number))
+
+# interpretation of traverse:
+
+# * do the traverse traversal first
+
+# * if this raises an error, propagate exception, do not swallow it (test)
+
+# * if this returns None, fall back on "normal" traversal for the
+    object (i.e. container traversal)
+
+"""
+http://.../calendar/2006/10/13/1/
+           ^^^^^^^^ ^^^^ ^^ ^^ ^
+            Cal.    Year  ...  A.
+"""
+
+#XXX routes (http://routes.groovie.org/) for advanced cases
+
+
+# instead of traverser on the model, you can also write a separate
+# traverser component:
+
+class CalendarTraverser(grok.Traverser):
+    grok.context(Calendar)  # this is actually the default
+    grok.register(site=CalendarApp)  #...
+
+    def traverse(self, name):
+        now look up stuff on self.context with 'name'...
+        return that

Added: megrok.cherry/trunk/grok/doc/design/utility.py
===================================================================
--- megrok.cherry/trunk/grok/doc/design/utility.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/utility.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,14 @@
+import grok
+
+class CalcApp(grok.App):
+    """calculator model that's also a site
+
+    whenever you create one of those, all local utilities will be
+    registered with it automatically.
+    """
+
+class Calculator(grok.Utility):
+    grok.implements(ICalculator)  # if this is not specified, it breaks
+    grok.name('')  # this is actually the default
+    grok.register(site=CalcApp)  # register this only in calculator app sites
+

Added: megrok.cherry/trunk/grok/doc/design/views.py
===================================================================
--- megrok.cherry/trunk/grok/doc/design/views.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/design/views.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,93 @@
+import grok
+
+grok.resources('calc')  # this is actually the default (from module name, calc.py)
+
+class Calculator(grok.Model):
+    pass
+
+class Sum(grok.View):
+    """Simple view for a model"""
+    grok.context(Calculator)  # this is actually the default (from module)
+    grok.template('sum.html')  # this is actually the default (from class name)
+    grok.name('sum')  # this is actually the default (from class name)
+    grok.require('zope.Public')  # this is actually the default   #XXX protect???
+
+    def calculateSum(self):
+        """you can pull this in the template through view/calculateSum"""
+    
+    @grok.before
+    def precalculatedSum(self):
+        """executed before the template is rendered"""
+        self.sum = self.calculateSum()
+
+    @grok.before
+    def sendEmail(self):
+        """send an email here"""
+
+    @grok.before
+    def afterSendingAnEmail(self):
+        """this is also executed before the template is rendered, but
+        since it's defined after sendEmail, it's also executed after
+        sendEmail."""
+
+class PDFSum(grok.View):
+
+    @grok.before
+    def dosomething(self):
+        pass
+
+    def render(self):
+        return pdfdata
+
+
+sum = grok.PageTemplate("""\
+<p tal:content="view/calculateSum">...</p>
+<p tal:content="view/precalculatedSum">...</p>
+""")
+
+from zope import schema
+
+class Index(grok.Form):
+    """a form
+
+    this is the default view for the Calculator model (because it's
+    called Index)
+    """
+
+    class fields:
+        operand = schema.Int(title=u'Operand')
+        operator = schema.Choice(...)
+        operand2 = schema.Int(...)
+
+    @grok.action('Calculate')
+    def calculate(self, operand, operator, operand2):
+        """it's possible to receive any number of form fields in any
+        order, or just use **kw to receive them all in a dict"""
+        self.result = operator(operand, operand2)
+
+    # this will raise a helpful error message at startup time (encoded
+    # strings)
+    @grok.action('Bääää')
+    def whatever(self, **data):
+        pass
+
+index = grok.PageTemplate("""\
+<form tal:attributes="action request/URL">
+<p tal:condition="exists:view/result">
+  The result is: <span tal:replace="view/result" />
+</p>
+
+XXX render fields
+XXX render actions
+</form>
+""")
+
+class CalculatorXMLRPC(grok.XMLRPC):
+
+    @grok.require('zope.Public')  # this is actually the default
+    def sum(self, operand, operator, operand2):
+        return ...
+
+    @grok.require('something.else')
+    def whatever(self):
+        return ...

Added: megrok.cherry/trunk/grok/doc/reference/README.txt
===================================================================
--- megrok.cherry/trunk/grok/doc/reference/README.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/reference/README.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,24 @@
+=========================
+The grok reference manual
+=========================
+
+The manual is written using LaTeX with support for the Python documentation
+markup. The tex sources can be compiled to HTML and PDF. To build the manual,
+you need the 'mkhowto' script from a recent Python source distribution.
+
+Build the HTML
+--------------
+
+  $ mkhowto --html reference.tex
+
+The directory 'reference/' keeps all files required to display the manual after
+that call and can be put on a static webserver.
+
+Build the PDF
+-------------
+
+The file 'reference.pdf' will contain the PDF version of the manual after this
+call:
+
+  $ mkhowto --html reference.tex
+

Added: megrok.cherry/trunk/grok/doc/reference/components.tex
===================================================================
--- megrok.cherry/trunk/grok/doc/reference/components.tex	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/reference/components.tex	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,22 @@
+\chapter{Components}
+
+The \module{grok} module defines a set of components that provide basic Zope 3
+functionality in a convenient way.
+
+    \section{grok.Model}
+
+    \section{grok.Container}
+
+    \section{grok.Adapter}
+
+    \section{grok.MultiAdapter}
+
+    \section{grok.Utility}
+
+    \section{grok.View}
+
+    \section{grok.XMLRPC}
+
+    \section{grok.Traverser}
+
+    \section{grok.EditForm}

Added: megrok.cherry/trunk/grok/doc/reference/core.tex
===================================================================
--- megrok.cherry/trunk/grok/doc/reference/core.tex	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/reference/core.tex	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,6 @@
+\chapter{Core}
+
+The \module{grok} module defines a few functions to interact with grok itself.
+
+    \section{grok.grok}
+

Added: megrok.cherry/trunk/grok/doc/reference/decorators.tex
===================================================================
--- megrok.cherry/trunk/grok/doc/reference/decorators.tex	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/reference/decorators.tex	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,23 @@
+\chapter{Decorators}
+
+grok uses a few decorators to register functions or methods for specific
+functionality.
+
+    \section{\function{grok.subscribe} -- Register a function as a subscriber
+    for an event}
+
+
+        \begin{funcdesc}{subscribe}{*classes_or_interfaces}
+
+        Declare that the decorated function subscribes to an event or a
+        combination of objects and events. (Similar to Zope 3's
+        \function{subscriber} decorator.)
+
+        Applicable on module-level for functions. Requires at least one class
+        or interface as argument.
+
+        \end{funcdesc}
+
+
+    \section{grok.action}
+

Added: megrok.cherry/trunk/grok/doc/reference/directives.tex
===================================================================
--- megrok.cherry/trunk/grok/doc/reference/directives.tex	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/reference/directives.tex	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,23 @@
+\chapter{Directives}
+
+The \module{grok} module defines a set of directives that allow you to
+configure and register your components. Most directives assume a default, based
+on the environment of a module. (For example, a view will be automatically
+associated with a model if the association can be made unambigously.) 
+
+If no default can be assumed for a value, grok will explicitly tell you what is
+missing and how you can provide a default or explicit assignment for the value
+in question.
+
+    \section{grok.implements}
+
+    \section{grok.context}
+
+    \section{grok.name}
+
+    \section{grok.template}
+
+    \section{grok.templatedir}
+
+    \section{grok.templatedir}
+

Added: megrok.cherry/trunk/grok/doc/reference/events.tex
===================================================================
--- megrok.cherry/trunk/grok/doc/reference/events.tex	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/reference/events.tex	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,20 @@
+\chapter{Events}
+
+grok provides convenient access to a set of often-used events from Zope 3.
+Those events include object and containment events. All events are available as
+interface and implemented class.
+
+    \section{grok.IObjectCreatedEvent}
+
+    \section{grok.IObjectModifiedEvent}
+
+    \section{grok.IObjectCopiedEvent}
+
+    \section{grok.IObjectAddedEvent}
+
+    \section{grok.IObjectMovedEvent}
+
+    \section{grok.IObjectRemovedEvent}
+
+    \section{grok.IContainerModifiedEvent}
+

Added: megrok.cherry/trunk/grok/doc/reference/exceptions.tex
===================================================================
--- megrok.cherry/trunk/grok/doc/reference/exceptions.tex	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/reference/exceptions.tex	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,49 @@
+\chapter{Exceptions}
+
+grok tries to inform you about errors early and with as much guidance as
+possible. grok can detect some errors already while importing a module, which
+will lead to the \class{GrokImportError}.  Other errors require more context
+and can only be detected while executing the \function{grok} function.
+
+    \section{\class{grok.GrokImportError} -- errors while importing a module}
+
+    This exception is raised if a grok-specific problem was found while
+    importing a module of your application. \class{GrokImportError} means there
+    was a problem in how you are using a part of grok. The error message tries
+    to be as informative as possible tell you why something went wrong and how
+    you can fix it.
+
+    \class{GrokImportError} is a subclass of Python's \class{ImportError}.
+
+    Examples of situations in which a GrokImportError occurs:
+
+    \begin{itemize}
+        \item Using a directive in the wrong context (e.g. grok.templatedir on
+        class-level instead of module-level.)
+        \item Using a decorator with wrong arguments (e.g. grok.subscribe
+        without any argument)
+        \item \ldots
+    \end{itemize}
+
+    \section{\class{grok.GrokError} -- errors while grokking a module}
+
+    This exception is raised if an error occurs while grokking a module.
+
+    Typically a \class{GrokError} will be raised if one of your modules uses a
+    feature of grok that requires some sort of unambigous context to establish
+    a reasonable default.
+
+    For example, the \class{grok.View} requires exactly one model to be defined
+    locally in the module to assume a default module to be associated with.
+    Having no model defined, or more than one model, will lead to an error
+    because the context is either underspecified or ambigous.
+
+    The error message of a \class{GrokError} will include the reason for the
+    error, the place in your code that triggered the error, and a hint, to help
+    you fix the error.
+
+    \begin{classdesc}{GrokError}{Exception}
+        \begin{memberdesc}{component}
+            The component that was grokked and triggered the error.
+        \end{memberdesc}
+    \end{classdesc}

Added: megrok.cherry/trunk/grok/doc/reference/model.tex
===================================================================
--- megrok.cherry/trunk/grok/doc/reference/model.tex	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/reference/model.tex	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,3 @@
+\chapter{grok.Model}
+
+  \section{Models}

Added: megrok.cherry/trunk/grok/doc/reference/reference.tex
===================================================================
--- megrok.cherry/trunk/grok/doc/reference/reference.tex	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/reference/reference.tex	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,89 @@
+% Complete documentation on the extended LaTeX markup used for Python
+% documentation is available in ``Documenting Python'', which is part
+% of the standard documentation for Python.  It may be found online
+% at:
+%
+%     http://www.python.org/doc/current/doc/doc.html
+
+\documentclass{manual}
+\RequirePackage[latin9]{inputenc}
+\usepackage{graphicx}
+
+\title{grok reference}
+
+% Please at least include a long-lived email address;
+% the rest is at your discretion.
+\authoraddress{
+    The grok team\\
+    Email: <grok-dev at zope.org>
+}
+
+\date{\today}   % update before release!
+                % Use an explicit date so that reformatting
+                % doesn't cause a new date to be used.  Setting
+                % the date to \today can be used during draft
+                % stages to make it easier to handle versions.
+
+\release{unreleased}      % release version; this is used to define the
+                          % \version macro
+
+\makeindex          % tell \index to actually write the .idx file
+
+\begin{document}
+
+\maketitle
+
+% This makes the contents more accessible from the front page of the HTML.
+\ifhtml
+\chapter*{Front matter\label{front}}
+\fi
+
+\copyright 2006 gocept gmbh \& co. kg, Martijn Faassen and Philipp von
+Weitershausen
+
+Licensed under the Zope Public License, Version 2.1 (the ``License''); you may
+not use this file except in compliance with the License. You may obtain a copy
+of the License at \url{http://www.zope.org/Resources/ZPL}.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+\begin{abstract}
+
+\noindent
+
+Grok: now even cavemen can use Zope 3!
+
+\begin{notice}
+``Grok means to understand so thoroughly that the observer becomes a part of the
+observed --- merge, blend, intermarry, lose identity in group experience.
+It means almost everything that we mean by religion, philosophy, and
+science --- it means as little to us (because we are from Earth) as color
+means to a blind man.'' -- Robert A. Heinlein, Stranger in a Strange Land
+\end{notice}
+
+\end{abstract}
+
+\tableofcontents
+
+\include{core}
+
+\include{components}
+
+\include{directives}
+
+\include{decorators}
+
+\include{events}
+
+\include{exceptions}
+
+\end{document}

Added: megrok.cherry/trunk/grok/doc/tutorial.txt
===================================================================
--- megrok.cherry/trunk/grok/doc/tutorial.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/doc/tutorial.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,84 @@
+Grok tutorial
+=============
+
+Welcome to the Grok tutorial! Grok is an extension to Zope 3 that
+makes it easier and quicker to develop web applications for Zope 3,
+*without* losing the power and flexibility of Zope 3 itself. Grok
+builds on existing Zope 3 technology like the component architecture
+but exposes them in a different way to the developer.
+
+In this tutorial, we will be writing a simple wiki. We'll lead you all
+the way from the start, setting up Grok on your computer, to a simple
+but complete wiki implementation. All you're expected to know is the
+Python programming language and a basic understanding of web
+programming.
+
+Setting up Grok
+---------------
+
+In order to set up Grok so you can start development, you need a
+computer that is connected to the internet and the grokstart_
+download. Unpack grokstart_ somewhere. It will result in a directory
+called 'grokstart'. Then inside that directory, run the following with
+a Python 2.4 interpreter::
+
+  $ python bootstrap.py
+ 
+This will bootstrap the "buildout" system, which then can be used to
+install grok. Still in the same directory, run the following command:
+
+  $ bin/buildout
+
+Now you have to wait a while as the system downloads grok, Zope 3, and
+any other dependencies from the internet and installs them.
+
+Grok, along with Zope 3 is now ready to go. A Zope 3 instance has been
+installed in the 'parts/instance' directory. Of course it doesn't
+contain any Grok applications yet - we're to write one in this
+tutorial.
+
+What you've just used is a technology called Buildout. Buildout is not
+Zope 3 specific. It is a generic system that builds heavily on
+existing Python technology like setuptools and eggs. Buildout goes
+beyond distribution of libraries and applications; it allows the
+installation of servers, C libraries and much more.
+
+Note: it may be that you have an older version of setuptools installed
+on your system. bootstrap.py will complain in this case. You can
+upgrade to the most recent version of setuptools by running the
+following (with permissions to write to your Python installation, so
+possibly you need to use `sudo` or become root):
+
+  $ easy_install -U setuptools
+
+Note: renaming grokstart to something else.
+
+Creating the tutorial project
+-----------------------------
+
+In order to start writing code with Grok we first need to set up a
+Python project that will contain the code. It's not hard to do by
+hand, but it's a bit of typing, so your buildout has a tool installed
+called `pyprogen` which helps with the generation of a new project.
+Let's run it from the `grokstart` buildout directory::
+
+  $ bin/pyprogen groktut
+
+A new directo
+
+
+A modern Python project
+typically is a directory that contains a `setup.py` to install the
+project using distutils or setuptools and `src` directory that
+contains a Python package (a directory with a file `__init__.py` in
+it. The project directory can also contain things like a `README.txt`,
+`INSTALL.txt` and so on.
+
+Grok comes with a tool to easily create such projects, called
+pyprogen.  pyprogen takes care of creating the right directories and a
+minimal `setup.py`.
+
+
+It also adds the project to the buildout.cfg so
+that it is automatically included in the installation and a testrunner
+is generated.

Added: megrok.cherry/trunk/grok/grokblog/setup.py
===================================================================
--- megrok.cherry/trunk/grok/grokblog/setup.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokblog/setup.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,23 @@
+from setuptools import setup, find_packages
+
+setup(
+    name='grokblog',
+    version='0.1',
+    author='Grok Team',
+    author_email='grok-dev at zope.org',
+    url='http://svn.zope.org/grok/trunk',
+    description="""\
+Grok: the blog
+""",
+    packages=find_packages('src'),
+    package_dir = {'': 'src'},
+    include_package_data = True,
+    zip_safe=False,
+    license='ZPL',
+
+    install_requires=[
+        'grok',
+        'docutils',
+        'setuptools',
+        ],
+)

Added: megrok.cherry/trunk/grok/grokblog/src/grokblog/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/grokblog/src/grokblog/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokblog/src/grokblog/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,2 @@
+#
+

Added: megrok.cherry/trunk/grok/grokblog/src/grokblog/blog.py
===================================================================
--- megrok.cherry/trunk/grok/grokblog/src/grokblog/blog.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokblog/src/grokblog/blog.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,203 @@
+import random
+from datetime import datetime, timedelta
+from docutils.core import publish_parts
+import grok
+
+from zope import interface, schema
+
+class Blog(grok.Container, grok.Site):
+    def __init__(self):
+        super(Blog, self).__init__()
+        self['entries'] = Entries()
+
+    def traverse(self, name):
+        try:
+            year = int(name)
+        except ValueError:
+            return None
+        return Year(year)
+
+class Entries(grok.Container):
+    pass
+
+class IEntry(interface.Interface):
+    published = schema.Datetime(title=u'Published')
+    title = schema.TextLine(title=u'Title')
+    body = schema.Text(title=u'Body')
+
+class Entry(grok.Model):
+    interface.implements(IEntry)
+
+    def __init__(self, title, body):
+        self.title = title
+        self.published = datetime.now()
+        self.body = body
+
+class Year(grok.Model):
+    def __init__(self, year):
+        self.year = year
+
+    def traverse(self, name):
+        try:
+            month = int(name)
+        except ValueError:
+            return None
+        if month < 1 or month > 12:
+            return None
+        return Month(self.year, month)
+
+class YearIndex(grok.View):
+    grok.name('index')
+    grok.context(Year)
+    grok.template('dateindex')
+
+    def entries(self):
+        from_ = datetime(self.context.year, 1, 1)
+        until = datetime(self.context.year + 1, 1, 1)
+        return entriesInDateRange(from_, until)
+
+class Month(grok.Model):
+    def __init__(self, year, month):
+        self.year = year
+        self.month = month
+
+    def traverse(self, name):
+        try:
+            day = int(name)
+        except ValueError:
+            return None
+        # XXX should check whether day is acceptable
+        return Day(self.year, self.month, day)
+
+class MonthIndex(grok.View):
+    grok.name('index')
+    grok.context(Month)
+    grok.template('dateindex')
+
+    def entries(self):
+        from_ = datetime(self.context.year,
+                         self.context.month,
+                         1)
+        month = self.context.month + 1
+        year = self.context.year
+        if month > 12:
+            month = 1
+            year += 1
+        until = datetime(year, month, 1)
+        return entriesInDateRange(from_, until)
+
+class Day(grok.Model):
+    def __init__(self, year, month, day):
+        self.year = year
+        self.month = month
+        self.day = day
+
+class DayIndex(grok.View):
+    grok.name('index')
+    grok.context(Day)
+    grok.template('dateindex')
+
+    def entries(self):
+        from_ = datetime(self.context.year,
+                         self.context.month,
+                         self.context.day)
+        until = from_ + timedelta(days=1)
+        return entriesInDateRange(from_, until)
+
+def entriesInDateRange(from_, until):
+    entries = grok.getSite()['entries']
+    result = []
+    for entry in entries.values():
+        if from_ <= entry.published <= until:
+            result.append(entry)
+    return sorted(
+        result, key=lambda entry: entry.published, reverse=True
+        )
+
+def lastEntries(amount):
+    entries = grok.getSite()['entries'].values()
+    return sorted(
+        entries, key=lambda entry: entry.published, reverse=True
+        )[:amount]
+
+class BlogIndex(grok.View):
+    grok.context(Blog)
+    grok.name('index')
+
+    def entries(self):
+        return lastEntries(10)
+
+    def renderEntry(self, entry):
+        return renderRest(entry.body)
+
+class AddEntry(grok.View):
+    grok.context(Blog)
+    grok.name('add')
+
+    def before(self):
+        id = self.request.form.get('id')
+        if not id:
+            return
+        title = self.request.form.get('title', '')
+        body = self.request.form.get('body', '')
+        self.context['entries'][id] = Entry(title, body)
+        self.redirect(self.url(self.context))
+
+class EntriesIndex(grok.View):
+    grok.context(Entries)
+    grok.name('index')
+
+    def render(self):
+        return "Entries: %s" % ' '.join(self.context.keys())
+
+rest_settings = {'file_insertion_enabled': False}
+
+def renderRest(source):
+    return publish_parts(
+        source, writer_name='html', settings_overrides=rest_settings
+        )['html_body']
+
+class EntryIndex(grok.View):
+    grok.context(IEntry)
+    grok.name('index')
+
+    def before(self):
+        self.body = renderRest(self.context.body)
+
+class EntryEdit(grok.View):
+    grok.context(IEntry)
+    grok.name('edit')
+
+    def before(self):
+        title = self.request.form.get('title', '')
+        if not title:
+            return
+        body = self.request.form.get('body', '')
+        self.context.title = title
+        self.context.body = body
+        self.redirect(self.url(self.context))
+
+class EntryBody(grok.View):
+    grok.context(IEntry)
+    grok.name('body')
+
+    def render(self):
+        return renderRest(self.context.body)
+
+class EntryItem(grok.View):
+    grok.context(IEntry)
+    grok.name('item')
+
+class EntryRandomDate(grok.View):
+    grok.context(IEntry)
+    grok.name('random_date')
+
+    def render(self):
+        self.context.published = datetime(
+            2006,
+            11,
+            random.randrange(1, 29),
+            random.randrange(0, 24),
+            random.randrange(0, 60),
+            )
+        return str(self.context.published)

Added: megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/addentry.pt
===================================================================
--- megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/addentry.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/addentry.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,15 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>add entry</title>
+  </head>
+  <body>
+    <h1>add entry</h1>
+    <form tal:attributes="action python:view.url()" method="POST">
+      id: <input type="text" name="id" value="" /><br />
+      title: <input type="text" name="title" value="" /><br />
+      body: <textarea name="body"></textarea><br />
+      <input type="submit" value="Add Entry" />
+    </form>
+  </body>
+</html>

Added: megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/blogindex.pt
===================================================================
--- megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/blogindex.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/blogindex.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>blog title</title>
+  </head>
+  <body>
+    <h1>blog index</h1>
+    <a tal:attributes="href python:view.url('add')">Add Blog Entry</a>
+    <tal:block repeat="entry view/entries">
+      <tal:block content="structure entry/@@item"/>
+    </tal:block>
+
+  </body>
+</html>

Added: megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/dateindex.pt
===================================================================
--- megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/dateindex.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/dateindex.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>blog title</title>
+  </head>
+  <body>
+    <h1>blog index</h1>
+
+    <tal:block repeat="entry view/entries">
+      <tal:block content="structure entry/@@item"/>
+    </tal:block>
+
+  </body>
+</html>

Added: megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/entryedit.pt
===================================================================
--- megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/entryedit.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/entryedit.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>edit entry</title>
+  </head>
+  <body>
+    <h1>edit entry</h1>
+    <form tal:attributes="action python:view.url()" method="POST">
+      title: <input type="text" name="title"
+      tal:attributes="value context/title"
+      /><br />
+      body: <textarea name="body"
+      tal:content="context/body"/><br />
+      <input type="submit" value="Save changes..." />
+    </form>
+  </body>
+</html>

Added: megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/entryindex.pt
===================================================================
--- megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/entryindex.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/entryindex.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title tal:content="context/title"/>
+  </head>
+  <body>
+    <h1>
+      <tal:block content="context/title"/>
+      (<span class="published" tal:content="context/published"/>)
+    </h1>
+    <a tal:attributes="href python:view.url('edit')">edit...</a>
+    <p tal:content="structure context/@@body"/>
+  </body>
+</html>

Added: megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/entryitem.pt
===================================================================
--- megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/entryitem.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokblog/src/grokblog/blog_templates/entryitem.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,9 @@
+<div class="entry">
+  <h2>
+    <a tal:attributes="href python:view.url(context)">
+      <tal:block content="context/title"/>
+    </a>
+    (<span class="published" tal:content="context/published"/>)
+  </h2>
+  <p tal:content="structure context/@@body"/>
+</div>
\ No newline at end of file

Added: megrok.cherry/trunk/grok/grokblog/src/grokblog/configure.zcml
===================================================================
--- megrok.cherry/trunk/grok/grokblog/src/grokblog/configure.zcml	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokblog/src/grokblog/configure.zcml	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,15 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    xmlns:grok="http://namespaces.zope.org/grok"
+    i18n_domain="grok"
+    >
+    <grok:grok package="."/>
+
+    <browser:addMenuItem
+        class=".blog.Blog"
+        title="Grok Blog"
+        description="Grok Blog"
+        permission="zope.ManageContent"
+        />
+</configure>

Added: megrok.cherry/trunk/grok/grokwiki/README.txt
===================================================================
--- megrok.cherry/trunk/grok/grokwiki/README.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokwiki/README.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,9 @@
+=============
+The grok wiki
+=============
+
+The grok wiki is our first demo application, used to demonstrate how grok can
+be used to efficiently write web applications with Zope 3.
+
+It is not so much intended as a tutorial, but as a test bed for ourselves and
+to provide guidance for new users how we are using grok ourselves.

Added: megrok.cherry/trunk/grok/grokwiki/setup.py
===================================================================
--- megrok.cherry/trunk/grok/grokwiki/setup.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokwiki/setup.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,20 @@
+from setuptools import setup, find_packages
+
+setup(
+    name='grokwiki',
+    version='0.1',
+    author='Grok Team',
+    author_email='grok-dev at zope.org',
+    url='http://svn.zope.org/grok/trunk',
+    description="""\
+Grok: Now even cavemen can use wikis!
+""",
+    packages=find_packages('src'),
+    package_dir = {'': 'src'},
+    include_package_data = True,
+    zip_safe=False,
+    license='ZPL',
+
+    install_requires=['setuptools',
+                     ],
+)

Added: megrok.cherry/trunk/grok/grokwiki/src/grokwiki/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/grokwiki/src/grokwiki/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokwiki/src/grokwiki/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/grokwiki/src/grokwiki/configure.zcml
===================================================================
--- megrok.cherry/trunk/grok/grokwiki/src/grokwiki/configure.zcml	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokwiki/src/grokwiki/configure.zcml	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,15 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    xmlns:grok="http://namespaces.zope.org/grok"
+    i18n_domain="grok"
+    >
+    <grok:grok package="."/>
+
+    <browser:addMenuItem
+        class=".wiki.Wiki"
+        title="GROK WIKI"
+        description="GROK NOW WIKI"
+        permission="zope.ManageContent"
+        />
+</configure>

Added: megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page.py
===================================================================
--- megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,54 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""The grok demo wiki
+"""
+import re
+import grok
+
+
+LINK_PATTERN = re.compile('\[\[(.*?)\]\]')
+find_wiki_links = LINK_PATTERN.findall
+
+
+class WikiPage(grok.Model):
+
+    def __init__(self):
+        self.text = u"GROK EMPTY WIKI PAGE. FILL!"
+
+    def update(self, text):
+        links = find_wiki_links(text)
+        for link in links:
+            if link not in self.__parent__:
+                self.__parent__[link] = WikiPage()
+        self.text = text
+
+
+class Index(grok.View):
+
+    def before(self):
+        wiki_url = self.url(self.context.__parent__)
+        self.rendered_text, replacements = (
+            LINK_PATTERN.subn(r'<a href="%s/\1">\1</a>' % wiki_url, 
+                              self.context.text))
+
+class Edit(grok.View):
+
+    def before(self):
+        text = self.request.form.get('wikidata')
+        if not text:
+            return # Just render the template
+
+        # Update the text and redirect
+        self.context.update(text)
+        self.redirect(self.url(self.context))

Added: megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/edit.pt
===================================================================
--- megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/edit.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/edit.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,10 @@
+<html metal:use-macro="context/@@layout/main">
+    <div metal:fill-slot="content">
+        <h1>Edit &raquo;<span tal:replace="context/__name__">WikiPage</span>&laquo;</h1>
+
+        <form tal:attributes="action view/url" method="POST">
+        <textarea name="wikidata" tal:content="context/text" cols="80" rows="20"/><br/>
+        <input type="submit" value="Update"/>
+        </form>
+    </div>
+</html>

Added: megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/index.pt
===================================================================
--- megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/index.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/index.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,10 @@
+<html metal:use-macro="context/@@layout/main">
+    <div metal:fill-slot="content">
+        <h1 tal:content="context/__name__">WikiPage</h1>
+
+        <div tal:content="structure view/rendered_text" class="wikicontent">
+        </div>
+
+        <p><a tal:attributes="href python:view.url('edit')">Edit this page</a></p>
+    </div>
+</html>

Added: megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/layout.pt
===================================================================
--- megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/layout.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokwiki/src/grokwiki/page_templates/layout.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,26 @@
+<html metal:define-macro="main">
+    <head>
+        <link rel="stylesheet" tal:attributes="href static/wiki.css" type="text/css">
+    </head>
+
+    <body
+        tal:define="wiki context/__parent__">
+        <div metal:define-slot="content">
+            Page content goes here ...
+        </div>
+
+        <hr/>
+        <h3>Other pages</h3>
+        <p>
+            <span tal:repeat="page wiki">
+                <a tal:attributes="href python:view.url(wiki, page)"
+                   tal:content="page"
+                   />
+            </span>
+        </p>
+        <hr/>
+        <div id="footer">
+        This Wiki was grokked by Zope 3.
+        </div>
+    </body>
+</html>

Added: megrok.cherry/trunk/grok/grokwiki/src/grokwiki/static/wiki.css
===================================================================
--- megrok.cherry/trunk/grok/grokwiki/src/grokwiki/static/wiki.css	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokwiki/src/grokwiki/static/wiki.css	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,15 @@
+body {
+    font-family: Helvetica, Arial, sans;
+}
+
+.wikicontent {
+    margin:1em;
+    background-color:#EEEEEE;
+    border:1px solid #999999;
+    padding:1em;
+}
+
+#footer {
+    font-size:70%;
+    color:#999999;
+}

Added: megrok.cherry/trunk/grok/grokwiki/src/grokwiki/wiki.py
===================================================================
--- megrok.cherry/trunk/grok/grokwiki/src/grokwiki/wiki.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokwiki/src/grokwiki/wiki.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,31 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""The grok demo wiki
+"""
+
+import grok
+import grokwiki.page
+
+class Wiki(grok.Container):
+    """This is our wiki application wich contains all wiki pages."""
+
+class Index(grok.View):
+    def render(self):
+        self.redirect(self.url('home'))
+
+ at grok.subscribe(Wiki, grok.IObjectAddedEvent)
+def setupHomepage(wiki, event):
+    """Creates a home page for every wiki."""
+    page = grokwiki.page.WikiPage()
+    wiki['home'] = page

Added: megrok.cherry/trunk/grok/grokwiki/src/grokwiki/xmlrpc.py
===================================================================
--- megrok.cherry/trunk/grok/grokwiki/src/grokwiki/xmlrpc.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/grokwiki/src/grokwiki/xmlrpc.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""XML/RPC access to the wiki pages
+"""
+
+import grok
+import grokwiki.page
+
+
+class WikiPageRPC(grok.XMLRPC):
+    grok.context(grokwiki.page.WikiPage)
+
+    def edit(self, text):
+        self.context.update(text)
+
+    def show(self):
+        return self.context.text

Added: megrok.cherry/trunk/grok/ldapaddressbook/README.txt
===================================================================
--- megrok.cherry/trunk/grok/ldapaddressbook/README.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/ldapaddressbook/README.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,26 @@
+=================
+LDAP address book
+=================
+
+A grok example application that shows how to load and store objects from
+external data sources.
+
+
+Dependencies
+------------
+
+  - Requires python-ldap (no egg available yet)
+
+
+Issues
+------
+
+  - grok.EditForm gives no chance to perform a single update with the data of
+    all forms
+
+  - Container (AddressBook) has to set __parent__ and __name__ for the
+    dynamically created contacts 
+
+  - Mapping outside schemata to inside schema is tedious
+
+  - formlib acquires standard ZMI layout

Added: megrok.cherry/trunk/grok/ldapaddressbook/setup.py
===================================================================
--- megrok.cherry/trunk/grok/ldapaddressbook/setup.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/ldapaddressbook/setup.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,20 @@
+from setuptools import setup, find_packages
+
+setup(
+    name='ldapaddressbook',
+    version='0.1',
+    author='Christian Theune',
+    author_email='ct at gocept.com',
+    url='http://svn.zope.org/repos/main/grok/trunk/',
+    description="""\
+Allows to edit addressbook entries of ~inetOrgPerson in LDAP
+""",
+    packages=find_packages('src'),
+    package_dir = {'': 'src'},
+    include_package_data = True,
+    zip_safe=False,
+    license='ZPL',
+
+    install_requires=['setuptools',
+                     ],
+)

Added: megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+#

Added: megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/addressbook.py
===================================================================
--- megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/addressbook.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/addressbook.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,146 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""The grok LDAP address book.
+"""
+
+import ldap
+
+from zope import schema
+
+import grok
+
+
+LDAP_SERVER = "ldap://ldaphost:389"
+LDAP_LOGIN = "cn=admin,dc=example,dc=com"
+LDAP_PASSWORD = "password"
+LDAP_SEARCH_BASE = "ou=Addresses,dc=example,dc=com"
+
+
+class AddressBook(grok.Model):
+
+    @grok.traverse
+    def getContact(self, name):
+        contact = Contact(name)
+        contact.__parent__ = self
+        contact.__name__ = name
+        return contact
+
+    def listContacts(self):
+        return get_contact_list()
+
+
+class AddressBookListing(grok.View):
+    grok.context(AddressBook)
+    grok.name("index")
+
+
+addressbooklisting = grok.PageTemplate("""\
+<html>
+<body>
+<ul>
+    <li tal:repeat="contact context/listContacts">
+        <a tal:attributes="href string:${context/@@absolute_url}/$contact" tal:content="contact">Peter Kummer</a></li>
+</ul>
+</body>
+</html>""")
+
+
+class Contact(grok.Model):
+
+    class fields:
+        cn = schema.TextLine(title=u"Display name")
+        #displayName = fileAs
+
+        givenName = schema.TextLine(title=u"First name")
+        sn = schema.TextLine(title=u"Last name")
+        initials = schema.TextLine(title=u"Initials")
+
+        # jpegPhoto
+        description = schema.TextLine(title=u"Description")
+
+        title = schema.TextLine(title=u"Title")
+
+        o = schema.TextLine(title=u"Organisation")
+        ou = schema.TextLine(title=u"Organisational Unit")
+        businessRole = schema.TextLine(title=u"Role")
+
+        category = schema.TextLine(title=u"Category")
+
+        mail = schema.TextLine(title=u"Email")
+
+        telephoneNumber = schema.TextLine(title=u"Phone (business)")
+        mobile = schema.TextLine(title=u"Mobiltelefon")
+        facsimileTelephoneNumber = schema.TextLine(title=u"Telefax (business)")
+        homePhone = schema.TextLine(title=u"Phone (private)")
+        otherPhone = schema.TextLine(title=u"Phone (other)")
+
+        note = schema.TextLine(title=u"Note")
+
+        postalAddress = schema.Text(title=u"Address (business)")
+        postalCode = schema.TextLine(title=u"ZIP")
+        street = schema.TextLine(title=u"Street")
+        l = schema.TextLine(title=u"City")
+        st = schema.TextLine(title=u"State")
+
+        homePostalAddress = schema.Text(title=u"Address (private)")
+        otherPostalAddress = schema.Text(title=u"Address (other)")
+
+        labeledURI = schema.TextLine(title=u"Homepage")
+
+    def __init__(self, cname):
+        # Initialize from LDAP data
+        data = get_contact(cname)
+        if data is not None:
+            for field in grok.schema_fields(Contact):
+                field_data = data.get(field.__name__)
+                if not field_data:
+                    continue
+                if isinstance(field, schema.TextLine):
+                    setattr(self, field.__name__, field_data[0])
+                elif isinstance(field, schema.Text):
+                    setattr(self, field.__name__, '\n'.join(field_data))
+                else:
+                    raise TypeError, "Invalid schema field type: %r" % field
+
+
+class EditContact(grok.EditForm):
+    grok.context(Contact)
+    grok.name("index")
+
+
+# LDAP helper functions
+
+def get_contact_list():
+    l = ldap.initialize(LDAP_SERVER)
+    l.simple_bind_s(LDAP_LOGIN, LDAP_PASSWORD)
+    results = l.search_s(LDAP_SEARCH_BASE, ldap.SCOPE_SUBTREE, "(objectclass=inetOrgPerson)")
+    if results is None:
+        return []
+    cnames = [unicode(x[1]['cn'][0], 'utf-8') for x in results]
+    cnames.sort()
+    return cnames
+
+def get_contact(cname):
+    l = ldap.initialize(LDAP_SERVER)
+    l.simple_bind_s(LDAP_LOGIN, LDAP_PASSWORD)
+    results = l.search_s(LDAP_SEARCH_BASE,
+                         ldap.SCOPE_SUBTREE,
+                         "(&(objectclass=inetOrgPerson)(cn=%s))" % cname)
+    if results:
+        # Get data dictionary
+        data = results[0][1] 
+        for key, value in data.items():
+            value = [unicode(v, 'utf-8') for v in value]
+            data[key] = value
+        return data

Added: megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/configure.zcml
===================================================================
--- megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/configure.zcml	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/ldapaddressbook/src/ldapaddressbook/configure.zcml	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,15 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    xmlns:grok="http://namespaces.zope.org/grok"
+    i18n_domain="grok"
+    >
+    <grok:grok package="."/>
+
+    <browser:addMenuItem
+        class=".addressbook.AddressBook"
+        title="LDAP Addressbook"
+        description="LDAP Addressbook"
+        permission="zope.ManageContent"
+        />
+</configure>

Added: megrok.cherry/trunk/grok/setup.py
===================================================================
--- megrok.cherry/trunk/grok/setup.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/setup.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,20 @@
+from setuptools import setup, find_packages
+
+setup(
+    name='grok',
+    version='0.1',
+    author='Grok Team',
+    author_email='grok-dev at zope.org',
+    url='http://svn.zope.org/grok/trunk',
+    description="""\
+Grok: Now even cavemen can use Zope 3!
+""",
+    packages=find_packages('src'),
+    package_dir = {'': 'src'},
+    include_package_data = True,
+    zip_safe=False,
+    license='ZPL',
+    
+    install_requires=['setuptools',
+                     ],
+)

Added: megrok.cherry/trunk/grok/src/grok/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,45 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Grok
+"""
+
+from zope.interface import implements
+from zope.component import adapts
+from zope.formlib.form import action
+from zope.event import notify
+from zope.app.component.hooks import getSite
+from zope.lifecycleevent import (
+    IObjectCreatedEvent, ObjectCreatedEvent,
+    IObjectModifiedEvent, ObjectModifiedEvent,
+    IObjectCopiedEvent, ObjectCopiedEvent)
+
+from zope.app.container.contained import (
+    IObjectAddedEvent, ObjectAddedEvent,
+    IObjectMovedEvent, ObjectMovedEvent, 
+    IObjectRemovedEvent, ObjectRemovedEvent,
+    IContainerModifiedEvent, ContainerModifiedEvent)
+
+from grok.components import Model, Adapter, MultiAdapter, View, XMLRPC
+from grok.components import PageTemplate, Utility, Container, Traverser, Site
+from grok.components import EditForm, DisplayForm, schema_fields
+from grok.directive import context, name, template, templatedir
+from grok._grok import do_grok as grok  # Avoid name clash within _grok
+from grok._grok import SubscribeDecorator as subscribe
+from grok.error import GrokError, GrokImportError
+
+# Our __init__ provides the grok API directly so using 'import grok' is enough.
+from grok.interfaces import IGrokAPI
+from zope.interface import moduleProvides
+moduleProvides(IGrokAPI)
+__all__ = list(IGrokAPI)

Added: megrok.cherry/trunk/grok/src/grok/_grok.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/_grok.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/_grok.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,382 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Grok
+"""
+import os
+import sys
+import inspect
+
+from zope import component
+from zope import interface
+import zope.component.interface
+from zope.component.interfaces import IDefaultViewName
+from zope.security.checker import (defineChecker, getCheckerForInstancesOf,
+                                   NoProxy)
+from zope.publisher.interfaces.browser import (IDefaultBrowserLayer,
+                                               IBrowserRequest,
+                                               IBrowserPublisher)
+
+from zope.app.publisher.xmlrpc import MethodPublisher
+from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest
+from zope.app.component.site import LocalSiteManager
+
+import grok
+
+from grok import util, scan, components, security
+from grok.error import GrokError, GrokImportError
+from grok.directive import (ClassDirectiveContext, ModuleDirectiveContext,
+                            ClassOrModuleDirectiveContext,
+                            TextDirective, InterfaceOrClassDirective,
+                            frame_is_module, frame_is_class)
+
+
+_bootstrapped = False
+def bootstrap():
+    component.provideAdapter(components.ModelTraverser)
+    component.provideAdapter(components.ContainerTraverser)
+    
+    # register the name 'index' as the default view name
+    component.provideAdapter('index',
+                             adapts=(grok.Model, IBrowserRequest),
+                             provides=IDefaultViewName)
+    component.provideAdapter('index',
+                             adapts=(grok.Container, IBrowserRequest),
+                             provides=IDefaultViewName)
+    # register a subscriber for when grok.Sites are added to make them
+    # into Zope 3 sites
+    component.provideHandler(
+        addSiteHandler, adapts=(grok.Site, grok.IObjectAddedEvent))
+
+def addSiteHandler(site, event):
+    sitemanager = LocalSiteManager(site)
+    site.setSiteManager(sitemanager)
+
+# add a cleanup hook so that grok will bootstrap itself again whenever
+# the Component Architecture is torn down.
+def resetBootstrap():
+    global _bootstrapped
+    _bootstrapped = False
+from zope.testing.cleanup import addCleanUp
+addCleanUp(resetBootstrap)
+
+
+def do_grok(dotted_name):
+    global _bootstrapped
+    if not _bootstrapped:
+        bootstrap()
+        _bootstrapped = True
+
+    module_info = scan.module_info_from_dotted_name(dotted_name)
+    grok_tree(module_info)
+
+
+def grok_tree(module_info):
+    grok_module(module_info)
+
+    if not module_info.isPackage():
+        return
+
+    resource_path = module_info.getResourcePath('static')
+    if os.path.isdir(resource_path):
+        static_module = module_info.getSubModuleInfo('static')
+        if static_module is not None:
+            if static_module.isPackage():
+                raise GrokError("The 'static' resource directory must not "
+                                "be a python package.", module_info.getModule())
+            else:
+                raise GrokError("A package can not contain both a 'static' "
+                                "resource directory and a module named "
+                                "'static.py'", module_info.getModule())
+
+        register_static_resources(module_info.dotted_name, resource_path)
+
+    for sub_module_info in module_info.getSubModuleInfos():
+        grok_tree(sub_module_info)
+
+
+def grok_module(module_info):
+    (models, adapters, multiadapters, utilities, views, xmlrpc_views,
+     traversers, templates, subscribers) = scan_module(module_info)
+
+    find_filesystem_templates(module_info, templates)
+
+    context = util.determine_module_context(module_info, models)
+
+    register_models(models)
+    register_adapters(context, adapters)
+    register_multiadapters(multiadapters)
+    register_utilities(utilities)
+    register_views(context, views, templates)
+    register_xmlrpc(context, xmlrpc_views)
+    register_traversers(context, traversers)
+    register_unassociated_templates(context, templates, module_info)
+    register_subscribers(subscribers)
+
+    # Do various other initializations
+    initialize_schema(models)
+
+
+def scan_module(module_info):
+    models = []
+    components = {
+            grok.Model: models,
+            grok.Container: models,
+            grok.Adapter: [],
+            grok.MultiAdapter: [],
+            grok.Utility: [],
+            grok.View: [],
+            grok.XMLRPC: [],
+            grok.Traverser: []
+            }
+    templates = TemplateRegistry()
+    subscribers = module_info.getAnnotation('grok.subscribers', [])
+
+    module = module_info.getModule()
+    for name in dir(module):
+        obj = getattr(module, name)
+
+        if not util.defined_locally(obj, module_info.dotted_name):
+            continue
+
+        if isinstance(obj, grok.PageTemplate):
+            templates.register(name, obj)
+            obj._annotateGrokInfo(name, module_info.dotted_name)
+            continue
+        # XXX refactor
+        elif util.check_subclass(obj, grok.View):
+            obj.module_info = module_info
+            components[grok.View].append(obj)
+            continue
+
+        for candidate_class, found_list in components.items():
+            if util.check_subclass(obj, candidate_class):
+                found_list.append(obj)
+                break
+
+    return (components[grok.Model], components[grok.Adapter], 
+            components[grok.MultiAdapter], components[grok.Utility],
+            components[grok.View], components[grok.XMLRPC],
+            components[grok.Traverser], templates, subscribers)
+
+def find_filesystem_templates(module_info, templates):
+    template_dir_name = module_info.getAnnotation(
+        'grok.templatedir',
+        module_info.name + '_templates')
+    template_dir = module_info.getResourcePath(template_dir_name)
+    if os.path.isdir(template_dir):
+        template_files = os.listdir(template_dir)
+        for template_file in template_files:
+            if template_file.startswith('.') or template_file.endswith('~'):
+                continue
+
+            if not template_file.endswith('.pt'):
+                raise GrokError("Unrecognized file '%s' in template directory "
+                                "'%s'." % (template_file, template_dir),
+                                module_info.getModule())
+
+            template_name = template_file[:-3] # cut off .pt
+            template_path = os.path.join(template_dir, template_file)
+
+            f = open(template_path, 'rb')
+            contents = f.read()
+            f.close()
+
+            template = grok.PageTemplate(contents)
+            template._annotateGrokInfo(template_name, template_path)
+
+            inline_template = templates.get(template_name)
+            if inline_template:
+                raise GrokError("Conflicting templates found for name '%s' "
+                                "in module %r, both inline and in template "
+                                "directory '%s'."
+                                % (template_name, module_info.getModule(),
+                                   template_dir), inline_template)
+            templates.register(template_name, template)
+
+
+def register_static_resources(dotted_name, resource_directory):
+    resource_factory = components.DirectoryResourceFactory(resource_directory,
+                                                    dotted_name)
+    component.provideAdapter(resource_factory, (IDefaultBrowserLayer,),
+                             interface.Interface, name=dotted_name)
+
+def register_models(models):
+    for model in models:
+        # TODO minimal security here (read: everything is public)
+        if not getCheckerForInstancesOf(model):
+            defineChecker(model, NoProxy)
+
+def initialize_schema(models):
+    # Set the default values as class attributes to make formlib work
+    for model in models:
+        for field in components.schema_fields(model):
+            setattr(model, field.__name__, field.default)
+
+def register_adapters(context, adapters):
+    for factory in adapters:
+        adapter_context = util.determine_class_context(factory, context)
+        util.check_implements_one(factory)
+        name = util.class_annotation(factory, 'grok.name', '')
+        component.provideAdapter(factory, adapts=(adapter_context,), name=name)
+
+def register_multiadapters(multiadapters):
+    for factory in multiadapters:
+        util.check_implements_one(factory)
+        util.check_adapts(factory)
+        name = util.class_annotation(factory, 'grok.name', '')
+        component.provideAdapter(factory, name=name)
+
+def register_utilities(utilities):
+    for factory in utilities:
+        util.check_implements_one(factory)
+        name = util.class_annotation(factory, 'grok.name', '')
+        component.provideUtility(factory(), name=name)
+
+def register_xmlrpc(context, views):
+    for view in views:
+        view_context = util.determine_class_context(view, context)
+        candidates = [getattr(view, name) for name in dir(view)]
+        methods = [c for c in candidates if inspect.ismethod(c)]
+
+        for method in methods:
+            # Make sure that the class inherits MethodPublisher, so that the views
+            # have a location
+            method_view = type(view.__name__, (view, MethodPublisher), 
+                               {'__call__': method,
+                                '__Security_checker__': security.GrokChecker()}
+                               )
+            component.provideAdapter(
+                method_view, (view_context, IXMLRPCRequest), interface.Interface,
+                name=method.__name__)
+
+def register_views(context, views, templates):
+    for factory in views:
+        view_context = util.determine_class_context(factory, context)
+        factory_name = factory.__name__.lower()
+
+        # find inline templates
+        template_name = util.class_annotation(factory, 'grok.template',
+                                              factory_name)
+        template = templates.get(template_name)
+
+        if factory_name != template_name:
+            # grok.template is being used
+            if templates.get(factory_name):
+                raise GrokError("Multiple possible templates for view %r. It "
+                                "uses grok.template('%s'), but there is also "
+                                "a template called '%s'."
+                                % (factory, template_name, factory_name),
+                                factory)
+
+        if template:
+            if getattr(factory, 'render', None):
+                raise GrokError("Multiple possible ways to render view %r. "
+                                "It has both a 'render' method as well as "
+                                "an associated template." % factory,
+                                factory)
+
+            templates.markAssociated(template_name)
+            factory.template = template
+        else:
+            if not getattr(factory, 'render', None):
+                raise GrokError("View %r has no associated template or "
+                                "'render' method." % factory,
+                                factory)
+
+        view_name = util.class_annotation(factory, 'grok.name', factory_name)
+        # __view_name__ is needed to support IAbsoluteURL on views
+        factory.__view_name__ = view_name
+        component.provideAdapter(factory,
+                                 adapts=(view_context, IDefaultBrowserLayer),
+                                 provides=interface.Interface,
+                                 name=view_name)
+
+        # TODO minimal security here (read: everything is public)
+        defineChecker(factory, NoProxy)
+
+def register_traversers(context, traversers):
+    for factory in traversers:
+        factory_context = util.determine_class_context(factory, context)
+        component.provideAdapter(factory,
+                                 adapts=(factory_context, IBrowserRequest),
+                                 provides=IBrowserPublisher)
+
+def register_unassociated_templates(context, templates, module_info):
+    for name, unassociated in templates.listUnassociatedTemplates():
+        util.check_context(unassociated, context)
+
+        module_info_ = module_info
+        class TemplateView(grok.View):
+            template = unassociated
+            module_info = module_info_
+
+        templates.markAssociated(name)
+
+        TemplateView.__view_name__ = name
+        component.provideAdapter(TemplateView,
+                                 adapts=(context, IDefaultBrowserLayer),
+                                 provides=interface.Interface,
+                                 name=name)
+
+        # TODO minimal security here (read: everything is public)
+        defineChecker(TemplateView, NoProxy)
+
+def register_subscribers(subscribers):
+    for factory, subscribed in subscribers:
+        component.provideHandler(factory, adapts=subscribed)
+        for iface in subscribed:
+            zope.component.interface.provideInterface('', iface)
+
+class TemplateRegistry(object):
+
+    def __init__(self):
+        self._reg = {}
+
+    def register(self, name, template):
+        self._reg[name] = dict(template=template, associated=False)
+
+    def markAssociated(self, name):
+        self._reg[name]['associated'] = True
+
+    def get(self, name):
+        entry = self._reg.get(name)
+        if entry is None:
+            return None
+        return entry['template']
+
+    def listUnassociatedTemplates(self):
+        for name, entry in self._reg.iteritems():
+            if not entry['associated']:
+                yield name, entry['template']
+
+
+# decorators
+class SubscribeDecorator:
+    def __init__(self, *args):
+        self.subscribed = args
+
+    def __call__(self, function):
+        frame = sys._getframe(1)
+        if not frame_is_module(frame):
+            raise GrokImportError("@grok.subscribe can only be used on module "
+                                  "level.")
+
+        if not self.subscribed:
+            raise GrokImportError("@grok.subscribe requires at least one "
+                                  "argument.")
+
+        subscribers = frame.f_locals.get('__grok_subscribers__', None)
+        if subscribers is None:
+            frame.f_locals['__grok_subscribers__'] = subscribers = []
+        subscribers.append((function, self.subscribed))

Added: megrok.cherry/trunk/grok/src/grok/components.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/components.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/components.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,286 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Grok components
+"""
+
+import persistent
+import types
+import urllib
+
+from zope import component
+from zope import interface
+from zope import schema
+from zope.security.proxy import removeSecurityProxy
+from zope.publisher.browser import BrowserPage
+from zope.publisher.interfaces import NotFound
+from zope.publisher.interfaces.browser import (IBrowserPublisher,
+                                               IBrowserRequest)
+from zope.pagetemplate import pagetemplate
+from zope.formlib import form
+from zope.formlib.namedtemplate import INamedTemplate
+from zope.schema.interfaces import IField
+from zope.traversing.browser.interfaces import IAbsoluteURL
+from zope.traversing.browser.absoluteurl import AbsoluteURL
+from zope.traversing.browser.absoluteurl import _safe as SAFE_URL_CHARACTERS
+
+from zope.app.pagetemplate.engine import TrustedAppPT
+from zope.app.publisher.browser import getDefaultViewName
+from zope.app.publisher.browser import directoryresource
+from zope.app.publisher.browser.pagetemplateresource import \
+    PageTemplateResourceFactory
+from zope.app.container.btree import BTreeContainer
+from zope.app.container.contained import Contained
+from zope.app.component.site import SiteManagerContainer
+
+from grok import util, security, interfaces
+
+
+class Model(Contained, persistent.Persistent):  
+    # XXX Inheritance order is important here. If we reverse this, 
+    # then containers can't be models anymore because no unambigous MRO 
+    # can be established.
+    pass
+
+
+class Container(BTreeContainer):
+    pass
+
+
+class Site(SiteManagerContainer):
+    pass
+    
+class Adapter(object):
+
+    def __init__(self, context):
+        self.context = context
+
+
+class Utility(object):
+    pass
+
+
+class MultiAdapter(object):
+    pass
+
+
+class View(BrowserPage):
+    interface.implements(interfaces.IGrokView)
+    
+    def __init__(self, context, request):
+        # Jim would say: WAAAAAAAAAAAAH!
+        self.context = removeSecurityProxy(context)
+        self.request = removeSecurityProxy(request)
+        self.directory_resource = component.queryAdapter(self.request,
+                interface.Interface, name=self.module_info.package_dotted_name)
+
+    def __call__(self):
+        self.before()
+
+        template = getattr(self, 'template', None)
+        if not template:
+            return self.render()
+
+        namespace = template.pt_getContext()
+        namespace['request'] = self.request
+        namespace['view'] = self
+        namespace['context'] = self.context
+        # XXX need to check whether we really want to put None here if missing
+        namespace['static'] = self.directory_resource
+        return template.pt_render(namespace)
+
+    def __getitem__(self, key):
+        # XXX give nice error message if template is None
+        return self.template.macros[key]
+
+    def url(self, obj=None, name=None):
+        # if the first argument is a string, that's the name. There should
+        # be no second argument
+        if isinstance(obj, basestring):
+            if name is not None:
+                raise TypeError(
+                    'url() takes either obj argument, obj, string arguments, '
+                    'or string argument')
+            name = obj
+            obj = None
+            
+        if name is None and obj is None:
+            # create URL to view itself
+            obj = self
+        elif name is not None and obj is None:
+            # create URL to view on context
+            obj = self.context
+        url = component.getMultiAdapter((obj, self.request), IAbsoluteURL)()
+        if name is None:
+            # URL to obj itself
+            return url
+        # URL to view on obj
+        return url + '/' + urllib.quote(name.encode('utf-8'),
+                                        SAFE_URL_CHARACTERS)
+
+    def redirect(self, url):
+        return self.request.response.redirect(url)
+    
+    def before(self):
+        pass
+
+
+class GrokViewAbsoluteURL(AbsoluteURL):
+    def _getContextName(self, context):
+        return getattr(context, '__view_name__', None)
+    # XXX breadcrumbs method on AbsoluteURL breaks as it does not use
+    # _getContextName to get to the name of the view. What does breadcrumbs do?
+    
+
+class XMLRPC(object):
+    pass
+
+
+class PageTemplate(TrustedAppPT, pagetemplate.PageTemplate):
+    expand = 0
+
+    def __init__(self, template):
+        super(PageTemplate, self).__init__()
+        if util.not_unicode_or_ascii(template):
+            raise ValueError("Invalid page template. Page templates must be "
+                             "unicode or ASCII.")
+        self.write(template)
+
+        # __grok_module__ is needed to make defined_locally() return True for
+        # inline templates
+        # XXX unfortunately using caller_module means that
+        # PageTemplate cannot be subclassed
+        self.__grok_module__ = util.caller_module()
+
+    def __repr__(self):
+        return '<%s template in %s>' % (self.__grok_name__,
+                                        self.__grok_location__)
+
+    def _annotateGrokInfo(self, name, location):
+        self.__grok_name__ = name
+        self.__grok_location__ = location
+
+
+class DirectoryResource(directoryresource.DirectoryResource):
+    # We subclass this, because we want to override the default factories for
+    # the resources so that .pt and .html do not get created as page
+    # templates
+
+    resource_factories = {}
+    for type, factory in (directoryresource.DirectoryResource.
+                          resource_factories.items()):
+        if factory is PageTemplateResourceFactory:
+            continue
+        resource_factories[type] = factory
+
+
+class DirectoryResourceFactory(object):
+    # We need this to allow hooking up our own GrokDirectoryResource
+    # and to set the checker to None (until we have our own checker)
+
+    def __init__(self, path, name):
+        # XXX we're not sure about the checker=None here
+        self.__dir = directoryresource.Directory(path, None, name)
+        self.__name = name
+
+    def __call__(self, request):
+        resource = DirectoryResource(self.__dir, request)
+        resource.__Security_checker__ = security.GrokChecker()
+        resource.__name__ = self.__name
+        return resource
+
+class Traverser(object):
+    interface.implements(IBrowserPublisher)
+
+    def __init__(self, context, request):
+        # Jim would say: WAAAAAAAAAAAAH!
+        self.context = removeSecurityProxy(context)
+        self.request = removeSecurityProxy(request)
+
+    def browserDefault(self, request):
+        view_name = getDefaultViewName(self.context, request)
+        view_uri = "@@%s" % view_name
+        return self.context, (view_uri,)
+
+    def publishTraverse(self, request, name):
+        subob = self.traverse(name)
+        if subob:
+            return subob
+
+        view = component.queryMultiAdapter((self.context, request), name=name)
+        if view:
+            return view
+
+        raise NotFound(self.context, name, request)
+
+    def traverse(self, name):
+        # this will be overridden by subclasses
+        pass
+
+
+class ModelTraverser(Traverser):
+    component.adapts(Model, IBrowserRequest)
+
+    def traverse(self, name):
+        traverse = getattr(self.context, 'traverse', None)
+        if traverse:
+            return traverse(name)
+
+class ContainerTraverser(Traverser):
+    component.adapts(Container, IBrowserRequest)
+
+    def traverse(self, name):
+        traverse = getattr(self.context, 'traverse', None)
+        if traverse:
+            result = traverse(name)
+            if result is not None:
+                return result
+        # try to get the item from the container
+        return self.context.get(name)
+        
+class Form(View):
+    def _init(self):
+        fields = schema_fields(self.context)
+        self.form_fields = form.Fields(*fields)
+
+        self.template = component.getAdapter(self, INamedTemplate,
+                                             name='default')
+    def __call__(self):
+        self.update()
+        return self.render()
+
+class EditForm(Form, form.EditForm):
+    def __init__(self, context, request):
+        super(EditForm, self).__init__(context, request)
+        self._init()
+
+    def default_handle_apply(self, action, data):
+         form.EditForm.handle_edit_action.success_handler(self, action, data)
+
+class DisplayForm(Form, form.DisplayForm):
+    def __init__(self, context, request):
+        super(DisplayForm, self).__init__(context, request)
+        self._init()
+
+def schema_fields(obj):
+    fields = []
+    fields_class = getattr(obj, 'fields', None)
+    if fields_class is not None:
+        if type(fields_class) == types.ClassType:
+            for name in dir(fields_class):
+                field = getattr(fields_class, name)
+                if IField.providedBy(field):
+                    if not getattr(field, '__name__', None):
+                        field.__name__ = name
+                    fields.append(field)
+    return fields

Added: megrok.cherry/trunk/grok/src/grok/configure.zcml
===================================================================
--- megrok.cherry/trunk/grok/src/grok/configure.zcml	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/configure.zcml	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,26 @@
+<configure    
+  xmlns="http://namespaces.zope.org/zope">
+
+  <!-- we register special IAbsoluteURL views on grok views so that
+       can have them inspect __view_name__ instead of __name__.  
+       __name__ is already used as the class name, and overriding it
+       may make error messages more confusing.  -->
+
+  <view
+      for=".interfaces.IGrokView"
+      name="absolute_url"
+      factory=".components.GrokViewAbsoluteURL"
+      type="zope.publisher.interfaces.http.IHTTPRequest"
+      permission="zope.Public"
+      allowed_interface="zope.traversing.browser.interfaces.IAbsoluteURL"
+      />
+
+  <view
+      for=".interfaces.IGrokView"
+      factory=".components.GrokViewAbsoluteURL"
+      type="zope.publisher.interfaces.http.IHTTPRequest"
+      permission="zope.Public"
+      provides="zope.traversing.browser.interfaces.IAbsoluteURL"
+      />
+
+</configure>

Added: megrok.cherry/trunk/grok/src/grok/directive.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/directive.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/directive.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,121 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Grok directives.
+"""
+
+import sys
+from zope import interface
+from zope.interface.interfaces import IInterface
+
+from grok import util
+from grok.error import GrokImportError
+
+
+def frame_is_module(frame):
+    return frame.f_locals is frame.f_globals
+
+def frame_is_class(frame):
+    return '__module__' in frame.f_locals    
+
+class IDirectiveContext(interface.Interface):
+    description = interface.Attribute("The correct place in which the "
+                                      "directive can be used.")
+
+    def matches(frame):
+        """returns whether the given frame is the correct place in
+        which the directive can be used.
+        """
+
+class ClassDirectiveContext(object):
+    interface.implements(IDirectiveContext)
+    
+    description = "class"
+
+    def matches(self, frame):
+        return frame_is_class(frame)
+
+    
+class ModuleDirectiveContext(object):
+    interface.implements(IDirectiveContext)
+    
+    description = "module"
+
+    def matches(self, frame):
+        return frame_is_module(frame)
+    
+class ClassOrModuleDirectiveContext(object):
+    interface.implements(IDirectiveContext)
+    
+    description = "class or module"
+
+    def matches(self, frame):
+        return frame_is_module(frame) or frame_is_class(frame)
+
+class Directive(object):
+    """
+    Directive sets a value into the context's locals as __<name>__
+    ('.' in the name are replaced with '_').
+    A directive can be called only once.
+    """
+
+    def __init__(self, name, directive_context):
+        self.name = name
+        self.directive_context = directive_context
+
+    def __call__(self, value):
+        self.check(value)
+
+        frame = sys._getframe(1)
+        if not self.directive_context.matches(frame):
+            raise GrokImportError("%s can only be used on %s level."
+                                  % (self.name,
+                                     self.directive_context.description))
+
+        local_name = '__%s__' % self.name.replace('.', '_')
+        if local_name in frame.f_locals:
+            raise GrokImportError("%s can only be called once per %s."
+                                  % (self.name,
+                                     self.directive_context.description))
+        frame.f_locals[local_name] = value
+
+    def check(self, value):
+        pass
+
+class TextDirective(Directive):
+    """
+    Directive that only accepts unicode/ASCII values.
+    """
+
+    def check(self, value):
+        if util.not_unicode_or_ascii(value):
+            raise GrokImportError("You can only pass unicode or ASCII to "
+                                  "%s." % self.name)
+
+class InterfaceOrClassDirective(Directive):
+    """
+    Directive that only accepts classes or interface values.
+    """
+
+    def check(self, value):
+        if not (IInterface.providedBy(value) or util.isclass(value)):
+            raise GrokImportError("You can only pass classes or interfaces to "
+                                  "%s." % self.name)
+
+
+# Define grok directives
+name = TextDirective('grok.name', ClassDirectiveContext())
+template = TextDirective('grok.template', ClassDirectiveContext())
+context = InterfaceOrClassDirective('grok.context',
+                                    ClassOrModuleDirectiveContext())
+templatedir = TextDirective('grok.templatedir', ModuleDirectiveContext())

Added: megrok.cherry/trunk/grok/src/grok/error.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/error.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/error.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,24 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Grok errors.
+"""
+
+class GrokError(Exception):
+
+    def __init__(self, message, component):
+        Exception.__init__(self, message)
+        self.component = component
+
+class GrokImportError(ImportError):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/ftests/README.txt
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/README.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/README.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,6 @@
+Creating functional tests
+-------------------------
+
+Unfortunately, functional doctests do not import themselves (as the
+unit-test ones do), so you need to manually import the module in the
+doctest part.

Added: megrok.cherry/trunk/grok/src/grok/ftests/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/ftests/form/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/form/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/form/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/ftests/form/actions.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/form/actions.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/form/actions.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,50 @@
+"""
+Using the @grok.action decorator, different actions can be defined on a
+grok.EditForm. When @grok.action is used, the default behaviour (the 'Apply'
+action) is not available anymore, but it can triggered manually by calling
+self.default_handle_apply(action, data).
+
+  >>> import grok
+  >>> from grok.ftests.form.actions import Mammoth
+  >>> grok.grok('grok.ftests.form.actions')
+  >>> getRootFolder()["manfred"] = Mammoth()
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.open("http://localhost/manfred/@@edit")
+  >>> browser.getControl(name="form.name").value = "Manfred the Mammoth"
+  >>> browser.getControl(name="form.size").value = "Really big"
+  >>> browser.getControl("Apply").click()
+  >>> print browser.contents
+  <!DOCTYPE ...
+  ...Manfred the Mammoth...
+  ...Really big...
+  ...
+
+  >>> browser.open("http://localhost/manfred/@@edit")
+  >>> browser.getControl("Hairy").click()
+  >>> print browser.contents
+  <!DOCTYPE ...
+  ...Manfred the Mammoth...
+  ...Really big and hairy...
+  ...
+"""
+import grok
+from zope import schema
+
+class Mammoth(grok.Model):
+    class fields:
+        name = schema.TextLine(title=u"Name")
+        size = schema.TextLine(title=u"Size")
+
+class Edit(grok.EditForm):
+    @grok.action("Apply")
+    def handle_apply(self, action, data):
+        self.default_handle_apply(action, data)
+
+    @grok.action("Hairy")
+    def handle_hairy(self, action, data):
+        self.default_handle_apply(action, data)
+        self.context.size += " and hairy"
+    

Added: megrok.cherry/trunk/grok/src/grok/ftests/form/form.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/form/form.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/form/form.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,44 @@
+"""
+A grok.EditForm is a special grok.View that renders an edit form.
+
+  >>> import grok
+  >>> from grok.ftests.form.form import Mammoth
+  >>> grok.grok('grok.ftests.form.form')
+  >>> getRootFolder()["manfred"] = Mammoth()
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.open("http://localhost/manfred/@@edit")
+  >>> browser.getControl(name="form.name").value = "Manfred the Mammoth"
+  >>> browser.getControl(name="form.size").value = "Really big"
+  >>> browser.getControl("Apply").click()
+  >>> print browser.contents
+  <!DOCTYPE ...
+  ...Manfred the Mammoth...
+  ...Really big...
+  ...
+
+grok.DisplayForm renders a display form:
+
+  >>> browser.open("http://localhost/manfred/@@display")
+  >>> print browser.contents
+  <!DOCTYPE ...
+  ...Manfred the Mammoth...
+  ...Really big...
+  ...
+
+"""
+import grok
+from zope import schema
+
+class Mammoth(grok.Model):
+    class fields:
+        name = schema.TextLine(title=u"Name")
+        size = schema.TextLine(title=u"Size")
+
+class Edit(grok.EditForm):
+    pass
+
+class Display(grok.DisplayForm):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/ftests/static/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/static/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/static/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/ftests/static/simple.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/static/simple.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/static/simple.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,37 @@
+"""
+If there is a static/ directory inside of a grokked package, its
+contents will be available as static resources under a URL:
+
+  >>> import grok
+  >>> grok.grok('grok.ftests.static.simple_fixture')
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.open('http://localhost/@@/grok.ftests.static.simple_fixture/file.txt')
+  >>> print browser.contents
+  some text
+
+We use a special name 'static' in page templates to allow easy linking to resources:
+
+  >>> root = getRootFolder()
+  >>> from grok.ftests.static.simple_fixture.ellie import Mammoth
+  >>> root[u'ellie'] = Mammoth()
+  >>> browser.open('http://localhost/ellie')
+  >>> print browser.contents
+  <html>
+  <body>
+  <a href="http://localhost/@@/grok.ftests.static.simple_fixture/file.txt">Some text in a file</a>
+  </body>
+  </html>
+
+Static also means that page templates will not be interpreted:
+
+  >>> browser.open('http://localhost/@@/grok.ftests.static.simple_fixture/static.pt')
+  >>> print browser.contents
+  <html>
+  <body>
+  <h1 tal:content="string:will not be interpreted"/>
+  </body>
+  </html>
+
+"""

Added: megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/ellie.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/ellie.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/ellie.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,11 @@
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+index = grok.PageTemplate("""\
+<html>
+<body>
+<a tal:attributes="href static/file.txt">Some text in a file</a>
+</body>
+</html>""")

Added: megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/static/file.txt
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/static/file.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/static/file.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+some text

Added: megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/static/static.pt
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/static/static.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/static/simple_fixture/static/static.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,5 @@
+<html>
+<body>
+<h1 tal:content="string:will not be interpreted"/>
+</body>
+</html>

Added: megrok.cherry/trunk/grok/src/grok/ftests/test_grok_functional.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/test_grok_functional.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/test_grok_functional.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,64 @@
+import unittest
+from pkg_resources import resource_listdir
+from zope.testing import doctest
+from zope.app.testing.functional import HTTPCaller, getRootFolder, FunctionalTestSetup, sync, Functional
+
+# XXX bastardized from zope.app.testing.functional.FunctionalDocFileSuite :-(
+def FunctionalDocTestSuite(*paths, **kw):
+    globs = kw.setdefault('globs', {})
+    globs['http'] = HTTPCaller()
+    globs['getRootFolder'] = getRootFolder
+    globs['sync'] = sync
+
+    #kw['package'] = doctest._normalize_module(kw.get('package'))
+
+    kwsetUp = kw.get('setUp')
+    def setUp(test):
+        FunctionalTestSetup().setUp()
+
+        if kwsetUp is not None:
+            kwsetUp(test)
+    kw['setUp'] = setUp
+
+    kwtearDown = kw.get('tearDown')
+    def tearDown(test):
+        if kwtearDown is not None:
+            kwtearDown(test)
+        FunctionalTestSetup().tearDown()
+    kw['tearDown'] = tearDown
+
+    if 'optionflags' not in kw:
+        old = doctest.set_unittest_reportflags(0)
+        doctest.set_unittest_reportflags(old)
+        kw['optionflags'] = (old
+                             | doctest.ELLIPSIS
+                             | doctest.REPORT_NDIFF
+                             | doctest.NORMALIZE_WHITESPACE)
+
+    suite = doctest.DocTestSuite(*paths, **kw)
+    suite.layer = Functional
+    return suite
+
+def suiteFromPackage(name):
+    files = resource_listdir(__name__, name)
+    suite = unittest.TestSuite()
+    for filename in files:
+        if not filename.endswith('.py'):
+            continue
+        if filename == '__init__.py':
+            continue
+
+        dottedname = 'grok.ftests.%s.%s' % (name, filename[:-3])
+        test = FunctionalDocTestSuite(dottedname)
+
+        suite.addTest(test)
+    return suite
+
+def test_suite():
+    suite = unittest.TestSuite()
+    for name in ['view', 'static', 'xmlrpc', 'traversal', 'form', 'url']:
+        suite.addTest(suiteFromPackage(name))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: megrok.cherry/trunk/grok/src/grok/ftests/traversal/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/traversal/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/traversal/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/ftests/traversal/containertraverse.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/traversal/containertraverse.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/traversal/containertraverse.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,86 @@
+"""
+Containers can determine how they want to be traversed by
+implementing a 'traverse' method, but the behavior falls back to
+basic container traversal if the 'traverse' method returns None:
+
+  >>> import grok
+  >>> from grok.ftests.traversal.containertraverse import Herd, Mammoth
+  >>> grok.grok('grok.ftests.traversal.containertraverse')
+  >>> getRootFolder()["herd"] = herd = Herd()
+  >>> herd['manfred'] = Mammoth('Manfred')
+  >>> herd['ellie'] = Mammoth('Ellie')
+
+Let's first try to look up the special traversed item:
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.open("http://localhost/herd/special")
+  >>> print browser.contents
+  special view
+  >>> browser.open("http://localhost/herd/special/index")
+  >>> print browser.contents
+  special view
+
+Even if we have a container item called 'special', we should still
+get our special object:
+
+  >>> herd['special'] = Mammoth('Special invisible mammoth')
+  >>> browser.open("http://localhost/herd/special")
+  >>> print browser.contents
+  special view
+  >>> browser.open("http://localhost/herd/special/index")
+  >>> print browser.contents
+  special view
+  
+The fall-back behavior should work for items that aren't traversed:
+
+  >>> browser.open("http://localhost/herd/manfred")
+  >>> print browser.contents
+  <html>
+  <body>
+  <h1>Hello, Manfred!</h1>
+  </body>
+  </html>
+
+  >>> browser.open("http://localhost/herd/ellie")
+  >>> print browser.contents
+  <html>
+  <body>
+  <h1>Hello, Ellie!</h1>
+  </body>
+  </html>
+
+"""
+import grok
+
+class Herd(grok.Container):
+
+    def traverse(self, name):
+        if name == 'special':
+            return Special()
+        return None
+    
+class Mammoth(grok.Model):
+
+    def __init__(self, name):
+        self.name = name
+
+class Special(grok.Model):
+    pass
+
+class SpecialIndex(grok.View):
+    grok.context(Special)
+    grok.name('index')
+    
+    def render(self):
+        return "special view"
+
+grok.context(Mammoth)
+index = grok.PageTemplate("""\
+<html>
+<body>
+<h1>Hello, <span tal:replace="context/name/title" />!</h1>
+</body>
+</html>
+""")

Added: megrok.cherry/trunk/grok/src/grok/ftests/traversal/modeltraverse.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/traversal/modeltraverse.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/traversal/modeltraverse.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,52 @@
+"""
+Models can determine how they want to be traversed by
+implementing a 'traverse' method:
+
+  >>> import grok
+  >>> from grok.ftests.traversal.modeltraverse import Herd
+  >>> grok.grok('grok.ftests.traversal.modeltraverse')
+  >>> getRootFolder()["herd"] = Herd()
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.open("http://localhost/herd/manfred")
+  >>> print browser.contents
+  <html>
+  <body>
+  <h1>Hello, Manfred!</h1>
+  </body>
+  </html>
+
+  >>> browser.open("http://localhost/herd/ellie")
+  >>> print browser.contents
+  <html>
+  <body>
+  <h1>Hello, Ellie!</h1>
+  </body>
+  </html>
+
+"""
+import grok
+
+class Herd(grok.Model):
+
+    def getMammoth(self, name):
+        return Mammoth(name)
+
+    def traverse(self, name):
+        return self.getMammoth(name)
+    
+class Mammoth(grok.Model):
+
+    def __init__(self, name):
+        self.name = name
+
+grok.context(Mammoth)
+index = grok.PageTemplate("""\
+<html>
+<body>
+<h1>Hello, <span tal:replace="context/name/title" />!</h1>
+</body>
+</html>
+""")

Added: megrok.cherry/trunk/grok/src/grok/ftests/traversal/traverser.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/traversal/traverser.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/traversal/traverser.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,53 @@
+"""
+Apart from using the ``traverse`` method on a model, you can
+also create a separate traverser component:
+
+  >>> import grok
+  >>> from grok.ftests.traversal.modeltraverse import Herd
+  >>> grok.grok('grok.ftests.traversal.modeltraverse')
+  >>> getRootFolder()["herd"] = Herd()
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.open("http://localhost/herd/manfred")
+  >>> print browser.contents
+  <html>
+  <body>
+  <h1>Hello, Manfred!</h1>
+  </body>
+  </html>
+
+  >>> browser.open("http://localhost/herd/ellie")
+  >>> print browser.contents
+  <html>
+  <body>
+  <h1>Hello, Ellie!</h1>
+  </body>
+  </html>
+
+"""
+import grok
+
+class Herd(grok.Model):
+    pass
+
+class HerdTraverser(grok.Traverser):
+    grok.context(Herd)
+
+    def traverse(self, name):
+        return Mammoth(name)
+
+class Mammoth(grok.Model):
+
+    def __init__(self, name):
+        self.name = name
+
+grok.context(Mammoth)
+index = grok.PageTemplate("""\
+<html>
+<body>
+<h1>Hello, <span tal:replace="context/name/title" />!</h1>
+</body>
+</html>
+""")

Added: megrok.cherry/trunk/grok/src/grok/ftests/url/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/url/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/url/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+#

Added: megrok.cherry/trunk/grok/src/grok/ftests/url/redirect.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/url/redirect.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/url/redirect.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,34 @@
+"""
+Views have a redirect() method to easily create redirects:
+
+  >>> import grok
+  >>> grok.grok('grok.ftests.url.redirect')
+
+  >>> from grok.ftests.url.redirect import Mammoth
+  >>> getRootFolder()['manfred'] = manfred = Mammoth()
+
+Since the index view redirects to mammoth, we expect to see the URL
+point to mammoth:
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.open('http://localhost/manfred')
+  >>> browser.url
+  'http://localhost/manfred/another'
+  
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class Index(grok.View):
+    def render(self):
+        self.redirect(self.url('another'))
+
+class Another(grok.View):
+    def render(self):
+        return "Another view"
+    
+

Added: megrok.cherry/trunk/grok/src/grok/ftests/url/url.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/url/url.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/url/url.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,124 @@
+# -*- coding: UTF-8 -*-
+"""
+Views have a method that can be used to construct URLs:
+
+  >>> import grok
+  >>> grok.grok('grok.ftests.url.url')
+
+  >>> from grok.ftests.url.url import Herd, Mammoth
+  >>> herd = Herd()
+  >>> getRootFolder()['herd'] = herd
+  >>> manfred = Mammoth()
+  >>> herd['manfred'] = manfred
+
+The views in this test implement self.url():
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.open("http://localhost/herd/manfred/index")
+  >>> print browser.contents
+  http://localhost/herd/manfred/index
+  >>> browser.open("http://localhost/herd/manfred/another")
+  >>> print browser.contents
+  http://localhost/herd/manfred/another
+  >>> browser.open("http://localhost/herd/manfred/yet_another")
+  >>> print browser.contents
+  http://localhost/herd/manfred/yet_another
+  
+We get the views manually so we can do a greater variety of url() calls:
+
+  >>> from zope import component
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> index_view = component.getMultiAdapter((manfred, request), name='index')
+  >>> index_view.url()
+  'http://127.0.0.1/herd/manfred/index'
+  >>> another_view = component.getMultiAdapter((manfred, request),
+  ... name='another')
+  >>> another_view.url()
+  'http://127.0.0.1/herd/manfred/another'
+  >>> yet_another_view = component.getMultiAdapter((manfred, request),
+  ... name='yet_another')
+  >>> yet_another_view.url()
+  'http://127.0.0.1/herd/manfred/yet_another'
+
+Now let's get a URL for a specific object:
+
+  >>> index_view.url(manfred)
+  'http://127.0.0.1/herd/manfred'
+
+This works with any other view too (as they share the same request):
+
+  >>> another_view.url(manfred)
+  'http://127.0.0.1/herd/manfred'
+
+This shows that the default argument is the view itself:
+
+  >>> another_view.url(another_view)
+  'http://127.0.0.1/herd/manfred/another'
+
+We can get the URL for any object in content-space:
+
+  >>> another_view.url(herd)
+  'http://127.0.0.1/herd'
+
+We can also pass a name along with this, to generate a URL to a
+particular view on the object:
+
+  >>> another_view.url(herd, 'something')
+  'http://127.0.0.1/herd/something'
+
+It works properly in the face of non-ascii characters in URLs:
+
+  >>> url = another_view.url(herd, unicode('árgh', 'UTF-8'))
+  >>> url
+  'http://127.0.0.1/herd/%C3%A1rgh'
+  >>> import urllib
+  >>> expected = unicode('http://127.0.0.1/herd/árgh', 'UTF-8')
+  >>> urllib.unquote(url).decode('utf-8') == expected
+  True
+
+It's also possible to just pass in a name. In this case, a URL to that
+view on the context object will be constructed:
+
+  >>> another_view.url('yet_another_view')
+  'http://127.0.0.1/herd/manfred/yet_another_view'
+
+Some combinations of arguments just don't make sense:
+
+  >>> another_view.url('foo', 'bar')
+  Traceback (most recent call last):
+    ...
+  TypeError: url() takes either obj argument, obj, string arguments, or
+  string argument
+  >>> another_view.url('foo', herd)
+  Traceback (most recent call last):
+    ...
+  TypeError: url() takes either obj argument, obj, string arguments, or
+  string argument
+  >>> another_view.url(herd, 'bar', 'baz')
+  Traceback (most recent call last):
+    ...
+  TypeError: url() takes at most 3 arguments (4 given)
+  
+"""
+import grok
+
+class Herd(grok.Container, grok.Model):
+    pass
+
+class Mammoth(grok.Model):
+    pass
+
+grok.context(Mammoth)
+
+class Index(grok.View):
+    def render(self):
+        return self.url()
+    
+class Another(grok.View):
+    def render(self):
+        return self.url()
+
+yet_another = grok.PageTemplate('<p tal:replace="view/url" />')

Added: megrok.cherry/trunk/grok/src/grok/ftests/view/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/view/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/view/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/ftests/view/index.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/view/index.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/view/index.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,34 @@
+"""
+  >>> import grok
+  >>> from grok.ftests.view.index import Mammoth
+  >>> grok.grok('grok.ftests.view.index')
+  >>> getRootFolder()["manfred"] = Mammoth()
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.open("http://localhost/manfred")
+  >>> print browser.contents
+  <html>
+  <body>
+  <h1>Hello, world!</h1>
+  <span>Blue</span>
+  <span>Blue</span>
+  </body>
+  </html>
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    teeth = u"Blue"
+
+index = grok.PageTemplate("""\
+<html>
+<body>
+<h1>Hello, world!</h1>
+<span tal:content="python:context.teeth">green</span>
+<span tal:content="context/teeth">green</span>
+</body>
+</html>
+""")

Added: megrok.cherry/trunk/grok/src/grok/ftests/view/macros.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/view/macros.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/view/macros.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,81 @@
+"""
+  >>> import grok
+  >>> from grok.ftests.view.macros import Mammoth
+  >>> grok.grok('grok.ftests.view.macros')
+  >>> getRootFolder()["manfred"] = Mammoth()
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.open("http://localhost/manfred/@@painting")
+  >>> print browser.contents
+  <html>
+  <body>
+  <h1>GROK MACRO!</h1>
+  <div>
+  GROK SLOT!
+  </div>
+  </body>
+  </html>
+
+Views without a template do not support macros:
+
+  >>> browser.open("http://localhost/manfred/@@dancing")
+  Traceback (most recent call last):
+  AttributeError: 'DancingHall' object has no attribute 'template'
+
+If the view has an attribute with the same name as a macro, the macro 
+shadows the view. XXX This should probably generate a warning at runtime.
+
+  >>> browser.open("http://localhost/manfred/@@grilldish")
+  >>> print browser.contents
+  <html>
+  Curry
+  </html>
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class DancingHall(grok.View):
+
+    def render(self):
+        return "A nice large dancing hall for mammoths."
+
+class Grilled(grok.View):
+
+    def before(self):
+        self.spices = "Pepper and salt"
+
+painting = grok.PageTemplate("""\
+<html metal:use-macro="context/@@layout/main">
+<div metal:fill-slot="slot">
+GROK SLOT!
+</div>
+</html>
+""")
+
+layout = grok.PageTemplate("""\
+<html metal:define-macro="main">
+<body>
+<h1>GROK MACRO!</h1>
+<div metal:define-slot="slot">
+</div>
+</body>
+</html>""")
+
+dancing = grok.PageTemplate("""\
+<html metal:use-macro="context/@@dancinghall/something">
+</html>
+""")
+
+grilldish = grok.PageTemplate("""
+<html metal:use-macro="context/@@grilled/spices">
+</html>""")
+
+grilled = grok.PageTemplate("""\
+<html metal:define-macro="spices">
+Curry
+</html>""")

Added: megrok.cherry/trunk/grok/src/grok/ftests/view/view.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/view/view.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/view/view.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,30 @@
+"""
+  >>> import grok
+  >>> from grok.ftests.view.view import Mammoth
+  >>> grok.grok('grok.ftests.view.view')
+  >>> getRootFolder()["manfred"] = Mammoth()
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.open("http://localhost/manfred/@@painting")
+  >>> print browser.contents
+  <html>
+  <body>
+  <h1>Hello, world!</h1>
+  </body>
+  </html>
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+painting = grok.PageTemplate("""\
+<html>
+<body>
+<h1>Hello, world!</h1>
+</body>
+</html>
+""")

Added: megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc/xmlrpc.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc/xmlrpc.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc/xmlrpc.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,29 @@
+"""
+  >>> import grok
+  >>> grok.grok('grok.ftests.xmlrpc.xmlrpc')
+  >>> from grok.ftests.xmlrpc.xmlrpc import Mammoth
+  >>> getRootFolder()["Manfred"] = Mammoth()
+
+  >>> from grok.ftests.xmlrpc_helper import ServerProxy
+
+  >>> server = ServerProxy("http://localhost/")
+  >>> server.Manfred.stomp()
+  'Manfred stomped.'
+  >>> server.Manfred.dance()
+  "Manfred doesn't like to dance."
+
+"""
+import grok
+
+
+class Mammoth(grok.Model):
+    pass
+
+
+class MammothRPC(grok.XMLRPC):
+
+    def stomp(self):
+        return '%s stomped.' % self.context.__name__
+
+    def dance(self):
+        return '%s doesn\'t like to dance.' % self.context.__name__

Added: megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc_helper.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc_helper.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/ftests/xmlrpc_helper.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,75 @@
+# XXX This code is duplicated from Zope 3 trunk (future Zope 3.4) as we want to
+# stay compatible with Zope 3.3
+#
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""XMLRPC testing helpers for Zope 3.
+
+$Id: xmlrpc_helper.py 70787 2006-10-18 16:52:48Z ctheune $
+"""
+
+import StringIO
+import xmlrpclib 
+
+from zope.app.testing.functional import HTTPCaller
+
+
+class ZopeTestTransport(xmlrpclib.Transport):
+    """xmlrpclib transport that delegates to
+    zope.app.testing.functional.HTTPCaller.
+
+    It can be used like a normal transport, including support for basic
+    authentication.
+    """
+
+    verbose = False
+    handleErrors = True
+
+    def request(self, host, handler, request_body, verbose=0):
+        request = "POST %s HTTP/1.0\n" % (handler,)
+        request += "Content-Length: %i\n" % len(request_body)
+        request += "Content-Type: text/xml\n"
+
+        host, extra_headers, x509 = self.get_host_info(host)
+        if extra_headers:
+            request += "Authorization: %s\n" % (dict(extra_headers)["Authorization"],)
+
+        request += "\n" + request_body
+        response = HTTPCaller()(request, handle_errors=self.handleErrors)
+
+        errcode = response.getStatus()
+        errmsg = response.getStatusString()
+        # This is not the same way that the normal transport deals with the headers.
+        headers = response.getHeaders()
+
+        if errcode != 200:
+            raise xmlrpclib.ProtocolError(
+                host + handler,
+                errcode, errmsg,
+                headers
+                )
+
+        return self._parse_response(
+            StringIO.StringIO(response.getBody()), sock=None)
+
+
+def ServerProxy(uri, transport=ZopeTestTransport(), encoding=None,
+                verbose=0, allow_none=0, handleErrors=True):
+    """A factory that creates a server proxy using the ZopeTestTransport
+    by default.
+    
+    """
+    if isinstance(transport, ZopeTestTransport):
+        transport.handleErrors = handleErrors
+    return xmlrpclib.ServerProxy(uri, transport, encoding, verbose, allow_none)

Added: megrok.cherry/trunk/grok/src/grok/interfaces.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/interfaces.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/interfaces.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,156 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Grok interfaces
+"""
+from zope import interface
+
+class IGrokBaseClasses(interface.Interface):
+
+    Model = interface.Attribute("Base class for persistent content objects "
+                                "(models).")
+    Container = interface.Attribute("Base class for containers.")
+    Site = interface.Attribute("Mixin class for sites.")
+    Adapter = interface.Attribute("Base class for adapters.")
+    MultiAdapter = interface.Attribute("Base class for multi-adapters.")
+    Utility = interface.Attribute("Base class for utilities.")
+    View = interface.Attribute("Base class for browser views.")
+    XMLRPC = interface.Attribute("Base class for XML-RPC methods.")
+    Traverser = interface.Attribute("Base class for custom traversers.")
+    EditForm = interface.Attribute("Base class for edit forms.")
+
+class IGrokErrors(interface.Interface):
+
+    def GrokError(message, component):
+        """Error indicating that a problem occurrend during the
+        grokking of a module (at "grok time")."""
+
+    def GrokImportError(*args):
+        """Error indicating a problem at import time."""
+
+class IGrokDirectives(interface.Interface):
+
+    def implements(*interfaces):
+        """Declare that a class implements the given interfaces."""
+
+    def adapts(*classes_or_interfaces):
+        """Declare that a class adapts objects of the given classes or
+        interfaces."""
+
+    def context(class_or_interface):
+        """Declare the context for views, adapters, etc.
+
+        This directive can be used on module and class level.  When
+        used on module level, it will set the context for all views,
+        adapters, etc. in that module.  When used on class level, it
+        will set the context for that particular class."""
+
+    def name(name):
+        """Declare the name of a view or adapter/multi-adapter.
+
+        This directive can only be used on class level."""
+
+    def template(template):
+        """Declare the template name for a view.
+
+        This directive can only be used on class level."""
+
+    def templatedir(directory):
+        """Declare a directory to be searched for templates.
+
+        By default, grok will take the name of the module as the name
+        of the directory.  This can be overridden using
+        ``templatedir``."""
+
+class IGrokDecorators(interface.Interface):
+
+    def subscribe(*classes_or_interfaces):
+        """Declare that a function subscribes to an event or a
+        combination of objects and events."""
+
+    def action():
+        """XXX see zope.formlib.form.action"""
+
+    traverse = interface.Attribute("Specify a method to be used for "
+                                   "traversing URL paths.")
+
+class IGrokEvents(interface.Interface):
+
+    IObjectCreatedEvent = interface.Attribute("")
+
+    ObjectCreatedEvent = interface.Attribute("")
+
+    IObjectModifiedEvent = interface.Attribute("")
+
+    ObjectModifiedEvent = interface.Attribute("")
+
+    IObjectCopiedEvent = interface.Attribute("")
+
+    ObjectCopiedEvent = interface.Attribute("")
+
+    IObjectAddedEvent = interface.Attribute("")
+
+    ObjectAddedEvent = interface.Attribute("")
+
+    IObjectMovedEvent = interface.Attribute("")
+
+    ObjectMovedEvent = interface.Attribute("")
+
+    IObjectRemovedEvent = interface.Attribute("")
+
+    ObjectRemovedEvent = interface.Attribute("")
+
+    IContainerModifiedEvent = interface.Attribute("")
+
+    ContainerModifiedEvent = interface.Attribute("")
+
+class IGrokAPI(IGrokBaseClasses, IGrokDirectives, IGrokDecorators,
+               IGrokEvents, IGrokErrors):
+
+    def grok(dotted_name):
+        """Grok a module or package specified by ``dotted_name``."""
+
+    def notify(event):
+        """Send ``event`` to event subscribers."""
+
+    def getSite():
+        """Get the current site."""
+        
+    def PageTemplate(template):
+        """Create a Grok PageTemplate object from ``template`` source
+        text.  This can be used for inline PageTemplates."""
+
+    def schema_fields(class_):
+        """Return a list of schema fields defined for a model or view."""
+
+class IGrokView(interface.Interface):
+    """Grok views all provide this interface.
+    """
+    def redirect(url):
+       """Redirect to given URL"""
+
+    def url(obj=None, name=None):
+        """Construct URL.
+        
+        If no arguments given, construct URL to view itself.
+    
+        If only obj argument is given, construct URL to obj.
+        
+        If only name is given as the first argument, construct URL
+        to context/name.
+        
+        If both object and name arguments are supplied, construct
+        URL to obj/name.
+        """
+
+

Added: megrok.cherry/trunk/grok/src/grok/meta.zcml
===================================================================
--- megrok.cherry/trunk/grok/src/grok/meta.zcml	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/meta.zcml	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,13 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:meta="http://namespaces.zope.org/meta">
+
+  <meta:directives namespace="http://namespaces.zope.org/grok">
+    <meta:directive
+        name="grok"
+        schema=".zcml.IGrokDirective"
+        handler=".zcml.grokDirective"
+        />
+  </meta:directives>
+</configure>
+  
\ No newline at end of file

Added: megrok.cherry/trunk/grok/src/grok/scan.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/scan.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/scan.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,122 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Scanning packages and modules
+"""
+
+import os
+
+from zope.dottedname.resolve import resolve
+
+
+def is_package(path):
+    if not os.path.isdir(path):
+        return False
+    init_py = os.path.join(path, '__init__.py')
+    init_pyc = init_py + 'c'
+    # Check whether either __init__.py or __init__.pyc exist
+    return os.path.isfile(init_py) or os.path.isfile(init_pyc)
+
+
+class ModuleInfo(object):
+
+    def __init__(self, path, dotted_name):
+        # Normalize .pyc files to .py
+        if path.endswith('c'):
+            path = path[:-1]
+        self.path = path
+        self.dotted_name = dotted_name
+
+        name_parts = dotted_name.split('.')
+        self.name = name_parts[-1]
+        if self.isPackage():
+            self.package_dotted_name = dotted_name
+        else:
+            self.package_dotted_name = '.'.join(name_parts[:-1])
+
+        self._module = None
+
+    def getResourcePath(self, name):
+        """Get the absolute path of a resource directory 'relative' to this
+        package or module.
+
+        Case one: get the resource directory with name <name> from the same
+        directory as this module
+
+        Case two: get the resource directory with name <name> from the children
+        of this package
+        """
+        return os.path.join(os.path.dirname(self.path), name)
+
+    def getSubModuleInfos(self):
+        if not self.isPackage():
+            return []
+        directory = os.path.dirname(self.path)
+        module_infos = []
+        seen = []
+        for entry in sorted(os.listdir(directory)):
+            entry_path = os.path.join(directory, entry)
+            name, ext = os.path.splitext(entry)
+            dotted_name = self.dotted_name + '.' + name
+
+            # Case one: modules
+            if (os.path.isfile(entry_path) and ext in ['.py', '.pyc']):
+                if name == '__init__':
+                    continue
+                # Avoid duplicates when both .py and .pyc exist
+                if name in seen:
+                    continue
+                seen.append(name)
+                module_infos.append(ModuleInfo(entry_path, dotted_name))
+            # Case two: packages
+            elif is_package(entry_path):
+                # We can blindly use __init__.py even if only
+                # __init__.pyc exists because we never actually use
+                # that filename.
+                module_infos.append(ModuleInfo(
+                    os.path.join(entry_path, '__init__.py'), dotted_name))
+        return module_infos
+
+    def getSubModuleInfo(self, name):
+        path = os.path.join(os.path.dirname(self.path), name)
+        if is_package(path):
+            return ModuleInfo(os.path.join(path, '__init__.py'),
+                              '%s.%s' % (self.package_dotted_name, name))
+        elif os.path.isfile(path + '.py') or os.path.isfile(path + '.pyc'):
+                return ModuleInfo(path + '.py',
+                                  '%s.%s' % (self.package_dotted_name, name))
+        else:
+            return None
+        
+
+    def getAnnotation(self, key, default):
+        key = key.replace('.', '_')
+        key = '__%s__' % key
+        module = self.getModule()
+        return getattr(module, key, default)
+
+    def getModule(self):
+        if self._module is None:
+            self._module = resolve(self.dotted_name)
+        return self._module
+
+    def isPackage(self):
+        return self.path.endswith('__init__.py')
+
+    def __repr__(self):
+        return "<ModuleInfo object for '%s'>" % self.dotted_name
+
+
+def module_info_from_dotted_name(dotted_name):
+    module = resolve(dotted_name)
+    return ModuleInfo(module.__file__, dotted_name)

Added: megrok.cherry/trunk/grok/src/grok/security.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/security.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/security.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,45 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Grok security-related stuff
+"""
+
+class GrokChecker(object):
+    # ME GROK ANGRY.
+    # ME GROK NOT KNOW WHY CHECKER.
+
+    # We have no idea why we need a custom checker here. One hint was
+    # that the DirectoryResource already does something manually with
+    # setting up the 'correct' checker for itself and we seem to interfere
+    # with that. However, we couldn't figure out what's going on and this
+    # solves our problem for now. 
+
+    # XXX re-implement this in a sane way.
+
+    def __init__(self):
+        pass
+
+    def check_getattr(self, object, name):
+        pass
+
+    def check_setattr(self, ob, name):
+        pass
+
+    def check(self, ob, operation):
+        pass
+
+    def proxy(self, value):
+        return value
+
+
+

Added: megrok.cherry/trunk/grok/src/grok/tests/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/adapter.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/adapter.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/adapter.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,23 @@
+"""
+  >>> grok.grok(__name__)
+
+  >>> cave = Cave()
+  >>> home = IHome(cave)
+
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home)
+  True
+"""
+
+import grok
+from zope import interface
+
+class Cave(grok.Model):
+    pass
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.Adapter):
+    grok.implements(IHome)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/alphabetical.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/alphabetical.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/alphabetical.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,26 @@
+"""
+Grok does not depend on the alphabetical order:
+
+  >>> grok.grok(__name__)
+
+  >>> cave = ZCave()
+  >>> home = IHome(cave)
+
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home)
+  True
+
+"""
+import grok
+from zope import interface
+
+class ZCave(grok.Model):
+    """we call this `ZCave` because we want to test that we do not
+    depend on alphabetical order"""
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.Adapter):
+    grok.implements(IHome)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontext.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontext.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontext.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,29 @@
+"""
+Explicit class-level context in case of multiple models:
+
+  >>> grok.grok(__name__)
+
+  >>> cave = Cave()
+  >>> home = IHome(cave)
+
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home)
+  True
+
+"""
+import grok
+from zope import interface
+
+class Cave(grok.Model):
+    pass
+
+class Club(grok.Model):
+    pass
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.Adapter):
+    grok.implements(IHome)
+    grok.context(Cave)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontextimported.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontextimported.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontextimported.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,24 @@
+"""
+Explicit class-level context for an imported model:
+
+  >>> grok.grok(__name__)
+
+  >>> cave = Cave()
+  >>> painting = IPainting(cave)
+
+  >>> IPainting.providedBy(painting)
+  True
+  >>> isinstance(painting, Painting)
+  True
+
+"""
+import grok
+from grok.tests.adapter.adapter import Cave
+from zope import interface
+
+class IPainting(interface.Interface):
+    pass
+
+class Painting(grok.Adapter):
+    grok.implements(IPainting)
+    grok.context(Cave)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontextmultiple.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontextmultiple.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontextmultiple.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,10 @@
+"""
+You can't call grok.context multiple times on class level:
+
+  >>> import grok.tests.adapter.classcontextmultiple_fixture
+  Traceback (most recent call last):
+    ...
+  GrokImportError: grok.context can only be called once per class or module.
+
+"""
+

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontextmultiple_fixture.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontextmultiple_fixture.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/classcontextmultiple_fixture.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,11 @@
+import grok
+
+class Cave(grok.Model):
+    pass
+
+class Club(grok.Model):
+    pass
+
+class Anything(object):
+    grok.context(Cave)
+    grok.context(Club)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/classorinterface.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/classorinterface.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/classorinterface.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,46 @@
+"""
+You can only use `grok.context` with interfaces or classes and not
+with anything else:
+
+  >>> function_context()
+  Traceback (most recent call last):
+    ...
+  GrokImportError: You can only pass classes or interfaces to grok.context.
+
+  >>> string_context()
+  Traceback (most recent call last):
+    ...
+  GrokImportError: You can only pass classes or interfaces to grok.context.
+
+  >>> module_context()
+  Traceback (most recent call last):
+    ...
+  GrokImportError: You can only pass classes or interfaces to grok.context.
+
+  >>> instance_context()
+  Traceback (most recent call last):
+    ...
+  GrokImportError: You can only pass classes or interfaces to grok.context.
+
+"""
+import grok
+
+def function_context():
+    def a():
+        pass
+
+    class FunctionContext(object):
+        grok.context(a)
+
+def string_context():
+    class StringContext(object):
+        grok.context('string')
+
+def module_context():
+    class ModuleContext(object):
+        grok.context(grok)
+
+def instance_context():
+    obj = object()
+    class InstanceContext(object):
+        grok.context(obj)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/functioncontext.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/functioncontext.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/functioncontext.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,30 @@
+"""
+You can't call grok.context from a function:
+
+  >>> func()
+  Traceback (most recent call last):
+    ...
+  GrokImportError: grok.context can only be used on class or module level.
+
+You can't call grok.context from a method either:
+
+  >>> SomeClass().meth()
+  Traceback (most recent call last):
+    ...
+  GrokImportError: grok.context can only be used on class or module level.
+
+"""
+import grok
+from grok.tests.adapter.adapter import Cave
+
+def func():
+    """We don't allow calling `grok.context` from anything else than a
+    module or a class"""
+    grok.context(Cave)
+
+class SomeClass(object):
+
+    def meth(self):
+        """We don't allow calling `grok.context` from anything else
+        than a module or a class"""
+        grok.context(Cave)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/implementsmany.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/implementsmany.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/implementsmany.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,24 @@
+"""
+Subclasses of grok.Adapter and grok.MultiAdapter must implement exactly one
+interface:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: <class 'grok.tests.adapter.implementsmany.Home'> must implement exactly one interface (use grok.implements to specify).
+"""
+import grok
+
+from zope import interface
+
+class Cave(grok.Model):
+    pass
+
+class IHome(interface.Interface):
+    pass
+
+class IFireplace(interface.Interface):
+    pass
+
+class Home(grok.Adapter):
+    grok.implements(IHome, IFireplace)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/implementsnone.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/implementsnone.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/implementsnone.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,17 @@
+"""
+Subclasses of grok.Adapter and grok.MultiAdapter must implement exactly one
+interface:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: <class 'grok.tests.adapter.implementsnone.Home'> must
+  implement exactly one interface (use grok.implements to specify).
+"""
+import grok
+
+class Cave(grok.Model):
+    pass
+
+class Home(grok.Adapter):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/implementsnonemulti.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/implementsnonemulti.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/implementsnonemulti.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,17 @@
+"""
+Subclasses of grok.Adapter and grok.MultiAdapter must implement exactly one
+interface:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: <class 'grok.tests.adapter.implementsnonemulti.Home'> must
+  implement exactly one interface (use grok.implements to specify).
+"""
+import grok
+
+class Cave(grok.Model):
+    pass
+
+class Home(grok.MultiAdapter):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/importedmodel.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/importedmodel.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/importedmodel.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,14 @@
+"""
+Imported model and adapter won't be grokked:
+
+  >>> import grok
+  >>> grok.grok(__name__)
+  >>> from grok.tests.adapter.adapter import IHome
+  >>> cave = Cave()
+  >>> home = IHome(cave)
+  Traceback (most recent call last):
+    ...
+  TypeError: ('Could not adapt', <grok.tests.adapter.adapter.Cave object at ...>, <InterfaceClass grok.tests.adapter.adapter.IHome>)
+
+"""
+from grok.tests.adapter.adapter import Cave, Home

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/importedmodel2.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/importedmodel2.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/importedmodel2.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,23 @@
+"""
+Grok error because import model doesn't count as context:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: No module-level context for
+  <class 'grok.tests.adapter.importedmodel2.Painting'>, please use grok.context.
+
+"""
+import grok
+from grok.tests.adapter.adapter import Cave
+from zope import interface
+
+class IPainting(interface.Interface):
+    pass
+
+class Painting(grok.Adapter):
+    """
+    Grokking of this should fail because there's no model (only an
+    imported one which doesn't count).
+    """
+    grok.implements(IPainting)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/interface.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/interface.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/interface.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,41 @@
+"""
+You can also specify interfaces instead of classes with
+`grok.context` (class-level):
+
+  >>> grok.grok(__name__)
+
+  >>> cave = Cave()
+  >>> home = IHome(cave)
+
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home)
+  True
+
+  >>> hole = Hole()
+  >>> home = IHome(hole)
+
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home)
+  True
+
+"""
+import grok
+from zope import interface
+
+class ICave(interface.Interface):
+    pass
+
+class Cave(grok.Model):
+    grok.implements(ICave)
+
+class Hole(grok.Model):
+    grok.implements(ICave)
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.Adapter):
+    grok.implements(IHome)
+    grok.context(ICave)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/interfacemodule.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/interfacemodule.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/interfacemodule.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,42 @@
+"""
+You can also specify interfaces instead of classes with
+`grok.context` (module-level):
+
+  >>> grok.grok(__name__)
+
+  >>> cave = Cave()
+  >>> home = IHome(cave)
+
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home)
+  True
+
+  >>> hole = Hole()
+  >>> home = IHome(hole)
+
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home)
+  True
+
+"""
+import grok
+from zope import interface
+
+class ICave(interface.Interface):
+    pass
+
+class Cave(grok.Model):
+    grok.implements(ICave)
+
+class Hole(grok.Model):
+    grok.implements(ICave)
+
+grok.context(ICave)
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.Adapter):
+    grok.implements(IHome)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontext.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontext.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontext.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,30 @@
+"""
+Explicit module-level context in case of multiple models:
+
+  >>> grok.grok(__name__)
+
+  >>> cave = Cave()
+  >>> home = IHome(cave)
+
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home)
+  True
+
+"""
+import grok
+from zope import interface
+
+class Cave(grok.Model):
+    pass
+
+class Club(grok.Model):
+    pass
+
+grok.context(Cave)
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.Adapter):
+    grok.implements(IHome)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontextimported.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontextimported.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontextimported.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,25 @@
+"""
+Explicit module-level context for an imported model:
+
+  >>> grok.grok(__name__)
+
+  >>> cave = Cave()
+  >>> painting = IPainting(cave)
+
+  >>> IPainting.providedBy(painting)
+  True
+  >>> isinstance(painting, Painting)
+  True
+
+"""
+import grok
+from grok.tests.adapter.adapter import Cave
+from zope import interface
+
+grok.context(Cave)
+
+class IPainting(interface.Interface):
+    pass
+
+class Painting(grok.Adapter):
+    grok.implements(IPainting)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontextmultiple.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontextmultiple.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontextmultiple.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,9 @@
+"""
+You can't call grok.context multiple times on module level:
+
+  >>> import grok.tests.adapter.modulecontextmultiple_fixture
+  Traceback (most recent call last):
+    ...
+  GrokImportError: grok.context can only be called once per class or module.
+
+"""

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontextmultiple_fixture.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontextmultiple_fixture.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/modulecontextmultiple_fixture.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,11 @@
+import grok
+from zope import interface
+
+class Cave(grok.Model):
+    pass
+
+class Club(grok.Model):
+    pass
+
+grok.context(Cave)
+grok.context(Club)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/multiadapter.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/multiadapter.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/multiadapter.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,64 @@
+"""
+Multi-Adapters are supported by subclassing grok.MultiAdapter, giving
+multiple arguments to grok.adapts, and supplying a matching
+__init__():
+
+  >>> grok.grok(__name__)
+
+  >>> cave = Cave()
+  >>> fireplace = Fireplace()
+
+  >>> from zope import component
+  >>> home = component.getMultiAdapter((cave, fireplace))
+
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home)
+  True
+  >>> home.cave is cave
+  True
+  >>> home.fireplace is fireplace
+  True
+
+This also works for named adapters using grok.name:
+
+  >>> home = component.getMultiAdapter((cave, fireplace), name='home2')
+
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home2)
+  True
+  >>> home.cave is cave
+  True
+  >>> home.fireplace is fireplace
+  True
+"""
+
+import grok
+from zope import interface
+
+class Cave(grok.Model):
+    pass
+
+class Fireplace(grok.Model):
+    pass
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.MultiAdapter):
+    grok.adapts(Cave, Fireplace)
+    grok.implements(IHome)
+
+    def __init__(self, cave, fireplace):
+        self.cave = cave
+        self.fireplace = fireplace
+
+class Home2(grok.MultiAdapter):
+    grok.adapts(Cave, Fireplace)
+    grok.implements(IHome)
+    grok.name('home2')
+
+    def __init__(self, cave, fireplace):
+        self.cave = cave
+        self.fireplace = fireplace

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/multiadaptsnone.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/multiadaptsnone.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/multiadaptsnone.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,18 @@
+"""
+Subclasses of grok.MultiAdapter must declare what they adapt, using grok.adapts:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: <class 'grok.tests.adapter.multiadaptsnone.Home'> must specify
+  which contexts it adapts (use grok.adapts to specify).
+"""
+import grok
+
+from zope import interface
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.MultiAdapter):
+    grok.implements(IHome)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/multiple.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/multiple.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/multiple.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,24 @@
+"""
+Multiple models lead to ambiguity:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: Multiple possible contexts for
+  <class 'grok.tests.adapter.multiple.Home'>, please use grok.context.
+
+"""
+import grok
+from zope import interface
+
+class Cave(grok.Model):
+    pass
+
+class Club(grok.Model):
+    pass
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.Adapter):
+    grok.implements(IHome)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/namedadapter.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/namedadapter.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/namedadapter.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,31 @@
+"""
+You can register a named adapter by using grok.name:
+
+  >>> grok.grok(__name__)
+
+  >>> cave = Cave()
+  >>> home = IHome(cave)
+  Traceback (most recent call last):
+    ...
+  TypeError: ('Could not adapt', <grok.tests.adapter.namedadapter.Cave object at 0x...>, <InterfaceClass grok.tests.adapter.namedadapter.IHome>)
+
+  >>> from zope.component import getAdapter
+  >>> home = getAdapter(cave, IHome, name='home')
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home)
+  True
+"""
+
+import grok
+from zope import interface
+
+class Cave(grok.Model):
+    pass
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.Adapter):
+    grok.implements(IHome)
+    grok.name('home')

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/nomodel.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/nomodel.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/nomodel.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,18 @@
+"""
+If no model can be found in the module, we get an error:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: No module-level context for
+  <class 'grok.tests.adapter.nomodel.Home'>, please use grok.context.
+
+"""
+import grok
+from zope import interface
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.Adapter):
+    grok.implements(IHome)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/oldstyleclass.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/oldstyleclass.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/oldstyleclass.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,25 @@
+"""
+Old-style classes are also supported:
+
+  >>> grok.grok(__name__)
+
+  >>> cave = Cave()
+  >>> home = IHome(cave)
+
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home)
+  True
+"""
+import grok
+from zope import interface
+
+class Cave:
+    pass
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.Adapter):
+    grok.implements(IHome)
+    grok.context(Cave)

Added: megrok.cherry/trunk/grok/src/grok/tests/adapter/order.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/adapter/order.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/adapter/order.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,26 @@
+"""
+If the model is defined after the adapter, it should still be grokked
+properly:
+
+  >>> grok.grok(__name__)
+
+  >>> cave = Cave()
+  >>> home = IHome(cave)
+
+  >>> IHome.providedBy(home)
+  True
+  >>> isinstance(home, Home)
+  True
+
+"""
+import grok
+from zope import interface
+
+class IHome(interface.Interface):
+    pass
+
+class Home(grok.Adapter):
+    grok.implements(IHome)
+
+class Cave(grok.Model):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/container/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/container/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/container/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/container/container.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/container/container.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/container/container.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,40 @@
+"""
+
+The grok.Container is a a model that is also a container.  It has a
+dictionary API. It in fact stores its information in a BTree so
+you can store a lot of items in a scalable way.
+
+    >>> grok.grok(__name__)
+
+    >>> from zope.app.container.interfaces import IContainer
+    >>> bag = BoneBag()
+    >>> IContainer.providedBy(bag)
+    True
+
+    >>> from zope.app.container.btree import BTreeContainer
+    >>> isinstance(bag, BTreeContainer)
+    True
+     
+We had problems when switching to grok.Container with the __parent__ attribute
+being set, we better make sure this doesn't happen again:
+
+    >>> skull = Bone()
+    >>> print skull.__parent__
+    None
+    >>> print skull.__name__
+    None
+    >>> bag['skull'] = skull
+    >>> skull.__parent__
+    <grok.tests.container.container.BoneBag object at 0x...>
+    >>> skull.__name__
+    u'skull'
+
+"""
+
+import grok
+
+class BoneBag(grok.Container):
+    pass
+    
+class Bone(grok.Model):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/container/container_model.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/container/container_model.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/container/container_model.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,24 @@
+"""
+We make sure we indeed have a view for BoneBag; this way we know it's
+registered as the default context object:
+
+
+    >>> grok.grok(__name__)
+    >>> bag = BoneBag()
+    >>> from zope import component
+    >>> from zope.publisher.browser import TestRequest
+    >>> request = TestRequest()
+    >>> view = component.getMultiAdapter((bag, request), name='index')
+    >>> view()
+    'Hello world'
+"""
+import grok
+
+class BoneBag(grok.Container):
+    pass
+
+class Index(grok.View):
+    """A simple view to test whether BoneBag is really registered as a model.
+    """
+    def render(self):
+        return "Hello world"

Added: megrok.cherry/trunk/grok/src/grok/tests/error/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/error/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/error/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/error/error.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/error/error.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/error/error.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,23 @@
+"""
+
+We expect this grok to fail, and give 
+
+  >>> try:
+  ...     grok.grok(__name__)
+  ... except grok.GrokError, error:
+  ...     pass
+  >>> error.component
+  <class 'grok.tests.error.error.CavePainting'>
+
+"""
+
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class CavePainting(grok.View):
+    grok.template("a")
+
+a = grok.PageTemplate("a")
+cavepainting = grok.PageTemplate("b")

Added: megrok.cherry/trunk/grok/src/grok/tests/error/filesystemtemplate.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/error/filesystemtemplate.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/error/filesystemtemplate.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,11 @@
+"""
+
+This test checks for the right name in the error message if a filesystem-based
+template can not find a context to be associated with:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+  GrokError: No module-level context for <nocontext template in ...grok/tests/error/filesystemtemplate_templates/nocontext.pt>, please use grok.context.
+
+"""
+import grok

Added: megrok.cherry/trunk/grok/src/grok/tests/error/filesystemtemplate_templates/nocontext.pt
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/error/filesystemtemplate_templates/nocontext.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/error/filesystemtemplate_templates/nocontext.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+This template has no context.

Added: megrok.cherry/trunk/grok/src/grok/tests/event/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/event/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/event/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/event/errorconditions.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/event/errorconditions.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/event/errorconditions.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,34 @@
+"""
+ at grok.subscribe can only be used on module level:
+
+  >>> function_context()
+  Traceback (most recent call last):
+    ...
+  GrokImportError: @grok.subscribe can only be used on module level.
+  
+  >>> class_context()
+  Traceback (most recent call last):
+    ...
+  GrokImportError: @grok.subscribe can only be used on module level.
+
+
+ at grok.subscribe can not be called without arguments:
+
+  >>> import grok.tests.event.errorconditions_fixture
+  Traceback (most recent call last):
+    ...
+  GrokImportError: @grok.subscribe requires at least one argument.
+  
+"""
+import grok
+
+def function_context():
+    @grok.subscribe(grok.Model, grok.IObjectCreatedEvent)
+    def subscriber():
+        pass
+    
+def class_context():
+    class Wrapper:
+        @grok.subscribe(grok.Model, grok.IObjectCreatedEvent)
+        def subscriber(self):
+            pass

Added: megrok.cherry/trunk/grok/src/grok/tests/event/errorconditions_fixture.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/event/errorconditions_fixture.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/event/errorconditions_fixture.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,5 @@
+import grok
+
+ at grok.subscribe()
+def subscriber():
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/event/subscriber.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/event/subscriber.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/event/subscriber.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,28 @@
+"""
+You can subscribe to events using the @grok.subscribe decorator:
+
+  >>> grok.grok(__name__)
+  >>> manfred = Mammoth('Manfred')
+  >>> grok.notify(grok.ObjectCreatedEvent(manfred))
+  >>> mammoths
+  ['Manfred']
+  >>> mammoths2
+  ['Manfred']
+
+"""
+import grok
+
+class Mammoth(object):
+    def __init__(self, name):
+        self.name = name
+
+mammoths = []
+mammoths2 = []
+
+ at grok.subscribe(Mammoth, grok.IObjectCreatedEvent)
+def mammothAdded(mammoth, event):
+    mammoths.append(mammoth.name)
+
+ at grok.subscribe(Mammoth, grok.ObjectCreatedEvent)
+def mammothAddedInstance(mammoth, event):
+    mammoths2.append(mammoth.name)

Added: megrok.cherry/trunk/grok/src/grok/tests/form/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/form/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/form/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/form/form.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/form/form.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/form/form.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,60 @@
+"""
+A grok.Model may contain a nested class named 'fields'. All attributes of
+'fields' that provide IField will cause attributes of the same name to appear on
+the grok.Model:
+
+  >>> grok.grok(__name__)
+  >>> manfred = Mammoth()
+  >>> print manfred.name
+  None
+  >>> print manfred.size
+  Quite normal
+  >>> manfred.somethingelse
+  Traceback (most recent call last):
+    ...
+  AttributeError: 'Mammoth' object has no attribute 'somethingelse'
+
+If the 'fields' attribute is not an old-style class, it will not trigger any
+attribute generation:
+
+  >>> cave = Cave()
+  >>> cave.ignored
+  Traceback (most recent call last):
+    ...
+  AttributeError: 'Cave' object has no attribute 'ignored'
+
+
+A grok.EditForm is a special grok.View that renders an edit form.
+
+We need to set up the default formlib template first, because even though we
+don't use the formlib NamedTemplates directly they need to be present to create
+a formlib form.
+
+  >>> from zope import component
+  >>> from zope.formlib import form
+  >>> component.provideAdapter(form.default_page_template, name='default')
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> view = component.getMultiAdapter((manfred, request), name='edit')
+  >>> len(view.form_fields)
+  2
+  >>> [w.__name__ for w in view.form_fields]
+  ['name', 'size']
+"""
+import grok
+from zope import schema
+
+class Mammoth(grok.Model):
+    class fields:
+        name = schema.TextLine(title=u"Name")
+        size = schema.TextLine(title=u"Size", default=u"Quite normal")
+        somethingelse = None
+
+grok.context(Mammoth)
+
+class Edit(grok.EditForm):
+    pass
+
+class Cave(grok.Model):
+    fields = ['ignored']

Added: megrok.cherry/trunk/grok/src/grok/tests/scan/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/scan/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/scan/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/scan/package.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/scan/package.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/scan/package.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,35 @@
+"""
+  >>> import grok
+  >>> grok.grok('grok.tests.scan.stoneage')
+
+  >>> from grok.tests.scan.stoneage.cave import Cave
+  >>> from grok.tests.scan.stoneage.hunt.mammoth import Mammoth
+  >>> manfred = Mammoth()
+  >>> cave = Cave()
+  
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+
+  >>> view = component.getMultiAdapter((cave, request), name='index')
+  >>> print view()
+  <html>
+  <body>
+  <h1>A comfy cave</h1>
+  </body>
+  </html>
+  
+  >>> view = component.getMultiAdapter((manfred, request), name='index')
+  >>> print view()
+  <html>
+  <body>
+  <h1>ME GROK HUNT MAMMOTH!</h1>
+  </body>
+  </html>
+
+grok() currently does a fair bit of bootstrapping. This is a whitebox test to check whether we do this only once:
+
+  >>> grok._grok._bootstrapped
+  True
+
+"""

Added: megrok.cherry/trunk/grok/src/grok/tests/scan/scan.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/scan/scan.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/scan/scan.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,76 @@
+"""
+
+  >>> from grok.scan import ModuleInfo, module_info_from_dotted_name
+  
+  >>> module_info = module_info_from_dotted_name('grok.tests.scan.stoneage')
+  >>> module_info
+  <ModuleInfo object for 'grok.tests.scan.stoneage'>
+  >>> module_info.isPackage()
+  True
+  >>> module_info.dotted_name
+  'grok.tests.scan.stoneage'
+  >>> module_info.package_dotted_name
+  'grok.tests.scan.stoneage'
+  >>> module_info.name
+  'stoneage'
+  >>> module_info.getSubModuleInfo('cave')
+  <ModuleInfo object for 'grok.tests.scan.stoneage.cave'>
+  >>> module_info.getSubModuleInfo('hunt')
+  <ModuleInfo object for 'grok.tests.scan.stoneage.hunt'>
+  >>> print module_info.getSubModuleInfo('doesnotexist')
+  None
+
+  >>> module = module_info.getModule()
+  >>> module
+  <module 'grok.tests.scan.stoneage' from '...__init__.py...'>
+
+  >>> module.__grok_foobar__ = 'GROK LOVE FOO'
+  >>> module_info.getAnnotation('grok.foobar', None)
+  'GROK LOVE FOO'
+  >>> module_info.getAnnotation('grok.barfoo', 42)
+  42
+
+  >>> sub_modules = module_info.getSubModuleInfos()
+  >>> sub_modules
+  [<ModuleInfo object for 'grok.tests.scan.stoneage.cave'>,
+   <ModuleInfo object for 'grok.tests.scan.stoneage.hunt'>,
+   <ModuleInfo object for 'grok.tests.scan.stoneage.painting'>]
+  >>> cave_module_info = sub_modules[0]
+
+Module-level specifics
+----------------------
+
+cave is a module, not a package.
+
+  >>> cave_module_info.isPackage()
+  False
+  >>> cave_module_info.dotted_name
+  'grok.tests.scan.stoneage.cave'
+  >>> module_info.package_dotted_name
+  'grok.tests.scan.stoneage'
+  >>> cave_module_info.name
+  'cave'
+  >>> cave_module_info.getSubModuleInfos()
+  []
+
+Resource paths
+--------------
+
+For packages, a resource path will be a child of the package directory:
+
+  >>> import os.path
+  >>> expected_resource_path = os.path.join(os.path.dirname(
+  ...     module.__file__), 'stoneage-templates')
+  >>> resource_path = module_info.getResourcePath('stoneage-templates')
+  >>> resource_path == expected_resource_path
+  True
+
+For modules, a resource path will be a sibling of the module's file:
+
+  >>> expected_resource_path = os.path.join(os.path.dirname(
+  ...     cave_module_info.getModule().__file__), 'cave-templates')
+  >>> resource_path = cave_module_info.getResourcePath('cave-templates')
+  >>> resource_path == expected_resource_path
+  True
+
+"""

Added: megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,3 @@
+# this is a package
+
+

Added: megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/cave.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/cave.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/cave.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,12 @@
+import grok
+
+class Cave(grok.Model):
+    pass
+
+index = grok.PageTemplate("""\
+<html>
+<body>
+<h1>A comfy cave</h1>
+</body>
+</html>
+""")

Added: megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/mammoth.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/mammoth.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/mammoth.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,4 @@
+import grok
+
+class Mammoth(grok.Model):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/mammoth_templates/index.pt
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/mammoth_templates/index.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/hunt/mammoth_templates/index.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,5 @@
+<html>
+<body>
+<h1>ME GROK HUNT MAMMOTH!</h1>
+</body>
+</html>

Added: megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/notpackage/dummy.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/notpackage/dummy.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/notpackage/dummy.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# foo

Added: megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/painting/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/painting/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/scan/stoneage/painting/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/security/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/security/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/security/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/security/modeldefaultpublic.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/security/modeldefaultpublic.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/security/modeldefaultpublic.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,24 @@
+"""
+Models are public by default:
+
+  >>> grok.grok(__name__)
+
+  >>> mammoth = Mammoth('manfred')
+
+  >>> from zope.security.proxy import ProxyFactory
+  >>> from zope.security.management import newInteraction, endInteraction
+  >>> mammoth = ProxyFactory(mammoth)
+  >>> newInteraction()
+
+  >>> mammoth.name
+  'manfred'
+
+  >>> endInteraction()
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+
+    def __init__(self, name):
+        self.name = name

Added: megrok.cherry/trunk/grok/src/grok/tests/security/viewdefaultpublic.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/security/viewdefaultpublic.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/security/viewdefaultpublic.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,43 @@
+"""
+Views are public by default:
+
+  >>> grok.grok(__name__)
+
+  >>> manfred = Mammoth()
+
+  >>> from zope.security.management import newInteraction, endInteraction
+  >>> newInteraction()
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+  >>> view = component.getMultiAdapter((manfred, request), name='cavepainting')
+
+  >>> from zope.security.proxy import ProxyFactory
+  >>> view = ProxyFactory(view)
+  >>> print view()
+  A cave painting of a mammoth
+
+Same goes for template-based views:
+
+  >>> view = component.getMultiAdapter((manfred, request), name='templatepainting')
+  >>> view = ProxyFactory(view)
+  >>> print view()
+  A template-based painting of a mammoth
+
+  >>> endInteraction()
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class CavePainting(grok.View):
+
+    def render(self):
+        return 'A cave painting of a mammoth'
+
+templatepainting = grok.PageTemplate("""\
+A template-based painting of a mammoth
+""")

Added: megrok.cherry/trunk/grok/src/grok/tests/site/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/site/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/site/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/site/site.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/site/site.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/site/site.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,51 @@
+"""
+A site can be created by mixing in grok.Site into a grok.Model or
+grok.Container.
+
+  >>> grok.grok(__name__)
+
+  >>> from zope import interface
+  >>> from zope.app.component.interfaces import IPossibleSite, ISite
+  >>> manfred = Mammoth()
+  >>> IPossibleSite.providedBy(manfred)
+  True
+  >>> herd = Herd()
+  >>> IPossibleSite.providedBy(herd)
+  True
+  >>> nonsite = NonSite()
+  >>> IPossibleSite.providedBy(nonsite)
+  False
+  >>> nonsitecontainer = NonSiteContainer()
+  >>> IPossibleSite.providedBy(nonsitecontainer)
+  False
+
+While manfred and herd are possible sites, they are not yet sites;
+
+  >>> ISite.providedBy(manfred)
+  False
+  >>> ISite.providedBy(herd)
+  False
+  
+When a site is added to a container it will be initialized as a site (
+when the ObjectAddedEvent is fired):
+
+  >>> nonsitecontainer['manfred'] = manfred
+  >>> ISite.providedBy(manfred)
+  True
+  >>> nonsitecontainer['herd'] = herd
+  >>> ISite.providedBy(herd)
+  True
+"""
+import grok
+
+class Mammoth(grok.Model, grok.Site):
+    pass
+
+class Herd(grok.Container, grok.Site):
+    pass
+
+class NonSite(grok.Model):
+    pass
+
+class NonSiteContainer(grok.Container):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/static/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/static/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/static/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,11 @@
+"""
+When a package contains a 'static' resource directory, it must not also contain
+a module called 'static.py':
+
+  >>> import grok
+  >>> grok.grok('grok.tests.static.statichaspy_fixture')
+  Traceback (most recent call last):
+    ...
+  GrokError: A package can not contain both a 'static' resource directory
+  and a module named 'static.py'
+"""

Added: megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy_fixture/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy_fixture/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy_fixture/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy_fixture/static.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy_fixture/static.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/static/statichaspy_fixture/static.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,2 @@
+# This is an error: there can be no python module named 'static.py' when there
+# is a 'static' resource directory

Added: megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,9 @@
+"""
+It is an error for the 'static' directory to be a python package:
+
+  >>> import grok
+  >>> grok.grok('grok.tests.static.staticispackage_fixture')
+  Traceback (most recent call last):
+    ...
+  GrokError: The 'static' resource directory must not be a python package.
+"""

Added: megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage_fixture/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage_fixture/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage_fixture/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage_fixture/static/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage_fixture/static/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/static/staticispackage_fixture/static/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/test_grok.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/test_grok.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/test_grok.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,42 @@
+import unittest
+from pkg_resources import resource_listdir
+from zope.testing import doctest, cleanup
+import zope.component.eventtesting
+
+def setUpZope(test):
+    zope.component.eventtesting.setUp(test)
+
+def cleanUpZope(test):
+    cleanup.cleanUp()
+
+def suiteFromPackage(name):
+    files = resource_listdir(__name__, name)
+    suite = unittest.TestSuite()
+    for filename in files:
+        if not filename.endswith('.py'):
+            continue
+        if filename.endswith('_fixture.py'):
+            continue
+        if filename == '__init__.py':
+            continue
+
+        dottedname = 'grok.tests.%s.%s' % (name, filename[:-3])
+        test = doctest.DocTestSuite(dottedname,
+                                    setUp=setUpZope,
+                                    tearDown=cleanUpZope,
+                                    optionflags=doctest.ELLIPSIS+
+                                    doctest.NORMALIZE_WHITESPACE)
+
+        suite.addTest(test)
+    return suite
+
+def test_suite():
+    suite = unittest.TestSuite()
+    for name in ['adapter', 'error', 'view', 'security', 'scan', 'event',
+                 'zcml', 'static', 'utility', 'xmlrpc', 'container',
+                 'traversal', 'form', 'site']:
+        suite.addTest(suiteFromPackage(name))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: megrok.cherry/trunk/grok/src/grok/tests/traversal/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/traversal/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/traversal/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/utility/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/utility/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/utility/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/utility/implementsnone.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/utility/implementsnone.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/utility/implementsnone.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,13 @@
+"""
+Subclasses of grok.Utility must implement exactly one interface:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: <class 'grok.tests.utility.implementsnone.Club'> must
+  implement exactly one interface (use grok.implements to specify).
+"""
+import grok
+
+class Club(grok.Utility):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/utility/utility.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/utility/utility.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/utility/utility.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,33 @@
+"""
+Global utilities can be created by subclassing grok.Utility:
+
+  >>> grok.grok(__name__)
+  >>> from zope import component
+
+  >>> normal_club = component.getUtility(IClub)
+  >>> IClub.providedBy(normal_club)
+  True
+  >>> isinstance(normal_club, NormalClub)
+  True
+
+Named utilities are registered using grok.name:
+
+  >>> huge_club = component.getUtility(IClub, name='huge')
+  >>> IClub.providedBy(huge_club)
+  True
+  >>> isinstance(huge_club, HugeClub)
+  True
+"""
+import grok
+
+from zope import interface
+
+class IClub(interface.Interface):
+    pass
+
+class NormalClub(grok.Utility):
+    grok.implements(IClub)
+
+class HugeClub(grok.Utility):
+    grok.implements(IClub)
+    grok.name('huge')    

Added: megrok.cherry/trunk/grok/src/grok/tests/view/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/view/ambiguouscontext.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/ambiguouscontext.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/ambiguouscontext.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,22 @@
+"""
+Templates with ambiguous context cannot be grokked:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: Multiple possible contexts for
+  <club template in grok.tests.view.ambiguouscontext>, please use grok.context.
+
+"""
+
+import grok
+
+class Cave(grok.Model):
+    pass
+
+class Mammoth(grok.Model):
+    pass
+
+club = grok.PageTemplate("""\
+<html><body><h1>GROK CLUB MAMMOTH!</h1></body></html>
+""")

Added: megrok.cherry/trunk/grok/src/grok/tests/view/before.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/before.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/before.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,46 @@
+"""
+Before a view is rendered, the before() method is executed. It can be
+used e. g. to execute side effects or set up data for use in the
+template.
+
+  >>> grok.grok(__name__)
+
+We need to set up a default ITraversable adapter so that TALES
+expressions can resolve paths:
+
+  >>> from zope import component
+  >>> from zope.traversing.adapters import DefaultTraversable
+  >>> component.provideAdapter(DefaultTraversable, (None,))
+
+  >>> manfred = Mammoth()
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> view = component.getMultiAdapter((manfred, request), name='cavepainting')
+  >>> print view()
+  <html>
+  <body>
+  <h1>red</h1>
+  <h1>red</h1>
+  </body>
+  </html>
+  
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class CavePainting(grok.View):
+    def before(self):
+        self.color = "red"
+
+
+cavepainting = grok.PageTemplate("""\
+<html>
+<body>
+<h1 tal:content="view/color"/>
+<h1 tal:content="python: view.color"/>
+</body>
+</html>
+""")

Added: megrok.cherry/trunk/grok/src/grok/tests/view/dirandinlinetemplate.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/dirandinlinetemplate.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/dirandinlinetemplate.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,21 @@
+"""
+If multiple templates can be found, one in the module and one in the
+template directory, there is an error:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: Conflicting templates found for name 'cavepainting' in module
+  <module 'grok.tests.view.dirandinlinetemplate' from '...'>,
+  both inline and in template directory '...dirandinlinetemplate_templates'.
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class CavePainting(grok.View):
+    pass
+
+cavepainting = grok.PageTemplate("nothing")

Added: megrok.cherry/trunk/grok/src/grok/tests/view/dirandinlinetemplate_templates/cavepainting.pt
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/dirandinlinetemplate_templates/cavepainting.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/dirandinlinetemplate_templates/cavepainting.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+nothing

Added: megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,33 @@
+"""
+Templates can also be found in a directory with the same name as the module:
+
+  >>> grok.grok(__name__)
+  
+  >>> manfred = Mammoth()
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+  >>> view = component.getMultiAdapter((manfred, request), name='cavepainting')
+  >>> print view()
+  <html>
+  <body>
+  A cave painting.
+  </body>
+  </html>
+
+  >>> view = component.getMultiAdapter((manfred, request), name='food')
+  >>> print view()
+  <html>
+  <body>
+  ME GROK EAT MAMMOTH!
+  </body>
+  </html>
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class CavePainting(grok.View):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate_templates/cavepainting.pt
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate_templates/cavepainting.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate_templates/cavepainting.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,5 @@
+<html>
+<body>
+A cave painting.
+</body>
+</html>

Added: megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate_templates/food.pt
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate_templates/food.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplate_templates/food.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,5 @@
+<html>
+<body>
+ME GROK EAT MAMMOTH!
+</body>
+</html>

Added: megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplateandrender.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplateandrender.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplateandrender.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,20 @@
+"""
+A View may either have an associated template or a render-method. Here
+we check that this also works for templates in a template-directory:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: Multiple possible ways to render view
+  <class 'grok.tests.view.dirtemplateandrender.CavePainting'>.
+  It has both a 'render' method as well as an associated template.
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class CavePainting(grok.View):
+    def render(self):
+        pass

Added: megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplateandrender_templates/cavepainting.pt
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplateandrender_templates/cavepainting.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplateandrender_templates/cavepainting.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,5 @@
+<html>
+<body>
+A cave painting.
+</body>
+</html>

Added: megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,12 @@
+"""
+A template directory may only contain recognized template files:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: Unrecognized file 'invalid.txt' in template directory '...dirtemplatesonly_templates'.
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly_templates/index.pt
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly_templates/index.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly_templates/index.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+nothing to see here

Added: megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly_templates/invalid.txt
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly_templates/invalid.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/dirtemplatesonly_templates/invalid.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+nothing to see here

Added: megrok.cherry/trunk/grok/src/grok/tests/view/eithertemplateorrender.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/eithertemplateorrender.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/eithertemplateorrender.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,20 @@
+"""
+Only one, either a template, or render() can be specified:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: Multiple possible ways to render view
+  <class 'grok.tests.view.eithertemplateorrender.CavePainting'>.
+  It has both a 'render' method as well as an associated template.
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class CavePainting(grok.View):
+    def render(self):
+        pass
+
+cavepainting = grok.PageTemplate("nothing")

Added: megrok.cherry/trunk/grok/src/grok/tests/view/explicitimplicittemplate.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/explicitimplicittemplate.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/explicitimplicittemplate.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,24 @@
+"""
+It is too confusing to have a template that would be implicitly
+associated with a view while that view already refers to another
+template using grok.template.  Therefore there is an error:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: Multiple possible templates for view
+  <class 'grok.tests.view.explicitimplicittemplate.Painting'>.
+  It uses grok.template('cavepainting'), but there is also a template
+  called 'painting'.
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class Painting(grok.View):
+    grok.template('cavepainting')
+
+cavepainting = grok.PageTemplate("GROK CAVEPAINT MAMMOTH!")
+painting = grok.PageTemplate("GROK PAINT MAMMOTH!")

Added: megrok.cherry/trunk/grok/src/grok/tests/view/inline.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/inline.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/inline.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,76 @@
+"""
+Templates can be specified in the same module as the view,
+using a variable named `viewname_pt`:
+
+  >>> grok.grok(__name__)
+  
+  >>> manfred = Mammoth()
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+  >>> view = component.getMultiAdapter((manfred, request), name='cavepainting')
+  >>> print view()
+  <html>
+  <body>
+  <h1>Mammoth Cave Painting</h1>
+  <ul>
+    <li><zope.publisher.browser.TestRequest instance URL=http://127.0.0.1></li>
+    <li><grok.tests.view.inline.CavePainting object at 0x...></li>
+    <li><grok.tests.view.inline.Mammoth object at 0x...></li>
+    <li><zope.app.pagetemplate.engine.TraversableModuleImporter object at 0x...></li>
+  </ul>
+  </body>
+  </html>
+
+Note that the CavePainting instance is bound to the ``view`` name in
+the template.  This shows that the association of inline PageTemplate
+and the view class is successful.
+
+Templates that are not associated with a view class will still be
+registered on the model:
+
+  >>> view = component.getMultiAdapter((manfred, request), name='club')
+  >>> print view()
+  <html><body><h1>GROK CLUB MAMMOTH!</h1></body></html>
+
+Finding a template does not depend on the view name, but on the class
+name:
+
+  >>> view = component.getMultiAdapter((manfred, request), name='hunting')
+  >>> print view()
+  <html><body><h1>GROK HUNT MAMMOTH!</h1></body></html>
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class CavePainting(grok.View):
+    pass
+
+cavepainting = grok.PageTemplate("""\
+<html>
+<body>
+<h1 tal:content="string:Mammoth Cave Painting"/>
+<ul>
+  <li tal:content="structure python:repr(request)" />
+  <li tal:content="structure nocall:view" />
+  <li tal:content="structure nocall:context" />
+  <li tal:content="structure nocall:modules" />
+</ul>
+</body>
+</html>
+""")
+
+club = grok.PageTemplate("""\
+<html><body><h1>GROK CLUB MAMMOTH!</h1></body></html>
+""")
+
+class Hunt(grok.View):
+    grok.name('hunting')
+
+hunt = grok.PageTemplate("""\
+<html><body><h1>GROK HUNT MAMMOTH!</h1></body></html>
+""")
+

Added: megrok.cherry/trunk/grok/src/grok/tests/view/inlinebogus.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/inlinebogus.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/inlinebogus.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,16 @@
+# -*- coding: latin-1 -*-
+"""
+We do not accept bogus inline template such as ones that contain
+encoded strings:
+
+  >>> import grok
+  >>> grok.PageTemplate('''
+  ... <html>
+  ... <body><h1 tal:content="string:Mammoth Cave Painting"/>
+  ... <p>ööö</p>
+  ... </body>
+  ... </html>''')
+  Traceback (most recent call last):
+    ...
+  ValueError: Invalid page template. Page templates must be unicode or ASCII.
+"""

Added: megrok.cherry/trunk/grok/src/grok/tests/view/missingcontext.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/missingcontext.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/missingcontext.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,16 @@
+"""
+Templates without a context cannot be grokked:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: No module-level context for
+  <club template in grok.tests.view.missingcontext>, please use grok.context.
+
+"""
+
+import grok
+
+club = grok.PageTemplate("""\
+<html><body><h1>GROK CLUB MAMMOTH!</h1></body></html>
+""")

Added: megrok.cherry/trunk/grok/src/grok/tests/view/namemultiple.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/namemultiple.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/namemultiple.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,9 @@
+"""
+You can't call grok.name multiple times for a view
+
+  >>> import grok.tests.view.namemultiple_fixture
+  Traceback (most recent call last):
+    ...
+  GrokImportError: grok.name can only be called once per class.
+
+"""

Added: megrok.cherry/trunk/grok/src/grok/tests/view/namemultiple_fixture.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/namemultiple_fixture.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/namemultiple_fixture.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,8 @@
+"""
+This should fail:
+"""
+import grok
+
+class MultipleNames(grok.View):
+    grok.name('mammoth')
+    grok.name('bear')

Added: megrok.cherry/trunk/grok/src/grok/tests/view/nameunicode.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/nameunicode.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/nameunicode.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,28 @@
+# -*- coding: latin-1 -*-
+"""
+You can only pass unicode to `grok.name`:
+
+  >>> pass_unicode()
+  >>> pass_encodedstring()
+  Traceback (most recent call last):
+    ...
+  GrokImportError: You can only pass unicode or ASCII to grok.name.
+  >>> pass_object()
+  Traceback (most recent call last):
+    ...
+  GrokImportError: You can only pass unicode or ASCII to grok.name.
+
+"""
+import grok
+
+def pass_unicode():
+    class View(object):
+        grok.name(u'name')
+
+def pass_encodedstring():
+    class View(object):
+        grok.name("ölkj")
+
+def pass_object():
+    class View(object):
+        grok.name(object())

Added: megrok.cherry/trunk/grok/src/grok/tests/view/nomodulename.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/nomodulename.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/nomodulename.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,9 @@
+"""
+You can't call grok.name on a module:
+
+  >>> import grok.tests.view.nomodulename_fixture
+  Traceback (most recent call last):
+    ...
+  GrokImportError: grok.name can only be used on class level.
+
+"""

Added: megrok.cherry/trunk/grok/src/grok/tests/view/nomodulename_fixture.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/nomodulename_fixture.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/nomodulename_fixture.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,5 @@
+"""
+This should fail:
+"""
+import grok
+grok.name('viewname')

Added: megrok.cherry/trunk/grok/src/grok/tests/view/notemplateorrender.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/notemplateorrender.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/notemplateorrender.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,18 @@
+"""
+Views either need an associated template or a ``render`` method:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: View <class 'grok.tests.view.notemplateorrender.CavePainting'>
+  has no associated template or 'render' method.
+
+"""
+
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class CavePainting(grok.View):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/view/template.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/template.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/template.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,41 @@
+"""
+
+  >>> grok.grok(__name__)
+
+View with an associated PageTemplate that is referred to using
+``grok.template``:
+
+  >>> manfred = Mammoth()
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+  >>> view = component.getMultiAdapter((manfred, request), name='painting')
+  >>> print view()
+  <html><body><h1>GROK PAINT MAMMOTH!</h1></body></html>
+
+``grok.name`` and ``grok.template`` can be combined:
+
+  >>> view = component.getMultiAdapter((manfred, request), name='meal')
+  >>> print view()
+  <html><body><h1>GROK EAT MAMMOTH!</h1></body></html>
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class Painting(grok.View):
+    grok.template('cavepainting')
+
+cavepainting = grok.PageTemplate("""\
+<html><body><h1>GROK PAINT MAMMOTH!</h1></body></html>
+""")
+
+class Food(grok.View):
+    grok.template('food_template')
+    grok.name('meal')
+
+food_template = grok.PageTemplate("""\
+<html><body><h1>GROK EAT MAMMOTH!</h1></body></html>
+""")

Added: megrok.cherry/trunk/grok/src/grok/tests/view/templatedirectory.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/templatedirectory.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/templatedirectory.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,24 @@
+"""
+You can explicitly specify the template directory using grok.templatedir on module level:
+
+  >>> grok.grok(__name__)
+
+  >>> manfred = Mammoth()
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+  >>> view = component.getMultiAdapter((manfred, request), name='food')
+  >>> print view()
+  <html>
+  <body>
+  ME GROK EAT MAMMOTH!
+  </body>
+  </html>
+
+"""
+import grok
+
+grok.templatedir('templatedirectoryname')
+
+class Mammoth(grok.Model):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/view/templatedirectoryname/food.pt
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/templatedirectoryname/food.pt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/templatedirectoryname/food.pt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,5 @@
+<html>
+<body>
+ME GROK EAT MAMMOTH!
+</body>
+</html>

Added: megrok.cherry/trunk/grok/src/grok/tests/view/templatenotfound.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/templatenotfound.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/templatenotfound.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,20 @@
+"""
+This should fail because ``grok.template`` points to a non-existing
+template:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: View <class 'grok.tests.view.templatenotfound.Painting'>
+  has no associated template or 'render' method.
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class Painting(grok.View):
+    grok.template('cavepainting')
+
+# no cavepainting template here

Added: megrok.cherry/trunk/grok/src/grok/tests/view/twoviewsusetemplate.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/twoviewsusetemplate.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/twoviewsusetemplate.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,61 @@
+"""
+A template can be used by multiple views at the same time:
+
+  >>> grok.grok(__name__)
+
+  >>> manfred = Mammoth()
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+
+  >>> view = component.getMultiAdapter((manfred, request), name='a')
+  >>> print view()
+  View A
+
+  >>> view = component.getMultiAdapter((manfred, request), name='b')
+  >>> print view()
+  View A
+
+It also works if templates are both associated explicitly:
+
+  >>> view = component.getMultiAdapter((manfred, request), name='c')
+  >>> print view()
+  Template
+
+  >>> view = component.getMultiAdapter((manfred, request), name='d')
+  >>> print view()
+  Template
+
+Because the template is associated, we do not expect it to be
+registered as its own view:
+
+  >>> view = component.getMultiAdapter((manfred, request), name='templ')
+  Traceback (most recent call last):
+    ...
+  ComponentLookupError:
+  ((<grok.tests.view.twoviewsusetemplate.Mammoth object at 0x...>,
+  <zope.publisher.browser.TestRequest instance URL=http://127.0.0.1>),
+  <InterfaceClass zope.interface.Interface>, 'templ')
+
+
+"""
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class A(grok.View):
+    pass
+
+a = grok.PageTemplate("View A")
+
+class B(grok.View):
+    grok.template('a')
+
+class C(grok.View):
+    grok.template('templ')
+
+class D(grok.View):
+    grok.template('templ')
+
+templ = grok.PageTemplate('Template')

Added: megrok.cherry/trunk/grok/src/grok/tests/view/view.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/view/view.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/view/view.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,50 @@
+"""
+
+  >>> grok.grok(__name__)
+
+We should find the ``cavepainting`` view for a mammoth:
+
+  >>> manfred = Mammoth()
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+  >>> view = component.getMultiAdapter((manfred, request), name='cavepainting')
+  >>> view()
+  'A cave painting of a mammoth'
+
+  >>> view.context is manfred
+  True
+  >>> view.request is request
+  True
+
+Look up a view with a name explicitly set with ``grok.name``:
+
+  >>> view = component.getMultiAdapter((manfred, request), name='meal')
+  >>> view()
+  'Mammoth burger'
+
+There's no view 'food':
+
+  >>> view = component.getMultiAdapter((manfred, request), name='food')
+  Traceback (most recent call last):
+    ...
+  ComponentLookupError: ((<grok.tests.view.view.Mammoth object at 0x...>, <zope.publisher.browser.TestRequest instance URL=http://127.0.0.1>), <InterfaceClass zope.interface.Interface>, 'food')
+
+"""
+
+import grok
+
+class Mammoth(grok.Model):
+    pass
+
+class CavePainting(grok.View):
+
+    def render(self):
+        return 'A cave painting of a mammoth'
+
+class Food(grok.View):
+    """Grok says: ME NO SEE MAMMOTH, ME SEE MEAL!"""
+    grok.name('meal')
+
+    def render(self):
+        return 'Mammoth burger'

Added: megrok.cherry/trunk/grok/src/grok/tests/xmlrpc/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/xmlrpc/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/xmlrpc/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/xmlrpc/nocontext.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/xmlrpc/nocontext.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/xmlrpc/nocontext.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,16 @@
+"""
+
+Context-determination follows the same rules as for adapters. We just check
+whether it's hooked up at all:
+
+  >>> grok.grok(__name__)
+  Traceback (most recent call last):
+    ...
+  GrokError: No module-level context for
+  <class 'grok.tests.xmlrpc.nocontext.HomeRPC'>, please use grok.context.
+
+"""
+import grok
+
+class HomeRPC(grok.XMLRPC):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/zcml/__init__.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/zcml/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/zcml/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+# this is a package

Added: megrok.cherry/trunk/grok/src/grok/tests/zcml/directiveerror.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/zcml/directiveerror.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/zcml/directiveerror.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,25 @@
+"""
+When a GrokImportError occurs, ZCML will give the proper stack trace:
+
+  >>> import grok
+  >>> from zope.configuration import xmlconfig
+  >>> context = xmlconfig.file('meta.zcml', grok)
+
+  >>> ignored = xmlconfig.string('''
+  ... <configure
+  ...     xmlns="http://namespaces.zope.org/zope"
+  ...     xmlns:grok="http://namespaces.zope.org/grok"
+  ...     >
+  ...     <grok:grok package="grok.tests.zcml.directiveerror"/>
+  ... </configure>''', context=context)
+  Traceback (most recent call last):
+    ...
+  ZopeXMLConfigurationError: File "...", line ...
+  GrokError: No module-level context for
+  <class 'grok.tests.zcml.directiveerror.CavePainting'>, please use grok.context.
+
+"""
+import grok
+
+class CavePainting(grok.View):
+    pass

Added: megrok.cherry/trunk/grok/src/grok/tests/zcml/directiveimporterror.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/zcml/directiveimporterror.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/zcml/directiveimporterror.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,19 @@
+"""
+When a GrokImportError occurs, ZCML will give the proper stack trace:
+
+  >>> import grok
+  >>> from zope.configuration import xmlconfig
+  >>> context = xmlconfig.file('meta.zcml', grok)
+
+  >>> ignored = xmlconfig.string('''
+  ... <configure
+  ...     xmlns="http://namespaces.zope.org/zope"
+  ...     xmlns:grok="http://namespaces.zope.org/grok"
+  ...     >
+  ...     <grok:grok package="grok.tests.zcml.directiveimporterror_fixture"/>
+  ... </configure>''', context=context)
+  Traceback (most recent call last):
+    ...
+  ZopeXMLConfigurationError: File "...", line ...
+  GrokImportError: grok.template can only be used on class level.
+"""

Added: megrok.cherry/trunk/grok/src/grok/tests/zcml/directiveimporterror_fixture.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/zcml/directiveimporterror_fixture.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/zcml/directiveimporterror_fixture.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,5 @@
+"""
+This will raise a GrokImportError
+"""
+import grok
+grok.template('invalid')

Added: megrok.cherry/trunk/grok/src/grok/tests/zcml/directivemodule.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/zcml/directivemodule.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/zcml/directivemodule.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,28 @@
+"""
+  >>> import grok
+  >>> from zope.configuration import xmlconfig
+  >>> context = xmlconfig.file('meta.zcml', grok)
+
+  >>> ignored = xmlconfig.string('''
+  ... <configure
+  ...     xmlns="http://namespaces.zope.org/zope"
+  ...     xmlns:grok="http://namespaces.zope.org/grok"
+  ...     >
+  ...     <grok:grok package="grok.tests.scan.stoneage.cave"/>
+  ... </configure>''', context=context)
+
+  >>> from grok.tests.scan.stoneage.cave import Cave
+  >>> cave = Cave()
+  
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+
+  >>> view = component.getMultiAdapter((cave, request), name='index')
+  >>> print view()
+  <html>
+  <body>
+  <h1>A comfy cave</h1>
+  </body>
+  </html>
+"""

Added: megrok.cherry/trunk/grok/src/grok/tests/zcml/directivepackage.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/tests/zcml/directivepackage.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/tests/zcml/directivepackage.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,38 @@
+"""
+  >>> import grok
+  >>> from zope.configuration import xmlconfig
+  >>> context = xmlconfig.file('meta.zcml', grok)
+
+  >>> ignored = xmlconfig.string('''
+  ... <configure
+  ...     xmlns="http://namespaces.zope.org/zope"
+  ...     xmlns:grok="http://namespaces.zope.org/grok"
+  ...     >
+  ...     <grok:grok package="grok.tests.scan.stoneage"/>
+  ... </configure>''', context=context)
+
+  >>> from grok.tests.scan.stoneage.cave import Cave
+  >>> from grok.tests.scan.stoneage.hunt.mammoth import Mammoth
+  >>> manfred = Mammoth()
+  >>> cave = Cave()
+  
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+
+  >>> view = component.getMultiAdapter((cave, request), name='index')
+  >>> print view()
+  <html>
+  <body>
+  <h1>A comfy cave</h1>
+  </body>
+  </html>
+  
+  >>> view = component.getMultiAdapter((manfred, request), name='index')
+  >>> print view()
+  <html>
+  <body>
+  <h1>ME GROK HUNT MAMMOTH!</h1>
+  </body>
+  </html>
+"""
\ No newline at end of file

Added: megrok.cherry/trunk/grok/src/grok/util.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/util.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/util.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,105 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Grok utility functions.
+"""
+
+import re
+import types
+import sys
+
+from zope import component
+from zope import interface
+
+from grok.error import GrokError, GrokImportError
+
+def not_unicode_or_ascii(value):
+    if isinstance(value, unicode):
+        return False
+    if not isinstance(value, str):
+        return True
+    return is_not_ascii(value)
+
+is_not_ascii = re.compile(eval(r'u"[\u0080-\uffff]"')).search
+
+
+def isclass(obj):
+    """We cannot use ``inspect.isclass`` because it will return True
+    for interfaces"""
+    return type(obj) in (types.ClassType, type)
+
+
+def check_subclass(obj, class_):
+    if not isclass(obj):
+        return False
+    return issubclass(obj, class_)
+
+
+def caller_module():
+    return sys._getframe(2).f_globals['__name__']
+
+
+def class_annotation(obj, name, default):
+    return getattr(obj, '__%s__' % name.replace('.', '_'), default)
+
+
+def defined_locally(obj, dotted_name):
+    obj_module = getattr(obj, '__grok_module__', None)
+    if obj_module is None:
+        obj_module = getattr(obj, '__module__', None)
+    return obj_module == dotted_name
+
+
+AMBIGUOUS_CONTEXT = object()
+def check_context(component, context):
+    if context is None:
+        raise GrokError("No module-level context for %r, please use "
+                        "grok.context." % component, component)
+    elif context is AMBIGUOUS_CONTEXT:
+        raise GrokError("Multiple possible contexts for %r, please use "
+                        "grok.context." % component, component)
+
+
+def check_implements_one(class_):
+    if len(list(interface.implementedBy(class_))) != 1:
+        raise GrokError("%r must implement exactly one interface "
+                        "(use grok.implements to specify)."
+                        % class_, class_)
+
+
+def check_adapts(class_):
+    if component.adaptedBy(class_) is None:
+        raise GrokError("%r must specify which contexts it adapts "
+                        "(use grok.adapts to specify)."
+                        % class_, class_)
+
+
+def determine_module_context(module_info, models):
+    if len(models) == 0:
+        context = None
+    elif len(models) == 1:
+        context = models[0]
+    else:
+        context = AMBIGUOUS_CONTEXT
+
+    module_context = module_info.getAnnotation('grok.context', None)
+    if module_context:
+        context = module_context
+
+    return context
+
+
+def determine_class_context(class_, module_context):
+    context = class_annotation(class_, 'grok.context', module_context)
+    check_context(class_, context)
+    return context

Added: megrok.cherry/trunk/grok/src/grok/zcml.py
===================================================================
--- megrok.cherry/trunk/grok/src/grok/zcml.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok/zcml.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,31 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Grok ZCML-Directives
+"""
+from zope import interface
+import zope.configuration.fields
+import grok
+
+class IGrokDirective(interface.Interface):
+    """Grok a package or module.
+    """
+
+    package = zope.configuration.fields.GlobalObject(
+        title=u"Package",
+        description=u"The package or module to be analyzed by grok.",
+        required=False,
+        )
+
+def grokDirective(_context, package):
+    grok.grok(package.__name__)

Added: megrok.cherry/trunk/grok/src/grok.egg-info/PKG-INFO
===================================================================
--- megrok.cherry/trunk/grok/src/grok.egg-info/PKG-INFO	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok.egg-info/PKG-INFO	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,11 @@
+Metadata-Version: 1.0
+Name: grok
+Version: 0.1
+Summary: Grok: Now even cavemen can use Zope 3!
+
+Home-page: http://svn.zope.org/grok/trunk
+Author: Grok Team
+Author-email: grok-dev at zope.org
+License: ZPL
+Description: UNKNOWN
+Platform: UNKNOWN

Added: megrok.cherry/trunk/grok/src/grok.egg-info/SOURCES.txt
===================================================================
--- megrok.cherry/trunk/grok/src/grok.egg-info/SOURCES.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok.egg-info/SOURCES.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,208 @@
+COPYRIGHT.txt
+CREDITS.txt
+INSTALL.txt
+LICENSE.txt
+README.txt
+TODO.txt
+buildout.cfg
+setup.py
+bootstrap/bootstrap.py
+doc/tutorial.txt
+doc/design/adapters.py
+doc/design/annotations.py
+doc/design/container.py
+doc/design/grok_beginner.txt
+doc/design/grok_dev.txt
+doc/design/model.py
+doc/design/permission.py
+doc/design/skin-minimal.py
+doc/design/skin.py
+doc/design/subscriber.py
+doc/design/traversal.py
+doc/design/utility.py
+doc/design/views.py
+doc/reference/README.txt
+doc/reference/components.tex
+doc/reference/core.tex
+doc/reference/decorators.tex
+doc/reference/directives.tex
+doc/reference/events.tex
+doc/reference/exceptions.tex
+doc/reference/model.tex
+doc/reference/reference.tex
+grokblog/setup.py
+grokblog/src/grokblog/__init__.py
+grokblog/src/grokblog/blog.py
+grokblog/src/grokblog/configure.zcml
+grokblog/src/grokblog/blog_templates/addentry.pt
+grokblog/src/grokblog/blog_templates/blogindex.pt
+grokblog/src/grokblog/blog_templates/dateindex.pt
+grokblog/src/grokblog/blog_templates/entryedit.pt
+grokblog/src/grokblog/blog_templates/entryindex.pt
+grokblog/src/grokblog/blog_templates/entryitem.pt
+grokwiki/README.txt
+grokwiki/setup.py
+grokwiki/src/grokwiki/__init__.py
+grokwiki/src/grokwiki/configure.zcml
+grokwiki/src/grokwiki/page.py
+grokwiki/src/grokwiki/wiki.py
+grokwiki/src/grokwiki/xmlrpc.py
+grokwiki/src/grokwiki/page_templates/edit.pt
+grokwiki/src/grokwiki/page_templates/index.pt
+grokwiki/src/grokwiki/page_templates/layout.pt
+grokwiki/src/grokwiki/static/wiki.css
+ldapaddressbook/README.txt
+ldapaddressbook/setup.py
+ldapaddressbook/src/ldapaddressbook/__init__.py
+ldapaddressbook/src/ldapaddressbook/addressbook.py
+ldapaddressbook/src/ldapaddressbook/configure.zcml
+src/grok/__init__.py
+src/grok/_grok.py
+src/grok/components.py
+src/grok/configure.zcml
+src/grok/directive.py
+src/grok/error.py
+src/grok/interfaces.py
+src/grok/meta.zcml
+src/grok/scan.py
+src/grok/security.py
+src/grok/util.py
+src/grok/zcml.py
+src/grok.egg-info/PKG-INFO
+src/grok.egg-info/SOURCES.txt
+src/grok.egg-info/dependency_links.txt
+src/grok.egg-info/not-zip-safe
+src/grok.egg-info/requires.txt
+src/grok.egg-info/top_level.txt
+src/grok/ftests/README.txt
+src/grok/ftests/__init__.py
+src/grok/ftests/test_grok_functional.py
+src/grok/ftests/xmlrpc_helper.py
+src/grok/ftests/form/__init__.py
+src/grok/ftests/form/actions.py
+src/grok/ftests/form/form.py
+src/grok/ftests/static/__init__.py
+src/grok/ftests/static/simple.py
+src/grok/ftests/static/simple_fixture/__init__.py
+src/grok/ftests/static/simple_fixture/ellie.py
+src/grok/ftests/static/simple_fixture/static/file.txt
+src/grok/ftests/static/simple_fixture/static/static.pt
+src/grok/ftests/traversal/__init__.py
+src/grok/ftests/traversal/containertraverse.py
+src/grok/ftests/traversal/modeltraverse.py
+src/grok/ftests/traversal/traverser.py
+src/grok/ftests/url/__init__.py
+src/grok/ftests/url/redirect.py
+src/grok/ftests/url/url.py
+src/grok/ftests/view/__init__.py
+src/grok/ftests/view/index.py
+src/grok/ftests/view/macros.py
+src/grok/ftests/view/view.py
+src/grok/ftests/xmlrpc/__init__.py
+src/grok/ftests/xmlrpc/xmlrpc.py
+src/grok/tests/__init__.py
+src/grok/tests/test_grok.py
+src/grok/tests/adapter/__init__.py
+src/grok/tests/adapter/adapter.py
+src/grok/tests/adapter/alphabetical.py
+src/grok/tests/adapter/classcontext.py
+src/grok/tests/adapter/classcontextimported.py
+src/grok/tests/adapter/classcontextmultiple.py
+src/grok/tests/adapter/classcontextmultiple_fixture.py
+src/grok/tests/adapter/classorinterface.py
+src/grok/tests/adapter/functioncontext.py
+src/grok/tests/adapter/implementsmany.py
+src/grok/tests/adapter/implementsnone.py
+src/grok/tests/adapter/implementsnonemulti.py
+src/grok/tests/adapter/importedmodel.py
+src/grok/tests/adapter/importedmodel2.py
+src/grok/tests/adapter/interface.py
+src/grok/tests/adapter/interfacemodule.py
+src/grok/tests/adapter/modulecontext.py
+src/grok/tests/adapter/modulecontextimported.py
+src/grok/tests/adapter/modulecontextmultiple.py
+src/grok/tests/adapter/modulecontextmultiple_fixture.py
+src/grok/tests/adapter/multiadapter.py
+src/grok/tests/adapter/multiadaptsnone.py
+src/grok/tests/adapter/multiple.py
+src/grok/tests/adapter/namedadapter.py
+src/grok/tests/adapter/nomodel.py
+src/grok/tests/adapter/oldstyleclass.py
+src/grok/tests/adapter/order.py
+src/grok/tests/container/__init__.py
+src/grok/tests/container/container.py
+src/grok/tests/container/container_model.py
+src/grok/tests/error/__init__.py
+src/grok/tests/error/error.py
+src/grok/tests/error/filesystemtemplate.py
+src/grok/tests/error/filesystemtemplate_templates/nocontext.pt
+src/grok/tests/event/__init__.py
+src/grok/tests/event/errorconditions.py
+src/grok/tests/event/errorconditions_fixture.py
+src/grok/tests/event/subscriber.py
+src/grok/tests/form/__init__.py
+src/grok/tests/form/form.py
+src/grok/tests/scan/__init__.py
+src/grok/tests/scan/package.py
+src/grok/tests/scan/scan.py
+src/grok/tests/scan/stoneage/__init__.py
+src/grok/tests/scan/stoneage/cave.py
+src/grok/tests/scan/stoneage/hunt/__init__.py
+src/grok/tests/scan/stoneage/hunt/mammoth.py
+src/grok/tests/scan/stoneage/hunt/mammoth_templates/index.pt
+src/grok/tests/scan/stoneage/notpackage/dummy.py
+src/grok/tests/scan/stoneage/painting/__init__.py
+src/grok/tests/security/__init__.py
+src/grok/tests/security/modeldefaultpublic.py
+src/grok/tests/security/viewdefaultpublic.py
+src/grok/tests/site/__init__.py
+src/grok/tests/site/site.py
+src/grok/tests/static/__init__.py
+src/grok/tests/static/statichaspy.py
+src/grok/tests/static/staticispackage.py
+src/grok/tests/static/statichaspy_fixture/__init__.py
+src/grok/tests/static/statichaspy_fixture/static.py
+src/grok/tests/static/staticispackage_fixture/__init__.py
+src/grok/tests/static/staticispackage_fixture/static/__init__.py
+src/grok/tests/traversal/__init__.py
+src/grok/tests/utility/__init__.py
+src/grok/tests/utility/implementsnone.py
+src/grok/tests/utility/utility.py
+src/grok/tests/view/__init__.py
+src/grok/tests/view/ambiguouscontext.py
+src/grok/tests/view/before.py
+src/grok/tests/view/dirandinlinetemplate.py
+src/grok/tests/view/dirtemplate.py
+src/grok/tests/view/dirtemplateandrender.py
+src/grok/tests/view/dirtemplatesonly.py
+src/grok/tests/view/eithertemplateorrender.py
+src/grok/tests/view/explicitimplicittemplate.py
+src/grok/tests/view/inline.py
+src/grok/tests/view/inlinebogus.py
+src/grok/tests/view/missingcontext.py
+src/grok/tests/view/namemultiple.py
+src/grok/tests/view/namemultiple_fixture.py
+src/grok/tests/view/nameunicode.py
+src/grok/tests/view/nomodulename.py
+src/grok/tests/view/nomodulename_fixture.py
+src/grok/tests/view/notemplateorrender.py
+src/grok/tests/view/template.py
+src/grok/tests/view/templatedirectory.py
+src/grok/tests/view/templatenotfound.py
+src/grok/tests/view/twoviewsusetemplate.py
+src/grok/tests/view/view.py
+src/grok/tests/view/dirandinlinetemplate_templates/cavepainting.pt
+src/grok/tests/view/dirtemplate_templates/cavepainting.pt
+src/grok/tests/view/dirtemplate_templates/food.pt
+src/grok/tests/view/dirtemplateandrender_templates/cavepainting.pt
+src/grok/tests/view/dirtemplatesonly_templates/index.pt
+src/grok/tests/view/dirtemplatesonly_templates/invalid.txt
+src/grok/tests/view/templatedirectoryname/food.pt
+src/grok/tests/xmlrpc/__init__.py
+src/grok/tests/xmlrpc/nocontext.py
+src/grok/tests/zcml/__init__.py
+src/grok/tests/zcml/directiveerror.py
+src/grok/tests/zcml/directiveimporterror.py
+src/grok/tests/zcml/directiveimporterror_fixture.py
+src/grok/tests/zcml/directivemodule.py
+src/grok/tests/zcml/directivepackage.py

Added: megrok.cherry/trunk/grok/src/grok.egg-info/dependency_links.txt
===================================================================
--- megrok.cherry/trunk/grok/src/grok.egg-info/dependency_links.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok.egg-info/dependency_links.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+

Added: megrok.cherry/trunk/grok/src/grok.egg-info/not-zip-safe
===================================================================
--- megrok.cherry/trunk/grok/src/grok.egg-info/not-zip-safe	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok.egg-info/not-zip-safe	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+

Added: megrok.cherry/trunk/grok/src/grok.egg-info/requires.txt
===================================================================
--- megrok.cherry/trunk/grok/src/grok.egg-info/requires.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok.egg-info/requires.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+setuptools
\ No newline at end of file

Added: megrok.cherry/trunk/grok/src/grok.egg-info/top_level.txt
===================================================================
--- megrok.cherry/trunk/grok/src/grok.egg-info/top_level.txt	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/grok/src/grok.egg-info/top_level.txt	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+grok

Added: megrok.cherry/trunk/setup.py
===================================================================
--- megrok.cherry/trunk/setup.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/setup.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,26 @@
+from setuptools import setup, find_packages
+
+setup(
+    name='megrok.cherry',
+    version='0.1',
+    author='Startifact',
+    author_email='faassen at startifact.com',
+    description="""\
+Using Zope Grok with CherryPy.
+""",
+    packages=find_packages('src'),
+    package_dir = {'': 'src'},
+    include_package_data = True,
+    zip_safe=False,
+    license='BSD',
+   entry_points= {
+    'console_scripts': [   
+      'startserver = megrok.cherry.server:startServer',
+      ]
+    },
+    install_requires=[
+    'CherryPy',
+    'grok',
+    'setuptools',
+    ],
+)

Added: megrok.cherry/trunk/site.zcml
===================================================================
--- megrok.cherry/trunk/site.zcml	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/site.zcml	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,44 @@
+<configure
+  xmlns="http://namespaces.zope.org/zope">
+
+  <include package="zope.component" file="meta.zcml" />
+  <include package="zope.app.component" file="meta.zcml" />
+  <include package="zope.app.publisher" file="meta.zcml" />
+  <include package="zope.app.publication" file="meta.zcml" />
+  <include package="zope.app.security" file="meta.zcml" />
+
+  <include package="zope.app.appsetup" />
+  <include package="zope.app.security" />
+  <include package="zope.app" file="menus.zcml" />
+<!--  <include package="zope.app.error" /> -->
+  <include package="zope.app.publication" />
+
+
+  <utility provides="zope.app.error.interfaces.IErrorReportingUtility"
+           component="megrok.cherry.error.globalErrorReporting" />
+  
+  <unauthenticatedPrincipal
+    id="zope.anybody"
+    title="Unauthenticated User" />
+
+  <unauthenticatedGroup
+    id="zope.Anybody"
+    title="Unauthenticated Users"
+    />
+
+  <authenticatedGroup
+    id="zope.Authenticated"
+    title="Authenticated Users"
+    />
+
+  <everybodyGroup
+    id="zope.Everybody"
+    title="All Users"
+    />
+
+  <include package="grok" file="meta.zcml" />
+  <include package="grok" />
+
+  <include package="megrok.cherry" />
+
+</configure>

Added: megrok.cherry/trunk/src/megrok/__init__.py
===================================================================
--- megrok.cherry/trunk/src/megrok/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/src/megrok/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,8 @@
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)
+

Added: megrok.cherry/trunk/src/megrok/cherry/__init__.py
===================================================================
--- megrok.cherry/trunk/src/megrok/cherry/__init__.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/src/megrok/cherry/__init__.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1 @@
+#

Added: megrok.cherry/trunk/src/megrok/cherry/configure.zcml
===================================================================
--- megrok.cherry/trunk/src/megrok/cherry/configure.zcml	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/src/megrok/cherry/configure.zcml	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,8 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:grok="http://namespaces.zope.org/grok"
+    >
+
+  <grok:grok package="." />
+
+</configure>

Added: megrok.cherry/trunk/src/megrok/cherry/demo.py
===================================================================
--- megrok.cherry/trunk/src/megrok/cherry/demo.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/src/megrok/cherry/demo.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,9 @@
+import grok
+from zope.app.container.interfaces import IContainer
+
+grok.context(IContainer)
+
+class Test(grok.View):
+    def render(self):
+        return "This is a test"
+

Added: megrok.cherry/trunk/src/megrok/cherry/error.py
===================================================================
--- megrok.cherry/trunk/src/megrok/cherry/error.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/src/megrok/cherry/error.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,10 @@
+from zope.interface import implements
+from zope.app.error.interfaces import IErrorReportingUtility
+
+class ErrorReporting(object):
+    implements(IErrorReportingUtility)
+    
+    def raising(self, info, request=None):
+        raise info[0], info[1], info[2]
+        
+globalErrorReporting = ErrorReporting()

Added: megrok.cherry/trunk/src/megrok/cherry/server.py
===================================================================
--- megrok.cherry/trunk/src/megrok/cherry/server.py	2006-11-28 22:05:09 UTC (rev 71321)
+++ megrok.cherry/trunk/src/megrok/cherry/server.py	2006-11-28 22:38:00 UTC (rev 71322)
@@ -0,0 +1,51 @@
+import cherrypy
+from zope.app.wsgi import getWSGIApplication
+from StringIO import StringIO
+from cherrypy import wsgi, _cpwsgiserver
+from zope.component import event # XXX voodoo necessary to make events work
+
+def startServer():
+    f = StringIO('''
+    site-definition /home/faassen/buildout/megrok.cherry/site.zcml
+    
+    <zodb>
+      <filestorage>
+        path /home/faassen/buildout/megrok.cherry/parts/data/Data.fs
+      </filestorage>
+    </zodb>
+
+    <eventlog>
+      <logfile>
+        path STDOUT
+      </logfile>
+    </eventlog>
+    ''')
+    app = getWSGIApplication(f)
+    
+    f.close()
+    server = WSGIServer(app)
+    try:
+        server.start()
+    except (KeyboardInterrupt, SystemExit):
+        server.stop()
+        
+class WSGIServer(wsgi.WSGIServer):
+    def __init__(self, app):
+        server = cherrypy.server
+        sockFile = server.socket_file
+        if sockFile:
+            bind_addr = sockFile
+        else:
+            bind_addr = (server.socket_host, server.socket_port)
+        s = _cpwsgiserver.CherryPyWSGIServer
+        s.__init__(self, bind_addr,
+                   app,
+                   server.thread_pool,
+                   server.socket_host,
+                   request_queue_size = server.socket_queue_size,
+                   timeout = server.socket_timeout,
+                   )
+        self.protocol = server.protocol_version
+        self.ssl_certificate = server.ssl_certificate
+        self.ssl_private_key = server.ssl_private_key
+



More information about the Checkins mailing list