Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing Hunchentoot request objects from the REPL for debugging

I find that incremental development tends to break when coding for Hunchentoot.

For example, I might write a web page that is composed of a few functions. If one of these inner functions contains a call to - say - hunchentoot:post-parameters* then I can't easily test the function in the REPL. It'll error out because *request* doesn't exist unless the page is called by a web client.

It would be nice if some function-or-other-source existed such that I could test my-function thus:

>(let* ((*request* (get-previous-request-from-somewhere))
       (*session* (slot-value *request* 'hunchentoot:session)))
    (my-function <whatever params>))

Does it or something similar exist? Am I overlooking a better approach to debugging?

like image 947
BnMcGn Avatar asked Apr 07 '14 03:04

BnMcGn


2 Answers

My interim solution looks something like this:

(defparameter *save-last-request* t)
(defvar *last-request* nil)

(defun store-request ()
  (when *save-last-request*
    (setf *last-request* *request*)))

(defmacro with-last-request (&body body)
  `(let* ((*request* *last-request*)
      (*session* (slot-value *request* 'hunchentoot:session)))
    ,@body))

It works fine with the caveat that each handler needs to make a call to store-request.

like image 64
BnMcGn Avatar answered Nov 16 '22 03:11

BnMcGn


I think the simplest thing to do might be to use a custom request class that introduces a way to persist requests somewhere into the initializer chain.

Here's a trivial example of one approach. A custom subclass of request which saves it's state, in a global stack.

You can set your acceptors to use custom request-classes using

(setf (acceptor-request-class acceptor ) new-value)

so something like this

(defparameter *requests* nil)
(defclass my-request (hunchentoot:request) ())
(defmethod initialize-instance  :after ((req my-request) &key)
  (push req *requests*))

and then set the acceptor request class to use this when you make your acceptor e.g.

(setf (hunchentoot:acceptor-request-class 
        (make-instance 'hunchentoot:easy-acceptor)) 'my-request)

Every time a request is created by this acceptor to pass to the handler, it will be added to the *requests* list.

if you use a variable to specify the request class name you could toggle this class on and off for development/debugging.

You could then take requests from this stack in your test binding.

like image 35
cms Avatar answered Nov 16 '22 01:11

cms