pvl/web/application.py
changeset 151 8a9f01036091
child 164 2c66ab45d91e
equal deleted inserted replaced
150:f12e83f8c34b 151:8a9f01036091
       
     1 """
       
     2     WSGI Application.
       
     3 """
       
     4 
       
     5 import werkzeug
       
     6 from werkzeug.wrappers import Request, Response
       
     7 from werkzeug.exceptions import HTTPException
       
     8 
       
     9 import logging; log = logging.getLogger('pvl.web.application')
       
    10 
       
    11 class Application (object) :
       
    12     """
       
    13         Main state.
       
    14     """
       
    15 
       
    16     URLS = None
       
    17 
       
    18     def __init__ (self, urls=None) :
       
    19         """
       
    20                 urls        - werkzeug.routing.Map -> Handler
       
    21         """
       
    22 
       
    23         if not urls :
       
    24             urls = self.URLS
       
    25 
       
    26         if not urls :
       
    27             raise ValueError("No URLS/urls=... given")
       
    28 
       
    29         self.urls = urls
       
    30 
       
    31     def respond (self, request) :
       
    32         """
       
    33             Lookup Request -> web.Handler, params
       
    34         """
       
    35         
       
    36         # bind to request
       
    37         urls = self.urls.bind_to_environ(request)
       
    38         
       
    39         # lookup
       
    40         handler, params = urls.match()
       
    41 
       
    42         # handler instance
       
    43         handler = handler(self, request, urls, params)
       
    44 
       
    45         try :
       
    46             # apply
       
    47             return handler.respond()
       
    48 
       
    49         finally :
       
    50             handler.cleanup()
       
    51 
       
    52     @Request.application
       
    53     def __call__ (self, request) :
       
    54         """
       
    55             WSGI entry point, werkzeug Request -> Response
       
    56         """
       
    57 
       
    58         try :
       
    59             return self.respond(request)
       
    60         
       
    61         except HTTPException as ex :
       
    62             return ex
       
    63 
       
    64 from pvl.invoke import merge # XXX
       
    65 from pvl.web.html import tags as html
       
    66 
       
    67 class Handler (object) :
       
    68     """
       
    69         Per-Request controller/view, containing the request context and generating the response.
       
    70     """
       
    71 
       
    72     TITLE = None
       
    73     CSS = (
       
    74         #"/static/...css", 
       
    75     )
       
    76     JS = (
       
    77         #"/static/....js"
       
    78     )
       
    79 
       
    80     def __init__ (self, app, request, urls, params) :
       
    81         """
       
    82             app     - wsgi.Application
       
    83             request - werkzeug.Request
       
    84             urls    - werkzeug.routing.Map.bind_to_environ()
       
    85         """
       
    86 
       
    87         self.app = app
       
    88         self.request = request
       
    89         self.urls = urls
       
    90         self.params = params
       
    91         
       
    92     def url (self, handler=None, **params) :
       
    93         """
       
    94             Return an URL for given endpoint, with parameters,
       
    95         """
       
    96 
       
    97         if not handler :
       
    98             # XXX: just generate a plain-relative '?foo=...' url instead?
       
    99             handler = self.__class__
       
   100             params = merge(self.params, params)
       
   101 
       
   102         return self.urls.build(handler, params)
       
   103 
       
   104     def title (self) :
       
   105         """
       
   106             Render site/page title as text.
       
   107         """
       
   108 
       
   109         return self.TITLE
       
   110  
       
   111     def render (self) :
       
   112         """
       
   113             Render page content (as <body>...</body>).
       
   114         """
       
   115 
       
   116         raise NotImplementedError()
       
   117 
       
   118     def render_html (self) :
       
   119         """
       
   120             Render page layout (as <html>).
       
   121         """
       
   122 
       
   123         title = self.title()
       
   124 
       
   125         return html.html(
       
   126             html.head(
       
   127                 html.title(title),
       
   128                 (
       
   129                     html.link(rel='Stylesheet', type="text/css", href=src) for src in self.CSS
       
   130                 ), 
       
   131                 (
       
   132                     html.script(src=src, type='text/javascript', _selfclosing=False) for src in self.JS
       
   133                 ),
       
   134             ),
       
   135             html.body(
       
   136                 html.h1(title),
       
   137                 self.render() 
       
   138             )
       
   139         )
       
   140 
       
   141     def process (self, **params) :
       
   142         """
       
   143             Process request args to build internal request state.
       
   144         """
       
   145 
       
   146         pass
       
   147 
       
   148     def respond (self) :
       
   149         """
       
   150             Generate a response, or raise an HTTPException
       
   151 
       
   152             Does an HTML layout'd response per default.
       
   153         """
       
   154         
       
   155         # returning e.g. redirect?
       
   156         response = self.process(**self.params)
       
   157 
       
   158         if response :
       
   159             return response
       
   160         
       
   161         # render as html per default
       
   162         render = self.render_html()
       
   163         text = unicode(html.document(render))
       
   164 
       
   165         return Response(text, mimetype='text/html')
       
   166     
       
   167     def cleanup (self) :
       
   168         """
       
   169             After request processing. Do not fail :)
       
   170         """
       
   171         
       
   172         pass