Jekyll: How to write a Tag
Tags and filters are all plugins and I found I could just include my new tag in the exact same file in which I implemented my filter.
Calling my cantag
tag looks like so:
{% cantag "key", "anchor text", "title text" %}
Tags inherit from Liquid::Tag
which means that it has 3 parameters that you can call anything you like. The first one, tag_name
in my case, is the name of the tag. Why you would need to have the name of the thing that you needed the name of in order to call passed in as a parameter… well, it becomes useful in our second tag example. The second parameter, params
in my case, is all the parameters passed in as a single string. The third parameter, tokens
in my case, are the delimiter tokens that the tag was called with, most likely {%
and %}
but also possibly {%-
and -%}
, etc.
Note that if your tag only needs a string input or one parameter then you can take it as is. If you need a list of parameters, it is up to you if they will be space delimited or whatever. I chose Comma Separated Values (CSV) since the text of my parameters could have spaces in them (also commas, so it’s not all that simple). Even though the built-in CSV library is not very robust, it is still better than trying to write your own so, idiosyncrasies, ahoy!
require 'csv' # call Ruby's CSV library
module Jekyll
######################################################################
# this is where my *filter* code is, removed for clarity
######################################################################
######################################################################
# Tag parameters come all together as a string that must be parsed if
# you're expecting more than one. Considering them to be comma
# separated values, use the Ruby csv functions to parse them out,
# except that Ruby's csv is crap and ultrasensitive to extra
# whitespace. Note also that blank/nil values are ok as long as they
# are not followed by non-blank values
# bad: ["a",,"c"], ok: ["a","","c"], also ok: ["a",,,,]
# also note that ["a", ,"c"] is the same as ["a"," ","c"] not ["a",,"c"]
######################################################################
class RenderCanTag < Liquid::Tag
def initialize(tag_name, params, tokens) #every <strong>tag</strong> needs an initialize function
super # super must be called. I have *no* idea what this does
# strip trailing whitespace
@params = params.strip # key, anchor, title # the "@" makes params global in scope (I think)
# strip whitespace between arguments
@params.gsub! '", "', '","' # comma space bad, replace with just comma
end
def render(context) # render is where the output happens
key, anchor, title = CSV.parse_line(@params) # get the parameters from the input string
# even with logic preventing its execution, strip will throw a Liquid exception if a variable is nil
# Liquid can't deal with nil for some reason even though Ruby can, so this next bit of nonsense
key = "" unless key
anchor = "" unless anchor
title = "" unless title
linkdata = context.registers[:site].data["links"] # the incantation required to access the site data
linkey = linkdata[key] # getting to my specific links.json data record
if linkey
anchor = anchor.strip # strip whitespace
anchor = nil if anchor == "" # unset if just blank
anchor = linkey["anchor"] unless anchor # use default from data if not set
anchor = key unless anchor # use key as anchor if data not set
title = title.strip
title = linkey["title"] if title == ""
title = " title=\"#{title}\""
url = linkey["url"]
anchor = "error: URL for #{key} not set" unless url
else
return "*error: link for #{ key } not found*"
end
return "<a href=\"#{url}\"#{title}>#{anchor}</a>"
end # render
end # class RenderCanTag
end # Jekyll module
Liquid::Template.register_tag('cantag', Jekyll::RenderCanTag) # this is all you need to start using your tags, no _config.yml stuff required
Next up, how to generate new tags on the fly