Custom tag with attributes in Jekyll
Process attributes in a tag within Jekyll
To illustrate how to process attributes in a Jekyll tag, we’ll create a tag that will include a partial file from the includes directory. Jekyll uses Liquid as the template languages. So, in order to hook into the template framework, we’ll need to extend Liquid::Tag
. This is pretty straight forward as you just need to override the render method, as seen below.
class PartialIncludeTag < Liquid::Tag
def render(context)
"hello we got a tag!"
end
end
And in order to use the tag within your pages you’ll need to register it with Jekyll
and call it in your pages. To register see the example below.
Liquid::Template.register_tag('partialinclude', PartialIncludeTag)
This tag can be called by the following:
{{ "{% partialinclude " }}%}
Now, let’s get to the meat of why you got here. Let’s process attributes in our liquid tag. Based on the tag definition of a template definition attribute and a name attribute.
{{ "{% partialinclude template: hello_world.html, name: Hello " }}%}
The tag will evolve to process the attributes and eventually include the template passing the attributes to the template. Here is how you process the attributes in a liquid tag:
class PartialIncludeTag < Liquid::Tag
def initialize(tag_name, markup, tokens)
super
@attributes = {}
markup.scan(::Liquid::TagAttributes) do |key, value|
@attributes[key] = value
end
@markup = markup
end
def render(context)
"hello we got a name: #{@attributes['name']}!"
end
end
To put it all together, we’ll need to get the source directory and concat it with the _includes
directory to retrieve the template. And we’ll need to make sure we can fallback to just having the template defined without a name or template variable. The Liquid::Template.parse
method will take the contents of the template and process it with the a map passed into a render
method. The file will be loaded in a local method called load_template
.
Below is the whole kit and caboodle of the whole tag.
class PartialIncludeTag < Liquid::Tag
def initialize(tag_name, markup, tokens)
super
@attributes = {}
markup.scan(::Liquid::TagAttributes) do |key, value|
@attributes[key] = value
end
@markup = markup
end
def load_template(file, context)
includes_dir = File.join(context.registers[:site].source, '_includes')
if File.symlink?(includes_dir)
return "Includes directory '#{includes_dir}' cannot be a symlink"
end
if file !~ /^[a-zA-Z0-9_\/\.-]+$/ || file =~ /\.\// || file =~ /\/\./
return "Include file '#{file}' contains invalid characters or sequences"
end
Dir.chdir(includes_dir) do
choices = Dir['**/*'].reject { |x| File.symlink?(x) }
if choices.include?(file)
source = File.read(file)
else
"Included file '#{file}' not found in _includes directory"
end
end
end
def render(context)
template_data = @attributes
unless template_file = @attributes["template"]
template_file = @markup.strip
end
template = load_template(template_file, context)
Liquid::Template.parse(template).render(template_data).gsub(/\t/, '')
end
end