[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 `>` 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.
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