How we use Liquid template language? (part 1)
(Everyday Code – instead of keeping our knowledge in a README.md let’s share it with the internet)
In FLLCasts and BuildIn3D platforms we use three template languages. ERB (as it is all Rails), Liquid and Mustache. This article gives a brief overview of how we are using and extending Liquid in our platforms. The main purpose is to give an overview for newcomers to our team, but I hope the developers community could benefit from it as a whole.
What is Liquid?
Quoting directly from the site
Liquid is an open-source template language created by Shopify and written in Ruby. It is the backbone of Shopify themes and is used to load dynamic content on storefronts.
https://shopify.github.io/liquid/
You create a template, you feed it values, it produces a result:
@template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template
@template.render('name' => 'tobi') # => "hi tobi"
Why Liquid?
Short answer – I thought: “It is good enough for Shopify so It should be useful for us”
With so many template engines it really is an interesting question why would we use Liquid.
When I made the decision years ago I was facing the following problem.
How to give admins the ability to create email templates where they write the id of an Episode. Before the email is sent, the template should evaluate what the title of this Episode is and fill it in the HTML.
The problem with Episode titles was the following. An author creates an Episode and sets the title to “Line following with LEGO Mindstorms”. An admin prepares a message for all users that the new “Line following with LEGO Mindstorms” tutorial is released. While the admins prepare the email and include the title of the tutorial in the Email, the title of the tutorial is changed to “Line following with LEGO Mindstorms EV3” by the author. So we end up with two titles of the tutorial. One in the email, and one on the site. This happens with Pictures also.
We are sending regular emails to subscribed users. As we are sending the emails I wanted to give the opportunity for admins to create a template of the email and then when the email is sent, the template is evaluated. The solution was to give admins the ability to create a “digest_message” in the platform and to fill this digest message with template values. Like for example:
Hello, {{ user.name }}, This is what we recommend you start with: {% episode = Episode.find(1) %} {{ episode.title }} {{ episode.destroy SHOULD NOT BE ALLOWED}}
This was my goal. To get an instance of Episode, but without all the dangerous methods like “.destroy”. As admin I should still be able to call “episode.title”, but I should not be able to call “episode.destroy”.
This was how we started with Liquid. This was the first time we needed.
How do we use Liquid?
The code above is close to Liquid, but it is not Liquid. You can not use Episode.find(1) in Liquid and this is good. I don’t want this method to be available for admins to write in the emails. Because they could also write other, more dangerous methods.
Liquid gave us the solution – Liquid Drops
Liquid::Drop
The idea of Liquid::Drop is that you get a real object and you can call this object real methods while the template is evaluated. Here is our Episode drop
module LiquidDrops
class EpisodeDrop < Liquid::Drop
def initialize episode
@episode = episode
end
def title
@episode.title.try(:html_safe)
end
def description
@episode.description.try(:html_safe)
end
def published_at
@episode.published_at.strftime('%d %B %Y')
end
end
end
Here is how we use this drop in a Liquid Template.
{% assign episode = episodes.1189 %}
{% assign title = episode.title %}
{% assign description = episode.description %}
<article>
<h3>
{{ title }}
</h3>
<p> {{ description }}</p>
</article>
As you can see we get an instance of episode 1189 and we can then call this instance some specific methods. Like episode.title or episode.description. This is not the real ActiveRecord object for Episode. This is a new instance that is wrapping the ActiveRecord object and is delegating specific methods to it. Liquid would only call methods that are available in the Drop.
The tricky part is to tell Liquid that it should connect ‘episodes.1189’ with the specific Drop class. We pass the drops as a context to template.render as:
@drops['episodes'] = LiquidDrops::EpisodesDrop.new
template = Liquid::Template.parse(template_content)
result = template.render(@drops)
In this way I’ve managed to solve our first challenge. Allow admins to write emails that can dynamically, when rendered, get the current name of the Episode without giving them access to more dangerous methods like Episode#destroy. I could have easily used ERB, but Liquid was such a good fit. It was designed exactly for this and Shopify were using it for their themes.
Screenshot and life example
If you register to FLLCasts (https://www.fllcasts.com) you will receive a couple of emails and all of them use Liquid.
If you don’t want to subscribe here is a screenshot from a recently sent email for a new Course.
You see the picture, title and description. These are all evaluated and added to the HTML with the use of Liquid.
Conclusion
This concludes the first part of this articles. In the next parts I will take a look at how we build Filters and Tags and how and why do we use them.
Reply
You must be logged in to post a comment.