Screwing around with Custom Liquid Tags
I’ve spent some time screwing around with creating custom Liquid tags.
The main motivation was to save some keystrokes because a lot of the time, I post links to articles in the following format:
<a href="http://url-to-article" title="article title • date • site">article title</a> • date • site
To cut down on the reduplication, I created the following Liquid tag:
{% link url:"http://url-to-article", title:"article title", date:"date", site:"site" %}
Initially, I had it only handle plain text strings, but then I got the bright idea to try and pass variables and Liquid templates as parameters, and this soon became a time-consuming endeavor.
For starters, there isn’t a lot of guidance on how to parse parameters of custom Liquid tags. A lot of them just have positional parameters, but this becomes problematic because (1) I don’t like having to remember the order parameters have to come in and (2) this also makes adding additional parameters cumbersome.
For example, when I realized that I also wanted to support this format:
<a href="http://url-to-article" title="article title • subtitle • date • author • site">article title</a> • subtitle • date • author • site
…I started with if
/then
/elsif
/else
statements. This got old real fast.
I figured out that the more recent versions of Liquid make it easier to parse parameters using this bit of code which creates a hash:
module Jekyll
class LinkTag < Liquid::Tag
def initialize(tag_name, markup, tokens)
super
@parameters = {}
markup.scan(Liquid::TagAttributes) do |key, value|
@parameters[key] = value
end
end
…
But the harder part was figuring out how to lookup variables.
A lot of plugins (including some of the plugins built into Jekyll) use hairy regexes to figure out whether a parameter is a string or a variable but I just decided to copy-and-paste the way Liquid does it, where quoted strings are passed literally while unquoted strings are interpreted as variables.
{% link url:"http://url-to-article", date:"date", site:site.title %}
In this case, site.title would be replaced with “mahiwaga”.
There is a Liquid class defined called Expression, but I couldn’t figure out how to properly incorporate it into the code of custom Liquid tag and it doesn’t seem to be able to look up variables in context.registers[:site]
so I just adapted it thus:
def expression_parse(context, markup)
case markup
when /\A'(.*)'\z/m # Single quoted strings
$1
when /\A"(.*)"\z/m # Double quoted strings
$1
else
lookup_variable(context, markup)
end
end
The lookup_variable
method is from Jekyll::LiquidExtensions.
Incorporating Liquid variables into a parameter was actually easier.
{% link url:"http://url-to-article", date: "date", site:"originally posted on {{ site.title }}" %}
You just have to send the parameter back through the Liquid parser this way:
parameter['key'] = Liquid::Template.parse(expression_parse(context, parameter['key'])).render(context)
The entirety of the code is here:
linktag.rb • 2015 Oct 21 • mahiwaga.net
Not sure how useful it will be for anyone else, but it might serve as an example for how to write your own custom tags.