OK, here's how I did it:

def variabledecode(func):
    def newfunc(self, *args, **kwargs):
        from formencode.variabledecode import variable_decode
        return func(self, *args, **(variable_decode(kwargs)))
    newfunc.func_name = func.func_name
    return newfunc

...

    @turbogears.expose(
        html="newsandevents.templates.admin_article_add",
        validators=dict(
...
            ),
        )
    @variabledecode
    def add(self, action=None, **kwargs):
...

and the template:

          <div id="audience" py:for="audience in audiences">
            <input type="checkbox" name="audience-${audience.id}"
            py:attrs="value=audience.id"
            py:content="audience.description">Audience</input></div>

The problem with this is code is, I have to have this sequence:

@turbogears.expose
@variabledecode
def func(...):

Otherwise, it won't be exposed, since the exposed method is wrapped by
a non-exposed function. I'll probably just change variabledecode() to
expose itself if the wrapped function is exposed. It sounds perverted
so it must be a good idea. In fact, it's very easily done:

def variabledecode(func):
    def newfunc(self, *args, **kwargs):
        from formencode.variabledecode import variable_decode
        return func(self, *args, **(variable_decode(kwargs)))
    newfunc.func_name = func.func_name
    newfunc.exposed = getattr(func, 'exposed', False)
    return newfunc

I like this better since the variable decoding is now done before
validation, which is what I need. The real question is: Should expose
do this automatically, or else have a variable_decode option? That sure
looks easy to implement. Stripped down implementation:

def expose(html=None, validators=None,
    template=None, format=None, content_type="text/html",
    variable_decode=False): ## new
    """..."""
    import controllers
    if template:
        html = template

    def decorator(func):
        def newfunc(self, *args, **kw):
            tg_format = kw.pop("tg_format", format)
            underscore = kw.pop("_", None)
            cherrypy.response.headerMap['Content-Type']= content_type

            errors = {}
            ## start new
            if variable_decode:
                kw = formencode.variabledecode.variable_decode(kw)
            ## end new
            if validators:
                if isinstance(validators, dict):
                    for field, validator in validators.items():
                        if field not in kw:
                            continue
                        try:
                            kw[field] = validator.to_python(kw[field])
                        except turbogearsvalid.Invalid, error:
                            errors[field] = error
                else:
                    try:
                        kw = validators.to_python(kw)
                    except turbogearsvalid.Invalid, error:
                        errors = error.error_dict
            if errors:
                if hasattr(self, "validation_error"):
                    output = self.validation_error(func.func_name, kw,
errors)
                else:
                    raise turbogearsvalid.Invalid(str(errors))
            else:
                output = func(self, *args, **kw)
            return controllers._process_output(tg_format, output, html)
        newfunc.func_name = func.func_name
        newfunc.exposed = True
        return newfunc

    return decorator

I've tested this, i.e.:

    @turbogears.expose(
        variable_decode=True,
        validators=dict(
...
            ),
        )
    def add(self, action=None, **kwargs):
...

and it seems to work.

Reply via email to