1. toscawidgets
  2. ToscaWidgets

Commits

alberto  committed 08d67f3

[svn] Some refactoring and new functionality:
* Cleaned up display rules and moved them to display_rules.py.
* HostFramework has a new attribute, 'default_view' which controlls how root widgets are displayed.
* TGWidgetsMiddleware now serves static files
* More rendering tests.
* Made wsgi sample app more interesting
* Created WSGIHostFramework for simple WSGI apps.

  • Participants
  • Parent commits 6d976ca
  • Branches trunk

Comments (0)

Files changed (16)

File docs/doc_index.txt

View file
     It could be stored in WSGI environ for example.
 
     This request-local storage object is used to store the ``formencode.Invalid`` exception
-    which is raised when validation fails so form widgets can redisplay the values the user
-    submitted and show validation errors without having to pass an ``error`` argument when
-    displaying them.
+    which is raised when validation fails so form widgets can display validation errors 
+    without having to pass an ``error`` argument when displaying them.
+    It also stores input_values to redisplay them.
 
-    It is the responsability of the framework to put the Invalid exception in the request,
-    although form widgets don't really need it if ``error`` is always passed explicitly as
-    an argument to ``display`` when rendering forms.
+    It is the responsability of the framework to put the Invalid exception and input values
+    in the request.
 
-    Widgets not rooted at the same container could also use it to pass messages among them.
+    Widgets not rooted at the same container could also use it to pass messages among them
+    (see wsgi_app.py in the examples folder).
 
 An `engines <class-toscawidgets.mods.base.HostFramework.html#engines>`_ object.
     With the initialized EngineManager with all needed engines loaded.
 
 An `url <class-toscawidgets.mods.base.HostFramework.html#url>`_ method.
-    Which can convert app-relative urls into site-absolute urls, prepending the 
-    apps app's root.
+    Which can convert app-relative urls into site-absolute urls, prepending SCRIPT_NAME
+    is usually enough.
 
-The framework should also make available all static directories needed my the CSS and JS Links
-through the web. These can be fetched from ``toscawidgets.api.registered_directories`` like this:
+A `default_view <class-toscawidgets.mods.base.HostFramework.html#default_view>`_ attribute.
+    Which specifies the default engine name used by the framework. This is used
+    to adapt the output of root widgets to display them on a page template. It
+    can be overriden by passing the ``_dest`` keyword to display with the
+    template engine name.
 
-    >>> from toscawidgets.api import registered_directories
-    >>> for webpath, dir in registered_directories.get_all():
-    ...     # make dir accessible from the web mounting it at webpath
-    ...     pass 
-
-This step should occur after all widgets have been initialized and, preferably, only once
-in the app's lifetime. Static directories must remain registered for every request.
 
 Examples
 ========

File docs/index.txt

View file
 Download
 ========
 
-Currently **ToscaWidgets** is only available from it's **Subversion** repository.
+The easiest way to install ToscaWidgets is using ``easy_install``
 
 ::
 
+  easy_install ToscaWidgets
+
+However, ToscaWidgets is currently in active development so it's recommended to
+run from the development version from it's Subversion repository.
+::
+
   svn co http://www.turbogears.org/svn/turbogears/projects/ToscaWidgets/trunk toscawidgets
 
 
 
   python ez_setup/__init__.py
 
-From your SVN checkout dir.
+From your SVN checkout dir. (You can skip this step if you downloaded and
+installed Toscawidgets with ``easy_install``)
 
 **ToscaWidgets** depends on other three `setuptools`_ packages (`FormEncode`_,
 `Genshi`_ and `RuleDispatch`_). The first two will get installed
 The ``forms`` and ``js_interface`` subpackages have been splitted into separate
 distributions, ``ToscaWidgetsForms`` and ``ToscaWidgetsJS`` respectively.
 
-You can checkout them by
+You can install them by:
+
+::
+
+  easy_install ToscaWidgetsJS
+  easy_install ToscaWidgetsForm
+  
+Alternatively, you can run from the development version by:
 
 ::
 

File examples/tgsample/dev.cfg

View file
 qualname='tgsample'
 handlers=['debug_out']
 
-[[[tgwidgets]]]
+[[[toscawidgets]]]
 level='INFO'
-qualname='tgwidgets'
+qualname='toscawidgets.display_rules'
 handlers=['debug_out']
 propagate=0
 

File examples/wsgi_app.py

View file
 from formencode import validators, Invalid
 from paste.wsgiwrappers import WSGIRequest
 
-from toscawidgets.api import WidgetsList
+import toscawidgets
+from toscawidgets.api import WidgetsList, CSSSource, JSSource
+from toscawidgets.js_interface import js_function_adapter, js_function, mochikit, dynamic_resources
 from toscawidgets import forms
 
 
           xmlns:py="http://genshi.edgewall.org/">
         <head>
             <title py:content="title" />
+            <link py:for="rsrc in resources['head']" 
+                  py:replace="rsrc.display()" />
         </head>
         <body>
