Read the article on DEV.to
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.”
Shields.io: Quality metadata badges for open source projects
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:
I was planning to use this in this website, 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 over at 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:
{% 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.
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:
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.
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!