This article explains how to process custom asset files with Rails Asset Pipeline by customizing Sprockets configuration.
Let’s consider an example case of Rails application that uses client-side Mustache templates rendering. What would be an efficient way to access the templates from JavaScript?
There are a number of approaches here. The basic one is to nest the templates directly into the HTML view code. Like this:
<script type='text/template' id='post-template'><h2>{{ title }}</h2><p>{{ body }}</p></script>
But this is far from ideal:
To solve these issues, let’s make Rails Asset Pipeline to generate a JSON object within JavaScript bundle, and populate this object from templates directory at app/assets/templates. For consumer it will look like this:
window.Templates = {post: "<h2>{{ title }}</h2><p>{{ body }}</p>",comment: "..."}
This way the templates will be preloaded with the common bundle, and remain available from every view that is using it.
Step 1. Register new MIME type
In a typical Rails application custom Sprockets configuration should be defined under config.assets.configure block within the Application class. So the examples below are based on the assumption that a block like this already exists in your config/application.rb:
module MyApplicationclass Application < Rails::Applicationconfig.assets.configure do |env|
**// Custom Sprockets configuration should go here**
end
endend
MIME type registration is straightforward. Just define an association between a new type, and the list of file extensions you plan to process:
env.register_mime_type('text/mustache', extensions: ['.mustache'])
Step 2. Register new assets transformer
Most important part here is the callback. It is a callable object that convert asset file content from source_type to something different of target_type. Callback should return a hash with processed content in :data key:
source_type = 'text/mustache'target_type = 'application/javascript'
callback = -> (input) { { data: '// HELLO ${input[:name]}' } }
env.register_transformer(source_type, target_type, callback)
Lambda expression could be not suitable for real life situations. A service class, defined somewhere outside Rails configuration, will be a better choice. But for now let’s finish the boilerplate first. Real transformer example will follow.
Step 3. Enable custom file type processing
To achieve that, add a regular expression to the precompile array in the assets initializer (config/initializers/assets.rb):
Rails.application.config.assets.precompile += [/\.(?:mustache)\z/]
Step 4. Bundle transformed assets
After new transformer is registered, both require and require_tree directives will play well with the custom assets type.
If you keep Mustache templates under app/assets/templates, adding this line to app/assets/javascripts/application.js will inject transformer callback output for each *.mustache file:
//= require_tree ../templates
Dummy lambda-transformer from the previous step will replace this line with a list of commented file names like this:
// HELLO template_file_name// HELLO another_template_file_name// ...
To see it working, don’t forget to restart Rails server, and make sure assets cache is purged.
And here is an actual transformer example, that generates JavaScript object from Mustache template files:
class MustacheTransformerdef self.call(input)key = input[:name]body = JSON.dump(input[:data])obj = 'window.Templates'{ data: "#{obj} = #{obj} || {}; #{obj}['#{key}'] = #{body}" }endend
Besides :data, the input object contains a number of other extension fields, that could be valuable during content transformation. Check out official reference for the full list.
The case of Mustache templates elaborated in this article is just a concrete example of extending Sprockets. Besides templates, it is possible to bundle any other assets with arbitrary transformation logic that suit your needs.
Peace.
References: