-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Liquid for Programmers
It's very simple to get started with Liquid. A Liquid template is rendered in two steps: Parse and Render. For an overview of the Liquid syntax, please read Liquid for Designers.
@template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template
@template.render( 'name' => 'tobi' ) # Renders the output => "hi tobi"
The parse
step creates a fully compiled template which can be re-used as often
as you like. You can store it in memory or in a cache for faster rendering
later.
All parameters you want Liquid to work with have to be passed as parameters to
the render
method. Liquid does not know about your Ruby local, instance, and
global variables.
Extending Liquid is very easy. However, keep in mind that Liquid is a young library and requires some outside help. If you create useful filters and tags, please consider making a pull request with them.
Filters are methods which take one or more parameters and return a value. (For details on how to call a filter with multiple parameters, see the "Output" section of Liquid for Designers.) You can use your own filters by:
- Writing filter methods in a Ruby module. (A given module can contain any number of filter methods.)
- Either passing an array of filter-containing modules to the render call (like
@template.render(assigns, [MyTextFilters, MyDateFilters])
) or globally registering your filter modules (likeLiquid::Template.register_filter(TextFilter)
).
With filters, note that Liquid just uses the method name as the filter name; there's no extra level of name-mapping like there is with tags and blocks. As such, make sure your filter method names don't conflict with any existing filters.
module TextFilter
def textilize(input) # will be available as the "textilize" filter
RedCloth.new(input).to_html
end
end
@template = Liquid::Template.parse(" {{ '*hi*' | textilize }} ")
@template.render({}, :filters => [TextFilter]) # => "<strong>hi</strong>"
Alternatively, you can register your filters globally:
module TextFilter
def textilize(input)
RedCloth.new(input).to_html
end
end
Liquid::Template.register_filter(TextFilter)
Once the filter is globally registered, you can simply use it:
@template = Liquid::Template.parse(" {{ '*hi*' | textilize }} ")
@template.render # => "<b>hi</b>"
"Tags" are tags that take any number of arguments, but do not contain a block of template code.
To create a new tag, simply inherit from Liquid::Tag
and register your class
with Liquid::Template.register_tag
. The register_tag
method takes two arguments: the user-facing name of the tag, and the class that implements it.
Your tag class's initialize
method must accept three arguments, and must call super
at some point.
- The first argument is the name of the tag. This is whatever gets passed to
Liquid::Template.register_tag
when the tag is registered. Usually you already know what your own tag's name is, but you can take advantage of this argument to avoid hardcoding things and repeating yourself (e.g. to ensure your error messages are consistent, etc.). - The second argument is whatever arguments were passed to your tag, as a single string.
- Note that Liquid provides no standard argument handling for custom tags (or even for its own built-in tags) — all you get is unstructured text, so you're in charge of determining how many arguments you got and validating their values.
- Liquid also provides no standard way to indicate whether an argument is meant to be an expression or a literal value, and it won't replace expressions with their value before passing them into your tag. Thus, if you need to treat an argument as an expression, you'll have to do something hairy like test it against the
Liquid::VariableSignature
constant and then callLiquid::Variable.new(<ARGUMENT>).render(context)
somewhere in your ownrender
method.
- The third argument is a hash that stores Liquid options. By default it has two keys:
:locale
and:line_numbers
, the first is aLiquid::I18n
object, and the second, a boolean parameter that determines if error messages should display the line number the error occurred. This argument is used mostly to display localized error messages on Liquid built-in Tags and Filters.
Your class must define a render
method that takes one argument (context
) and returns a string. The contents of the context
argument will vary depending on the environment your tag is being used in.
If the output of your tag depends on the arguments it was called with in the template, you'll need to preserve those arguments as instance variables in your initialize
method so they'll be available in render
.
class RandomTag < Liquid::Tag
def initialize(tag_name, max, tokens)
super
@max = max.to_i
end
def render(context)
rand(@max).to_s
end
end
Liquid::Template.register_tag('random', RandomTag)
@template = Liquid::Template.parse("{% random 5 %}")
@template.render # => "3"
You can also use assigned variables in your tags. To do so, use the context object passed to your render() method. In this example, we multiply any value supplied to the parameter as a tag by the configured "multiply_by" assigned value.
class Multiply < Liquid::Tag
def initialize(tag_name, factor, tokens)
super
@factor = factor.to_f
end
def render(context)
(@factor * context["multiply_by"].to_f).to_s
end
end
Liquid::Template.register_tag('multiply', Multiply)
@template = Liquid::Template.parse("{% multiply 5 %}")
@template.assigns["multiply_by"] = 10
@template.render # => "50"
or
@template = Liquid::Template.parse("{% multiply 5 %}")
@template.render('multiply_by' => 3) # => 15
When you render a template, you can pass it assigns and registers. The difference is subtle: assigns are exposed to the template, while registers are exposed to Drops, Tags, and Filters.
@template.render({a_drop: ADrop.new(dropped_resource)}, registers: { user: @user })
"Blocks" are tags that contain a block of template code which is delimited by a {% end<TAGNAME> %}
tag. The opening tag of a block can also take any number of arguments.
All tag blocks are parsed by Liquid. To create a new block, you just have to
inherit from Liquid::Block
and register your class with Liquid::Template.register_tag
. The register_tag
method takes two arguments: the user-facing name of the tag, and the class that implements it.
class RandomTag < Liquid::Block
def initialize(tag_name, markup, tokens)
super
@rand = markup.to_i
end
def render(context)
value = rand(@rand)
super.sub('^^^', value.to_s) # calling `super` returns the content of the block
end
end
Liquid::Template.register_tag('random', RandomTag)
text = " {% random 5 %} you have drawn number ^^^, lucky you! {% endrandom %} "
@template = Liquid::Template.parse(text)
@template.render # will return "you have drawn number 1, lucky you!" in 20% of cases
If you get errors like A copy of ... has been removed from the module tree but is still active!
it's probably because Liquid is caching your classes in development mode, the solution is to disable it in test and development modes:
# in config/environments/development.rb and config/environments/test.rb
Liquid.cache_classes = false