Search

Dark theme | Light theme

January 20, 2016

Ratpacked: Running With LiveReload Using Gradle

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.