-            ${form.display(person, error=error)}
+            <link py:for="rsrc in resources['bodytop']" 
+                  py:replace="rsrc.display()" />
+
+            ${form.display(person)}
+
+            <link py:for="rsrc in resources['bodybottom']" 
+                  py:replace="rsrc.display()" />
         </body>
     </html>
     """)
     email = forms.TextField(
         validator = validators.Email
         )
+    date = forms.CalendarDatePicker()
 
-myform = forms.ListForm(children=SomeFields, submit_text="Post")
+# Lil' chunk o' CSS for tasty eye-candy ;)
+# External css files can be wrapped with CSSLink
+css = CSSSource(src="""
+label.required, .fielderror {
+    font-weight: bold;
+    color: red;
+};
+""")
 
 
+# We define the source for some JS functions we're going to interface
+# External js files can be wrapped with JSLink
+functions = JSSource(src = """
+var form_submited = function (event) {
+    if ( !confirm("Are you sure?") )
+        event.stop();
+    }; 
+    """, javascript=[mochikit])
 
+# We can also interface builtin functions
+@js_function_adapter()
+def alert(msg):
+    return (`msg`,)
+
+
+# JS function create dummy wrappers which cannot be called but serve to be
+# passed as callbacks to JS code when connecting event handlers
+form_submited = js_function('form_submited')
+
+
+class MyForm(forms.ListForm):
+    fields = SomeFields
+    css = [css]
+    # We need to include dynamic_resources so the 'alert' js call in
+    # update_params gets rendered.
+    javascript=[dynamic_resources, functions]
+    submit_text="Post"
+    def __init__(self, *args, **kw):
+        super(MyForm, self).__init__(*args, **kw)
+        # Connect a event handler to the form itself. 
+        self.add_event_handler('onsubmit', form_submited)
+        # Connections made inside __init__ will be permanent for every request.
+
+    # Fun stuff ocurrs here...
+    def update_params(self, d):
+        super(MyForm, self).update_params(d)
+        if d.get('error', False):
+            alert('The form contains invalid data\n%s'% d['error'])
+
+myform = MyForm('form')
+
+
+# The WSGI app
 def app(environ, start_response):
     start_response('200 OK', [('Content-type','text/html')])
     ctx = Context(
         person = Person(),
         form = myform,
         title = 'Simplest WSGI app using TGWidgets',
-        error = None,
+        # send the resources needed for the form to the template so their links
+        # can be rendered.
+        # TGWidgetsMiddleware will take care of serving them.
+        resources = myform.retrieve_resources(),
         )
     input_values = dict(WSGIRequest(environ).params)
-    print input_values
     if input_values:
         try:
             value = myform.validate(input_values)
             return [value]
         except Invalid, error:
-            print error
-            ctx['error'] = error
-    return [page_template.generate(ctx).render()]
+            # We place the excpetion and input_values on request_local storage
+            # so the form can redisplay values and errors.
+            # We can also access it from toscawidgets.framework.request_local
+            environ['toscawidgets.request_local'].validation_exception = error
+            environ['toscawidgets.request_local'].input_values = input_values
+    return [page_template.generate(ctx).render(method='html')]
+
+
 
 import paste.httpserver
 from paste.registry import RegistryManager
-from toscawidgets.mods import base
 from toscawidgets.middleware import TGWidgetsMiddleware
+from toscawidgets.mods.wsgi import WSGIHostFramework
 
-app = TGWidgetsMiddleware(app, base.DummyHostFramework)
+# Stack needed middleware
+app = TGWidgetsMiddleware(app, WSGIHostFramework)
 app = RegistryManager(app)
 
 paste.httpserver.serve(app, port=8000, host='0.0.0.0')

File tests/test_render.py

View file
 from unittest import TestCase
 from toscawidgets.testutil import RequireMixin, WidgetMixin
 
-from types import GeneratorType
 
 class TestBasic(WidgetMixin, TestCase):
     class TestWidget(Widget):
-        #XXX Need this to raise AmbiguousMethod if no prio
-        #    is specified
-        engine_name = 'kid' 
+        pass
 
     def test_no_template_returns_None(self):
         self.failUnlessEqual(self.widget.display(), None)
 
     
     def test_display(self):
+        self.assert_(isinstance(self.widget.display(), basestring))
+    
+    def test_display_on_genshi(self):
         from genshi.core import Stream
-        self.assert_(isinstance(self.widget.display(), Stream))
-    
+        output = self.widget.display(_dest='genshi')
+        self.assert_(isinstance(output, Stream))
+
 class TestKid(RequireMixin, WidgetMixin, TestCase):
     require = ["Kid"]
     class TestWidget(Widget):
 
     def test_display(self):
         # Display from kid widgets get's coerced into a markup stream
-        self.assert_(isinstance(self.widget.display(), GeneratorType))
+        self.assert_(
+            isinstance(self.widget.display(), basestring)
+            )
+
+    def test_display_on_kid(self):
+        output = self.widget.display(_dest='kid')
+        #XXX: Better way to check this needed, where is Element defined??
+        self.assert_('Element' in repr(output))
     
 class TestKidInGenshi(RequireMixin, WidgetMixin, TestCase):
     require = ["Kid", "Genshi"]
         self.assertInOutput('foo', dict(kid='foo'))
 
     def test_display(self):
+        self.assert_(isinstance(self.widget.display(), basestring))
+
+    def test_display_on_genshi(self):
         from genshi.core import Stream
-        self.assert_(isinstance(self.widget.display(), Stream))
+        output = self.widget.display(_dest='genshi')
+        self.assert_(isinstance(output, Stream))
+
+    def test_display_on_kid(self):
+        from types import GeneratorType
+        output = self.widget.display(_dest='kid')
+        self.assert_(isinstance(output, GeneratorType))
 
 class TestGenshiInKid(RequireMixin, WidgetMixin, TestCase):
     require = ["Kid", "Genshi"]
         self.assertInOutput('foo', dict(genshi='foo'))
 
     def test_display(self):
-        self.assert_(isinstance(self.widget.display(), GeneratorType))
+        self.assert_(
+            isinstance(self.widget.display(), basestring)
+            )
 
+    def test_display_on_genshi(self):
+        from types import GeneratorType
+        output = self.widget.display(_dest='genshi')
+        self.assert_(isinstance(output, GeneratorType))
+
+    def test_display_on_kid(self):
+        output = self.widget.display(_dest='kid')
+        #XXX: Better way to check this needed, where is Element defined??
+        self.assert_('Element' in repr(output))
 
 class TestCheetah(RequireMixin, WidgetMixin, TestCase):
     require = ["Cheetah"]
         self.assertInOutput('foo', dict(cheetah='foo'))
 
     def test_display(self):
+        self.assert_(isinstance(self.widget.display(), basestring))
+
+    def test_display_on_genshi(self):
         from genshi.core import Stream
-        self.assert_(isinstance(self.widget.display(), Stream))
+        output = self.widget.display(_dest='genshi')
+        self.assert_(isinstance(output, Stream))
+
+    def test_display_on_cheetah(self):
+        self.assert_(isinstance(self.widget.display(_dest='cheetah'), basestring))
 
 class TestCheetahInKid(TestCheetah):
     require = ["Cheetah", "Kid"]
         self.assertInOutput('foo', dict(cheetah='foo'))
 
     def test_display(self):
-        self.assert_(isinstance(self.widget.display(), GeneratorType))
+        self.assert_(
+            isinstance(self.widget.display(), basestring)
+            )
+
+    def test_display_on_cheetah(self):
+        self.assert_(isinstance(self.widget.display(_dest='cheetah'), basestring))
+
+    def test_display_on_kid(self):
+        output = self.widget.display(_dest='kid')
+        #XXX: Better way to check this needed, where is Element defined??
+        self.assert_('Element' in repr(output))
+
+class TestStringInKid(RequireMixin, WidgetMixin, TestCase):
+    require = ["Kid"]
+
+    class TestWidget(Widget):
+        class StringWidget(Widget):
+            template = """<input value="$value" />"""
+        children = [StringWidget('string')]
+        template = """
+        <div xmlns:py="http://purl.org/kid/ns#"
+             id="${id}">${children.string.display(value_for('string'))}</div>
+        """
+        engine_name = "kid"
+
+    def test_render(self):
+        self.assertInOutput(['INPUT', 'DIV'])
+
+    def test_render_value(self):
+        self.assertInOutput('foo', dict(string='foo'))
+
+    def test_display(self):
+        self.assert_(
+            isinstance(self.widget.display(), basestring)
+            )
+
+    def test_display_on_kid(self):
+        output = self.widget.display(_dest='kid')
+        #XXX: Better way to check this needed, where is Element defined??
+        self.assert_('Element' in repr(output))
+
+class TestStringInGenshi(RequireMixin, WidgetMixin, TestCase):
+    require = ["Genshi"]
+
+    class TestWidget(Widget):
+        class StringWidget(Widget):
+            template = """<input value="$value" />"""
+        children = [StringWidget('string')]
+        template = """
+        <div xmlns="http://www.w3.org/1999/xhtml"
+             xmlns:py="http://genshi.edgewall.org/"
+             id="${id}">${children.string.display(value_for('string'))}</div>
+        """
+        engine_name = "genshi"
+
+    def test_render(self):
+        self.assertInOutput(['input', 'div'])
+
+    def test_render_value(self):
+        self.assertInOutput('foo', dict(string='foo'))
+
+    def test_display(self):
+        self.assert_(
+            isinstance(self.widget.display(), basestring)
+            )
+
+    def test_display_on_genshi(self):
+        from genshi.core import Stream
+        output = self.widget.display(_dest='genshi')
+        self.assert_(isinstance(output, Stream))

File toscawidgets/core.py

View file
 from itertools import ifilter, count, chain, izip, islice
 from inspect import isclass
 
-from dispatch import generic, strategy
+from toscawidgets.util import assert_bool_attr, callable_wo_args, \
+                              unflatten_args, OrderedSet, make_bunch, \
+                              install_framework
+from toscawidgets.genericfunctions import generic, PriorityDisambiguated, \
+                                          default_rule
 
-from toscawidgets.util import assert_bool_attr, callable_wo_args, unflatten_args,\
-                           OrderedSet, install_dummy_framework, make_bunch
+install_framework()
 
-# Need to set a dummy framework if None has been set
-install_dummy_framework()
-
+import toscawidgets.display_rules
 import toscawidgets.view as view
 from toscawidgets.exceptions import *
-from toscawidgets.meta import WidgetType, WidgetsListType, pre_init, post_init, \
-                           add_lists, radd_lists
-
-from toscawidgets.genericfunctions import PriorityDisambiguated
+from toscawidgets.meta import WidgetType, WidgetsListType, pre_init, \
+                              post_init, add_lists, radd_lists
 
 
 __all__ = [
         pass
 
 
-    @pre_init.when(strategy.default)
+    @pre_init.when(default_rule)
     def __pre_init(self, id=None, parent=None, children=[], **kw):
         """
         Takes care of Widget instances pre-initialization. It's executed by a 
                 
     def _collect_resources(self, attrname):
         oset = OrderedSet()
