/liquidizer

Extends Rails with liquid-powered views stored in the database

Primary LanguageRuby

Liquidizer

Liquidizer is a gem for Ruby on Rails that allows to render pages using templates stored in the database. It uses liquid (www.liquidmarkup.org/) templating system (thus the name), because it’s easy to use, extend and it’s safe to edit by users.

One example of when you might want to use it is this: Let’s say you want to have a blogging system where people can create their blogs about kittens, ninjas or vampires. Using this gem, you can allow them to edit the look and feel of their blog using just their browser. Every blog can have it’s own set of templates.

Installation

It’s hosted on gemcutter.org. Add it to your source list if you don’t have it already:

sudo gem source --add http://gemcutter.org

Then install:

sudo gem install liquidizer

And add this to your config/environment.rb:

config.gem 'liquidizer'

How to use

First you have to specify which actions of which controllers should be liquified (rendered with loadable liquid templates). To do that, use the liquify controller macro:

class PostsController < ApplicationController
  # This will apply the templates to all actions in this controller.
  liquify
end

You can use the :only and :except options that work the same as for filters to limit the set of actions to liquify:

class PostsController < ApplicationController
  liquify :only => [:show, :index]
end

class CommentsController < ApplicationController
  liquify :except => :edit
end

The liquify macro is inherited, so if you want to apply liquid templates to all actions in all controller, put it into ApplicationController. You can fine-tune it in derived controllers, if you want:

class ApplicationController < ActionController::Base
  liquify
end

class PostsController < ApplicationController
  liquify :only => :show
end

The layout is also liquified by default. You can disable it using the :layout => false options. Note that this will disable layout rendering with liquid, not layout rendering in general.

class ApplicationController < ActionController::Base
  liquify :layout => false
end

The Liqidizer uses the same naming convention for templates as Rails. So for the Blog::PostsController’s action :index, the looked up template will be “blog/posts/index”. If you want to override it, do it the standard Rails way:

class PostsController < ApplicationController
  liquify

  def index
    render :template => 'more_awesome_index_template'
  end
end

The last step is to tell Liquidizer where to load the liquid templates from. The way to implement this is completely up to you. For example, you can associate the templates with the Blog model (to follow the blog example) and have something like current_blog, which is loaded by the current subdomain.

In any case, you need to provide a current_liquid_templates method, which should return collection of liquid templates to use. This method should return something that responds at least to find_by_name which returns an object that responds to content which returns a string containing the liquid template.

The easies way to do this, is to have a LiquidTemplate model and return a collections of those in the current_liquid_templates. Liquidizer provides one such model for you, but you will probably want to use your own. To make your life easier, there is a module Liquidizer::LiquidTemplate which you can include into your template model to extend it with some helpful methods (see the docs for more info):

class Blog < ActiveRecord::Base
  has_many :liquid_templates
end

# The
class LiquidTemplate < ActiveRecord::Base
  include Liquidizer::LiquidTemplate
  belongs_to :blog
end

class ApplicationController < ActionController::Base
  private

  def current_liquid_templates
    current_blog.liquid_templates
  end
end

And that’s it. You are now ready to kick asses!

Instance variables

All instance variables that you assign in the controller are automatically available in the liquid templates. The variable will be automatically wrapped in a “drop”, if necessary (please check the liquid docs for more details about what types can be passed directly to liquid templates and what are drops). For a class Foo, a FooDrop will be used if it exists. If variable is not compatible with liquid and there is no corresponding drop class, it won’t be passed to the template.

Example:

A controller:

class PostsController < ActiveRecord::Base
  liquify

  def show
    @post = current_blog.posts.find(params[:id])
  end
end

A drop:

class PostDrop < Liquid::Drop
  def initialize(post)
    @post = post
  end

  def title
    filter_nasty_stuff(@post.title)
  end
end

Then you can do this in your liquid template:

<h1>{{ post.title }}</h1>

<!-- more stuff ... -->

And the post.title will call PostDrop#title, filtering all nasty stuff.

If the instance variable is an array (also association collection, active record scope or this kind of stuff), all it’s elements will be dropified too:

class PostsController < ActiveRecord::Base
  liquify

  def index
    @posts = current_blog.posts
  end
end

Then in your liquid tempalte:

{% for post in posts %}
<h2>{{ post.title }}</h2>

<!-- more stuff -->
{% endfor %}

The way how variables are wrapped with drops can be completelly customized by overriding the dropify method.

Default templates

The Liquidizer::LiquidTemplate module gives your model capability to fallback to a default template, if one is not found in the database. The default templates are stored in db/liquid_templates. You can configure this location by setting the Liquidizer.template_path:

Liqduidizer.template_path = Rails.root.join('app', 'views', 'liquid_templates')

TODO

There are several possible improvements to this gem:

  • Extend it so it can handle different template systems in addition (unlikely, since I don’t need it and can’t be bothered :) )

  • The Rails 3 has improved ActionView API and as far as I understand, abstracts away the concept of template storage. Taking advantage of this could potentialy simplify this gem. Also, support Rails 3 in general would be nice.

  • This does some nasty hacks into the ActionController::Base’s render method. That method originaly has quite liberal API, allowing to specify it’s parameters in many ways. To make this gem simple, some of those ways were sacrificed. Would be nice to support them too.

Potentialy many more. In any case, it’s open source project (MIT), so no fear, fork me and hack away!

Legal stuff

Copyright © 2010 Adam Cigánek <adam.ciganek@gmail.com>. Released under the MIT License: www.opensource.org/licenses/mit-license.php