Nathan Grigg

Jekyll plugin to look up page by url

I have used Jekyll for this site ever since I first created it. I’ve contemplated switching to something Python and Jinja based, since I’m more much more familiar with these tools than I am with Ruby. But there is something about Jekyll’s simple model that keeps me here. It’s probably for the best, since it mostly keeps me from fiddling, and there are better directions to steer my urge to fiddle.

Having said that, I couldn’t help but write one little plugin. I wrote this so I can look up a page or post by its URL. It is an excellent companion to Jekyll’s recent support for data files.

The plugin defines a new Liquid tag called assign_page which works kind of like the built-in assign tag. If you write {% assign_page foo = '/archive.html' %}, it creates a variable called foo that refers to object containing information about archive.html. You can then follow with {{ foo.title }} to get the page’s title.

The plugin code

Here is the code that I store in my _plugins folder.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
module Jekyll
  module Tags
    class AssignPage < Liquid::Assign
    TrailingIndex = /index\.html$/

      def page_hash(context)
        reg = context.registers
        site = reg[:site]
        if reg[:page_hash].nil?
          reg[:page_hash] = Hash[ (site.posts + site.pages).collect {
            |x| [x.url.sub(TrailingIndex, ''), x]}]
        end
        return reg[:page_hash]
      end

      # Assign's Initializer stores variable name
      # in @to and the value in @from.
      def render(context)
        url = @from.render(context)
        page = page_hash(context)[url.sub(TrailingIndex, '')]
        raise ArgumentError.new "No page with url #{url}." if page.nil?
        context.scopes.last[@to] = page
        ''
      end
    end
  end
end

Liquid::Template.register_tag('assign_page', Jekyll::Tags::AssignPage)

On Line 3, you see that my AssignPage class is a subclass of Liquid’s Assign class. Assign defines an intialize method to parse the tag, storing the variable name in @to and the value in @from. By not overriding initialize, I get that functionality for free.

On Line 6, I define a function that creates a hash table associating URLs with pages. Liquid lets you store stuff in context.registers, and Jekyll stores the site’s structure in context.registers[:site]. Lines 10 and 11 create the hash table and store it in context.registers so I don’t have to recreate it for each assign_page tag. Ignoring the removal of trailing index.html, this is the same as the Python dictionary comprehension

{x.url: x for x in site.posts + site.pages}

Line 20 uses the hash table to look up the URL. The rest of the lines are pretty much copied from Assign. Line 19 evaluates @from, which lets you specify a variable containing the URL instead of just a URL. Line 22 puts the page in the proper variable. Line 23 is very important because Ruby functions return the result of the last statement. Since Liquid will print our function’s return value, we want to make sure it is blank.