XP on Rails Extreme Programming Blog

3Jan/080

RESTful controller

Come gia’ detto in precedenza, la mia applicazione sara’ dotata di un’interfaccia RESTful.

Implementare un controller REST usando CherryPy non e’ eccessivamente complesso, sfruttando il metodo default.
Questo metodo viene infatti invocato ogni volta che nell’url si fa riferimento al controller, senza specificare alcun particolare metodo.
A questo punto, e’ relativamente semplice stabilire il method della request, se POST, GET, PUT o DELETE e agire di conseguenza.

Purtroppo, i browser attualmente non supportano metodi di tipo PUT o DELETE, quindi tutto si riduce ai due principali (GET per view, POST per new, edit e delete).

Un aspetto molto importante del controller e’ la gestione delle collections come friendly url: se uno snippet avra’ tanti commenti, bastera’ andare nell’url site/snippets/snippet_id/comments/ per visualizzare tutta la lista dei commenti relativi ad uno snippet e cosi’ via.

Il codice del controller e’ stato ripreso e modificato da RESTful TurboGears

class Resource(object):
    children = {}

    @classmethod
    def get_child(cls, token):
        return cls.children.get(token, None)

    @cherrypy.expose
    @utils.transaction
    def default(self, *path, **kw):
        request = cherrypy.request
        path = list(path)
        resource = None
        http_method = request.method.lower()
       
        #check the http method is supported.
        try:
            method_name = dict(get='get', post='post')[http_method]
        except KeyError:
            raise cherrypy.HTTPError(501)

        if not path: #If the request path is to a collection.  
            if http_method == 'get':        
                #If the method is a get, call the self.index method, which
                #should list the contents of the collection.
                return self.listAll(**kw)
            else:
                #Any other methods get rejected.
                raise cherrypy.HTTPError(501)

        if resource is None:
           
            token = path.pop(0)
            token = token.split(";")
           
            #check for additionals methods on resource
            if len(token)>1:
                if token[1] == 'edit':
                    resource = self.load(token[0])
                    method_name = 'modify'              
                elif token[1] == 'new':
                    resource = self.create(**kw)
                    method_name = 'new'
                else:
                    raise cherrypy.HTTPError(501)
            else:                    
                resource = self.load(token[0])
               
            if resource is None:
                #No resource found?
                raise cherrypy.HTTPError(404)

        #if we have a path, check if the first token matches this
        #classes children.
        if path:
            token = path.pop(0)        
            child = self.get_child(token)
            if child is not None:
                child.parent = resource
                #call down into the child resource.
                return child.default(*path, **kw)
            else:
                raise cherrypy.HTTPError(404)

        #run the requested method, passing it the resource
        method = getattr(self, method_name)
        response = method(resource, **kw)

        return response


    def load(self, token):
        """
        loads and returns a resource identified by the token.
        """

        return None
       
    def get(self, resource, **kw):
        """
        fetches the resource, and returns a representation of the resource.
        """

        raise cherrypy.HTTPError(501)
       
    def listAll(self, **kw):
        """
        returns the representation of a collection of resources.
        """

        raise cherrypy.HTTPError(501)

    def create(self, **kw):
        """
        returns a class or function which will be passed into the self.new
        method.
        """

        raise cherrypy.HTTPError(501)

    def new(self, resource, **kw):
        """
        uses resources factory to create a resource, commit it to the
        database.
        """

        raise cherrypy.HTTPError(501)

    def modify(self, resource, **kw):
        """
        uses kw to modifiy the resource.
        """

        raise cherrypy.HTTPError(501)

Gli altri controller REST, tipo UserController o SnippetController, dovranno solamente estendere Resource e implementare i metodi:

* load
* get
* listAll
* create
* new
* modify

19Dec/070

Killer Application

Killer Application? Non penso…
Il mio nuovo oggetto di sviluppo e’ un repository online di pezzetti di codice, i cosiddetti snippets. Come idea non e’ innovativa, e sicuramente non mi fara’ entrare nell’olimpo degli sviluppatori. Ma e’ senza dubbio un’applicazione completa, abbastanza complessa per poter imparare qualcosa di nuovo.

Le caratteristiche principali dell’applicazione saranno:

* gestione utenti (attivazione, login, ruoli ecc)
* gli utenti registrati e attivi possono aggiungere uno o piu’ snippet nel repository.
* uno snippet consiste in testo piu’ codice.
* uno snippet puo’ essere pubblico o privato
* uno snippet puo’ essere associato a dei tag per essere cercato piu’ facilmente
* gli utenti registrati e attivi possono commentare gli snippet altrui, aggiungendo ache altro codice.
* l’applicazione sara’ dotata di interfaccia REST

Queste saranno piu’ o meno le caratteristiche principali dell’applicazione, piu’ eventalmente features accessorie da definirsi man mano che procede lo sviluppo.