Tuesday, 6 January 2009

Restish and Formish

Over the last couple of days we’ve taken a few of the projects we’ve built in the past 4 months and prepared them to distribute as open source. I thought it would be useful to both talk about the projects themselves and also the process of making them open.

The projects are all at http://ish.io and are available on http://pypi.python.org.

restish

The main project is called restish (have a look here at the restish docs for more info) and was the result of myself and Matt Goodall trying to work out what framework to use for a new (yet to be launched) commercial project we are working on. We had previously worked with Nevow and Twisted and had developer the Formal form library (which had proved moderately interesting for a few people) but were a little frustrated about not being able to use a lot of the python libraries out there. (I should add that we love Twisted for a lot more reasons than we were frustrated by it!).

To cut a longish story short, we ended up choosing pylons but then replacing most of it for one reason or the other and ended up with a Nevow like wsgi application.

formish

We needed a form library and were biased because we’d already put most of our ideas about what we wanted into formal and so we took formish and made it templating language agnostic and fixed a lot of small issues we had with it. Have a look at http://ish.io:8891 for examples of how it works.. (This isn’t running on a proper server at the moment so please leave me a comment if you get a problem with it)

Whilst working on formish, we extracted out the schema library, converting library and validating library into schemaish, convertish and validatish (yeah I know, the naming is slightly forced but it works well as a pseudo namespace). The idea was that if anybody wanted to use the individual componenets, they don’t have to copy and paste bits.

wsgiapptools

This last package is just a place to put some useful wsgi tools, like repoze I suppose (although with less code!). The only wsgi apps at the moment are a restish html notification system (‘flash’ for simple messages that only need to appear once) and cookie, an easy to use wsgi cookie handler.

A quick example

I’m not going to write much about the application now but I’ll start with a couple of simple ‘hello world’ example.

from restish import http, resource

class Root(resource.Resource):

    @resource.GET()
    def html(self, request):
        return http.ok([('Content-Type', 'text/html')],
            "Hello from myproject!")

This is the ‘base metal’ version where you want to control the exact http response. You can use the templating decorator to make this a little simpler

from restish import http, resource

class Root(resource.Resource):

    @resource.GET()
    @templating.page('root.html')
    def html(self, request):
        return {'text': "Hello from myproject!"}

And here is a ‘brain dump’ of the types of things you can do with restish.

"""
How to pass control from one resource to another
"""

# implicit resource locator
@resource.child()
def blog(self, request, segments):
    return BlogResource()

# explicit resource locator
@resource.child('blog')
def whatever(self, request, segments):
    return BlogResource()

# templated resource locator
@resource.child('blog/{entry}')
def whatever(self, request, segments, entry=None):
    return BlogEntry(entry)

# templated resource locator with partial match
@resource.child('blog/entry-{id}')
def whatever(self, request, segments, id=None):
    return BlogEntry(id)

"""
Content negotiation
"""

# accept anything and return a 200 OK with content
@resource.GET()
def html(self, request):
    return http.ok([('Content-Type','text/html')], '<p>Hello Wolrd</p>' )

# match json only and return 200 OK (system works out content type)
@resource.GET(accept='text/json')
def json(self, request):
    return http.ok( [], '{"foo": "bar"}' )

# short cut for accept code
@resource.GET(accept='json')
def json(self, request):
    return http.ok( [], simplejson.dumps({'foo': 'bar'}) )

# accept html and build a template explicitly
@resource.GET(accept='text/html')
def html(self, request):
    content = templating.render(request, 'mytemplate.html', {'name': 'Tim'})
    return http.ok( [('Content-Type','text/html')], content )

# short cut accept html and use templating decorator
@resource.GET(accept='html')
@templating.page('mypage.html')
def html(self, request):
    return {'name': 'Tim'}

# accept anything and use url module to build a redirect
@resource.GET()
def html(self, request):
    current_url = request.url.path_qs
    return http.see_other( current_url.child('othersegment') )

And finally here is a sample comment form (as if for a blog). I’ll try to explain this in my next post.

class SimpleSchema(schemaish.Structure):
    """ A simple sommets form """
    email = schemaish.String(validator=schemaish.All(schemaish.NotEmpty, schemaish.Email))
    first_names = schemaish.String(validator=schemaish.NotEmpty)
    last_name = schemaish.String(validator=schemaish.NotEmpty)
    comments = schemaish.String()

def get_form():
    """ Creates a form and assigns a widget """
    form = formish.Form(SimpleSchema())
    form['comments'].widget = formish.TextArea()
    return form

class Root(resource.Resource):

    @resource.GET()
    @templating.page('test.html')
    def html(self, request, form=None):
        if form is None:
            form = get_form()
        return {'form': form}

    @resource.POST()
    def POST(self, request):
        return get_form().validate(request, self.html, self.thanks)

    @templating.page('thanks.html')
    def thanks(self, request, data):
        return {'data': data}

3 comments:

rhymes said...

Nice, but I'd like to know more about the reasons behind the rewriting. An experienced developer like you must had his reasons to ditch most of Pylons.

Taste? Just a different APIs? What drove you?

yannramin.com said...

Restish looks like a good blend between bare-metal and something up there like CherryPy.

Formish is also good looking, similar to my Django newforms adaptation.

Tim Parkin said...

rhymes: The two biggest reasons were an aversion to thread.locals and the fact that pylons just isn't really as agnostic as it makes out. We'd also stepped away from using Twisted and Nevow and liked the 'Resource Oriented' approach that Nevow gave but wanted something that played niceley with whatever tools came along.

Hopefully some of the ideas from each framework can rub against each other and create a little electricity ;-)

yannramin: I'm looking at your forms library and like a couple of the ideas already!! We've just got a drive on to make custom templating a smooth transition from totally automatic templating so keep and eye out.. I'll have a play with yafl and email you any feedback

Post a Comment