Ring helps your web server deliver cat pictures. end of story. Ok, well maybe there is more. Let's look at some code that sets up Jetty (our web server) that's adapted to used Ring. Keep in mind this means were juggling two concepts that work together. In this case, the Webserver program contains Ring:
Our first goal is to just display a html page on our local computer. To do that will need to
Running our server is a matter of importing that functionality e.g run-jetty
from the ring library and calling it
;; import
(use '[ring.adapter.jetty :refer [run-jetty]])
;; run-jetty takes two arguments
(run-jetty
;; a function which returns a response
(fn [_]
{:status 200
:body "hello"})
;; information about the server
{:port 3001})
This is all you need for a local running server! Evaling/running that code will give us a local running server, and we can visit https://localhost:3001
to see the word Hello
from our code above. That's really cool. Your now ready to start building production ready web applications!
well note quite, things get more complex from here. But how always depends on your goals. Let's cover some high level ideas that it's safe to assume you will need regardless. First off, both Rich Hickey and Kote agree that names are important and your likely going to run into these two early and often.
Here is a characterization of the whole webserver process:
on reflection, middleware would be more like tools to help work on the request, rather then a stylish tie. But hopefully you get the idea.
A ring handler handles requests. We already made one of these
;; run-jetty takes two arguments
(run-jetty
;; This anonymous function is a handler, were just ignoring the request
(fn [_]
{:status 200
:body "hello"})
;; information about the server
{:port 3001})
A ring middleware is in the middle... of the ware? Ok, I don't like this name personally. I like "handler-extender". Let's see what I mean, here is our "middleware" function:
(defn handler->handler-with-!-added-to-body
[handler]
(fn [request] (-> request handler (update :body #(str % "!")))))
That name is long, accurate, and less clear that directly reading the code. Unless that function is getting re-used, I think it should stay anonymous. Like the name implies it takes a handler and returns a handler. That's what middleware does. Here is it in action, reading from the inside out:
;; 2. that handler is called on the request
(
;; 1. our middleware takes our handler and produces a new handlers
(handler->handler-with-!-added-to-body
(fn [_]
{:status 200
:body "hello"}))
{:uri "http://localhost"})
That's about as simple as it gets, from there you should read the official docs on both these concepts and more, where they will elaborate on these concepts to deal with more complex situations, but hopefully this laid out the fundamentals in a quick and enjoyable way!
Thanks for reading,
If you are new to Ring and need help setting up an environment here is a great video of how to do that with Calva (which looks great, but I use emacs). If you want to know more about how to handle HTTP requests and how to encode that process in ring middleware then I highly recommend this video on building restful web api.
The goal is to explain Clojure's Ring library with as few words and as many pictures and code examples as possible, as to complementing existing documentation. Also as a way for me to start a discussion around ring and educate myself. In that vein, i'm going to ask questions myself.
"Kote" is from the fantasy series the KingKiller Chronicles.
run-jetty
why did we get a full html tree and not just text when we used our browser retrieve the information from the server?It should be clear that I can support myself on my pure artistic talent (sarcasm alert). But in case you need someone to help your team with Clojure full stack webdevelopemt, I'm always looking for my next opportunity.