(Quick Reference)

5 Grace

Version: 6.3.0

5 Grace

Grace

This section of the documentation discusses configuration and setup of the asset-pipeline plugin for Grace 2022+ based applications.

5.1 Getting Started

Getting Started

In Grace 2022 most assets live in the app/assets directory. This folder is automatically generated for you along with the organizational subdirectories javascripts, images, and stylesheets.

To get started simply add asset-pipeline to your build.gradle file (See Gradle usage). And the Grace plugin to the dependencies block:

dependencies {
  runtime 'org.graceframework.plugins:asset-pipeline-plugin:{project-version}'
}

Development Runtime

A great feature built into Grace is its development runtime support. All assets that are used in your gsp files are automatically generated and processed on the fly when requested. This means when a change is made in a css file or javascript file, the results are instant. A refresh of the page will reflect the changes without needing to wait for any type of FileWatcher. These results are also cached making it highly performant as your project grows.

Another great aspect of development runtime with Grace is each file will individually be required in the gsp. A dependency graph is generated and each file is required individually to make it easy to debug javascript errors and stylesheet errors. While sourcemap support is integrated for javascript, this is not supported in a lot of browsers developers still target compatibility for. It is possible to disable this feature via configuration or simply testing your app with the embedded tomcat war to see how post minified and bundled assets will behave.

5.2 Configuration

Configuration

Asset-Pipeline has several configuration options for use with Grace 2022. For most cases the configuration options listed in the Gradle guide pertain to Grace 2022. Some subtle differences is the concept of development runtime vs. build time. If you want settings to apply to both development runtime and build time the properties have to be duplicated in your applications application.yml project. This is currently a limitation in how grails reads configuration files and Gradle conveys properties.

Most build time options like skipNonDigests or enableGzip will never need specified in this YAML file as they will never be used during development runtime. It is also possible to disable development runtime entirely. In this scenario assets will always be packaged with the application and not monitored for changes unless Gradle continuous build is utilized. Simply set assets { developmentRuntime = false} in your build.gradle file.

Example yaml:

grails:
  assets:
    bundle: true #dont individually require files in development
    storagePath: /path/to/store #automatically copy assets to external folder on startup

Mappings and Asset Taglib URLs

In many cases you may want to change the URL for which to include your static assets. This can be useful when using a CDN or perhaps even using nginx to serve your static assets.

To change the URL for your taglibs use the following configuration option:

grails:
  assets:
    url: http://cdn.example.com/

Now your files are going to reference the CDN when running in the production environment. To go with this feature, you can have your application automatically copy your asset files out of your base WAR file on startup of your application. Optionally the asset URL config can also be defined as a closure that takes a request argument.

grails.assets.url = { request ->
        if(request.isSecure()) {
                return "https://cdn.example.com/"
        } else {
                return "http://cdn.example.com/"
        }
}

This allows more fine grained control of your asset URLs based on the incoming request. An example, might be SSL detection or even changing CDN region by source IP.

To use an asset closure config must be placed in an application.groovy file instead of application.yml
grails:
  assets:
    storagePath: /var/cdn/path

You can also change the default Tomcat path for both debugging and file inclusion using the mapping config option.

grails:
  assets:
    mapping: assets

For all these configuration options, you will want to put these config values in the appropriate environment in application.yml or in groovy format in application.groovy.

5.3 Asset Organization

Organization

Asset pipeline organization occurs within the "app/assets" folder. This folder can exist within both the main application as well as a plugin.

A plugin will also include its web-app directory to better deal with plugins that wish to support both the resources plugin as well as asset-pipeline.

Within the app/assets directory are several subfolders

  • app/assets/javascripts

  • app/assets/stylesheets

  • app/assets/images

A common folder that gets added to this set of organization is a "lib" folder. This folder can be useful in organizing third party libraries like jQuery, or Bootstrap.

Plugins

Plugins also can have the same "app/assets" folder and their URL mapping is also the same. This means it can be more important to ensure unique naming / path mapping between plugins. This is also powerful in the sense that a plugin can add helper manifests to be used within your apps like jquery, bootstrap, font-awesome, and more.

Plugins should make sure that the assets { packagePlugin } property is set to true in their build.gradle file otherwise assets will not properly be packaged into the plugin for use by the application.

Plugins with assets must have the packagePlugin property set to true on the assets extension in build.gradle

If, in the event, a file within a plugin needs to be overridden within your application, simply create the same file with the same relative path to app/assets and it will override / take precedence over the plugin. More on that later.

Since plugins share the same file structure for assets, as well as web-app. It can become more important to "namespace" your plugins by creating further nested folders. (i.e. the plugin SpudCore puts its application.js file within app/assets/javascripts/spud/admin/application.js).

Search Paths

When a file is referenced via a taglib or a manifest require directive, the asset-pipeline checks for the file in several locations.

First it tries to find the file relative to the manifest including it. For example "admin/application.js" looking for "table.js"

// FileName: admin/application.js
//= require table

The first place we will look is within app/assets/javascripts/admin/* We will proceed to do this within all of the asset sub folders across plugins after the main application is searched.

The next place we will look is the root of all app/assets plugin sub folders (e.g. app/assets//table.js, and web-app//table.js for plugins).

Finally all binary plugins are scanned in the classpath:META-INF/assets folder, classpath:META-INF/static and classpath:META-INF/resources.

In all cases, the applications assets folder takes precedence between the search paths, but plugins get scanned as well.

These same conditions should be implemented on any preprocessor extension plugin, e.g. LESS-asset-pipeline follows the same scan for @import directives.

5.4 Taglibs

Taglibs

Asset Pipeline adds a few new taglibs to properly reference your assets. These taglibs automatically handle swapout of cache digest names during production use as well as any custom URL mapping changes.

<head>
  <asset:javascript src="application.js"/>
  <asset:stylesheet src="application.css"/>
</head>

The primary include tags, as shown above, are quite useful for including your relevant JavaScript or stylesheet files. Notice that you do not have to prefix with '/assets', as this is handled automatically by the tag.

In GSP views, you can also reference images included in the asset-pipeline with the following tag:

<asset:image src="logo.png" width="200" height="200"/>

Assets can also be referenced within subdirectories if required and simply require the use of the relative path.

<asset:image src="icons/delete.png"/>

It is also possible to return an assetPath as a string for injection in your own tags:

<link href="${assetPath(src: 'manifest.json')}"/>

It is also possible to execute a code section only if an asset exists or to simply test for existence

<asset:assetPathExists src="test.js">
This will only be displayed if the asset exists
</asset:assetPathExists>

or

asset.assetPathExists(src: 'test.js') //returns true or false

Getting Resource

As of version 0.8.2 a new bean exists called assetResourceLocator This can be used to find assets by URI in both development and production mode.

class ExampleService {
  def assetResourceLocator

  def someMethod() {
    Resource myResource = assetResourceLocator.findAssetForURI('test.css')
  }
}

Deferred Scripts

Asset-Pipeline provides a set of tags that can be used to ensure script blocks are deferred to the end of your page. This is not recommended as it is obtrusive, but has been added to help newcomers upgrade existing apps from resources.

<asset:javascript src="application.js" asset-defer="true"/>
<asset:script type="text/javascript">
  console.log("Hello World");
</asset:script>
<asset:script type="text/javascript">
  console.log("Hello World 2");
</asset:script>

Now to render the output of these scripts simply use the following:

<asset:deferredScripts/>