Search code examples
rubyjekyllliquidjekyll-extensions

Why is this Jekyll plugin throwing an exception?


I have a Jekyll site in which I need to show the source of various files within the site. To that end, I tried to use {% include filename %} in my template. However, it turns out that the Liquid tag include will only accept paths under _includes. Initially, I symlinked my project root to a path under _includes. This works, but it makes building really slow as it takes Jekyll quite a while to work out that there are infinitely recursive symlinks in the project.

Now, I've decided that a better approach is to write a plugin to make a full_include tag which will accept any path relative to the project base directory. Here's the code:

#!/usr/bin/env ruby

module MyIncludes

  class FullIncludeTag < Liquid::Tag
    def initialize(tag_name, filename, other)
      super
      $stderr.puts "===DEBUG=== Plugin FullIncludeTag initialized. Arguments:\n\ttag_name:\t#{tag_name}\n\tfilename:\t#{filename}\n\tother:\t#{other}"
      items = Dir.children(Dir.pwd)
      unless items.include?('_config.yml') and items.include('_includes')
        raise RuntimeError, "The working directory doesn't appear to be Jekyll's base directory!"
      end
      @filename = "#{Dir.pwd}/#{filename}"
    end

    def render(context)
      $stderr.puts "===DEBUG=== Plugin FullIncludeTag render beginning.\n\tcontext:\t#{context}\n\tfilename:\t#{@filename}"

      # The following two lines produce the exact same output the first time the plugin is called.
      $stderr.puts "#{Dir.pwd}/resume/portfolio_entries/raw/dice.py"
      $stderr.puts @filename

#       File.open "#{Dir.pwd}/resume/portfolio_entries/raw/dice.py" do |f|
#         return f.read
#       end
      File.open @filename do |f|
        return f.read.chomp
      end
    end
  end
end

Liquid::Template.register_tag('full_include', MyIncludes::FullIncludeTag)

My Ruby skills are incredibly rusty as it's been many years since I've written it, but I can't seem to find the problem here. Here's what's happening:

  1. With the code as presented, Jekyll produces the following output:

    ===DEBUG=== Plugin FullIncludeTag render beginning.
            context:        #<Liquid::Context:0x018ee008>
            filename:       /home/scott/Main Sync/websites/scottseverance.mss/resume/portfolio_entries/raw/dice.py 
    /home/scott/Main Sync/websites/scottseverance.mss/resume/portfolio_entries/raw/dice.py
    /home/scott/Main Sync/websites/scottseverance.mss/resume/portfolio_entries/raw/dice.py 
      Liquid Exception: No such file or directory @ rb_sysopen - /home/scott/Main Sync/websites/scottseverance.mss/resume/portfolio_entries/raw/dice.py in resume/portfolio_entries/dice.html
    jekyll 3.8.4 | Error:  No such file or directory @ rb_sysopen - /home/scott/Main Sync/websites/scottseverance.mss/resume/portfolio_entries/raw/dice.py 
    
  2. If I switch which File.open block is commented out, then the exceptions go away. Of course, since the path is hardcoded, I only get the correct content for the first use of {% full_include %}, but that's to be expected.

Note in particular that the two $stderr.puts calls highlighted in the code comments produce identical output in either variation (at least for the liquid tag called with the hardcoded path). Therefore, I can think of no reason why one call would work and the other fail. Any ideas?


Solution

  • The only thing that I can think of is that you have space at the end of @filename.