-        oset.add_all(getattr(self, attrname))
+        for rsrc in getattr(self, attrname):
+            oset.add_all(getattr(rsrc, 'retrieve_' + attrname)())
         oset.add_all(getattr(self.children, 'retrieve_' + attrname)())
         return oset
         
 
 
-    @post_init.when(strategy.default)
+    @post_init.when(default_rule)
     def __post_init(self, *args, **kw):
         self._css = self._collect_resources('css') 
         self._js = self._collect_resources('javascript') 
 
 
 
-    @adapt_value.when(strategy.default, priority=-1)
+    @adapt_value.when(default_rule, priority=-1)
     def __adapt_value_passthrough(self, value):
         return value
 
 
 
 
-    @pre_init.around(strategy.default)
+    @pre_init.around(default_rule)
     def __pre_init_repeater(next_method, self, id, parent=None, children=[], **kw):
         self.widget = widget = kw.get('widget', getattr(self, 'widget', None))
         if widget is None:

File toscawidgets/display_rules.py

View file
+"""
+Rules to adapt the output of display/render to enable mixing widgets with
+different template engines.
+"""
+
+import logging
+
+import toscawidgets
+from toscawidgets.view import display, render
+from toscawidgets.genericfunctions import default_rule
+
+log = logging.getLogger(__name__)
+
+def _dflt_view():
+    return toscawidgets.framework.default_view
+
+
+# Default display
+@display.when(default_rule)
+def _display_default(self, widget, value, **kw):
+    """Default display is to generate a stream, be it genshi or kid"""
+    log.debug("_display_default: %r"% widget)
+    kw = widget.prepare_dict(value, kw)
+    template = kw.pop('template', widget.template)
+    tpl = self.load_template(template, widget.engine_name)
+    return self[widget.engine_name].transform(info=kw, template=tpl)
+
+
+
+
+
+
+# Default render
+def _render_string(self, widget, value, **kw):
+    """Default render is to generate a string. Independently of the template
+    language used"""
+    log.debug("_render_string: %r"% widget)
+    kw = widget.prepare_dict(value, kw)
+    template = kw.pop('template', widget.template)
+    tpl = self.load_template(template, widget.engine_name)
+    return self[widget.engine_name].render(info=kw, template=tpl)
+
+render.when(default_rule)(_render_string)
+render.when("widget.engine_name == 'toscawidgets'", priority=1)(_render_string)
+display.when("widget.engine_name == 'toscawidgets'", priority=1)(_render_string)
+
+
+
+
+
+
+# display/render when no template
+def __display_template_is_None(self, widget, value, **kw):
+    """When the template is None, both render and display should return None"""
+    log.debug("_display_template_is_None: %r"% widget)
+    import warnings; warnings.warn("%r has no template defined" % widget)
+    return None
+rule = "widget.template is None"
+display.around(rule,priority=100)(__display_template_is_None)
+render.around(rule,priority=100)(__display_template_is_None)
+
+
+
+
+
+
+# render for kid or genshi
+@render.when(
+    "widget.engine_name == 'genshi' or widget.engine_name == 'kid' "
+    )
+def _render_kid_or_genshi(self, widget, value, **kw):
+    """To render a Kid or Genshi widget we use the engine's 'render' method"""
+    log.debug("_render_kid_or_genshi: %r"% widget)
+    kw = widget.prepare_dict(value, kw)
+    template = kw.pop('template', widget.template)
+    tpl = self.load_template(template, widget.engine_name)
+    format = kw.pop('format', 'html')
+    return self[widget.engine_name].render(
+        fragment=True, info=kw, format=format, template=tpl
+        )
+
+
+
+# display/render for cheetah
+def _render_display_cheetah(self, widget, value, **kw):
+    log.debug("_render_display_cheetah: %r"% widget)
+    kw = widget.prepare_dict(value, kw)
+    template = kw.pop('template', widget.template)
+    return self['cheetah'].render(info=kw, template=template)
+render.when("widget.engine_name == 'cheetah'")(_render_display_cheetah)
+display.when("widget.engine_name == 'cheetah'", priority=1)(_render_display_cheetah)
+
+
+
+
+
+
+# Display wrappers to display Kid templates on Genshi
+@display.around(
+    "widget.engine_name == 'kid' and "
+    "((not widget.is_root and  widget.parent.engine_name == 'genshi') or "
+    "(widget.is_root and kw.get('_dest', _dflt_view()) == 'genshi'))"
+    )
+def _display_around_kid_on_genshi(next_method, self, widget, value, **kw):
+    """Output from Kid on Genshi should filter the stream through ET."""
+    log.debug("_display_around_kid_on_genshi: %r"% widget)
+    from genshi.plugin import ET
+    return ET(next_method(self, widget, value, **kw))
+
+
+
+
+
+# Display wrappers to display string templates on Genshi
+@display.around(
+    "widget.engine_name != 'genshi' and widget.engine_name != 'kid' and "
+    "((not widget.is_root and "
+    "widget.parent.engine_name == 'genshi') or "
+    "(widget.is_root and kw.get('_dest', _dflt_view()) == 'genshi'))"
+    )
+def _display_around_non_genshi_or_kid_on_genshi(
+    next_method, self, widget, value, **kw
+    ):
+    """Displaying non Kid or Genshi usually produces a string. We parse it into
+    a Genshi stream to insert it on a Genshi template.""" 
+    log.debug("_display_around_non_genshi_or_kid_on_genshi: %r"% widget)
+    from genshi.input import HTML
+    return HTML(next_method(self, widget, value, **kw))
+
+
+
+
+
+
+# Display wrappers to display string templates on Kid
+@display.around(
+    "widget.engine_name != 'genshi' and widget.engine_name != 'kid' and "
+    "((not widget.is_root and "
+    "widget.parent.engine_name == 'kid') or "
+    "(widget.is_root and "
+    "kw.get('_dest', _dflt_view()) == 'kid'))"
+    )
+def _display_around_non_genshi_or_kid_on_kid(
+    next_method, self, widget, value, **kw
+    ):
+    """Displaying non Kid or Genshi usually produces a string. We parse it into
+    a Kid stream to insert it on a Kid template.""" 
+    log.debug("_display_around_non_genshi_or_kid_on_kid: %r"% widget)
+    from kid import XML
+    return XML(next_method(self, widget, value, **kw))
+
+
+
+
+
+
+# Display on string templates
+@display.when(
+    "(not widget.is_root and "
+    "widget.parent.engine_name != 'genshi' and "
+    "widget.parent.engine_name != 'kid') or "
+    "(widget.is_root and "
+    "kw.get('_dest', _dflt_view()) != 'kid' and "
+    "kw.get('_dest', _dflt_view()) != 'genshi')"
+    )
+def _display_when_on_non_genshi_or_kid(self, widget, value, **kw):
+    """Output from display on non Genshi or Kid widgets is assumed to be a 
+    string. We hijack it's display method and call render instead."""
+    log.debug("_display_when_on_non_genshi_or_kid: %r"% widget)
+    return self.render(widget, value, **kw)
+
+
+
+
+
+
+
+# Display for genshi on kid
+@display.when(
+    "widget.engine_name == 'genshi' and "
+    "((not widget.is_root and widget.parent.engine_name == 'kid') or "
+    "(widget.is_root and kw.get('_dest', _dflt_view()) == 'kid'))"
+    )
+def _display_genshi_on_kid(self, widget, value, **kw):
+    """To display a Genshi widget on a Kid template we first serialize it's
+    generated stream and re-parse it into a kid stream. Note: This is
+    suboptimal, the inverse of genshi.ET is needed here."""
+    log.debug("_display_genshi_on_kid: %r"% widget)
+    #XXX: The inverse of genshi.plugins.ET to transform a Gesnhi stream into a
+    #     ET stream is needed here. We can serialize and re-parse for
+    #     the mean-time...
+    from kid import XML
+    kw['format'] = 'xhtml'
+    return XML(self.render(widget, value, **kw))

