qmsk/web/application.py
author Tero Marttila <terom@paivola.fi>
Sat, 31 Jan 2015 15:17:59 +0200
changeset 113 b4cb6e4313cb
parent 109 55f6a253cc15
permissions -rw-r--r--
qmsk.web: Handler.request_post()
from werkzeug.wrappers import Request, Response
from werkzeug.exceptions import (
        HTTPException,
        BadRequest,         # 400
        NotFound,           # 404
)
from werkzeug.utils import redirect

class Application:
    URLS = None

    def __init__ (self, urls=None):
        """
            urls        - werkzeug.routing.Map -> Handler
        """

        if not urls:
            urls = self.URLS

        if not urls:
            raise ValueError("Required URLS/urls=...")

        self.urls = urls

    def lookup (self, request):
        """
            Lookup Request -> Handler, params
        """
        
        # bind to request
        urls = self.urls.bind_to_environ(request)
        
        # lookup
        handler, params = urls.match()

        # handler instance
        handler = handler(self, request, urls)
        
        return handler, params
   
    def respond (self, request):
        """
            Process Request -> Response, using lookup()
        """

        handler, params = self.lookup(request)

        try :
            handler.init()

            # apply
            return handler.respond(**params)

        finally :
            handler.cleanup()

    @Request.application
    def __call__ (self, request) :
        """
            WSGI entry point, werkzeug Request -> Response
        """

        try :
            return self.respond(request)
        
        except HTTPException as ex :
            return ex

class Handler (object) :
    """
        Per-Request controller/view, containing the request context and generating the response.
    """
    
    # werkzeug defaults to UTF-8
    MIMETYPE = 'text/html'

    def __init__ (self, app, request, urls) :
        """
            app     - wsgi.Application
            request - werkzeug.Request
            urls    - werkzeug.routing.Map.bind_to_environ()
        """

        self.app = app
        self.request = request
        self.urls = urls

    def request_post(self):
        if self.request.method != 'POST':
            return None
        else:
            return self.request.form
 
    def url (self, handler=None, **params) :
        """
            Return an URL for given endpoint, with parameters,
        """

        if not handler :
            handler = self.__class__

        return self.urls.build(handler, params)

    ## processing stages
    def init (self) :
        """
            Initialize on request start.
        """

        pass

    def process (self, **params) :
        """
            Process request args to build internal request state.

            May optionally return a Response, to e.g. redirect after POST.
        """

        pass

    def render (self) :
        """
            Render response.
        """

        raise NotImplementedError()

    def status (self):
        return 200

    def headers (self):
        return [ ]
    
    def mimetype (self):
        return self.MIMETYPE


    def respond (self, **params) :
        """
            Generate a response, or raise an HTTPException
        """
        
        # returning e.g. redirect?
        response = self.process(**params)

        if response :
            return response
        
        return Response(self.render_response(), mimetype=self.mimetype(), status=self.status(), headers=self.headers())

    def cleanup (self) :
        """
            After request processing. Do not fail :)
        """
        
        pass