RSS Feed

Lisp Project of the Day

lack

You can support this project by donating at:

Donate using PatreonDonate using Liberapay

lackweb

Documentation😀
Docstrings🥺
Tests 😀
Examples😀
RepositoryActivity😀
CI 😀

Lack is a library, used by Clack to compose web apps from middlewares.

Yesterday we've used the lack-middleware-accesslog system to log every request to our webapp. But app configuration was not convenient. Lack provides a macro to compose an application from middlewares:

POFTHEDAY> (defparameter *app*
             (lack:builder
              ;; middlewares
              :accesslog
              ;; the app
              (lambda (env)
                (declare (ignorable env))
                '(200 (:content-type "text/plain")
                  ("Hello, World")))))

POFTHEDAY> (clack:clackup *app*
                          :port 8080)
Hunchentoot server is started.
Listening on 127.0.0.1:8080.

POFTHEDAY> (values (dex:get "http://localhost:8080/foo/bar"))
127.0.0.1 - [22/Jun/2020:22:15:23 +03:00] "GET /foo/bar HTTP/1.1"
         200 12 "-" "Dexador/0.9.14 (SBCL 2.0.2); Darwin; 19.5.0"
"Hello, World"

You can pass a middleware as a keyword or as a s-exp. In the slatter case, all values except the first one, will be passed to the middleware functions.

This way a middleware can be configured. Here is for example, how we can replace a logging function to use log4cl (by the way, remind me to tell you about log4cl, it is wonderful!):

POFTHEDAY> (defparameter *app*
             (lack:builder
              ;; middlewares
              (:accesslog :logger
                          (lambda (message)
                            (log:info message)))
              ;; the app
              (lambda (env)
                (declare (ignorable env))
                '(200 (:content-type "text/plain")
                  ("Hello, World")))))

POFTHEDAY> (clack:clackup *app*
                          :port 8081)

POFTHEDAY> (values (dex:get "http://localhost:8081/foo/bar"))

 <INFO> [22:38:06] poftheday () -
  POFTHEDAY::MESSAGE: "127.0.0.1 - [22/Jun/2020:22:38:06 +03:00] 
                \"GET /foo/bar HTTP/1.1\" 200 12 \"-\"
                \"Dexador/0.9.14 (SBCL 2.0.2); Darwin; 19.5.0\""
  
"Hello, World"

Multiple middlewares can be passed to the lack:builder.

When we are specifying the middleware's name as a keyword, lack tries to search a middleware function using lack.util:find-middleware.

POFTHEDAY> (lack.util:find-middleware :accesslog)

#<FUNCTION (LAMBDA (LACK.MIDDLEWARE.ACCESSLOG::APP &KEY :LOGGER :FORMATTER)
             :IN
             "/Users/art/projects/lisp/lisp-project-of-the-day/.qlot/dists/\
  ultralisp/software/fukamachi-lack-20200524065357/src/middleware/accesslog.lisp")
  {22D1F6CB}>

If you intend to create an opensource library providing Lack middleware and want this discovery work for it, then you have to follow these rules.

Your system has to define a package prefixed with LACK.MIDDLEWARE. And it should export a variable with name matched to the pattern *LACK-MIDDLEWARE-...*. This variable should be bound to a middleware function.

For example, access log middleware defines the LACK.MIDDLEWARE.ACCESSLOG:*LACK-MIDDLEWARE-ACCESSLOG* variable.

Another interesting feature, I didn't cover yet the ability to write an app which delays it's response or stream it back. Luckily, these kinds of applications are covered by Lack's documentation.

Tomorrow, we'll look at some Lack's middleware.


Brought to you by 40Ants under Creative Commons License