Liquid on Rails

Liquid on Rails

Now you have a basic grasp on the Liquid template engine itself, let’s learn how to utilize it in our Ruby on Rails application. First you need to add the gem to the Gemfile. Add the following line to the Gemfile:

1
gem 'liquid', '~> 2.6.1'

Then run bundle install to install the Liquid gem:

1
bundle install

Then create a model called Page. The Page model will be used to represent a page owned by a specific user. Run the commands below to create this model:

1
2
rails g model Page user:string template:text
rake db:migrate

Then open up your seeds (db/seeds.rb) file and modify it so that it looks somethings like below:

seeds
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Page.delete_all

Page.create(id: 1, user: "JingLong", template: %{
  Hello {{ user }}, here is your shopping list.
  <ul>
    {% for item in list %}
      <li>{{ item.name }}</li>
    {% endfor %}
  </ul>
}

)

Page.create(id: 2, user: "Bunlong", template: %{
  What is up my man? Here is your shopping list.
  <ul>
    {% for item in list %}
      <li>{{ item.name }}</li>
    {% endfor %}
  </ul>
}
)

Page.create(id: 3, user: "Rubyist", template: %{
  HTTP 200:  Shopping List Found

  Items in your list:
  <ul>
    {% for item in list %}
      <li>{{ item.name }}</li>
    {% endfor %}
  </ul>
}

)

Then run rake db:seed command to seed the database with your sample data:

1
rake db:seed

Then create a homes controller that will allow you to play with liquid. Run the command below to create the homes controller:

1
rails g controller Homes show

Then create a pages controller. This controller will be used to display a user’s page. Run the commands below to create the pages controller:

1
rails g controller Pages show

Then modify your routes (config/routes.rb) file so that it looks somethings like below:

routes.rb
1
2
3
root to: "homes#show"
resource :home, only: [:show]
resources :pages, only: [:show]

Then modify your homes controller (app/controllers/homes_controller.rb) to pull down a list of all the user’s pages. Open up your homes controller and modify it so that it looks somethings like below:

homes_controller.rb
1
2
3
4
5
class HomesController < ApplicationController
  def show
    @pages = Page.all
  end
end

Then modify your pages controller (app/controllers/pages_controller.rb) to pull down the correct page for the specified user. Open up your pages controller and modify it so that it looks somethings like below:

pages_controller.rb
1
2
3
4
5
class PagesController < ApplicationController
  def show
    @page = Page.find(params[:id])
  end
end

Then create a few helper (app/helpers/pages_helper.rb) methods for use with this example. Open up your pages helper and modify it so that it looks somethings like below:

pages_helper.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
module PagesHelper
  def shopping_list(user)
    {"user" => user, "list" => shopping_list_items}
  end

  def shopping_list_items
    [
      { "name" => 'Apple', "quantity_needed" => "2"},
      { "name" => 'Stoberry', "quantity_needed" => "1"},
      { "name" => 'Cherry', "quantity_needed" => "3"},
    ]
  end
end

The codes above gives you some sample data to play around with.

Then modify your pages show view (pp/views/pages/show.html.erb) to look somethings like below:

show.html.erb
1
2
3
4
5
<h1><%= @page.user %>'s Personal Page</h1>

<% template = Liquid::Template.parse(@page.template) %>

<%= template.render(shopping_list(@page.user)).html_safe %>

The codes above tells Rails to render your template using the Liquid Template Engine.

Finally, open up the show view for your home controller (app/views/homes/show.html.erb) and add in the code listed below:

show.html.erb
1
2
3
4
5
6
<h1>User Pages</h1>
<ul>
  <% @pages.each do |page| %>
    <li><%= link_to "#{page.user}'s page", page_path(page) %></li>
  <% end %>
</ul>

If you fire up a rails server and visit your development site, you’ll notice that you can browse each user’s pages and get a customized version of each one.

However, what if you want to use the Liquid templating engine as a replacement for erb itself? take it easy, first, create a new file in the lib folder called liquid_view.rb (lib/liquid_view.rb) and add in the codes listed below:

liquid_view.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class LiquidView
  def self.call(template)
    "LiquidView.new(self).render(#{template.source.inspect}, local_assigns)"
  end

  def initialize(view)
    @view = view
  end

  def render(template, local_assigns = {})
    @view.controller.headers["Content-Type"] ||= 'text/html; charset=utf-8'

    assigns = @view.assigns

    if @view.content_for?(:layout)
      assigns["content_for_layout"] = @view.content_for(:layout)
    end
    assigns.merge!(local_assigns.stringify_keys)

    controller = @view.controller
    filters = if controller.respond_to?(:liquid_filters, true)
                controller.send(:liquid_filters)
              elsif controller.respond_to?(:master_helper_module)
                [controller.master_helper_module]
              else
                [controller._helpers]
              end

    liquid = Liquid::Template.parse(template)
    liquid.render(assigns, :filters => filters, :registers => {:action_view => @view, :controller => @view.controller})
  end

  def compilable?
    false
  end
end

Create an initializer called liquid_template_handler (config/initializers/liquid_template_handler.rb) and add the code listed below:

liquid_template_handler.erb
1
2
require 'liquid_view'
ActionView::Template.register_template_handler :liquid, LiquidView

Let restarting your Rails server you will be able to create actual views with the .liquid extension that uses the liquid templating engine.

So far so good, That’s it! See ya! :)