简体   繁体   中英

Include jekyll / liquid template data in a YAML variable?

I am using the YAML heading of a markdown file to add an excerpt variable to blog posts that I can use elsewhere. In one of these excerpts I refer to an earlier blog post via markdown link markup, and I use the liquid template data variable {{ site.url }} in place of the base URL of the site.

So I have something like (trimmed it somewhat)

--- 
title: "Decluttering ordination plots in vegan part 2: orditorp()"
status: publish
layout: post
published: true
tags: 
- tag1
- tag2
excerpt: In the [earlier post in this series]({{ site.url }}/2013/01/12/
decluttering-ordination-plots-in-vegan-part-1-ordilabel/ "Decluttering ordination
plots in vegan part 1: ordilabel()") I looked at the `ordilabel()` function
----

However, jekyll and the Maruku md parser don't like this, which makes me suspect that you can't use liquid markup in the YAML header.

Is it possible to use liquid markup in the YAML header of pages handled by jekyll?

  1. If it is, what I am I doing wrong in the example shown?
  2. If it is not allowed, who else can I achieve what I intended? I am currently developing my site on my laptop and don't want to hard code the base URL as it'll have to change when I am ready to deploy.

The errors I am getting from Maruku are:

| Maruku tells you:
+---------------------------------------------------------------------------
| Must quote title
| ---------------------------------------------------------------------------
|  the [earlier post in this series]({{ site.url }}/2013/01/12/decluttering-o
| --------------------------------------|-------------------------------------
|                                       +--- Byte 40

and

| Maruku tells you:
+---------------------------------------------------------------------------
| Unclosed link
| ---------------------------------------------------------------------------
| the [earlier post in this series]({{ site.url }}/2013/01/12/decluttering-or
| --------------------------------------|-------------------------------------
|                                       +--- Byte 41

and

| Maruku tells you:
+---------------------------------------------------------------------------
| No closing ): I will not create the link for ["earlier post in this series"]
| ---------------------------------------------------------------------------
| the [earlier post in this series]({{ site.url }}/2013/01/12/decluttering-or
| --------------------------------------|-------------------------------------
|                                       +--- Byte 41

Today I ran into a similar problem. As a solution I created the following simple Jekyll filter-plugin which allows to expand nested liquid-templates in (eg liquid-variables in the YAML front matter):

module Jekyll
  module LiquifyFilter
    def liquify(input)
      Liquid::Template.parse(input).render(@context)
    end
  end
end

Liquid::Template.register_filter(Jekyll::LiquifyFilter)

Filters can be added to a Jekyll site by placing them in the '_plugins' sub-directory of the site-root dir. The above code can be simply pasted into a yoursite/_plugins/liquify_filter.rb file.

After that a template like...

---
layout: default
first_name: Harry
last_name: Potter
greetings: Greetings {{ page.first_name }} {{ page.last_name }}!
---
{{ page.greetings | liquify }}

... should render some output like "Greetings Harry Potter!". The expansion works also for deeper nested structures - as long as the liquify filter is also specified on the inner liquid output-blocks. Something like {{ site.url }} works of course, too.

Update - looks like this is now available as a Ruby gem: https://github.com/gemfarmer/jekyll-liquify .

I don't believe it's possible to nest liquid variables inside YAML. At least, I haven't figure out how to do it.

One approach that will work is to use a Liquid's replace filter . Specifically, define a string that you want to use for the variable replacement (eg !SITE_URL! ). Then, use the replace filter to switch that to your desired Jekyll variable (eg site.url ) during the output. Here's a cut down .md file that behaves as expected on my jekyll 0.11 install:

---
layout: post

excerpt: In the [earlier post in this series](!SITE_URL!/2013/01/12/)

---

{{ page.excerpt | replace: '!SITE_URL!', site.url }}

Testing that on my machine, the URL is inserted properly and then translated from markdown into an HTML link as expected. If you have more than one item to replace, you can string multiple replace calls together.

---
layout: post

my_name: Alan W. Smith
multi_replace_test: 'Name: !PAGE_MY_NAME! - Site: [!SITE_URL!](!SITE_URL!)'

---

{{ page.multi_replace_test | replace: '!SITE_URL!', site.url | replace: '!PAGE_MY_NAME!', page.my_name }}

An important note is that you must explicitly set the site.url value. You don't get that for free with Jekyll. You can either set it in your _config.yml file with:

url: http://alanwsmith.com

Or, define it when you call jekyll:

jekyll --url http://alanwsmith.com

If you need to replace values in data/yml from another data/yml file, I wrote plugin. It's not so elegant but works :

I did some code improvements. Now it catch all occurrences in one string and work with nested values.

module LiquidReplacer
  class Generator < Jekyll::Generator
    REGEX = /\!([A-Za-z0-9]|_|\.){1,}\!/
  
    def replace_str(str)
      out = str
      str.to_s.to_enum(:scan, REGEX).map { 
        m = Regexp.last_match.to_s
        val = m.gsub('!', '').split('.')
        vv = $site_data[val[0]]
        val.delete_at(0)
        val.length.times.with_index do |i|
          if val.nil? || val[i].nil? || vv.nil? ||vv[val[i]].nil?
            puts "ERROR IN BUILDING YAML WITH KEY:\n#{m}"
          else
            vv = vv[val[i]]
          end
        end
        out = out.gsub(m, vv)
      }
      out
    end

    def deeper(in_hash)
      if in_hash.class == Hash || in_hash.class == Array
        _in_hash = in_hash.to_a
        _out_hash = {}
        _in_hash.each do |dd|
          case dd
          when Hash
            _dd = dd.to_a
            _out_hash[_dd[0]] = deeper(_dd[1])
          when Array
            _out_hash[dd[0]] = deeper(dd[1])
          else
            _out_hash = replace_str(dd)
          end
        end
      else
        _out_hash = replace_str(in_hash)
      end
      return _out_hash
    end

    def generate(site)
        $site_data = site.data
        site.data.each do |data|
            site.data[data[0]] = deeper(data[1])
        end
    end
  end
end

place this code in site/_plugins/liquid_replacer.rb

in yml file use !something.someval! like as site.data.something.someval but without site.data part.

example :

_data/one.yml

foo: foo

_data/two.yml

bar: "!one.foo!bar"

calling {{ site.data.two.bar }} will produce foobar

======= OLD CODE ======

module LiquidReplacer
  class Generator < Jekyll::Generator
    REGEX = /\!([A-Za-z0-9]|_|\.){1,}\!/

    def generate(site)
      site.data.each do |d|
        d[1].each_pair do |k,v|
          v.to_s.match(REGEX) do |m|
            val = m[0].gsub('!', '').split('.')
            vv = site.data[val[0]]
            val.delete_at(0)
            val.length.times.with_index do |i|
              vv = vv[val[i]]
            end
            d[1][k] = d[1][k].gsub(m[0], vv)
          end
        end
      end
    end
  end
end

Another approach would be to add an IF statement to your head.html .

Instead of using page.layout like I did on my example below, you could use any variable from the page YAML header.

<title>
  {% if page.layout == 'post' %}
    Some text with {{ site.url }} variable
  {% else %}
    {{ site.description | escape }}
  {% endif %}
</title>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM