Closure Templates

Gem for using Google Closure Templates with jRuby.

Installation

gem install closure-templates

Usage

Assuming a simple template:

/** myapp.soy */

{namespace myapp.templates}

/**
  * Hello there template
  * @param name The name of the person to greet
  */
{template}
  Hi {$name}
{/template}

Use it in jRuby:

require 'closure-templates'

ClosureTemplates.config(
  :template_directory => '/path/to/soy/templates', 
  :output_directory => '/path/to/where/generated/javascript/should/go'
)

# render a template
ClosureTemplates.render('myapp.templates.hello', :name => 'Dylan')
# returns 'Hi Dylan'

Use it in JavaScript:

<script type="text/javascript" src="soyutils.js"></script>
<!-- following was generated by ClosureTemplates -->
<script type="text/javascript" src="myapp.templates.js"></script>
<script type="text/javascript">
var output = myapp.templates.hello({ name: 'Dylan' });
</script>

You will need to include either soyutils.js (if you are not also using Google Closure Library) or soyutils_usegoog.js (if you are). Read more here.

Details

Rails Usage

Currently I have an initializer that looks like this:

require 'closure-templates'

ClosureTemplates.config(
  :template_directory => "#{Rails.root}/app/templates",
  :output_directory => "#{Rails.root}/app/assets/javascripts/templates"
)

Then I render the templates where needed (HAML example):

!= ClosureTemplates.render('myapp.posts.index', :posts => @posts)

Template Inputs

Google Closure Templates have some requirements for the data passed in, specifically (from SoyData.java):

Note that in order for the conversion process to succeed, the given data structure must correspond to a valid SoyData tree. Some requirements include: (a) all Maps within your data structure must have string keys that are identifiers, (b) all non-leaf nodes must be Maps or Lists, (c) all leaf nodes must be null, boolean, int, double, or String (corresponding to Soy primitive data types null, boolean, integer, float, string).

This gem makes some attempts to meet these requirements by converting hash keys to strings and calling the 'attributes' method (by default) on objects which respond to it. This allows the use of Rails models, for example, to be passed to templates and have their basic attributes accessible. You can customize the method that the gem uses to try to get a hash of attributes by passing the 'attribute_method' option to the config method (see below).

Config Options

These are all passed to the ClosureTemplates.config method, which must be called before the first ClosureTemplates.render.

:template_directory

default: templates

Where you keep your soy templates. You should specify this.

:output_directory

default: closure_js

Where you want the gem to put the generated JavaScript files. You should specify this.

:recompile

default: true

The gem maintains a list of the soy template files and can check to see if any of them have been modified when calling render. If one has been modified, then if :recompile is true, it will regenerate the Java object and the JavaScript files from the latest soy templates, which is nice for development. You could turn this off in production to get slightly faster rendering.

:attribute_method

default: attributes

This is the method the gem will try to use to get a more Closure Templates friendly representation of Ruby Objects before passing them into the template. It will call this method on objects if they respond to it. If you want to customize which properties (or make methods available) you can customize this method to be one you implement in your classes.

Attributions

Idea for this gem came from a discussion on the Google Closure Template discuss group:

Closure Templates in Ruby?

where Aliaksandr Zahatski pointed me to a gist by Ilya Grigorik which led me to the Closure Sprockets gem also by Ilya. I took code and ideas from both places as starting points for this gem.

License

(MIT License) - Copyright (c) 2011 Dylan Vaughn