Include jekyll / liquid template data in a YAML variable? Include jekyll / liquid template data in a YAML variable? ruby ruby

Include jekyll / liquid template data in a YAML variable?


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 (e.g. liquid-variables in the YAML front matter):

module Jekyll  module LiquifyFilter    def liquify(input)      Liquid::Template.parse(input).render(@context)    end  endendLiquid::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: defaultfirst_name: Harrylast_name: Pottergreetings: 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 (e.g. !SITE_URL!). Then, use the replace filter to switch that to your desired Jekyll variable (e.g. site.url) during the output. Here's a cut down .md file that behaves as expected on my jekyll 0.11 install:

---layout: postexcerpt: 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: postmy_name: Alan W. Smithmulti_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  endend

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  endend