When we develop our Ratpack application using Gradle we can use the continuous build feature of Gradle. If we make a change in a source file then our Ratpack application is automatically restarted. It would be nice to combine this with LiveReload using the Gradle LiveReload plugin. Then when we change for example a stylesheet file it is automatically reloaded in the web browser without invoking a refresh action.
In the following build file we add the Gradle LiveReload plugin and configure it to watch for changes in the output directory of the processResources
task. This task is executed when we change a file in the source directory if we use Gradle's continuous build feature.
// File: build.gradle plugins { id "io.ratpack.ratpack-groovy" version "1.1.1" // Add LiveReload Gradle plugin. id "org.kordamp.gradle.livereload" version "0.2.1" id "com.github.johnrengelman.shadow" version "1.2.2" id "idea" } // Configure liveReload task. liveReload { // Set docRoot as the base directory for // the livereload server to watch for changes. // We choose the output directory of the // processResources task, because it will contain // the changed files if we run our application // with Gradle's continuous support ($ gradle -t run). // In another terminal we need to start this // task ($ gradle liveReload). docRoot processResources.destinationDir.canonicalPath } repositories { jcenter() } dependencies { // Default SLF4J binding. Note that this is a blocking implementation. runtime 'org.slf4j:slf4j-simple:1.7.12' testCompile "org.spockframework:spock-core:1.0-groovy-2.4" }
Now we have the LiveReload server implementation, but we need LiveReload support in the browser as well. We can use browser extensions that are available or we add a bit of Javascript code to our pages. In the following example application we write an implementation of the RenderableDecorator
interface to pass in the view model an attribute that denotes if we are running in development mode or not. When we run in development mode we add the custom Javascript code for LiveReload, otherwise we skip it.
// File: src/ratpack/Ratpack.groovy import ratpack.groovy.template.MarkupTemplate import ratpack.groovy.template.MarkupTemplateModule import ratpack.render.RenderableDecorator import ratpack.server.ServerConfig import static ratpack.groovy.Groovy.groovyMarkupTemplate import static ratpack.groovy.Groovy.ratpack ratpack { bindings { module MarkupTemplateModule // Add extra attribute development to the view model. // If we run in development mode the value is true otherwise // it is false. bindInstance RenderableDecorator.of(MarkupTemplate) { context, template -> // Get value for development property of server configuration. final boolean development = context.get(ServerConfig).development new MarkupTemplate( template.name, template.contentType, [development: development] + template.model) } } handlers { get { render groovyMarkupTemplate("index.gtpl", title: "Ratpack") } files { dir "public" } } }
In the application we use the Groovy markup template engine. The engine supports layouts and we use the layout to conditionally add the LiveReload Javascript snippet.
// File: src/ratpack/templates/index.gtpl layout 'layout.gtpl', true, bodyContents: contents { header { h1 'Welcome to ratpack' } section { p "Running in development mode: ${model.development}" } }
// File: src/ratpack/templates/layout.gtpl yieldUnescaped '' html { head { meta(charset:'utf-8') title(title) link(href: '/images/favicon.ico', rel: 'shortcut icon') link(href: '/styles/app.css', rel: 'stylesheet') } body { bodyContents() // Include livereload Javascript snippet, // if we run in development mode. if (model.development) { yieldUnescaped """ <script> document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>'); </script> """ } } }
We have all the code ready it is time to first start our application with Gradle:
$ gradle -t run Continuous build is an incubating feature. :compileJava UP-TO-DATE :compileGroovy :processResources :classes :configureRun :run [main] INFO ratpack.server.RatpackServer - Starting server... [main] INFO ratpack.server.RatpackServer - Building registry... [main] INFO ratpack.server.RatpackServer - Ratpack started (development) for http://localhost:5050 BUILD SUCCESSFUL Total time: 3.57 secs Waiting for changes to input files of tasks... (ctrl-d to exit)
In a second terminal or console we start the liveReload
task:
$ gradle liveReload :liveReload Enabling LiveReload at port 35729 for /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/ratpack/livereload/build/resources/main > Building 0% > :liveReload
We open the URL http://localhost:5050
in our browser. Next we change for example the index.gtpl
file, save it and see that the page is automatically refreshed in the web browser.
Written with Ratpack 1.1.1.