.. testsetup:: * import kajiki import ew .. testsetup:: resource import kajiki import ew Template=kajiki.XMLTemplate(filename='data/master.html') class c: pass class g: pass EasyWidgets Tutorial ======================================= Intro -------- EasyWidgets is a minimalistic reimplementation of TurboGears widgets. It does not, however, depend on TurboGears being installed in order to be useful. A widget provides one or more of the following features, all bundled together: * HTML markup (via a templating engine) * Validation (for forms) * Static resources to be injected into the rendered HTML page This tutorial will demonstrate how to integrate widgets into a simple TurboGears application as well as how to create your own widgets. Prerequisites ------------------- EasyWidgets is designed to work with Python 2.6 or higher and the excellent FormEncode form validation library. To install EasyWidgets, you can use pip or easy_install:: easy_install EasyWidgets pip install EasyWidgets You should also install a templating language such as kajiki or jinja2 (also available via pip or easy_install). Your First Widget ----------------------- The most basic widget does absolutely nothing and has no visible representation. This is boring, so let's do a simple "hello, world" style widget: >>> import ew >>> w = ew.Widget(template=ew.Snippet('Hello, world!')) >>> w.display() literal(u'Hello, world!') Two things to note here. First is that widgets can be configured to use either snippets (python strings containing template text) or files (filenames of templates on disk). You can also specify a template engine to use for your template. In this case, we omitted the template engine name and thus are using the core-ew engine, which provides simple expression substitution. .. note:: Template Engines You can also use one of the following engines, based on which template languages you have installed: json render a jsonified dictionary core-ew basic expression substitution using ${...} genshi Genshi templating engine jinja2 Jinja2 templating engine kajiki Kajiki templating engine, with output type autodetect kajiki-text Kajiki text templating kajiki-html4 Kajiki html4 templating kajiki-html5 Kajiki html5 templating kajiki-xml Kajiki xml/xhtml templating Many of the examples in this tutorial will use the Kajiki templating engine, though they should be trivially translatable to other engines. The second thing to note in the example above is the use of the `literal` object to wrap the output of the template. This is the `webhelpers.html.literal` subclass of `unicode`, and provides an `__html__` method which is used by many web frameworks to mark the strings as html-safe. (Since templates are used for rendering html, they are safe to embed without further escaping into your output page.) Let's add a tiny bit of interactivity to the template by using a variable: >>> w = ew.Widget(template=ew.Snippet('Hello, ${name}!')) >>> w.display(name=None) literal(u'Hello, None!') >>> w.display(name='Rick') literal(u'Hello, Rick!') Here we see that we can customize the output of the widget by passing in variables. But that "Hello, None" is ugly. Wouldn't it be better to use a default value? Let's try that, in the process looking at an alternative method for defining templates that you'll probably use a good bit more than ew.Widget: >>> class Hello(ew.Widget): ... template=ew.Snippet('Hello, $name') ... defaults=dict(name='World') ... >>> Hello().display() literal(u'Hello, World') >>> Hello(name='Rick').display() literal(u'Hello, Rick') >>> Hello(name='Rick').display(name='Paul') literal(u'Hello, Paul') Here, we see a couple of things. First, we see the declarative widget definition. We can declare a ew.Widget subclass that provides additional functionality. Using the declarative method, we are also able to provide defaults to the widget via the `defaults` class variable. You should also note that the defaults can be overridden in either the constructor or the `display` method, with `display` being the highest priority, followed by the constructor, followed by the defaults. Form Generation and Validation --------------------------------------------------- Although reusable html generation is useful in its own right, most templating languages already provide constructs to support this. Where EasyWidgets begins to be *really* useful is when you link form generation and validation together. To that end, EasyWidgets provides several base form classes that you can use to build and validate your forms. Here is a sample form: .. testcode:: import ew.kajiki_ew as kew class MyForm(kew.SimpleForm): fields = [ kew.TextField(name='name', label='Your Name'), kew.Checkbox(name='opt_in', label='Receive awesome offers?') ] f = MyForm() print f.display() If we run this, our output will be similar to the following: .. testoutput:: :options: +NORMALIZE_WHITESPACE



Again, nice that EasyWidgets provides HTML generation, but what about validation? >>> f.validate(dict(name='Some Name', opt_in='on'), None) {'opt_in': True, 'name': u'Some Name'} >>> f.validate(dict(name='Some Name'), None) {'opt_in': False, 'name': u'Some Name'} Note in particular how the 'on' value, as might be submitted by an HTML form, is converted to the Python `True` value. Omitting it, rather than causing the field to be omitted from the output, substitutes a `False` Value. Form validation also works in reverse: .. testcode:: print f.display(value=dict(name='Other Name', opt_in=True)) .. testoutput:: :options: +NORMALIZE_WHITESPACE