File toscawidgets/genericfunctions.py

View file
 import sys
-from dispatch import strategy, functions
+from dispatch import strategy, functions, generic
 from dispatch.interfaces import *
 from protocols.advice import add_assignment_advisor,getFrameInfo,addClassAdvisor
 
 
 __all__ = [
     "PriorityDisambiguated",
+    "prioritized_generic",
+    "default_rule",
+    "generic",
     ]
 
+default_rule = strategy.default
+
 def _prioritized_safe_methods(grouped_cases):
     """Yield all methods in 'grouped_cases' including those in groups with
     more than one case (AmbiguousMethods) in the priority specified by the 
             function = qualifier,function
         for signature in IDispatchPredicate(predicate):
             self[signature] = function
+
+prioritized_generic = generic(PriorityDisambiguated)

File toscawidgets/meta.py

View file
-from dispatch import generic
-
 from toscawidgets.util import pre_post_wrapper
-from toscawidgets.genericfunctions import PriorityDisambiguated
+from toscawidgets.genericfunctions import PriorityDisambiguated, generic
 from toscawidgets.exceptions import *
 
 

File toscawidgets/middleware.py

View file
 import toscawidgets
-from paste.registry import StackedObjectProxy
+from paste.urlparser import StaticURLParser
 from toscawidgets.mods import base
+from toscawidgets.resources import registered_directories
 
 class TGWidgetsMiddleware(object):
     def __init__(self, application, framework_factory):
         self.app = application
         self.framework_factory = framework_factory
-        if (not hasattr(toscawidgets, 'framework') or 
-           isinstance(toscawidgets.framework, base.DummyHostFramework)
-        ):
-            toscawidgets.framework = StackedObjectProxy()
-
-                                
 
     def __call__(self, environ, start_response):
         framework = self.framework_factory(environ)
         environ['paste.registry'].register(toscawidgets.framework, framework)
         environ.setdefault('toscawidgets.framework', toscawidgets.framework)
+        path_info = environ.get('PATH_INFO', '')
+        for webdir, dirname in registered_directories.get_all():
+            if path_info.startswith(webdir):
+                environ['PATH_INFO'] = path_info.replace(webdir, '')
+                if 'SCRIPT_NAME' in environ:
+                    environ['SCRIPT_NAME'] += webdir
+                return StaticURLParser(dirname)(environ, start_response)
         return self.app(environ, start_response)

File toscawidgets/mods/base.py

View file
     This class is the interface between TGWidgets and the framework that's
     using them.
 
-    The class is passed as second argument to TGWidgetsMiddleware which will take
-    care of initializing it passing it the environ and stacking it at 
+    The class is passed as second argument to TGWidgetsMiddleware which will 
+    take care of initializing it passing it the environ and stacking it at 
     ``toscawidgets.framework`` for convenient access.
 
     It should provide:
 
     `request_local`
-      A container in which widgets can store request-local data. InputWidgets will
-      look for an attribute here called ``validation_exception`` for a
-      ``formencode.Invalid`` exception, in case it finds one it will display errors
-      and values contained in it.
+      A container in which widgets can store request-local data. InputWidgets 
+      (in ToscaWidgetsForms) will look for two attribute here called 
+      ``validation_exception`` and `input_values``. 
+      The first is a ``formencode.Invalid`` exception, it should be the
+      exception raised by the form when validating input, it's used to 
+      display errors.
+      The second are the input values parsed form the POST/GET arguments.
 
     `engines`
-      An initialized EngineManager with the framework's settings. It's recommended
-      that the framework takes care of caching it so it doesn't need to be loaded
-      on every request.
+      An initialized EngineManager with the framework's settings. It's 
+      recommended that the framework takes care of caching it so it doesn't 
+      need to be loaded on every request.
 
     `url`
-      A method to translate app urls to site urls. This is used solely by Resources.
+      A method to translate app urls to site urls. This is used solely by 
+      Resources to the compute relative urls where the application will serve
+      their static files.
 
-    Besides these attrs., the framework should take care, when all resources are
-    initialized, of registering all static directories at their given webpaths so they,
-    and all of their subdirectoires, can be accesible from them web.
+    `default_view`
+      The name of the template engine used by default in the container app's
+      templates. It's used by ``Widget.display`` to determine what conversion
+      is neccesary when displaying root widgets on a template. It can be
+      overriden by passing the engine name in the ``_dest`` keyword arg to
+      ``display``.
+    """
 
