Bind Liquid Template Filter to a Context
Categories:
Binding Liquid Template Filters to a Ruby on Rails Context

Learn how to integrate custom Ruby methods as Liquid template filters, enabling powerful server-side logic directly within your Liquid templates in a Rails application.
Liquid is a templating language created by Shopify, often used for its secure, sandboxed environment, making it ideal for user-generated content or themes. While Liquid provides a rich set of built-in filters, there are often scenarios where you need to extend its functionality with custom logic defined in your Ruby on Rails application. This article will guide you through the process of binding your Ruby methods to the Liquid context, allowing them to be invoked as filters within your Liquid templates.
Understanding Liquid Filters and Context
Liquid filters are simple methods that modify the output of a Liquid object. They are applied using the pipe |
character, for example: {{ 'hello world' | capitalize }}
. By default, Liquid filters operate within their own isolated environment. To use custom Ruby methods as filters, you need to explicitly tell Liquid about them and provide a context object that holds these methods.
flowchart TD A[Liquid Template] --> B{"Filter Call: {{ value | custom_filter }}"} B --> C[Liquid Parser] C --> D{Does 'custom_filter' exist in Liquid's registry?} D -- No --> E[Error: Unknown filter] D -- Yes --> F[Look up 'custom_filter' in Context Object] F --> G[Execute Ruby Method 'custom_filter' on Context Object] G --> H[Return Modified Value] H --> I[Rendered Output]
Flowchart illustrating how Liquid resolves and executes custom filters bound to a context.
Creating a Custom Filter Module
The first step is to define your custom filter methods within a Ruby module. This module will encapsulate all the methods you wish to expose as Liquid filters. For instance, let's create a module CustomFilters
that includes a method to reverse a string and another to format a currency value.
# app/liquid/custom_filters.rb
module CustomFilters
def reverse_string(input)
input.to_s.reverse
end
def format_currency(amount, currency_symbol = '$')
"#{currency_symbol}%.2f" % amount.to_f
end
end
Defining custom filter methods within a Ruby module.
app/liquid
, and ensure it's loaded by Rails. You might need to add config.autoload_paths << "#{Rails.root}/app/liquid"
to your application.rb
.Binding the Filter Module to Liquid Context
Once your filter module is defined, you need to register it with Liquid. This is typically done when you initialize the Liquid template engine or when you render a template. The Liquid::Template.register_filter
method is used for this purpose. However, for filters that depend on a specific context (like helper methods from Rails), you'll pass an object that includes your module to the assigns
or registers
hash when rendering the template.
# Example in a Rails controller or helper
require_relative '../../app/liquid/custom_filters'
class ApplicationController < ActionController::Base
def render_liquid_template
template_content = "Hello {{ 'world' | reverse_string }}! Your total is {{ 123.456 | format_currency }}."
# Create a context object that includes your custom filters
# In a Rails view, 'self' (the view context) often works well
liquid_context_object = Object.new.extend(CustomFilters)
template = Liquid::Template.parse(template_content)
# Pass the context object via 'registers' or 'assigns'
# 'registers' is generally preferred for filters that need access to the current request/view context
rendered_html = template.render!({}, { registers: { filters: liquid_context_object } })
render html: rendered_html
end
end
Binding the CustomFilters
module to the Liquid rendering context.
In the example above, we create an anonymous object and extend it with CustomFilters
. This object is then passed into the registers
hash with the key filters
. Liquid will look for filter methods on the object associated with this key. When rendering a Liquid template, you can then use your custom filters:
{% raw %}{{ 'hello liquid' | reverse_string }}
{{ 99.99 | format_currency '€' }}{% endraw %}
Using custom filters within a Liquid template.
Integrating with Rails View Helpers
A common use case is to make Rails view helpers available as Liquid filters. This can be achieved by extending your custom filter module with ActionView::Helpers
or by passing the current view context directly. Here's how you might expose a Rails helper like pluralize
:
# app/liquid/rails_helpers_filters.rb
module RailsHelpersFilters
include ActionView::Helpers::TextHelper # For pluralize
include ActionView::Helpers::NumberHelper # For number_to_currency
# You might need to define a dummy controller/view context for some helpers
# Or pass the actual view context from your controller/view
def pluralize_word(count, singular, plural = nil)
pluralize(count, singular, plural)
end
def currency_format(number, options = {})
number_to_currency(number, options)
end
end
Exposing Rails view helpers as Liquid filters.
# In your controller or a dedicated Liquid renderer service
require_relative '../../app/liquid/rails_helpers_filters'
class SomeController < ApplicationController
def show_product
product = { name: 'Widget', price: 19.99, stock: 1 }
template_content = "You have {{ product.stock }} {{ product.stock | pluralize_word: 'item', 'items' }} in stock. Price: {{ product.price | currency_format }}."
# Pass the current view context if you want full access to helpers
# Or an object that includes your helper module
liquid_context_object = view_context.extend(RailsHelpersFilters)
template = Liquid::Template.parse(template_content)
rendered_html = template.render!({ 'product' => product }, { registers: { filters: liquid_context_object } })
render html: rendered_html
end
end
Rendering a Liquid template with Rails helper filters.
By passing view_context
(which is available in controllers and views) and extending it with RailsHelpersFilters
, your Liquid templates gain access to powerful Rails helpers, making your templates more dynamic and robust.