Note in the above output that the values for 'name' and 'opt_in' have been substituted into the form appropriately. This is especially useful when there are validation errors. Let's update our form just a bit: >>> from formencode import validators as fev >>> class MyForm2(kew.SimpleForm): ... fields = [ ... kew.TextField( ... name='name', ... label='Your Name', ... validator=fev.UnicodeString(min=3)), ... kew.Checkbox( ... name='opt_in', ... label='Receive awesome offers?') ] ... ... >>> f = MyForm2() Here, we have added an explicit validator to our `name` field, requiring that the name be at least 3 characters long. When trying to validate bad data, we get: >>> f.validate(dict(name='a'), None) Traceback (most recent call last): ... Invalid: name: Enter a value 3 characters long or more That's nice, but best practices for the web say you should present your user's erroneous input to them and allow them to correct it. Which is exactly what happens if we display a form after receiving a validation error: .. testcode:: print f.display() .. testoutput:: :options: +NORMALIZE_WHITESPACE

Enter a value 3 characters long or more


Note in particular that the error message from our validator is nicely arranged next to the field with the erroneous value, and that the data the user entered is redisplayed to allow the user to correct the form. Including Static Resources ---------------------------------------------------- In addition to encapsulating HTML generation and validation, EasyWidgets also provides a convenient method of including static resources such as CSS and Javascript in your pages automatically when using widgets. In order to use this feature effectively, you will need to make a couple of changes to the main templates you use (not the widget templates) to generate your site HTML. For instance, if you are using Kajiki, you may have a `master.html` template from which all your page templates inherit that looks something like this: .. code-block:: xml (Of course, your actual template probably has a shared header and footer, styling, etc. But for simplicity we'll assume the tiny fragment above.) In order to allow EasyWidgets to inject static resources into your page, you'll need to place a couple of constructs in the template. Once we've "widgetized" the template, it looks something like this: .. code-block:: xml $blob $blob $blob $blob $blob .. note:: The ResourceManager In the example above, we use a Python variable `g` which has an attribute `resource_manager`. This is inspired by the Pylons web framework, which makes a "application globals" object available to all templates. The actual resource manager provided by EasyWidgets is available as `ew.widget_context.resource_manager`. .. note:: Widget Registration In the example above, we use a Python variable `c`. This is inspired by the Pylons web framework, which makes a "context" object available to all templates. In this example, it is assumed that all the widgets used in the page will be attached to the `c` object as attributes. The `register_widgets` call is responsible for scanning the widgets attached to the `c` object and discovering the static resources they require. Note that the `register_widgets` call must precede any `resource_manager.emit` calls to ensure that all the resources required have been collected by the resource manager. Now that we've widgetized our template, let's see what happens when we render it: .. code-block:: html Here, we can see that EasyWidgets inserted several comments. Let's try actually using a widget now, assuming our template is loaded as `Template`: .. testcode:: resource import ew.kajiki_ew as kew class TestWidget(ew.Widget): template=ew.Snippet('Hello, world') def resources(self): yield kew.JSLink('js/script.js') yield kew.JSScript('// no args') yield kew.JSScript('// head_js', location='head_js') yield kew.JSScript('// body_js (default)', location='body_js') yield kew.JSScript('// body_top_js', location='body_top_js') yield kew.JSScript('// body_js_tail', location='body_js_tail') yield kew.CSSLink('css/styles.css') yield kew.CSSScript('/* Here would be some inline styles */') c.widget = TestWidget() g.resource_manager = ew.ResourceManager(compress=False) print Template(dict(c=c,g=g)).render() Our output will be something like the following: .. testoutput:: resource :options: +NORMALIZE_WHITESPACE Now that we finally have some actual generated HTML, there are several things to discuss. - Our widget specifies the resources it requires via the `resources()` generator method. This - Wherever we have a `JSLink` or a `CSSLink` resource referenced, EasyWidgets has prefixed it with an "/_ew_resources/" path. This is a "hook" that allows the EasyWidgets resource middleware (covered below) to serve up the resource. We will see how to map such URLs to actual files below. - The various "locations" specified in our widget's `resources()` generator correspond to various locations in the `resource_manager.emit()` calls. Resource Middleware ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EasyWidgets provides a helpful hook into the WSGI stack for serving up static resources required by widgets: the `ew.middleware.WidgetMiddleware` class. In order to really investigate it, we'll need to build a little WSGI application. The `bottle` web framework is helpful for such examples. Here is our example bottle application, test_app.py: .. literalinclude:: data/test_app.py Note in particular the calls to `ew.widget_context.resource_manager.register_directory`. This is where we let the resource manager know that resources living under the URL "/_ew_resources/js" can be found at "data/js" and resources living under the URL "/_ew_resources/css" can be found at "data/css". If we place resources at those locations, we will find that indeed our little test application can find and serve them up. Encapsulation of widgets is nice, but if you've looked at optimizing client-side performance of the web, you probably have noticed hints such as "concatenate your css and javascript" and "minify your css and javascript." Luckily, EasyWidgets has support for both. For minification, you simply install JSMin and CSSMin, turn on compression, and let EasyWidgets know it can use these libraries. This will change our middleware declaration to the following: .. literalinclude:: data/test_app2.py :linenos: :lines: 41-47 Now, if we view the page we will see the following cryptic links: .. code-block: html ...