-    It's recommended that this step is done on first call to ``url`` to make sure all
-    Resources needed have been initialized and all static directories registered.
-
-    These directories are located at ``Resources.registered_directories`` and can be
-    accessed like:
-
-        >>> from toscawidgets.resources import registered_directories
-        >>> for webpath, dir in registered_directories.get_all():
-        ...     # register dir to be accessible from webpath
-        ...     pass
-        ...
-    """
+    request_local = None
+    engines = None
+    default_view = None
 
 
     def __init__(self, environ):
         as an argument.
         """
     
-    @property
-    def request_local(self):
-        """
-        Returns the framework's specific request-local storage object.
-        """
-
-    @property
-    def engines(self):
-        """
-        Returns the engine manager properly configured by the framework
-        """
-
     def url(self, url):
         """
         Returns the absolute path for the given url.
         """
 
 
-
 class DummyHostFramework(HostFramework):
     """
     This dummy, non-thread-safe, HostFramework is used for testing and debugging
         # set some defaults so toscawidgets can be used on the commandline for
         # testing, etc...
         from toscawidgets.view import EngineManager
-        self._engines = EngineManager()
-        self._engines.load_all()
-        self._request_local = type('DummyRequest', (object,), {})
+        self.engines = EngineManager()
+        self.engines.load_all()
+        self.request_local = type('DummyRequest', (object,), {})
+        self.default_view = 'toscawidgets'
     
-    @property
-    def request_local(self):
-        """
-        Returns the framework's specific request-local storage object.
-        """
-        return self._request_local
-
-    @property
-    def engines(self):
-        """
-        Returns the engine manager properly configured by the framework
-        """
-        return self._engines
-
     def url(self, url):
         """
         Returns the absolute path for the given url.

