[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 `>` and `<`. 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 »<span tal:replace="context/__name__">WikiPage</span>«</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