Manage your templates in your database instead of view files, and with Liquid templating support.
JavaScript Ruby
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
coverage
generators/dynamic_templates
lib
spec
MIT-LICENSE
README.textile
Rakefile
coverage.data
init.rb

README.textile

Dynamic Liquid Templates

Use Case

The Rails View system is very complex and complete, 90% of the time you don’t need much else but the occasional Haml support.

But if you want to have your users have the feature of editing their own set of view templates, then the default filesystem-based view system will not be enough. That’s why I wanted to have a way to retrieve templates from the database instead.

By having your templates being user-editable, it’s also not acceptable to use ERB or Haml,because they are too powerful and they expose just too much in the views. You want an engine that exposes just enough so the users can create their own templates without having the risk of they running something nasty such as system(‘rm -Rf /’)

That’s why I chose Tobias Lütke’s excellent Liquid engine that he created for Shopify.com for exactly the same reason. I have no idea how he manages the user templates but in my case I created this plugin to fulfill this requirement.

Usage

Just install it as a plugin into your Rails project and create the needed migration:


./script/plugin install git://github.com/akitaonrails/dynamic_liquid_templates.git
./script/generate dynamic_templates
rake db:migrate

The requirement is to install Tobias’ Liquid gem:


gem install tobi-liquid --source=http://gems.github.com

You also have to require this gem from your config/environment.rb file:


config.gem "tobi-liquid", :lib => "liquid", :source => "http://gems.github.com"

Now, from within the controller that you want to be user-accessible, edit the ‘format.html’ block with the new render method:


...
respond_to do |format|
  format.html { render_with_dynamic_liquid }
end
...

If you created the default migration file, you now have a dynamic_templates table to hold your Liquid templates. Don’t forget to register new entries to the dynamic_templates table. See the spec/fixtures folder for examples on how to write simple Liquid templates. You will register entries such as:


DynamicTemplate.create(:path => "posts/show", :body => "<h2>{{ post.title }}</h2>\n<p>{{ post.body }}</p>")

Every normal controller instance variables such as @post or @comments will be available as ‘post’ or ‘comments’ from inside the Liquid template. Your usual Rails helpers are also available as Liquid filters. In ERB and HAML you usually have access to everything, but Liquid restricts your access (that’s the whole idea) and you have to explicitly assign extra objects to the render method, for instance:


...
@foo = Foo.find_by_name("Bar")
render_with_dynamic_liquid('foo' => @foo)

This will make ‘foo’ available from the Liquid Template. Read the library source code for more details, but it’s enough to understand that this render method will follow Rails’ convention over configuration principals so, for instance, if you’re inside the index action of a PostsController, you’re expected to have a collection named ‘@posts’ and named routes such as posts_path and so on, and so forth.

If you’re inside a nested controller (such as ‘Comment belongs_to Post’), declare a method named ‘parent’, for example:


class CommentsController < ApplicationController
  ...
  def parent
    @post ||= Post.find(params[:post_id])
  end
end

The plugin will use this method to refer to the proper named routes, such as post_comments_path(parent) or post_comment_path(parent, @comment) and so on. Namespaces will work too, for example:


class Admin::PostsController < ApplicationController
  def index
     @posts = Post.all
     respond_to do |format|
       format.html { render_with_dynamic_liquid(:namespace => 'admin') } 
     end
	end
end

There’s probably a better syntax for that, but it works like that too. The other thing is that your models need to be ‘liquified’ in order to be usable from inside a Liquid template. You have to declare a to_liquid method explicitly saying which columns you want exposed:


class Post < ActiveRecord::Base
  ...
  def to_liquid
    { 'title' => self.title, 'body' => self.body }
  end
end

Notice that the hash keys have to be strings, and not symbols. The other thing is that the plugin will append other attributes to this hash automagically, so you can do the following from within your Liquid templates:

{{ 'Show' | link_to: post.show_path }}

It adds ‘show_path’ and ‘edit_path’ to your model instances, so you can use them to create links or form actions. There are other globally assigned variables as well, such as:

  • collection – refers to the controller collection (for index action) such as @posts
  • object – referes to the controller single object (for non-index actions) such as @post
  • collection_path – the named route for the index action, including nested and namespaced versions
  • object_path – the named route for the non-index action, including nested and namespaced versions
  • parent – if you have the ‘parent’ method in your controller, it’s exposed within Liquid
  • parent_path – if you have the ‘parent’ method in your controller, it’s used for named routes

And, of course, your models are properly assigned to Liquid too so, for example, ‘@posts’ is exposed as ‘posts’ to Liquid.

To do

Credits

Fabio Akita (fabioakita@gmail.com) – http://www.akitaonrails.com