Search code examples
htmlrubypluginsjekyllliquid

How to write valid HTML out of a liquid tag?


Contextualizing

I've been learning how to working with Jekyll and by now I'm trying to develop my first liquid tag. It purpose will be render my flair of a given StackExchange community.

This way, when I inject {% flair stackoverflow %} in a page, it should be replaced by the equivalent HTML. Here follows the code:

module Jekyll
    class FlairTag < Liquid::Tag

        def initialize(tag_name, community, tokens)
            super
            @community = community
        end

        def render(context)
            output = \
            "<a href=\"{{ #{@community}.url }}/users/{{ #{@community}.id }}/{{ site.data.stackexchange.user.nickname }}\">" \
                "<img src=\"https://stackexchange.com/users/flair/{{ site.data.stackexchange.user.id }}\" " \
                    "width=\"208\" height=\"58\" " \
                    "title=\"Profile for {{ site.data.stackexchange.user.nickname }} on {{ include.label }}\">" \
                    "alt=\"Profile for {{ site.data.stackexchange.user.nickname }} on {{ include.label }}\"" \
            "</a>"

            return output
        end
    end
end

Liquid::Template.register_tag('flair', Jekyll::FlairTag)

The Question

I've read here that returning a string containing the desired HTML code I would achieve my goal, however it hasn't occurred properly, I mean, it has not been the same as if I wrote the HTML directly in the page.

Is there something I'm missing out? Or would have another way to write out HTML code as the return of the ruby function / liquid tag?


Solution

  • Your liquid expressions like {{ site.data.stackexchange.user.id }} will not be interpreted here. You need to use variables to output your datas.

    As I'm guessing that you store your datas in a _data/stackoverflow.yml file that may look like :

    url: https://stackoverflow.com
    id: 2989289
    user: artu-hnrq
    

    This code will do the job :

    module Jekyll
        class FlairTag < Liquid::Tag
    
            def initialize(tag_name, community, tokens)
                super
                # if this tag is called with {% flair stackoverflow %}
                # the "community" variable will be "stackoverflow "
                # in order to remove the trailing space, we strip the variable
                @community = community.strip
            end
    
            def render(context)
                site = context.registers[:site]
                data = site.data[@community]
                url  = data["url"]
                id   = data["id"]
                user = data["user"]
                alt  = "Profile for #{user} on #{@community}"
    
                output = %(
                  <a href="#{url}/users/#{id}/#{user}">
                    <img src="#{url}/users/flair/#{id}.png"
                         width="208" height="58"
                         title="#{alt}"
                         alt="#{alt}"
                  </a>
                )
            end
        end
    end
    
    Liquid::Template.register_tag('flair', Jekyll::FlairTag)
    

    Note : from my point of view if this snippet is located in a unique place on your site (cv, footer, ...) this can be achieved with a simple include.

    There is nearly no performance gain if this tag doesn't need to be customized for each pages, and your code stays maintainable by someone with only html/liquid knowledge.