<?xml version="1.0"?>
<paste-with-annotations>
  <paste>
    <number>
      <integer>54156</integer>
    </number>
    <user>
      <string>baggles</string>
    </user>
    <title>
      <string>hunchentoot all-powerful handler macro</string>
    </title>
    <contents>
      <string>
;; for hunchentoot


(defmacro define-handler (name (&amp;rest lambda-args) regex &amp;body forms)
  (let* ((keys lambda-args)
         (named (loop until (or (when (eql (car keys) '&amp;key)
                                  (pop keys))
                                (null keys))
                   collecting (pop keys)))
         (m0 (gensym &quot;M0-&quot;))
         (m1 (gensym &quot;M1-&quot;))
         (rn0 (gensym &quot;RN0-&quot;))
         (rn1 (gensym &quot;RN1-&quot;))
         (request-path (gensym &quot;REQUEST-PATH-&quot;))
         (request-query (gensym &quot;REQUEST-QUERY-&quot;))
         (key-vals (gensym &quot;KEY-VALS-&quot;)))
    `(defun ,name (request)
       (destructuring-bind (,request-path &amp;optional ,request-query)
           (split-sequence:split-sequence #\? (request-uri request))
         (multiple-value-bind (,m0 ,m1 ,rn0 ,rn1) 
             (cl-ppcre:scan ,regex ,request-path)
           (declare (ignore ,m1))
           (when ,m0
             (let ((,key-vals (mapcan #'(lambda (k=v)
                                          (destructuring-bind (key &amp;optional (val t)) 
                                              (split-sequence:split-sequence #\= k=v)
                                            (list (intern (string-upcase key) (find-package '#:keyword))
                                                  val)))
                                      (split-sequence:split-sequence #\&amp; ,request-query))))
               (declare (ignorable ,key-vals))
               (lambda ()
                 (let (,@(loop for i from 0
                            for arg in named collecting
                            `(,arg (when (aref ,rn0 ,i)
                                     (subseq ,request-path (aref ,rn0 ,i) (aref ,rn1 ,i)))))
                       (keys ,key-vals)
                         ,@(loop for key in keys collecting 
                                `(,key (getf ,key-vals 
                                             (intern (symbol-name ',key) 
                                                     (find-package '#:keyword))))))
                   ,@forms)))))))))


;;eg. like this

(define-handler picpage (id dash title &amp;key one three five) &quot;/picpage/(\\d+)(-([^/?]+))?&quot;
  (declare (optimize (debug 3)))
  (declare (ignore dash))
  (with-template (title)
    (:p (esc (format nil &quot;If this works, i'll be shocked. id is ~a; title is ~a; one is ~a; three is ~a; five is ~a&quot; id title one three five)))
    (:p (esc (format nil &quot;keys ~s; key one: ~s&quot; keys (getf keys 'one))))))
</string>
    </contents>
    <universal-time>
      <integer>3409249286</integer>
    </universal-time>
    <channel>
      <string>#lispcafe</string>
    </channel>
    <colorization-mode>
      <string></string>
    </colorization-mode>
    <maybe-spam>
      <null/>
    </maybe-spam>
    <is-unicode>
      <null/>
    </is-unicode>
    <deletion-requested>
      <null/>
    </deletion-requested>
    <deletion-requested-email>
      <null/>
    </deletion-requested-email>
    <expiration-time>
      <null/>
    </expiration-time>
  </paste>
</paste-with-annotations>
