Accessing Hunchentoot request objects from the REPL for debugging
Asked Answered
J

2

8

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?

Jamin answered 7/4, 2014 at 3:31 Comment(1)
It seem you just need to create a fixture returning a mock request. That shouldn't be very hard. For example see: github.com/russell/planet-git/blob/master/t/… fiveam has fixtures if you are so inclinedMakebelieve
J
8

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.

Jamin answered 23/4, 2014 at 1:23 Comment(1)
As this is at least half a kludge (as store-request must be called explicitly), I have doubts as to grant the whole bounty -- if somebody would be so kind to give one more +1, half the bounty would be granted automatically - so please vote this up :-)Anecdotage
M
1

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.

Meara answered 20/2, 2015 at 12:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.