mahiwaga

I'm not really all that mysterious

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.

posted by Author's profile picture mahiwaga

posted by Author's profile picture mahiwaga