[Checkins] SVN: grok/trunk/doc/ Design notes.

Philipp von Weitershausen philikon at philikon.de
Sun Oct 15 13:55:43 EDT 2006


Log message for revision 70664:
  Design notes.
  

Changed:
  A   grok/trunk/doc/
  A   grok/trunk/doc/design/
  A   grok/trunk/doc/design/adapters.py
  A   grok/trunk/doc/design/addform.py
  A   grok/trunk/doc/design/annotations.py
  A   grok/trunk/doc/design/container.py
  A   grok/trunk/doc/design/grok_beginner.txt
  A   grok/trunk/doc/design/grok_dev.txt
  A   grok/trunk/doc/design/model.py
  A   grok/trunk/doc/design/permission.py
  A   grok/trunk/doc/design/subscriber.py
  A   grok/trunk/doc/design/traversal.py
  A   grok/trunk/doc/design/utility.py
  A   grok/trunk/doc/design/views.py
  A   grok/trunk/doc/design/widget.py

-=-
Added: grok/trunk/doc/design/adapters.py
===================================================================
--- grok/trunk/doc/design/adapters.py	2006-10-15 17:51:41 UTC (rev 70663)
+++ grok/trunk/doc/design/adapters.py	2006-10-15 17:55:42 UTC (rev 70664)
@@ -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
+
+    # ...


Property changes on: grok/trunk/doc/design/adapters.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/doc/design/addform.py
===================================================================
--- grok/trunk/doc/design/addform.py	2006-10-15 17:51:41 UTC (rev 70663)
+++ grok/trunk/doc/design/addform.py	2006-10-15 17:55:42 UTC (rev 70664)
@@ -0,0 +1 @@
+#XXX


Property changes on: grok/trunk/doc/design/addform.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/doc/design/annotations.py
===================================================================
--- grok/trunk/doc/design/annotations.py	2006-10-15 17:51:41 UTC (rev 70663)
+++ grok/trunk/doc/design/annotations.py	2006-10-15 17:55:42 UTC (rev 70664)
@@ -0,0 +1,17 @@
+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):
+        self.comments = OOTreeSet()
+
+    def addComment(self, text):
+        self.comments.insert(text)
+
+    def getComments(self):
+        return list(self.comments)


Property changes on: grok/trunk/doc/design/annotations.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/doc/design/container.py
===================================================================
--- grok/trunk/doc/design/container.py	2006-10-15 17:51:41 UTC (rev 70663)
+++ grok/trunk/doc/design/container.py	2006-10-15 17:55:42 UTC (rev 70664)
@@ -0,0 +1,8 @@
+import grok
+
+class Contact(grok.Model):
+    pass
+
+class AddressBook(grok.App, grok.Container):
+    pass
+


Property changes on: grok/trunk/doc/design/container.py
___________________________________________________________________
Name: svn:eol-style
   + native

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


Property changes on: grok/trunk/doc/design/grok_beginner.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/doc/design/grok_dev.txt
===================================================================
--- grok/trunk/doc/design/grok_dev.txt	2006-10-15 17:51:41 UTC (rev 70663)
+++ grok/trunk/doc/design/grok_dev.txt	2006-10-15 17:55:42 UTC (rev 70664)
@@ -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
+


Property changes on: grok/trunk/doc/design/grok_dev.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/doc/design/model.py
===================================================================
--- grok/trunk/doc/design/model.py	2006-10-15 17:51:41 UTC (rev 70663)
+++ grok/trunk/doc/design/model.py	2006-10-15 17:55:42 UTC (rev 70664)
@@ -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)


Property changes on: grok/trunk/doc/design/model.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/doc/design/permission.py
===================================================================
--- grok/trunk/doc/design/permission.py	2006-10-15 17:51:41 UTC (rev 70663)
+++ grok/trunk/doc/design/permission.py	2006-10-15 17:55:42 UTC (rev 70664)
@@ -0,0 +1,36 @@
+import grok
+
+grok.permission('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')
+


Property changes on: grok/trunk/doc/design/permission.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/doc/design/subscriber.py
===================================================================
--- grok/trunk/doc/design/subscriber.py	2006-10-15 17:51:41 UTC (rev 70663)
+++ grok/trunk/doc/design/subscriber.py	2006-10-15 17:55:42 UTC (rev 70664)
@@ -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???
+


Property changes on: grok/trunk/doc/design/subscriber.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/doc/design/traversal.py
===================================================================
--- grok/trunk/doc/design/traversal.py	2006-10-15 17:51:41 UTC (rev 70663)
+++ grok/trunk/doc/design/traversal.py	2006-10-15 17:55:42 UTC (rev 70664)
@@ -0,0 +1,39 @@
+import grok
+
+class Appointment(grok.Model):
+    pass
+
+class Day(grok.Model):
+
+    @grok.traverse
+    def getAppointment(self, number):
+        if number in self.appointments:
+            return Appointment(number)
+        return None # try to look up views then
+
+class Calendar(grok.Model):
+
+    @grok.traverse
+    def getYear(self, year):
+        return Year(year)
+
+
+"""
+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


Property changes on: grok/trunk/doc/design/traversal.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/doc/design/utility.py
===================================================================
--- grok/trunk/doc/design/utility.py	2006-10-15 17:51:41 UTC (rev 70663)
+++ grok/trunk/doc/design/utility.py	2006-10-15 17:55:42 UTC (rev 70664)
@@ -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
+


Property changes on: grok/trunk/doc/design/utility.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/doc/design/views.py
===================================================================
--- grok/trunk/doc/design/views.py	2006-10-15 17:51:41 UTC (rev 70663)
+++ grok/trunk/doc/design/views.py	2006-10-15 17:55:42 UTC (rev 70664)
@@ -0,0 +1,94 @@
+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_html = """
+<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)
+    """
+
+    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_html = """
+<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 ...
+


Property changes on: grok/trunk/doc/design/views.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/doc/design/widget.py
===================================================================
--- grok/trunk/doc/design/widget.py	2006-10-15 17:51:41 UTC (rev 70663)
+++ grok/trunk/doc/design/widget.py	2006-10-15 17:55:42 UTC (rev 70664)
@@ -0,0 +1 @@
+#XXX


Property changes on: grok/trunk/doc/design/widget.py
___________________________________________________________________
Name: svn:eol-style
   + native



More information about the Checkins mailing list