Creating Custom Tags in Jekyll, with an actual example
Tags: ruby, jekyll
Published: 2022-07-19 03:58:45

---
title: Creating Custom Tags in Jekyll, with an actual example
published: true
description: Here's my take on creating custom Jekyll tags.
tags: ruby, jekyll
//cover_image: https://direct_url_to_image.jpg
---

Shields.io is a "service for concise, consistent, and legible badges in SVG and raster format, which can easily be included in GitHub readmes or any other web page."

{% embed https://shields.io/ %}

Their service takes query parameters to their API endpoint to generate a badge / shield. For example:

```
https://img.shields.io/static/v1?label=Find%20me%20on&message=GitHub&color=181717&style=flat&logo=github
```

becomes:

![My GitHub](https://img.shields.io/static/v1?label=Find%20me%20on&message=GitHub&color=181717&style=flat&logo=github)

I was planning to use this in my personal blog, when I realized a problem; it is hard to maintain.
While it may be rare to change the content of those shields, it would be nice to have a maintainable way to put a shield on my blog.

Since I use Jekyll, I can define a custom 'Liquid tag.' Just like we can embed stuff from compatible services with liquid tags here in DEV.to, e.g.,  `{% github repo %}` and `{% embed website %}`, we can make a custom tag, say, `{% shields_io payload %}`, to display a shield.

## How does Liquid tag on Jekyll work?

The Liquid tag on Jekyll takes the following format:

```liquid
{% tag_name [tag_argument] %}
```

It has a tag name, and one optional argument.

See the problem here? There is at most one value accepted as an argument. Shields.io takes way more than that, and putting query parameter here does not solve anything.

There is still hope, though; let's take a look at the code we'll be writing.

```ruby
module Jekyll
  class CustomTag < Liquid::Tag
    def initialize(tag_name, arg, parse_context)
      super
      # @type [String]
      @arg = arg
    end
    def render(_context)
      "The argument is #{@arg}" # return the render result here
    end
  end
end
```

The `arg` argument is the argument passed from the tag. It's a string. So we can do something with the input.  
And looking back at the original problem, we are trying to pass a set of key-value pairs to an API endpoint.

So, I decided to pass a JSON payload here; it can be prettified for our purposes, and Ruby supports JSON deserialization out of the box.

## How to turn it into the URL

Since Ruby can turn the input JSON into a hash, we can iterate on this hash and construct the query parameter.

So the idea is to deserialize the JSON and store it in a variable:

```ruby
    def initialize(tag_name, input, parse_context)
      super
      # @type [Hash]
      @config = JSON.load(input.strip)
    end
```

And construct the query parameter. I also decided to include `href` and `alt` for other purposes into the JSON payload, but they are not relevant for the request. So I extract their values and remove it from the input hash before turning the rest into the query parameter.

```ruby
def render(_context)
  href = @config[:href]
  alt = @config[:alt]
  @config.delete(:href)
  @config.delete(:alt)
  shield_tag = <<HTML
  <img src="https://img.shields.io/static/v1?#{hash_to_query}"
HTML
  if alt != nil
    shield_tag += " alt=\"#{alt}\" />"
  else
    shield_tag += " />"
  end
  if href != nil
    <<HTML
<a href="#{href}">
#{shield_tag}
</a>
HTML
  else
    shield_tag
  end
end

private
def hash_to_query
  @config.to_a.map { |k, v|
    "#{k}=#{v}"
  }.join '&'
end
```

## Result

Here is my creation - copy the last two files and run a local server!

{% gist https://gist.github.com/Clpsplug/53fafc4d901e41d8506a0fcf466771bc %}