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