In the last chapter, we generated a new web application using the Luminus template. However, before we get too deep into the development of our app and playing with all the toys, it's important for us to get a high-level understanding of two technologies that will support everything we build and do, and that's Ring and the Ring Server.
You're reading from Clojure Web Development Essentials
"Ring is a Clojure web applications library inspired by Python's WSGI and Ruby's Rack. By abstracting the details of HTTP into a simple, unified API, Ring allows web applications to be constructed of modular components that can be shared among a variety of applications, web servers, and web frameworks." | ||
--- James Reeves |
James Reeves is also known as Weavejester; he is the creator and maintainer of Ring and about a billion other Clojure-based technologies (https://github.com/ring-clojure/ring/blob/master/README.md).
In simple terms, Ring handles all the nitty gritty HTTP implementation details, such as HTTP request/response, parameters, cookies, and so on. It abstracts the underlying implementations away from our code, allowing us to focus on writing our application instead of low-level HTTP crud. This abstraction, coupled with the fact that Ring is built on top of the HTTP Servlet specification, enables us to package our application and host it in a variety...
The first thing to know is that Ring and the Ring Server are not, I repeat, are not the same thing. Whereas Ring provides a suite of libraries which abstract the underlying implementation details, the Ring Server library provides the ability to start an actual web server to serve a Ring handler.
Whenever we use lein ring server
from the command line, we start the Ring Server (there are other ways, but we'll get to those later in this chapter, and in Chapter 11, Environment Configuration and Deployment). At startup, the Ring Server will execute any registered application initialization hooks, and then start an embedded Jetty server, which serves our application handler. Incoming requests are then processed by Ring, as described in the previous section, until we shut down our app. On shutdown, Ring stops the embedded Jetty server and executes any registered shutdown hooks.
We can see this in the interaction between our hipstr.handler
and hipstr.repl
namespaces. Let's...
There are two ways you can run the Ring Server. The first is by loading the hipstr.repl
namespace into a REPL and calling start-server
. The second is from the command line (which we've seen earlier):
# lein ring server
In either case, an embedded Jetty server will be spun up to serve our application handler, and a browser will pop open. If you don't want the browser to open, you can run the server in the headless
mode:
# lein ring server-headless
How we start the server determines how we configure the server. We've already seen how to configure the server when running through the REPL (by adjusting the options map that's passed as part of the call to ring.server.standalone/serve
), but how do we configure the server if running from the command line?
The lein ring
command is made available through the lein-ring
plugin. Luminus includes this plugin when generating the project for us. In our project dependencies file (project.clj
), you'll see the following...
Congratulations on successfully making it through the driest chapter in the book! A cruel but necessary exercise. In this chapter, you learned the difference between Ring and the Ring Server. We got a taste of how to modify route behavior by creating a new route handler, and played around with a bit of middleware. Finally, you learned how to start and stop the Ring Server from both the REPL and the command line, and how to configure each, respectively. In the next chapter, we're going to take a look at a developer's best and only set of binoculars—logging.