A server-side guy interacts with React
May 21st, 2015
A tour of how ReactJS works with plenty of examples.
Gradle (and other build systems) have done a good job of managing classpath dependencies. They are effective at pulling new versions of packages to get bug fixes, et al and including dependencies in packaging. JavaScript, stylesheets and the like also have tools to manage dependencies but are not first class citizens with classpath dependencies. Bower is one tool that handles dependencies like JavaScript and stylesheets. This post will show you a way to leverage Bower to manage those dependencies using Gradle tasks.
There are other solutions that handle the dependency problem. For example, there are Grails plugins for a few popular frameworks. The drawback is that when the source project releases a new version, the plugin author must release a new plugin. It’s very likely Bower will be updated by the source project, so the approach here will provide better dependency management.
One problem with Bower is that the packages usually have more files than one would want on a production server. We’ll use the Gradle Sync task to handle that. It’s a bit more work, but worth it.
Another issue with using Bower is that it’s a NodeJS application. That means we need Node and NPM installed. We’ll solve that using the gradle-node-plugin.
In summary, here’s what we’ll be doing:
All source code for this post and a working application are available on GitHub.com/double16/pet-store.
# Managing Node and Bower in Gradle Bower is a NodeJS package. Although many development and CI environments have Node installed, that’s not guaranteed to be true or perhaps differing versions cause problems for your application. Ideally a Gradle build consists of having a JDK installed, pull the repo, and run ./gradlew build
.
The gradle-node-plugin manages an install of Node and NPM (Node Package Manager) from your Gradle build to deliver consistent behavior whenever you build. It can be configured to use an existing Node install, or always download the specified version. Now we’ll add this plugin to our build.
Add the dependency:
buildscript {
dependencies {
classpath 'com.moowork.gradle:gradle-node-plugin:0.9'
}
}
Configure Node versions:
node {
version = '0.12.2'
npmVersion = '2.7.5'
download = true
}
Since caching of dependencies is important for a faster build, we’ll instruct the Gradle managed NPM to cache packages into our Gradle cache. In order for this to work, the tasks we’ll create later need to depend on this task.
task npmCacheConfig(type: NpmTask) {
description = "Configure the NPM cache"
def npmCacheDir = "${gradle.getGradleUserHomeDir()}/caches/npm"
outputs.files file(npmCacheDir)
args = [ 'config', 'set', 'cache', npmCacheDir ]
}
## Installing Bower We’ll use the package.json
file to specify dependencies for NPM and then invoke it to install those dependencies. This file belongs in the root of the project. You can read about the contents of the file at docs.npmjs.com.
[object Object]
Now we need a task to install NPM dependencies. There is an npmInstall
task provided with the gradle-node-plugin, but it doesn’t install into our local node_modules
folder and we want that to avoid version conflicts and also to be able to find the bower
command without depending on the search path. It looks like the location of the node_modules
folder can be specified in the latest plugin, but that code was not released as of this writing.
task npmPackages(type: NpmTask, dependsOn: npmCacheConfig) {
description = "Install Node.js packages"
args = [ 'install' ]
inputs.files file('package.json')
outputs.files file('node_modules')
}
Run ./gradlew npmPackages
and Gradle will install Node, NPM and Bower. All will be cached in the Gradle user home. This should work across operating systems and not depend on anything but a properly installed JDK 1.7+.
# Specifying Bower Packages Bower uses a file named bower.json
to specify package dependencies. In this project we’re bringing in AngularJS and Animate.css.
[object Object]
The following task will invoke Bower to download, cache and install the packages at bower_components
:
task bowerInstall(type: NodeTask) {
script = file('node_modules/bower/bin/bower')
args = ["--config.storage.cache=${gradle.getGradleUserHomeDir()}/caches/bower/cache",
"--config.storage.packages=${gradle.getGradleUserHomeDir()}/caches/bower/packages",
"--config.storage.registry=${gradle.getGradleUserHomeDir()}/caches/bower/registry",
'install']
inputs.files file('bower.json')
outputs.files file('bower_components')
dependsOn npmPackages
}
# Copying Production Files The most difficult part of this process is how to get the files Bower has fetched for us into our application. Bower fetches the entire repository so we have more files than we want in production. There is no definitive way to determine which files we need out of each repo automatically, so we’ll use a Gradle Sync task to copy over the files.
You will need to inspect each package for the files you want. In this example we’ve excluded the minified versions because we’re using the asset-pipeline
plugin to minify things for us. We’re including all of the files in one application.js
file and they will be minified together. This part isn’t necessary, you can include the minified versions and include each individually. The important part is that you need to identify which files will be installed in production.
Here is the task for the JavaScript files:
task bowerSyncJavascript(type: Sync) {
from 'bower_components'
into "grails-app/assets/javascripts/bower_components"
exclude '**/*.min.js'
exclude '**/angular*/index.js'
exclude '**/angular*/ng*.js'
include 'jquery/dist/jquery.js'
include 'angular*/**/*.js'
dependsOn bowerInstall
}
Here is the task for the Stylesheets:
task bowerSyncStylesheets(type: Sync) {
from 'bower_components'
into "grails-app/assets/stylesheets/bower_components"
include 'angular*/**/*.css'
include 'animate.css/animate.css'
dependsOn bowerInstall
}
For convenience we’ll have one task that depends on both:
task bowerPackages() {
dependsOn bowerSyncJavascript, bowerSyncStylesheets
}
Now we’ll have the processResources
and assetCompile
tasks depend on the bowerPackages
task so our files will be copied before running the application or compiling. processResources
is specific to the java
Gradle plugin, and assetCompile
is specific to the asset-pipeline
plugin used by Grails. You might need to adjust these dependencies to ensure the files are available when needed. It’s a good idea to use the --dry-run
flag to verify the tasks are called at the correct time and try your builds on a clean workspace.
processResources.dependsOn bowerPackages
assetCompile.dependsOn bowerPackages
The final Gradle change is to augment the clean
task to remove the node_modules
and bower_components
folders.
clean.delete << file('grails-app/assets/javascripts/bower_components')
clean.delete << file('grails-app/assets/stylesheets/bower_components')
clean.delete << file('node_modules')
clean.delete << file('bower_components')
You’ll want to add the following to your .gitignore
file:
/node_modules
bower_components
# Include the File in the Grails Application One more thing to do and that’s include the bower-managed dependencies in our application. This is a Grails 3 application, when you’re using another stack including the files will be different.
//= require_tree bower_components
//= require_self
/*
*= require_tree bower_components
*= require main
*= require mobile
*= require_self
*/
There’s more work left to actually use these in our application. The pet-store application is intended to demonstrate that the dependencies are brought in correctly and not necessarily an example of best practice use of AngularJS and Animate.css in a Grails application, but go ahead and take a look at the page source and note that the dependencies are included.
# Run It! We should now be able to run our application and use the JS/CSS Bower has installed.
shell ./grails run-app
# Conclusion Dependency management is an important problem to solve and we want to avoid manually downloading and copying JS/CSS and the like into our repository. Bower is a popular solution and many projects publish to it. There’s a bit of work to set up Gradle to invoke Bower (made *much* easier by the gradle-node-plugin), but once done maintenance will be significantly easier. Now we have proper dependency management in a Gradle build with one step to an executable. Also, it appears that most of this work could be added to the gradle-node-plugin and a new task created for installing Bower dependencies.
A tour of how ReactJS works with plenty of examples.
This past weekend, I was able to attend and present at the 2015 Nebraska.Code() Conference in Lincoln, along with a few other Object Partners developers. With roughly 500 in attendance from presenters, to attendees, to sponsors, the conference had…
With frameworks like Jasmine, Karma, and Grails, we can write Javascript tests and run them with grails test-app.
I have been coding since 6th grade, circa 1986, professionally (i.e. college graduate) since 1998 when I graduated from the University of Nebraska-Lincoln. Most of my career has been in web applications using JEE. I work the entire stack from user interface to database. I especially like solving application security and high availability problems.