[tbnl-devel] Question about dispatchers and their actions

pete-tbnl-dev at kazmier.com pete-tbnl-dev at kazmier.com
Thu Jul 22 14:55:09 UTC 2004


I've got a question about the *dispatch-table* and the associated
actions that are returned from the dispatch functions.  Based on the
code in modlisp.lisp:

    (loop for dispatcher in *dispatch-table*
          for action = (funcall dispatcher *request*)
          when action
            return (funcall action)
          finally (setf (return-code *reply*)
                          +http-not-found+))))

Might it be desirable to check the return of (funcall action), and if
nil, continue processing with the next dispatcher rather than returning
and sending output at this point?  If one truly wanted to return an
empty body, one could still throw to the tbnl-handler-done tag which
would bypass the rest of the dispatch table.

I'm writing a template dispatcher which will process a request using
HTML-TEMPLATE.  Of course, this is only possible if I can map a request
to a template (I may store the templates on the filesystem or in a db,
not sure yet).  If a template cannot be found for the request, then I'd
like to fall back to processing the request with the rest of the
dispatch table (which may just serve a static file or may be another
template dispatcher thats resolves templates differently).

In order to accomplish the above in the today's TBNL, I need to make the
template resolution occur in the dispatch function (instead of the
action function that the dispatcher returns) because I can return nil 
from the dispatcher if resolution fails and thus instruct TBNL to move 
on to the next dispatcher. In addition, I'd also like to make my
dispatch decision based on a prefix. So I'd like to  use
create-prefix-dispatcher to limit the template processing to only
part of the URI namespace.  Thus, I need two 'tests' to occur in my
dispatch function:  1) is there a prefix match (replicating the
create-prefix-dispatcher functionality), and 2) does the request map to
a template.  With that said, I have created:

(defun create-template-dispatcher (prefix resolver-function)
  #'(lambda (request)
      (when (prefix-match-p prefix (script-name request))
        (let ((template (funcall resolver-function request)))
          (when template
            #'(lambda ()
                (funcall *default-template-handler* template)))))))

prefix-match-p is refactored code from create-prefix-dispatcher, but is
essentially the same, does the request start with the given prefix?  If
it does not, I return nil, and processing continues with the rest of the
dispatch table.  Assuming the prefix does match, I then go on to call
the template resolution function (resolver-function) which returns a
template as a string or nil if a template could not be found.  Template
resolution might query a db or just read a template from the filesystem.
Finally, if a template if found, I process the template with the default
template handler which points to:

(defun template-handler (template)
  (let ((context (assemble-context)))
    (with-output-to-string (*default-template-output*)
      (fill-and-print-template template context))))

Here is a sample template resolver function that maps a request to a template
on the filesystem (only works on systems with '/' as a separator, my lisp path
manipulations skills are absent at the moment):

(defun filesystem-resolver (base &optional (request *request*))
  (contents-of-file
    (parse-namestring
     (concatenate 'string
                  base
                  (script-name request)))))

And here is my sample dispatch table that pulls templates from multiple
locations on the filesystem depending on the request URI:

(setf *dispatch-table*
      (list (create-template-dispatcher "/tbnl/space1"
                 #'(lambda (request)
                     (filesystem-resolver "/tmp" request)))
            (create-template-dispatcher "/tbnl/space2"
                 #'(lambda (request)
                     (filesystem-resolver "/var/www/html" request)))
            #'default-dispatcher))

Back to the original question and my rationale for asking it, I think
I'd prefer if I didn't have to put all of my predicates for testing
whether or not the template handler should be run within the dispatch
function as it just doesn't seem to be the right spot for this logic.
However, I definitely want the template handler executed based on the
prefix.  If TBNL could continue processing the dispatch table upon the
return of nil from the handler (action), my dispatch table would look
like:

(setq *dispatch-table*
        (nconc
         (mapcar (lambda (args)
                   (apply #'create-prefix-dispatcher args))
                 '(("/tbnl/space1" template-dispatcher)
                   ("/tbnl/space2" template-dispatcher)))
         (list #'default-dispatcher)))

And the template-dispatcher would consult a *resolver-table* and invoke
the appropriate resolution function for the request.  The nice thing
about this approach is that my *dispatch-table* is not cluttered with
non-dispatch related stuff such as template resolution.  Of course that
all requires the ability for an action (the template-dispatcher above)
to signify that it cannot handle the request by returning a nil, which
could then indicate to TBNL that the next entry in the *dispatch-table*
should be tried.  Would this functionality appeal to anyone else?

Sorry about the  length of email, but I thought it would be useful to
share what I'm doing with TBNL.  This is all just for fun and non-profit
and my pursuit of learning lisp so if I've said something stupid or more
specifically am not doing things in the traditional "lisp way", please
let me know.

Thanks,
Pete





More information about the Tbnl-devel mailing list