Ratpacked: Special Routing Of Promise Values Using Predicates
One of the strengths of Ratpack is the asynchronous execution model. An important class is the Promise
class. An instance of the class will represent a value that is available later. We can invoke several operations that need be applied to a value when a Promise
is activated. Usually the activation happens when we subscribe to a Promise
using the then
method. We can use the route
method for a Promise
to have a different action when a certain predicate is true. The action will stop the flow of operations, so methods that are executed after the route
method are not executed anymore if the predicate is true. If the predicate is false then those methods are invoked.
The Promise
class has a method onNull
as a shorthand for the route
method where the predicate checks if the value is null
. For example we could have a service in our application that returns a Promise
. If the value is null
we want some special behaviour like sending a 404
status code to the client. With the following code we could achieve this:
import ratpack.jackson.Jackson
import static ratpack.groovy.Groovy.ratpack
ratpack {
bindings {
bind(UserService, DefaultUserService)
}
handlers { UserService userService ->
get('user/:username') {
final String username = pathTokens.username
userService
.findUser(username)
// If user is not found, then value
// of Promise object is null.
.onNull { clientError(404) }
.map(Jackson.&json)
.then { JsonRender userAsJson ->
render(bookAsJson)
}
}
}
}
In the following Spock specification we can see both the onNull
and route
methods and how they work:
package com.mrhaki.ratpack.promise
import ratpack.exec.Promise
import ratpack.func.Predicate
import ratpack.test.exec.ExecHarness
import spock.lang.AutoCleanup
import spock.lang.Specification
class PromiseValueRoutingSpec extends Specification {
@AutoCleanup
ExecHarness execHarness = ExecHarness.harness()
def "when Promise value is null then special action is used"() {
given:
String execResult
when:
execHarness.run({
Promise.value(promiseValue)
// If Promise has null value then
// execute the action, otherwise
// proceed.
.onNull {
execResult = 'null value'
}
// Promise value was not null, so
// we get to this action block
.then { String value ->
execResult = value
}
})
then:
execResult == result
where:
promiseValue || result
'Ratpack rules' || 'Ratpack rules'
'' || ''
null || 'null value'
}
def "when a Promise value meets a predicate a custom action is used"() {
given:
String execResult
and:
// Create Ratpack predicate for checking if Optional object
// is empty.
final Predicate\> emptyOptional = { Optional optional ->
// Check if value is present
!optional.present
}
when:
execHarness.run({
Promise.value(promiseValue)
// If Promise has null value then
// execute the action, otherwise
// proceed.
.onNull {
execResult = 'null value'
}
// Custom check for the Promise value
// which executes the given action when
// the predicate is true.
.route(emptyOptional) {
execResult = 'empty optional value'
}
// Promise value was not null and
// optional value was present, so
// we get to this action block
.then { Optional optional ->
execResult = optional.get()
}
})
then:
execResult == result
where:
promiseValue || result
Optional.empty() || 'empty optional value'
Optional.of('Ratpack rules') || 'Ratpack rules'
null || 'null value'
}
}
Written with Ratpack 1.1.1