web.Handler
authorTero Marttila <terom@paivola.fi>
Wed, 10 Oct 2012 22:45:50 +0300
changeset 3 5990b188c54b
parent 2 b0659c867226
child 4 b09436772d46
web.Handler
pvl/verkko/hosts.py
pvl/verkko/web.py
pvl/verkko/wsgi.py
--- a/pvl/verkko/hosts.py	Wed Oct 10 22:10:16 2012 +0300
+++ b/pvl/verkko/hosts.py	Wed Oct 10 22:45:50 2012 +0300
@@ -64,14 +64,14 @@
     #_name   = db.dhcp_hosts.c.name,
 ))
 
-HOST_SORT = {
+HOST_ATTRS = {
     'id':   Host.id,
     'ip':   Host.ip,
     'mac':  Host.mac,
     'name': Host.name,
     'seen': Host.last_seen,
-    None:   Host.last_seen.desc(),
 }
+HOST_SORT = Host.last_seen.desc()
 
 def render_hosts (hosts, title=None) :
     COLS = (
@@ -135,62 +135,86 @@
         html.a(href='/hosts')(html('&laquo;'), 'Back'),
     )
 
-def respond (request, db, path) :
-    """
-        Handle request
-    """
-
-    hosts = db.query(Host)
-
-    # sort ?
-    sort = request.args.get('sort')
-    sort = HOST_SORT[sort]
-    log.debug("sort: %s", sort)
-
-    hosts = hosts.order_by(sort)
+class Handler (web.Handler) :
+    TITLE = "DHCP Hosts"
     
-    # index
-    if not path :
-        title = "DHCP Hosts"
-        html = render_hosts(hosts)
-    
-    # id
-    elif len(path) == 1 :
-        id, = path
-        id = int(id)
+    def index (self) :
+        return render_hosts(self.hosts)
 
-        host = hosts.get(id)
-        hosts = hosts.filter((Host.ip == host.ip) | (Host.mac == host.mac))
+    def host (self, id) :
+        host = self.hosts.get(id)
         
-        # render
-        title = "DHCP Host: {host}".format(host=unicode(host))
-        html = render_host(host, hosts)
+        if not host :
+            raise web.NotFound("No such host: {id}".format(id=id))
+
+        hosts = self.hosts.filter((Host.ip == host.ip) | (Host.mac == host.mac))
+        
+        # XXX
+        #self.title = "DHCP Host: {host}".format(host=unicode(host))
+
+        return render_host(host, hosts)
     
-    # query
-    elif len(path) == 2 :
-        attr, value = path
-        
+    def list (self, attr, value) :
         # fake host
         host = { 'ip': None, 'mac': None, 'name': None }
+
+        if attr not in HOST_ATTRS :
+            raise web.BadRequest("Invalid attribute: {attr}".format(attr=attr))
+
         host[attr] = value
 
         host = Host(**host)
 
         # query
-        attr = HOST_SORT[attr]
+        attr = HOST_ATTRS[attr]
         log.debug("%s == %s", attr, value)
 
-        hosts = hosts.filter(attr == value)
+        hosts = self.hosts.filter(attr == value)
         
-        # render
-        title = "DHCP Hosts: {value}".format(value=value)
-        html = render_host(host, hosts)
+        # XXX
+        #self.title = "DHCP Hosts: {value}".format(value=value)
 
-    else :
-        return Response("Not Found", status=404)
+        return render_host(host, hosts)
+    
+    def process (self) :
+        hosts = self.db.query(Host)
 
-    # render
-    html = web.render_layout(title, html)
+        # sort ?
+        sort = self.request.args.get('sort')
 
-    return web.Response(html, content_type='text/html; charset=UTF-8')
+        if sort :
+            sort = HOST_ATTRS[sort]
+        else :
+            sort = HOST_SORT
 
+        log.debug("sort: %s", sort)
+
+        hosts = hosts.order_by(sort)
+        
+        # store
+        self.hosts = hosts
+    
+    def render (self) :
+        # index
+        if not self.path :
+            return self.index()
+        
+        # id
+        elif len(self.path) == 1 :
+            try :
+                id, = self.path
+                id = int(id)
+            except ValueError as ex :
+                raise web.BadRequest("Invalid host ID: {id}: {ex}".format(id=id, ex=ex))
+
+            return self.host(id)
+
+        # query
+        elif len(self.path) == 2 :
+            attr, value = self.path
+            
+            return self.list(attr, value)
+
+        else :
+            raise web.NotFound
+
--- a/pvl/verkko/web.py	Wed Oct 10 22:10:16 2012 +0300
+++ b/pvl/verkko/web.py	Wed Oct 10 22:45:50 2012 +0300
@@ -1,31 +1,105 @@
+# encoding: utf-8
+
 from werkzeug.wrappers import Response
 
 # view
 from pvl.html import tags as html
 
-def render_layout (title, content) :
-    css = [
-        "/static/layout.css", 
+# errors
+from werkzeug.exceptions import (
+        HTTPException, 
+        BadRequest,         # 400
+        NotFound,           # 404
+)
+
+class Handler (object) :
+    """
+        Per-Request controller/view, containing the request context and generating the response.
+    """
+    
+    TITLE = None
+    CSS = (
+        #"/static/layout.css", 
         "/static/style.css", 
-    ]
+    )
 
-    return unicode(html.document(html.html(
-        html.head(
-            html.title(title),
-            (
-                html.link(rel='Stylesheet', type="text/css", href=src) for src in css
+    def __init__ (self, app, request, path) :
+        """
+            app     - wsgi.Application
+            request - werkzeug.Request
+        """
+
+        self.app = app
+        self.db = app.db
+        self.request = request
+        
+        # TODO
+        self.path = path
+    
+    @property
+    def title (self) :
+        """
+            Render site/page title as text.
+        """
+        
+        if self.TITLE :
+            return u"Päivölä Verkko :: {title}".format(title=self.TITLE)
+        else :
+            return u"Päivölä Verkko"
+
+    def render (self) :
+        """
+            Render page content (as <body>...</body>).
+        """
+
+        raise NotImplementedError()
+
+    def render_html (self) :
+        """
+            Render page layout (as <html>).
+        """
+
+        return html.html(
+            html.head(
+                html.title(self.title),
+                (
+                    html.link(rel='Stylesheet', type="text/css", href=src) for src in self.CSS
+                ),
             ),
-        ),
-        html.body(
-            html.h1(title),
-            content 
+            html.body(
+                html.h1(self.title),
+                self.render() 
+            )
         )
-    )))
 
-def render_index () :
-    return Response(render_layout("Verkko", (
-        html.ul(
-            html.a(href='/hosts')("DHCP Hosts"),
+    def process (self) :
+        """
+            TODO: process request args to build internal state
+        """
+
+        pass
+
+    def respond (self) :
+        """
+            Generate a response.
+
+            Does an HTML layout'd response per default.
+        """
+        
+        try :
+            # XXX: returning e.g. redirect? args?
+            self.process()
+
+            return Response(unicode(html.document(self.render_html())), mimetype='text/html')
+        
+        except HTTPException as ex :
+            return ex
+
+class Index (Handler) :
+    def render (self) :
+        return (
+            html.ul(
+                html.a(href='/hosts')("DHCP Hosts"),
+            )
         )
-    )), content_type='text/html')
 
--- a/pvl/verkko/wsgi.py	Wed Oct 10 22:10:16 2012 +0300
+++ b/pvl/verkko/wsgi.py	Wed Oct 10 22:45:50 2012 +0300
@@ -30,13 +30,15 @@
 
         log.debug("path: %s", path)
         
-        # respond
+        # lookup handler/respond
         if not path :
-            return web.render_index()
+            handler = web.Index
 
         elif path[0] == 'hosts' :
-            return hosts.respond(request, self.db, path[1:])
+            handler = hosts.Handler
+            path.pop(0)
 
         else :
             return Response("Not Found", status=404)
         
+        return handler(self, request, path).respond()