Ice Templates

So last year I released the Ice template engine, which lets people use Javascript on top of Rails for generating templates. These templates are then evaluated with therubyracer, so they execute with speedy V8 Javascript engine.

The main benefit of this is that these templates execute in a sandbox. Therefore, people can upload templates that do their own thing, and can execute with their own branding.

As an example, say you have a SaaS site known as mywonderfulrestaurantsite.com. This would be a CMS that hosts different websites on different subdomains for different restaurants. And on top of this site you have two restaurants as your customers:

  • -> Burger Delite - at burgerdelite.mywonderfulrestaurantsite.com
  • -> The Shrimp Shack - at shrimpshack.mywonderfulrestaurantsite.com

Now you want the webmaster for each of these sites to be able to edit the partials for their restaurant items. You might even like them to have customized contact forms and things like that. However, while you would like them to be able to execute some server-side code, you'd prefer that they can't execute malicious code.

This is good: <%= item.name %> costs $<%= item.price %>
And this is not: <%= item.name %> costs $<%= item.price %><% Account.delete_all %>

So this code needs to execute in a sandbox of some sort. One approach for Rails developers is the Liquid template library. Liquid is a great library and has a syntax similar to Django Templates. In fact the name "Ice" is a play on (and tribute to) Liquid. I used Liquid extensively and built out my own libraries like Clots to add form builders to it.

However, when I learned about The Ruby Racer I decided to build out Ice so people could use Javascript templates instead of Liquid, Mustache or ERB. Using Javascript gives a few key benefits

  • -> Developers don't need to learn a new language because it's is assumed that they already know some Javascript.
  • -> Developers can use the same templates on both the server and the client side.
  • -> If a developer (or site user) needs to build out additional functionality, the can write a Javascript library to do it.

So I wrote Ice, used it, but mostly kept it to myself. But then three things happened:

  • 1. Coffeescript's ascendency began with Rails 3.1, make Javascript much less lame and almost required on the serverside.
  • 2. Eco templates came out for Ruby, giving a much more battletested approach to templating on top of Coffeescript.
  • 3. CoffeeKup was created, which gave me a Coffeescript alternative for the HAML-heads out there.
  • 4. Jose Valim's book came out. This book gave me a clear-cut approach to building a Rails gem that takes advantage of all the new features.

So I decided to build Ice out more for public consumption. To aid in understanding, below is a quick example (code on github).

Twitter on Ice (AKA Frozen Birds)

So say you want to host a twitter clone, but where the users have different subdomains. For example, if you go to jason.frozenbirds.com you should only get tweets for the "jason" subdomain. This is easy enough to do in a standard Rails app. Just add to your controller the lines:

    subdomain = Subdomain.where(:name => request.subdomain).first
    @statuses = subdomain.statuses

Provided a "subdomain" has many "statuses", this will filter out the status messages (AKA Tweets) on a per-subdomain-basis.

So following this, your status index file uses a _status partial (in ERB) which can be written like

    Date: <%= status.created_at %>
    Status: <%= status.message %>

All is well and good, but now a feature request comes in. Each subdomain wants to "rebrand" their messages. Some want their messages to include their logo, some want the avatar to appear on the right, and others want the messages to contain a shortlink. This is tricky, because you can't just give a subdomain access to the ERB that renders for the whole site.

This is easy enough to solve with Ice. You can keep in your database partials for each subdomain's status messages. These partials would be written in either Eco or CoffeeKup. Your subdomain owners would update the code for these partials, which are then served up.

I'm going to run through the workflow using Eco templates (at the bottom of the article is a film about how to do this with CoffeeKup).

First, I convert the page to an Eco template. In the case of the above mentioned partial, I change the lines

    <p>Date: <%= status.created_at %></p>
    <p>Status: <%= status.message %></p>
to
    <p>Date: <%= @status.created_at %></p>
    <p>Status: <%= @status.message %></p>

Then, I add the ice gem to my Gemfile. I try to see my messages index but get an error: "Cannot find Cube class for model that you are calling to_ice on".

This error is because all ActiveModels need to either have a to_ice method, or an equivalent model (called a Cube) that they expose to ice. After adding this Cube object (name it StatusCube and have it inherit from Ice::BaseCube) everything would work fine:

    class StatusCube < Ice::BaseCube
        revealing :message
    end

This Cube object is automatically instantiated when to_ice is called on a class including ActiveModel::Serializable. Of course, in order to be "automatically found", a cubes directory needs to be added to your path.

Once the Eco template for your partial is rendering out, you can confirm that Ice is working correctly. You then can go on to do other fun things, like serving your templates from a database.

Here is another example of the above process, but with CoffeeKup templates instead of Eco:

Ice Templates from Nate Kidwell on Vimeo.

blog comments powered by Disqus

Saki
Encumbered by Cucumber (by Nate Kidwell)

Article List

Tags coffeekup steak testing eco cucumber rspec ice ruby coffeescript javascript