|
From: <sub...@co...> - 2008-03-27 15:20:55
|
Author: ianb
Date: 2008-03-27 09:20:54 -0600 (Thu, 27 Mar 2008)
New Revision: 3361
Added:
FormEncode/trunk/docs/form-generation-example/
FormEncode/trunk/docs/form-generation-example/crud_index.html.tmpl
FormEncode/trunk/docs/form-generation-example/generatorapp.py
FormEncode/trunk/docs/form-generation-example/generatoreditable.py
FormEncode/trunk/docs/form-generation-example/index.html.tmpl
FormEncode/trunk/docs/form-generation-example/server.py
FormEncode/trunk/docs/form-generation.txt
Modified:
FormEncode/trunk/docs/news.txt
FormEncode/trunk/tests/test_htmlfill.py
Log:
update news for new release
Added: FormEncode/trunk/docs/form-generation-example/crud_index.html.tmpl
===================================================================
--- FormEncode/trunk/docs/form-generation-example/crud_index.html.tmpl (rev 0)
+++ FormEncode/trunk/docs/form-generation-example/crud_index.html.tmpl 2008-03-27 15:20:54 UTC (rev 3361)
@@ -0,0 +1,35 @@
+{{py:cls_title = crudapp.object_class_title(object_class)}}
+<html>
+ <head>
+ <title>Index of {{cls_title}}</title>
+ </head>
+ <body>
+ <h1>Index of {{cls_title}}</h1>
+
+ <div>
+ Description: {{crudapp.object_class_description(object_class)}}
+ </div>
+
+ <div>
+ <a href="{{req.application_url}}/__create__">Create {{cls_title}}</a>
+ </div>
+
+ Existing objects:
+
+ <ul>
+ {{for name, object in objects:}}
+ {{py:desc = crudapp.object_description(object)}}
+ <li>{{crudapp.object_title(object)}} (<a href="{{req.application_url}}/{{name|url}}">Edit {{name}}</a>)
+ {{if desc}}
+ <br> {{desc}}
+ {{endif}}
+ </li>
+ {{endfor}}
+ </ul>
+
+ {{if not objects:}}
+ <p>No objects exist!</p>
+ {{endif}}
+
+ </body>
+</html>
Added: FormEncode/trunk/docs/form-generation-example/generatorapp.py
===================================================================
--- FormEncode/trunk/docs/form-generation-example/generatorapp.py (rev 0)
+++ FormEncode/trunk/docs/form-generation-example/generatorapp.py 2008-03-27 15:20:54 UTC (rev 3361)
@@ -0,0 +1,111 @@
+from webob import Request, Response
+from webob import exc
+from tempita import HTMLTemplate, html
+import os
+import generatoreditable
+import textwrap
+
+template_dir = os.path.dirname(__file__)
+# Dict of template_name: (template_mtime, template_object)
+_templates = {}
+
+def get_template(template_name):
+ filename = os.path.join(template_dir, template_name)
+ mtime = os.stat(filename).st_mtime
+ if (template_name not in _templates
+ or _templates[template_name][0] < mtime):
+ _templates[template_name] = (mtime, HTMLTemplate.from_filename(filename))
+ return _templates[template_name][1]
+
+
+
+class CrudApp(object):
+
+ def __init__(self):
+ self.objects = {}
+ self.object_classes = dict(
+ State: generatoreditable.State,
+ )
+
+ def __call__(self, environ, start_response):
+ req = Request(environ)
+ name = req.path_info_peek()
+ req.urlvars['generatorapp.root'] = req.application_uri
+ if not name:
+ # Index page:
+ resp = self.index(req)
+ elif name not in self.object_classes:
+ resp = exc.HTTPNotFound(
+ "No class is registered with the name %r" % name)
+ else:
+ resp = self.crud(req, self.object_classes[name])
+ return resp(environ, start_response)
+
+ def index(self, req):
+ tmpl = get_template('index.html.tmpl')
+ return Response(
+ tmpl.substitute(req=req,
+ crudapp=self,
+ object_classes=self.object_classes))
+
+ def crud(self, req, object_class):
+ obj_class = req.urlvars['generatorapp.class']
+ name = req.path_info_peek()
+ if not name:
+ return self.crud_index(req, object_class)
+ elif name == '__create__':
+ return self.crud_edit(req, object_class, None)
+ else:
+ try:
+ obj = obj_class.get_from_url(name, req, self)
+ except exc.HTTPException, e:
+ return e
+ return self.crut_edit(req, object_class, obj)
+
+ def crud_index(self, req, object_class):
+ tmpl = get_template('crud_index.html.tmpl')
+ objects = object_class.get_all(req, crudapp)
+ if isinstance(objects, dict):
+ objects = sorted(objects.iteritems())
+ if not isinstance(objects, list):
+ objects = list(objects)
+ return Response(
+ tmpl.substitute(req=req,
+ crudapp=self,
+ objects=objects,
+ object_class=object_class))
+
+ ## Adapters for use with templates:
+
+ def render_template(self_, obj, attr, default, **kw):
+ tmpl = getattr(obj, attr, None)
+ if not tmpl:
+ return default
+ if isinstance(obj, type):
+ obj_class = obj
+ else:
+ obj_class = obj.__class__
+ if isinstance(tmpl, basestring):
+ tmpl = textwrap.dedent(tmpl).strip()
+ tmpl = HTMLTemplate(tmpl, name="%s.%s.%s" %
+ (obj_class.__module__, object_class.__name__, attr))
+ return tmpl.substitute(crudapp=self_, **kw)|html
+
+ def object_class_title(self, req, cls):
+ return self.render_template(
+ cls, 'class_title', cls.__name__, req=req, class_=cls)
+
+ def object_class_description(self, req, cls):
+ return self.render_template(
+ cls, 'class_description', cls.__doc__, req=req, class_=cls)
+
+ def object_title(self, req, obj):
+ return self.render_template(
+ obj, 'title', repr(obj), req=req, self=obj)
+
+ def object_description(self, req, obj):
+ return self.render_template(
+ obj, 'description', None, req=req, self=obj)
+
+
+
Property changes on: FormEncode/trunk/docs/form-generation-example/generatorapp.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: FormEncode/trunk/docs/form-generation-example/generatoreditable.py
===================================================================
--- FormEncode/trunk/docs/form-generation-example/generatoreditable.py (rev 0)
+++ FormEncode/trunk/docs/form-generation-example/generatoreditable.py 2008-03-27 15:20:54 UTC (rev 3361)
@@ -0,0 +1,166 @@
+from formencode.schema import Schema
+from formencode import validators
+
+class AbstractEditable(object):
+ """
+ This represents a basic editable object. It's more of a
+ self-documenting example; you should not subclass this object.
+ """
+
+ class_title = '''
+ Title that can use class_ and req variables. Defaults to __name__ if not set
+ '''
+
+ class_description = '''
+ Longer description that can use class_ and req variables. Default to __doc__ if not set.
+ '''
+
+ title = '''
+ Title for an instance that can use self and req variables. Defaults to repr() if not set.
+ '''
+
+ description = '''
+ Description for an instance that can use self and req variables. Defaults to empty if not set.
+ '''
+
+ @classmethod
+ def get_from_url(cls, urlsegment, req, crudapp):
+ """
+ Gets one object from a url segment. You should only use the
+ req object for access control. You may raise a
+ webob.exc.HTTPException exception.
+ """
+ raise NotImplementedError
+
+ @classmethod
+ def get_all(cls, req, crudapp):
+ """
+ Returns all the objects for this class, as a dictionary of
+ {urlsegment: object} or a list of [(urlsegment, object)] (or
+ an iterator of those tuples).
+ """
+ raise NotImplementedError
+
+ @classmethod
+ def default_attrs(cls, req, crudapp):
+ """
+ Returns a dictionary of the default attrs for a new object
+ """
+ raise NotImplementedError
+
+ @classmethod
+ def create_object(cls, req, crudapp, **attrs):
+ """
+ Creates a new object using the given attrs, and returns that object.
+ """
+ raise NotImplementedError
+
+ def object_attrs(self, req, crudapp):
+ """
+ Returns the object attrs for this object.
+ """
+ raise NotImplementedError
+
+ urlsegment = property(doc="Gives the instance's urlsegment for fetching")
+
+ def update_object(self, req, crudapp, **attrs):
+ """
+ Updates an existing instance using the given attrs.
+ """
+ raise NotImplementedError
+
+ # Used to provide an ordering for fields; if this exists then any
+ # fields not listed will end up last. Other fields will be
+ # ordered according to this:
+ field_order = ['field1', 'field2']
+
+ # A schema to validate and convert the attrs:
+ schema = Schema()
+
+ @classmethod
+ def validate_fields(cls, attrs, req, crudapp, state, self):
+ """
+ This is a simpler setup for validating the attrs; it may
+ update the attrs in-place.
+
+ state is the formencode state object
+
+ self will be None if this is a create
+ """
+ pass
+
+ @classmethod
+ def fields(cls, req, crudapp, self=None):
+ """
+ A classmethod (if called on an instance, then self is also
+ passed in). This returns a list of fields to use to edit the
+ form.
+ """
+
+
+class State(object):
+
+ _states = {}
+
+ class_title = 'States'
+ class_description = '''
+ All the states. Contains the full state name and the state postal code.
+ '''
+ title = '''The state {{self.name}} (code {{self.code}})'''
+
+ def __init__(self, name, code):
+ self.name = name
+ self.code = code
+ self._states[code] = self
+
+ @classmethod
+ def get_from_url(cls, urlsegment, req, crudapp):
+ ## FIXME: not found?
+ return cls._states[urlsegment]
+
+ @classmethod
+ def get_all(cls, req, crudapp):
+ return cls._states
+
+ @classmethod
+ def create_object(cls, req, crudapp, name, code):
+ return cls(name, code)
+
+ def object_attrs(self, req, crudapp):
+ return dict(name=self.name, code=self.code)
+
+ @property
+ def urlsegment(self):
+ return self.code
+
+ def update_object(self, req, crudapp, name, code):
+ self.name = name
+ self.code = code
+
+ field_order = ['name', 'code']
+
+ def validate_fields(self, attrs, req, crudapp, state):
+ code = attrs['code']
+ if self is None:
+ if code in self._states:
+ return {'code': 'A state with the code %s already exists' % code}
+ attrs['code'] = attrs['code'].upper()
+
+ class schema(Schema):
+ name = validators.String(not_empty=True)
+ code = validators.String(min=2, max=2)
+
+class Address(object):
+
+ _addresses = []
+
+ def __init__(self, name, street, city, state, postal):
+ self.name = name
+ self.street = street
+ self.city = city
+ self.state = state
+ self.postal = postal
+ self._addresses.append(self)
+
+
+
Property changes on: FormEncode/trunk/docs/form-generation-example/generatoreditable.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: FormEncode/trunk/docs/form-generation-example/index.html.tmpl
===================================================================
--- FormEncode/trunk/docs/form-generation-example/index.html.tmpl (rev 0)
+++ FormEncode/trunk/docs/form-generation-example/index.html.tmpl 2008-03-27 15:20:54 UTC (rev 3361)
@@ -0,0 +1,21 @@
+<html>
+ <head>
+ <title>Index of Object Types</title>
+ </head>
+ <body>
+ <h1>Index of Object Types</h1>
+
+ <ul>
+ {{for class_name in object_classes:}}
+ <li><a href="{{req.application_url}}/{{class_name|url}}">{{class_name}}</a></li>
+ {{endfor}}
+ </ul>
+
+ {{if not object_classes:}}
+ No classes are defined!
+ {{endif}}
+
+ </body>
+</html>
+
+
Added: FormEncode/trunk/docs/form-generation-example/server.py
===================================================================
--- FormEncode/trunk/docs/form-generation-example/server.py (rev 0)
+++ FormEncode/trunk/docs/form-generation-example/server.py 2008-03-27 15:20:54 UTC (rev 3361)
@@ -0,0 +1,26 @@
+import sys, os
+
+# This sets up the path so we can import the other modules:
+sys.path.insert(0, os.path.dirname(__file__))
+
+from generatorapp import CrudApp
+
+if __name__ == '__main__':
+ import optparse
+ parser = optparse.OptionParser(
+ usage='%prog --port=PORT BASE_DIRECTORY'
+ )
+ parser.add_option(
+ '-p', '--port',
+ default='8080',
+ dest='port',
+ type='int',
+ help='Port to serve on (default 8080)')
+ app = CrudApplication()
+ from wsgiref.simple_server import make_server
+ httpd = make_server('localhost', options.port, app)
+ print 'Serving on http://localhost:%s' % options.port
+ try:
+ httpd.serve_forever()
+ except KeyboardInterrupt:
+ print '^C'
Property changes on: FormEncode/trunk/docs/form-generation-example/server.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: FormEncode/trunk/docs/form-generation.txt
===================================================================
--- FormEncode/trunk/docs/form-generation.txt (rev 0)
+++ FormEncode/trunk/docs/form-generation.txt 2008-03-27 15:20:54 UTC (rev 3361)
@@ -0,0 +1,16 @@
+Form Generation with FormEncode and htmlfill
+++++++++++++++++++++++++++++++++++++++++++++
+
+.. contents::
+
+Introduction
+============
+
+This is a tutorial to teach you how to do form generation using FormEncode for `validation <Validation.html>`_, and `htmlfill <htmlfill.html>`_ for filling the forms, together with your own ad hoc code to generate the HTML.
+
+This tutorial does not use a framework, as several frameworks that use FormEncode have their own wrappers for invoking validation. These wrappers are typically closely bound to the framework and cover up some of the steps. While this is fine and those wrappers are useful, they could be distracting in this example. A very simple framework using `WebOb <http://pythonpaste.org/webob/>`_ is used in these examples. For templating `Tempita <http://pythonpaste.org/tempita/>`_ will be used.
+
+The example itself is a simple CRUD framework that wraps arbitrary objects that have had some annotation added to the class. These annotations will be read to do the validation and form generation. In a way this tutorial is showing how to create a simple framework for form generation.
+
+The example code used here is available in its finished form in `form-generation-example/ <form-generation-example/>`_.
+
Property changes on: FormEncode/trunk/docs/form-generation.txt
___________________________________________________________________
Name: svn:keywords
+ LastChangedDate LastChangedRevision
Name: svn:eol-style
+ native
Modified: FormEncode/trunk/docs/news.txt
===================================================================
--- FormEncode/trunk/docs/news.txt 2008-03-26 17:17:48 UTC (rev 3360)
+++ FormEncode/trunk/docs/news.txt 2008-03-27 15:20:54 UTC (rev 3361)
@@ -3,8 +3,8 @@
.. contents::
-svn trunk
----------
+1.0.1
+-----
* ``chained_validators`` were removed from Schema somehow; now
replaced and working.
Modified: FormEncode/trunk/tests/test_htmlfill.py
===================================================================
--- FormEncode/trunk/tests/test_htmlfill.py 2008-03-26 17:17:48 UTC (rev 3360)
+++ FormEncode/trunk/tests/test_htmlfill.py 2008-03-27 15:20:54 UTC (rev 3361)
@@ -88,3 +88,8 @@
{}, {}) ==
u'<input type="submit" value="Japan - 日本 Nihon" />')
+def test_xhtml():
+ result = htmlfill.render('<form:error name="code"/>', errors={'code': 'an error'})
+ print result
+ assert 0
+
|