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.
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'
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!
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.
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')
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!
Copyright © 2010 Adam Cigánek <adam.ciganek@gmail.com>. Released under the MIT License: www.opensource.org/licenses/mit-license.php