Search code examples
lispcommon-lispsbclcaveman2

How to define caveman2 routes in multiple files/packages?


I want to have routes defined over multiple files, not just web.lisp. What is the correct way of doing this?

I tried writing another file with this code and it works:

(in-package :mywebapp.web)

;; for @route annotation
(syntax:use-syntax :annot)


(defroute "/hello" ()
  (format nil "Hello World!"))

However, what I thought was the correct approach was to have another package definition and then either import :mywebapp.web into my new package, or import my new package :mywebapp.controllers.hello into :mywebapp.web.

Can you please explain to me the correct approach?

When I created my own package and imported :mywebapp.web I got the following error:

There is no applicable method for the generic function #<STANDARD-GENERIC-FUNCTION (COMMON-LISP:SETF NINGLE/APP:ROUTE) (1)> when called with arguments (#<FUNCTION (LAMBDA (#:PARAMS1141) :IN "/project/src/controllers/hello.lisp") {53CE5F6B}> NIL "/hello"). See also: The ANSI Standard, Section 7.6.6

However I had imported into the new packages all the same imports as in the .web package...

Note that I redacted the path...

Thanks!


Solution

  • So, in package CL-USER (in the REPL) I wrote the following form and asked Emacs/Slime for its macroexpansion:

    (caveman2:defroute "/hello" ())
    

    The result is:

    (SETF (APPLY #'NINGLE/APP:ROUTE
                 (CONS
                  (CAVEMAN2.APP:FIND-PACKAGE-APP #<PACKAGE "COMMON-LISP-USER">)
                  (LIST "/hello")))
            (LAMBDA (#:PARAMS1721) (DECLARE (IGNORABLE #:PARAMS1721))))
    

    This corresponds to your error, because ultimately when combining apply with the (cons ...) forms, the code will be calling route with the result of find-package-app and "/hello".

    But in your case, the result of find-package-app is NIL, meaning no such app was found. I probably would have expected a better error message here, but anyway the solution is to explicitly specify the application:

    (caveman2:defroute (other-package:*app* "/hello") ())
    

    This macroexpands as:

    (SETF (APPLY #'NINGLE/APP:ROUTE (LIST OTHER-PACKAGE:*APP* "/hello"))
            (LAMBDA (#:PARAMS1723) (DECLARE (IGNORABLE #:PARAMS1723))))
    

    There is however little documentation about that, I had to look at the source code.

    Basically when an application is instantiated, the current value of *package* is captured and associated in a hash-table to the application instance.