File toscawidgets/mods/tg.py

View file
 import toscawidgets
 from toscawidgets import view
 from toscawidgets.mods.base import HostFramework
-from toscawidgets.api import display
-from toscawidgets.view import _display_genshi_on_kid, _display_default,\
-                           _display_around_non_genshi_or_kid_on_genshi,\
-                           _display_around_non_genshi_or_kid_on_kid
 
 
 
         _first_call_lock.release()
     return self.__class__.url(self,url)
 
-# Declare a rule for display to render genshi widgets on kid
-# page templates, and string widgets in kid and genshi templates
-default_view = turbogears.config.get('tg.defaultview', 'kid')
-
-display.around(
-    "widget.engine_name != 'genshi' and widget.engine_name != 'kid' "
-    "and widget.is_root and  default_view == 'kid'")(_display_around_non_genshi_or_kid_on_kid)
-
-display.around(
-    "widget.engine_name != 'genshi' and widget.engine_name != 'kid' "
-    "and widget.is_root and default_view == 'genshi'")(_display_around_non_genshi_or_kid_on_genshi)
-
-display.when(
-    "widget.is_root and widget.engine_name == 'genshi' and "
-    "default_view == 'kid'")(_display_genshi_on_kid)
-
-# Override with priority view._display_around_kid 
-display.around(
-    "widget.engine_name == 'kid' and default_view == 'kid'", priority=1,
-    )(_display_default)
-
-
 
 class Turbogears(HostFramework):
 
         
 
     @property
