Ratpacked: Customising Renderers With Decorators
When we use the Context.render
method Ratpack's rendering mechanism kicks in. The type of the argument we pass to the render
method is used to look up the correct renderer. The renderer implements the Renderer
interface and provides the real output. We can add functionality that can work with the object of the Renderer
implementation before the actual output is created. We do this by adding a class or object to the registry that implements the RenderableDecorator
interface. The interface has a method decorate
that accepts the Context
and object that needs to be rendered. The code is invoked after the Context.render
method, but before the Renderer.render
method. This is especially useful when we use template renderers with a view model and with a RenderableDecorator
implementation we can augment the view model with some general attributes. Suppose we have a Ratpack application that uses the Groovy text template engine provided by the TextTemplateModule
. The module adds a Renderer
for TextTemplate
objects. Let's write a RenderableDecorator
implementation for the TextTemplate
, where we add an extra attribute createdOn
to the view model:
// File: src/main/groovy/com/mrhaki/ratpack/CreatedOnRendererDecorator.groovy
package com.mrhaki.ratpack
import ratpack.exec.Promise
import ratpack.groovy.template.TextTemplate
import ratpack.handling.Context
import ratpack.render.RenderableDecorator
import java.time.Clock
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
/**
* Add extra attribute to view model for all TextTemplate renderers.
*/
class CreatedOnRendererDecorator implements RenderableDecorator {
/**
* Apply this decorator for TextTemplate renderers.
*
* @return TextTemplate class.
*/
@Override
Class getType() {
return TextTemplate
}
/**
* Add an extra attribute createdOn to the view model with the current
* date and time.
*
* @param context Context to get Clock instance for this Ratpack application from.
* @param template Template with view model to extend.
* @return Promise with new TextTemplate instance with the extended view model.
*/
@Override
Promise decorate(final Context context, final TextTemplate template) {
final footerModel = [createdOn: createdOn(context)]
return Promise.value(
new TextTemplate(
template.model + footerModel,
template.id,
template.type))
}
/**
* Create formatted date/time String based on
* the Clock available on the Ratpack registry.
*
* @param context Context to get Clock instance from.
* @return Formatted date/time String.
*/
private String createdOn(final Context context) {
final Clock clock = context.get(Clock)
final LocalDateTime now = LocalDateTime.now(clock)
final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return formatter.format(now)
}
}
To use this decorator we must add it to the registry in our Ratpack application. We also use the RenderableDecorator.of
method to create a decorator for our application directory in our Ratpack Groovy DSL:
// File: src/ratpack/Ratpack.groovy
import com.mrhaki.ratpack.CreatedOnRendererDecorator
import ratpack.groovy.template.TextTemplate
import ratpack.groovy.template.TextTemplateModule
import ratpack.render.RenderableDecorator
import ratpack.util.RatpackVersion
import static ratpack.groovy.Groovy.groovyTemplate
import static ratpack.groovy.Groovy.ratpack
ratpack {
bindings {
// Use Groovy's simple text template engine.
module TextTemplateModule
// Use class that implements the
// RenderableDecorator interface.
bind CreatedOnRendererDecorator
// Create RenderableDecorator instance using the
// RenderableDecorator.of method.
// Here we add the model attribute createdWith.
bindInstance RenderableDecorator.of(TextTemplate) { context, template ->
new TextTemplate(
template.model + [createdWith: "Ratpack ${RatpackVersion.version}"],
template.id,
template.type)
}
}
handlers {
get {
// Set model attributes used on the template.
final model =
[title: 'Ratpack Application',
welcomeMessage: 'Welcome to Ratpack']
render groovyTemplate(model, 'index.html')
}
files { dir "public" }
}
}
To complete the example we create the following index.html
file in the directory src/ratpack/templates
: Written with Ratpack 1.1.1. Original blog post