Basics of Ratpack Handlers
Ratpack is a "simple, capable, toolkit for creating high performance applications" on the JVM.
It was originally inspired by Sinatra but it has taken a life of its own with very interesting concepts.
It is virtually a treasure cove of neat tricks with Groovy and its @CompileStatic feature. It has not
reached 1.0 yet, and it is under heavy development, with releases the 1st of every month.
Ratpack has always attracted me, and I finally got some time to play with it. The documentation is a work in progress, but the core devs are very helpful and the API seems to be stabilizing. So it seemed like the right time to get my feet wet.
Why I'm excited about Ratpack
I've tried Rails-type frameworks (Grails, Django, Rails, etc) and I seem to be very productive with them, but as the application starts to grow, all those initial 'benefits' seem to become obstacles. They seem to encourage tight coupling and it feels like we are building 'Rails/Grails/Django' apps instead of whatever application we are trying to build.
Ratpack tries to encourage us to build very small, lightweight applications that are not tightly coupled to Ratpack itself. It seems to be just a thin shell that allows us to connect to external resources or expose our system to outside world. Since it doesn't provide all these extra heavyweight 'features' like ORMs, mailers, etc; it allows us to think about our design and not just blindly create objects for our ORM that then drive our business logic.
Setting Up
The easiest way to setup a ratpack project is with Gradle and Lazybones. If you have gvm installed you can easily install them:
gvm install gradle
gvm install lazybones
And starting a ratpack project is as easy as:
lazybones create ratpack ratpack-app-name
This will create a folder in the current directory with the ratpack-app-name. For more information, you can
refer to the official documentation
Handlers
Handlers are responsible for handling response and request objects. They are stateless & asynchronous by default, although we can block explicitly (to be covered in a later blog post). A simple example looks like this:
ratpack {
handlers {
get("hello") {
render "Hello!"
}
}
}
This will render "Hello!" when we visit http://localhost:5050/hello.
The handler exposes methods whose names match the HTTP verbs - get, post, delete, put - and each
method takes a string as a parameter that defines the unique path. For example, get("books") allows us to do a GET at /books.
And post("books") allows us to do a DELETE on /books.
Order Matters
The order of the handlers also matter. Those declared at the top are evaluated first. E.g.:
ratpack {
handlers {
get("hello") {
render "Hello!"
}
get("bye") {
render "Bye Bye!"
}
}
}
Ratpack will first check if the route matches "hello", if not, it will proceed to the next handler, until it finds a matching one.
JSON
If we want to render json, we need to use the JacksonModule:
import ratpack.jackson.JacksonModule
import static ratpack.jackson.Jackson.json
ratpack {
bindings {
new JacksonModule()
}
handlers {
get("api/books") {
def books = [
[title: 'A Tale of Two Cities'],
[title: 'Daughter of Smoke & Bone'],
[title: 'Wheel of Time'],
[title: "Spider's Bite (Elemental Assasin #1)"]
]
render json(books)
}
get("api/authors") {
def authors = [
[name: 'Charles Dickens'],
[name: 'Laini Taylor'],
[name: 'Robert Jordan'],
[name: 'Jennifer Estep']
]
render json(authors)
}
}
}
We can also use prefix if we want to group all these handlers:
import ratpack.jackson.JacksonModule
import static ratpack.jackson.Jackson.json
ratpack {
bindings {
new JacksonModule()
}
handlers {
prefix("api") {
get("books") {
def books = [
[title: 'A Tale of Two Cities'],
[title: 'Daughter of Smoke & Bone'],
[title: 'Wheel of Time'],
[title: "Spider's Bite (Elemental Assasin #1)"]
]
render json(books)
}
get("authors") {
def authors = [
[name: 'Charles Dickens'],
[name: 'Laini Taylor'],
[name: 'Robert Jordan'],
[name: 'Jennifer Estep']
]
render json(authors)
}
}
}
}
Dynamic Paths
We can create dynamic paths pretty easily by prepending a : to the dynamic parts of the route.
Every handler has a `pathTokens attribute that allows us to access the dynamic parts of the url through its name:
import ratpack.jackson.JacksonModule
import static ratpack.jackson.Jackson.json
ratpack {
bindings {
new JacksonModule()
}
handlers {
get("hello/:name") {
def name = pathTokens.name
render "Hello $name"
}
}
Static Assets
If we are using the lazybones template to generate a Ratpack application, we can serve up static assets quite easily.
Ratpack provides an assets function that takes a string which represents the directory that we want to serve. This
directory has to be inside the src/ratpack folder:
ratpack {
handlers {
assets "css"
}
}
The above snippet will serve everything under src/ratpack/css as static assets. We can also serve more than one folder:
ratpack{
handlers{
assets "css"
assets "js"
}
}
These are the basics of how handlers work in Ratpack. In the next installment we will see how we can render templates.
Source
Roberto Guerra
Roberto Guerra is a developer living in the Caribbean.
In his spare time, he likes to read fantasy books and goof around with his dogs. He also started learning to play the classical guitar late in life. His current tech interests are Go, serverless and MacOS development with Swift.