+    def default_view(self):
+        """The default engine name where root widgets are displayed on. This
+        can be overriden by passing it as the ``_dest`` keyword to ``display``.
+
+        Example:
+          To display a widget on a Genshi template do:
+            widget.display(..., _dest='genshi')
+
+          To avoid passing this argument when all (or most of) your templates
+          are written in Genshi, set this attribute to 'genshi' in your
+          framework object.
+        """
+        return turbogears.config.get('tg.defaultview', 'kid')
+
+    @property
     def request_local(self):
         """
         Returns the framework's specific request-local storage object.

File toscawidgets/mods/wsgi.py

View file
+from new import instancemethod
+from toscawidgets.view import EngineManager
+from toscawidgets.mods.base import HostFramework
+
+
+__all__ = ["WSGIHostFramework"]
+class _RequestLocalStorage(object):
+    pass
+
+class WSGIHostFramework(HostFramework):
+    engines = EngineManager()
+    engines.load_all()
+    default_view = 'genshi'
+
+    def __init__(self, environ):
+        r_local = _RequestLocalStorage()
+        self.request_local = environ['toscawidgets.request_local'] = r_local
+        def url(self, _url):
+            return environ['SCRIPT_NAME']  + _url
+        self.url = instancemethod(url, self, self.__class__)

File toscawidgets/resources.py

View file
 class RegisteredDirectories(object):
     _lock = threading.Lock()
     _dirs = {}
+    prefix = '/toscawidgets'
     def add(self, webpath, dirname):
-        prefix = '/toscawidgets'
-        webpath = prefix + webpath
+        webpath = self.prefix + webpath
         self._lock.acquire()
         try:
             old_dir = self._dirs.get(webpath)
         finally:
             self._lock.release()
         return all
+    
+    def dump_for_apache2(self, file, follow_symlinks=True, allow_index=False):
+        """Dump into 'file' (file-like object) a configuration file that can
+        be included in a VirtualHost config to let Apache serve all static
+        resources and save some Python valuable threads for dynamic content."""
+        cg = _Apache2ConfigGen(
+            follow_symlinks=follow_symlinks, allow_index=allow_index
+            )    
+        for rsrc in self.get_all():
+            file.write(cg.render(rsrc))
+
+
+class _Apache2ConfigGen(Widget):
+    params = ("follow_symlinks", "allow_index")
+    follow_symlinks = True
+    allow_index = False
+    template = """
+Alias $webdir $dirname
+<Location "$webdir">
+    SetHandler None
+</Location>
+<Directory "$dirname">
+    AllowOverride None
+    Options -ExecCGI ${index}Indexes -Multiviews ${follow}FollowSymLinks
+    Order allow,deny
+    allow from all
+</Directory>
+"""
+    def update_params(self,d):
+        super(_Apache2ConfigGen, self).update_params(d)
+        d.follow = ('-','+')[int(d.follow_symlinks)]
+        d.index = ('-','+')[int(d.allow_index)]
+        d.webdir, d.dirname = d.value
+
+        
              
 registered_directories = RegisteredDirectories()
 
     """
     An inlined chunk of JS source code.
     """
-    template = """<script type="text/javascript"> $src </script>"""
+    template = """<script type="text/javascript">$src</script>"""
 
 

File toscawidgets/util.py

View file
             self.add(item)
 
 
-def install_dummy_framework():
+def install_framework():
     import toscawidgets
     if not hasattr(toscawidgets, 'framework'):
+        from paste.registry import StackedObjectProxy
         from toscawidgets.mods import base
-        toscawidgets.framework = base.DummyHostFramework(None)
+        toscawidgets.framework = StackedObjectProxy(
+            base.DummyHostFramework(None),
+            name='ToscaWidgetsFramework',
+            )
 
 class LooseEnum(dict):
     def __init__(self, *args):

File toscawidgets/view.py

View file
 import logging, re
 
-from dispatch import generic, strategy
 from pkg_resources import iter_entry_points
 
 import toscawidgets
-from toscawidgets.genericfunctions import PriorityDisambiguated
+from toscawidgets.genericfunctions import generic, PriorityDisambiguated
 
 
 __all__ = ["EngineManager", "display", "render"]
 
 display = EngineManager.display.im_func
 render = EngineManager.render.im_func
-
-
-
-
-
-
-
-
-
-#------------------------------------------------------------------------------
-# Rules to adapt the output of display/render to enable mixing widgets with
-# different template engines.
-#------------------------------------------------------------------------------
-
-@display.when(strategy.default)
-def _display_default(self, widget, value, **kw):
-    log.debug("_display_default")
-    kw = widget.prepare_dict(value, kw)
-    template = kw.pop('template', widget.template)
-    tpl = self.load_template(template, widget.engine_name)
-    return self[widget.engine_name].transform(info=kw, template=tpl)
-
-
-def _render_default(self, widget, value, **kw):
-    log.debug("_render_default")
-    kw = widget.prepare_dict(value, kw)
-    template = kw.pop('template', widget.template)
-    tpl = self.load_template(template, widget.engine_name)
-    return self[widget.engine_name].render(info=kw, template=tpl)
-
-render.when(strategy.default)(_render_default)
-render.when("widget.engine_name == 'toscawidgets'")(_render_default)
-display.when("widget.engine_name == 'toscawidgets'")(_render_default)
-
-def __display_template_is_None(self, widget, value, **kw):
-    log.debug("_display_template_is_None")
-    import warnings; warnings.warn("%r has no template defined" % widget)
-    return None
-rule = "widget.template is None"
-display.around(rule,priority=100)(__display_template_is_None)
-render.around(rule,priority=100)(__display_template_is_None)
-
-
-
-
-@display.around("widget.engine_name == 'kid'")
-def _display_around_kid(next_method, self, widget, value, **kw):
-    log.debug("_display_around_kid")
-    from genshi.plugin import ET
-    return ET(next_method(self, widget, value, **kw))
-
- 
-@display.around(
-    "widget.engine_name != 'genshi' and widget.engine_name != 'kid' and "
-    "widget.parent and widget.parent.engine_name == 'genshi'"
-    )
-def _display_around_non_genshi_or_kid_on_genshi(
-    next_method, self, widget, value, **kw
-    ):
-    log.debug("_display_around_non_genshi_or_kid_on_genshi")
-    from genshi.input import HTML
-    return HTML(next_method(self, widget, value, **kw))
-
-@display.around(
-    "widget.engine_name != 'genshi' and widget.engine_name != 'kid' and "
-    "widget.parent and widget.parent.engine_name == 'kid'"
-    )
-def _display_around_non_genshi_or_kid_on_kid(
-    next_method, self, widget, value, **kw
-    ):
-    log.debug("_display_around_non_genshi_or_kid_on_kid")
-    from kid import XML
-    return XML(next_method(self, widget, value, **kw))
-
-@render.when(
-    "widget.engine_name == 'genshi' or widget.engine_name == 'kid' "
-    )
-def _render_kid_or_genshi(self, widget, value, **kw):
-    log.debug("_render_kid_or_genshi")
-    kw = widget.prepare_dict(value, kw)
-    template = kw.pop('template', widget.template)
-    tpl = self.load_template(template, widget.engine_name)
-    format = kw.pop('format', 'html')
-    return self[widget.engine_name].render(
-        fragment=True, info=kw, format=format, template=tpl
-        )
-
-def _render_display_cheetah(self, widget, value, **kw):
-    log.debug("_render_display_cheetah")
-    kw = widget.prepare_dict(value, kw)
-    template = kw.pop('template', widget.template)
-    return self['cheetah'].render(info=kw, template=template)
-render.when("widget.engine_name == 'cheetah'")(_render_display_cheetah)
-display.when("widget.engine_name == 'cheetah'")(_render_display_cheetah)
-
-
-
-@display.when(
-    "not widget.is_root and widget.engine_name == 'genshi' and "
-    "widget.parent.engine_name == 'kid' "
-    )
-def _display_genshi_on_kid(self, widget, value, **kw):
-    log.debug("_display_genshi_on_kid")
-    #XXX: The inverse of genshi.plugins.ET to transform a ET stream into a
-    #     markup stream is needed here. We can serialize and re-parse for
-    #     the mean-time...
-    from kid import XML
-    kw['format'] = 'xhtml'
-    return XML(self.render(widget, value, **kw))