如何在 YAML 变量中包含 Jekyll/Liquid 模板数据?

41

我正在使用markdown文件的YAML标题,在博客文章中添加excerpt变量,以便我可以在其他地方使用它。其中一个摘录中,我通过markdown链接标记引用了早期的博客文章,并且我使用了liquid模板数据变量{{ site.url }}代替站点的基本URL。

因此,我有类似于以下内容(已经削减了一些):

--- 
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
----

然而,Jekyll和Maruku Markdown解析器不支持此操作,这使我怀疑是否可以在YAML头文件中使用Liquid标记。

在Jekyll处理的页面的YAML头文件中使用Liquid标记是可能的吗?

  1. 如果可以,在示例中我做错了什么?
  2. 如果不允许,我该如何实现我想要的效果?我目前正在我的笔记本上开发网站,不想将基础URL硬编码,因为当我准备部署时,它将不得不更改。

我从Maruku那里收到的错误信息是:

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

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

| 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

一个可能的问题是你的excerpt值包含了冒号(:)。如果一个值包含冒号,那么在YAML中应该将该值用引号括起来。 - mipadi
@mipadi 谢谢你的回复。即使我引用了你的话,问题仍然存在。 - Gavin Simpson
4个回答

52

今天我遇到了类似的问题。为了解决这个问题,我创建了以下简单的Jekyll过滤器插件,它可以扩展嵌套的liquid模板(例如在YAML前置元数据中的liquid变量):

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

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

将筛选器放置在站点根目录下的“_plugins”子目录中,即可在Jekyll网站中添加筛选器。可以将上述代码简单地粘贴到yoursite/_plugins/liquify_filter.rb文件中。

之后,像这样的模板...

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

...应该呈现出类似于“问候哈利波特!”的输出结果。扩展也适用于更深层次的嵌套结构,只要在内部液态输出块上指定了liquify过滤器。当然,像{{ site.url }}这样的东西也可以工作。

更新 - 看起来现在可以作为Ruby gem使用了:https://github.com/gemfarmer/jekyll-liquify


4
非常好,这正是我在寻找的东西。这个插件应该完全成为Jekyll核心的一个特性。 - alxrb
2
有没有办法在 GitHub Pages 中使用这个? - cregox
4
GitHub Pages 不允许使用插件进行构建。在 Jekyll 插件文档中有一条提示:"GitHub Pages 使用 Jekyll 生成网页,但鉴于安全原因,所有 Pages 网站都会使用 --safe 选项来禁用自定义插件。不幸的是,这意味着如果你要部署到 GitHub Pages 上,你的插件将无法使用。你仍然可以使用 GitHub Pages 发布你的网站,但需要在本地转换站点并将生成的静态文件推送到 GitHub 存储库中,而不是使用 Jekyll 源文件。" 来源:http://jekyllrb.com/docs/plugins/ - Brian Zelip
2
@cregox 或者可以将 Netlify 与 GitHub 结合使用,https://netlify.com。 - Brian Zelip
3
@BrianZ,感谢你提供的 Netlify 暗示!我以前看过它,甚至在那里创建了一个帐户,但出于某种原因我放弃了它...如果其他人可能有兴趣的话,他们为非商业开源项目提供免费的专业计划,并且与 Jekyll 集成非常简单。 https://www.netlify.com/blog/2015/10/28/a-step-by-step-guide-jekyll-3.0-on-netlify/ - 我今天会试着设置它 :) - cregox
显示剩余5条评论

36

我不相信可以在YAML中嵌套Liquid变量。至少,我还没有找到如何做到的方法。

一种可行的方法是使用Liquid的replace filter。具体而言,定义一个你想要用于变量替换的字符串(例如!SITE_URL!)。然后,在输出期间使用replace filter将其切换到您所需的Jekyll变量(例如site.url)。这里是一个简化的.md文件,它在我的jekyll 0.11安装中表现得像预期的那样:

---
layout: post

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

---

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

测试一下在我的电脑上,URL是否被正确插入并按预期从markdown翻译为HTML链接。如果您需要替换多个项目,则可以将多个替换调用串联在一起。

---
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 }}

重要提示是您必须明确设置site.url的值。您不能在Jekyll中免费获得它。您可以通过以下方式之一在_config.yml文件中设置:

url: http://alanwsmith.com

或者,在调用jekyll时定义它:

jekyll --url http://alanwsmith.com

2
+1 感谢Alan详细而生动的回答。(我在_config.yml中设置了site.url,但应该在提问时提及。)我会再等一会儿看看是否还有其他想法/建议,如果没有,我就接受这个答案。干杯。 - Gavin Simpson

0
如果您需要从另一个data/yml文件中替换data/yml中的值,我编写了一个插件。它不是很优雅,但可以使用:
我进行了一些代码改进。现在它可以捕获一个字符串中的所有出现并处理嵌套值。
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

将此代码放置在 site/_plugins/liquid_replacer.rb 中。

yml 文件中使用 !something.someval!,就像使用 site.data.something.someval 一样,但不需要使用 site.data 部分。

例如:

_data/one.yml

foo: foo

_data/two.yml

bar: "!one.foo!bar"

调用 {{ site.data.two.bar }} 将会产生 foobar

======= 旧代码 ======

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

0
另一种方法是在您的head.html中添加一个IF语句。
与其像下面我的示例那样使用page.layout,您可以使用页面YAML头部的任何变量。
<title>
  {% if page.layout == 'post' %}
    Some text with {{ site.url }} variable
  {% else %}
    {{ site.description | escape }}
  {% endif %}
</title>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接