[Checkins] SVN: grokapps/gbe99bottles/ Initial import

Michael Haubenwallner michael at d2m.at
Sat Aug 16 03:21:59 EDT 2008


Log message for revision 89905:
  Initial import

Changed:
  A   grokapps/gbe99bottles/
  A   grokapps/gbe99bottles/README.txt
  A   grokapps/gbe99bottles/REVIEW.txt
  A   grokapps/gbe99bottles/bootstrap.py
  A   grokapps/gbe99bottles/buildout.cfg
  A   grokapps/gbe99bottles/setup.py
  A   grokapps/gbe99bottles/src/
  A   grokapps/gbe99bottles/src/gbe99bottles/
  A   grokapps/gbe99bottles/src/gbe99bottles/__init__.py
  A   grokapps/gbe99bottles/src/gbe99bottles/app.py
  A   grokapps/gbe99bottles/src/gbe99bottles/app.txt
  A   grokapps/gbe99bottles/src/gbe99bottles/configure.zcml
  A   grokapps/gbe99bottles/src/gbe99bottles/ftesting.zcml
  A   grokapps/gbe99bottles/src/gbe99bottles/tests.py

-=-
Added: grokapps/gbe99bottles/README.txt
===================================================================
--- grokapps/gbe99bottles/README.txt	                        (rev 0)
+++ grokapps/gbe99bottles/README.txt	2008-08-16 07:21:58 UTC (rev 89905)
@@ -0,0 +1,71 @@
+Grok-by-Example: 99 Bottles Song
+================================
+
+:Author: d2m (michael at d2m.at)
+:Motivation: look at the original source and the Grok code side-by-side 
+             and deduce from both
+
+A basic Grok 'Hello world' application [source__], cf implementations in other
+languages at the '99 Bootles of Beer' website, eg. the Python version [source__].
+
+__ http://svn.zope.org/grokapps/gbe99bottles/src/gbe99bottles/app.py?view=markup
+__ http://99-bottles-of-beer.net/language-python-808.html
+
+
+Overview
+--------
+
+The '99 Bottles of Beer' website holds a collection of the Song 99 Bottles of 
+Beer programmed in different programming languages. Actually the song is 
+represented in more than 1200 different programming languages and variations.
+
+The Grok implementation uses persistent 'wall' and 'bottle' objects. It follows
+the song lyrics by putting bottles on the wall, taking them off again and at
+each step reporting the state of the scenery.
+
+Usage
+-----
+
+This example is a complete Grok app on its own. Here is how to use it::
+
+	# checkout the example to your harddisk
+	svn co svn://svn.zope.org/repos/main/grokapps/gbe99bottles
+	
+	# change to the newly created directory
+	cd gbe99bottles
+	
+	# make it a virtualenv 
+	virtualenv --no-site-packages .
+	
+	# activate the virtualenv
+	source bin/activate
+	
+	# bootstrap the buildout environment
+	bin/python bootstrap.py
+	
+	# run the buildout
+	bin/buildout
+	
+	# test the example app
+	bin/test
+	
+	# run the example app
+	bin/zopectl fg
+	
+	# point your browser to
+	http://localhost:8080
+	
+	# login
+	username: grok
+	password: grok
+	
+	# create an instance of the registered grok app
+	# and use it	
+	
+That's it!
+
+Need help? There is the Grok Users mailinglist at grok-dev at zope.org
+(http://mail.zope.org/mailman/listinfo/grok-dev), 
+the Grok IRC channel at irc.freenode.net/#grok
+and the Grok website at http://grok.zope.org
+

Added: grokapps/gbe99bottles/REVIEW.txt
===================================================================
--- grokapps/gbe99bottles/REVIEW.txt	                        (rev 0)
+++ grokapps/gbe99bottles/REVIEW.txt	2008-08-16 07:21:58 UTC (rev 89905)
@@ -0,0 +1,126 @@
+Grok-by-Example: 99 Bottles Song
+================================
+
+:Author: d2m (michael at d2m.at)
+
+A basic Grok 'Hello world' application [source__], cf implementations in other
+languages at the '99 Bootles of Beer' website, eg. the Python version [source__].
+
+__ http://svn.zope.org/grokapps/gbe99bottles/src/gbe99bottles/app.py?view=markup
+__ http://99-bottles-of-beer.net/language-python-808.html
+
+
+Overview
+========
+
+The '99 Bottles of Beer' website holds a collection of the Song 99 Bottles of 
+Beer programmed in different programming languages. Actually the song is 
+represented in more than 1200 different programming languages and variations.
+
+The Grok implementation uses persistent 'Wall' and 'Bottle' objects. It follows
+the song lyrics by putting bottles on the wall, taking them off again and - at
+each step - reporting the state of the scenery.
+
+Review
+~~~~~~
+
+This example might look like a stupid exercise to you, but it shows a few fine 
+features of the Grok framework:
+
+Application initialization
+--------------------------
+
+When we create the 'Song' Application object, we want to set it to an initial 
+state, like creating a Wall, a number of Bootles and add them to the Wall.
+
+Grok also is an event driven framework. Events can be sent and subscribed to.
+When the Application object is created it is added to the object graph an 
+'ObjectAddedEvent' is sent by the framework. We can now simply *subscribe* to
+that event (or the Interface that describes the event type) and initialize our
+application objects by adding a container and filling it with objects::
+
+    @grok.subscribe(Song, grok.IObjectAddedEvent)
+    def handle(obj, event):
+        obj['wall']=wall=Wall()
+        wall.add_99_bottles()
+
+More on events can be found at the `Grok website`__.
+
+__ http://grok.zope.org/doc/current/grok_overview.html#events
+
+Request
+-------
+
+The application accepts only one request: the default view.
+
+Grok uses 'index' for its default view name (lowercase of the class name). 
+This view class is bound to the 'Song' application object::
+
+    class Index(grok.View):
+        grok.context(Song)
+
+Response
+--------
+
+The view classes are created and called for each Request. The 'update' and 
+'render' methods are essential parts of rendering a Response from a view class.
+
+'update' - if existing - is always called first. It allows us to collect request
+parameters and bind them to properties or - in the example - create a shortcut 
+to the Wall object::
+
+    def update(self):
+        self.wall=self.context['wall']
+
+'render' is called last, its output is returned to the user.
+
+Models
+------
+
+Grok content objects are subclassed from grok.Model or grok.Container. 
+
+    class Bottle(grok.Model):
+        pass
+    
+    class Wall(grok.Container):
+        ...
+
+The application object itself is subclassed from grok.Application and 
+grok.Container.
+
+    class Song(grok.Application,grok.Container):
+        pass
+
+Persistent Storage
+------------------
+
+When a Response is created by the view class Bottle objects are removed from and
+added to the Wall container by calling methods of the Wall instance object::
+
+    def remove_a_bottle(self):
+        bottles=list(self.keys())
+        del self[bottles[-1]]
+
+    def add_99_bottles(self):
+        for n in range(1,100):
+            self[str(n)]=Bottle()
+
+Searching
+---------
+
+The objects can simply be queried using python::
+
+    def contents(self):
+        return len(self.items())
+
+Testing
+-------
+
+A functional doctest ('app.txt') is added to ensure correct operations.
+
+Overall
+-------
+
+It was fun and easy to implement the '99 Bottles' Song using persistent objects.
+Functional testing made development even simpler.
+

Added: grokapps/gbe99bottles/bootstrap.py
===================================================================
--- grokapps/gbe99bottles/bootstrap.py	                        (rev 0)
+++ grokapps/gbe99bottles/bootstrap.py	2008-08-16 07:21:58 UTC (rev 89905)
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# 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 85041 2008-03-31 15:57:30Z andreasjung $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+try:
+    import pkg_resources
+except ImportError:
+    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
+
+if sys.platform == 'win32':
+    def quote(c):
+        if ' ' in c:
+            return '"%s"' % c # work around spawn lamosity on windows
+        else:
+            return c
+else:
+    def quote (c):
+        return c
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+ws  = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, quote (sys.executable),
+    '-c', quote (cmd), '-mqNxd', quote (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)
+
+# grokproject specific addition to standard bootstrap.py:
+# Install eggbasket too.
+zc.buildout.buildout.main(sys.argv[1:] + ['install', 'eggbasket'])

Added: grokapps/gbe99bottles/buildout.cfg
===================================================================
--- grokapps/gbe99bottles/buildout.cfg	                        (rev 0)
+++ grokapps/gbe99bottles/buildout.cfg	2008-08-16 07:21:58 UTC (rev 89905)
@@ -0,0 +1,74 @@
+[buildout]
+develop = .
+parts = eggbasket app data zopectl i18n test
+newest = false
+extends = http://grok.zope.org/releaseinfo/grok-0.13.cfg
+# eggs will be installed in the default buildout location
+# (see .buildout/default.cfg in your home directory)
+# unless you specify an eggs-directory option here.
+
+versions = versions
+
+[app]
+recipe = zc.zope3recipes>=0.5.3:application
+eggs = gbe99bottles
+site.zcml = <include package="gbe99bottles" />
+            <include package="zope.app.twisted" />
+
+            <configure i18n_domain="gbe99bottles">
+              <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" />
+              <principal id="zope.manager"
+                         title="Manager"
+                         login="grok"
+                         password_manager="Plain Text"
+                         password="grok"
+                         />
+
+              <!-- Replace the following directive if you do not want
+                   public access -->
+              <grant permission="zope.View"
+                     principal="zope.Anybody" />
+              <grant permission="zope.app.dublincore.view"
+                     principal="zope.Anybody" />
+
+              <role id="zope.Manager" title="Site Manager" />
+              <role id="zope.Member" title="Site Member" />
+              <grantAll role="zope.Manager" />
+              <grant role="zope.Manager"
+                     principal="zope.manager" />
+           </configure>
+
+[data]
+recipe = zc.recipe.filestorage
+
+# this section named so that the start/stop script is called bin/zopectl
+[zopectl]
+recipe = zc.zope3recipes:instance
+application = app
+zope.conf = ${data:zconfig}
+			devmode on
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = gbe99bottles
+defaults = ['--tests-pattern', '^f?tests$', '-v']
+
+# this section named so that the i18n scripts are called bin/i18n...
+[i18n]
+recipe = lovely.recipe:i18n
+package = gbe99bottles
+domain = gbe99bottles
+location = src/gbe99bottles
+output = locales
+
+[eggbasket]
+recipe = z3c.recipe.eggbasket
+eggs = grok
+url = http://grok.zope.org/releaseinfo/grok-eggs-0.13.tgz

Added: grokapps/gbe99bottles/setup.py
===================================================================
--- grokapps/gbe99bottles/setup.py	                        (rev 0)
+++ grokapps/gbe99bottles/setup.py	2008-08-16 07:21:58 UTC (rev 89905)
@@ -0,0 +1,34 @@
+from setuptools import setup, find_packages
+
+version = '0.1'
+
+setup(name='gbe99bottles',
+      version=version,
+      description="Grok-by-Example: 99 Bottles App",
+      long_description="""\
+      created for http://99-bottles-of-beer.net (language: Grok)
+""",
+      # Get strings from http://www.python.org/pypi?%3Aaction=list_classifiers
+      classifiers=['Development Status :: 4 - Beta',
+                   'Framework :: Zope3',
+                   'License :: OSI Approved :: Zope Public License',
+                   'Programming Language :: Python',
+                   'Programming Language :: Zope',], 
+      keywords="Grok Example",
+      author="d2m",
+      author_email="michael at d2m.at",
+      url="http://blog.d2m.at",
+      license="",
+      package_dir={'': 'src'},
+      packages=find_packages('src'),
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=['setuptools',
+                        'grok',
+                        'z3c.testsetup',
+                        # Add extra requirements here
+                        ],
+      entry_points="""
+      # Add entry points here
+      """,
+      )

Added: grokapps/gbe99bottles/src/gbe99bottles/__init__.py
===================================================================
--- grokapps/gbe99bottles/src/gbe99bottles/__init__.py	                        (rev 0)
+++ grokapps/gbe99bottles/src/gbe99bottles/__init__.py	2008-08-16 07:21:58 UTC (rev 89905)
@@ -0,0 +1 @@
+# this directory is a package

Added: grokapps/gbe99bottles/src/gbe99bottles/app.py
===================================================================
--- grokapps/gbe99bottles/src/gbe99bottles/app.py	                        (rev 0)
+++ grokapps/gbe99bottles/src/gbe99bottles/app.py	2008-08-16 07:21:58 UTC (rev 89905)
@@ -0,0 +1,68 @@
+import grok
+
+class Song(grok.Application,grok.Container):
+    pass
+
+class Bottle(grok.Model):
+    pass
+
+class Wall(grok.Container):
+    
+    def add_99_bottles(self):
+        for n in range(1,100):
+            self[str(n)]=Bottle()
+
+    def remove_a_bottle(self):
+        bottles=list(self.keys())
+        del self[bottles[-1]]
+        
+    def describe_me(self):
+        num_bottles=self.contents()
+        if num_bottles == 0:
+            text='No more bottles of beer'
+        elif num_bottles == 1:
+            text='1 bottle of beer'
+        else:
+            text='%d bottles of beer' % num_bottles
+        return text
+    
+    def contents(self):
+        return len(self.items())
+
+class Index(grok.View):
+    grok.context(Song)
+
+    def still_beer(self):
+        return self.wall.contents() > 0
+    
+    def take_one_down(self):
+        self.wall.remove_a_bottle()
+
+    def buy_some_more(self):
+        self.wall.add_99_bottles()
+        
+    def describe_wall(self):
+        return self.wall.describe_me()
+        
+    def update(self):
+        self.wall=self.context['wall']
+        
+    def render(self):
+        out=[]
+        while self.still_beer():
+            description=self.describe_wall()
+            out.append('%s on the wall, %s.' % (description, description.lower()))
+            self.take_one_down()
+            description=self.describe_wall()
+            out.append('Take one down and pass it around, %s on the wall.\n' % description.lower())
+        description=self.describe_wall()
+        out.append('%s on the wall, %s.' % (description, description.lower()))
+        self.buy_some_more()
+        description=self.describe_wall()
+        out.append('Go to the store and buy some more, %s on the wall.' % description.lower())
+        return '\n'.join(out)
+        
+ at grok.subscribe(Song, grok.IObjectAddedEvent)
+def handle(obj, event):
+    obj['wall']=wall=Wall()
+    wall.add_99_bottles()

Added: grokapps/gbe99bottles/src/gbe99bottles/app.txt
===================================================================
--- grokapps/gbe99bottles/src/gbe99bottles/app.txt	                        (rev 0)
+++ grokapps/gbe99bottles/src/gbe99bottles/app.txt	2008-08-16 07:21:58 UTC (rev 89905)
@@ -0,0 +1,43 @@
+Do a functional doctest test on the app.
+========================================
+
+:Test-Layer: functional
+
+Let's first create an instance of the Wall at the top level:
+
+   >>> from gbe99bottles.app import Wall
+   >>> root = getRootFolder()
+   >>> root['wall'] = Wall()
+
+Creating the wall implicitely puts 99 beers on it.
+
+   >>> root['wall'].contents()
+   99
+
+Create a Browser and visit the instance you just created:
+
+   >>> from zope.testbrowser.testing import Browser
+   >>> browser = Browser()
+   >>> browser.open('http://localhost/wall')
+
+Sing the '99 Bottles of Beer' Song (original lyrics at
+http://99-bottles-of-beer.net/lyrics.html):
+
+   >>> print browser.contents
+   99 bottles of beer on the wall, 99 bottles of beer.
+   Take one down and pass it around, 98 bottles of beer on the wall.
+   ...
+   2 bottles of beer on the wall, 2 bottles of beer.
+   Take one down and pass it around, 1 bottle of beer on the wall.
+   <BLANKLINE>
+   1 bottle of beer on the wall, 1 bottle of beer.
+   Take one down and pass it around, no more bottles of beer on the wall.
+   <BLANKLINE>
+   No more bottles of beer on the wall, no more bottles of beer.
+   Go to the store and buy some more, 99 bottles of beer on the wall.
+
+Again check the number of beers on the wall.
+
+   >>> root['wall'].contents()
+   99
+   
\ No newline at end of file

Added: grokapps/gbe99bottles/src/gbe99bottles/configure.zcml
===================================================================
--- grokapps/gbe99bottles/src/gbe99bottles/configure.zcml	                        (rev 0)
+++ grokapps/gbe99bottles/src/gbe99bottles/configure.zcml	2008-08-16 07:21:58 UTC (rev 89905)
@@ -0,0 +1,6 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:grok="http://namespaces.zope.org/grok">
+  <include package="grok" />
+  <includeDependencies package="." />
+  <grok:grok package="." />
+</configure>

Added: grokapps/gbe99bottles/src/gbe99bottles/ftesting.zcml
===================================================================
--- grokapps/gbe99bottles/src/gbe99bottles/ftesting.zcml	                        (rev 0)
+++ grokapps/gbe99bottles/src/gbe99bottles/ftesting.zcml	2008-08-16 07:21:58 UTC (rev 89905)
@@ -0,0 +1,35 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   i18n_domain="gbe99bottles"
+   package="gbe99bottles"
+   >
+
+  <include package="grok" />
+  <include package="gbe99bottles" />
+
+  <!-- Typical functional testing security setup -->
+  <securityPolicy
+      component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy"
+      />
+
+  <unauthenticatedPrincipal
+      id="zope.anybody"
+      title="Unauthenticated User"
+      />
+  <grant
+      permission="zope.View"
+      principal="zope.anybody"
+      />
+
+  <principal
+      id="zope.mgr"
+      title="Manager"
+      login="mgr"
+      password="mgrpw"
+      />
+
+  <role id="zope.Manager" title="Site Manager" />
+  <grantAll role="zope.Manager" />
+  <grant role="zope.Manager" principal="zope.mgr" />
+
+</configure>

Added: grokapps/gbe99bottles/src/gbe99bottles/tests.py
===================================================================
--- grokapps/gbe99bottles/src/gbe99bottles/tests.py	                        (rev 0)
+++ grokapps/gbe99bottles/src/gbe99bottles/tests.py	2008-08-16 07:21:58 UTC (rev 89905)
@@ -0,0 +1,12 @@
+import os.path
+import z3c.testsetup
+import gbe99bottles
+from zope.app.testing.functional import ZCMLLayer
+
+
+ftesting_zcml = os.path.join(
+    os.path.dirname(gbe99bottles.__file__), 'ftesting.zcml')
+FunctionalLayer = ZCMLLayer(ftesting_zcml, __name__, 'FunctionalLayer',
+                            allow_teardown=True)
+
+test_suite = z3c.testsetup.register_all_tests('gbe99bottles')



More information about the Checkins mailing list