Learning how to Jekyll: the projects page

26 Oct 2014

So I wanted to create a better projects page. One that generates itself and generates every project page from some kind of input. Enter the world of Jekyll generators.

I first was a bit put off by the prospect of diving into ruby. But I really wanted a way to generate new project pages based on content and not handcraft each page. I also wanted to use markdown for this because It would then be similar to the posts.

So first I define a list of associative arrays in my main config that define my projects. These associative arrays contain a name, a markdown file and a thumbnail to be used on the main projects page. This is all information that I need to add in the config.

But then I need to generate the project page itself. This is where the Jekyll plug-in system comes into play. This is my project page generator.

module Jekyll

    class ProjectPage < Page
        def initialize(site, base, dir, project)
            @site = site
            @base = base
            @dir = dir
            @name = project['name'] + '.html'

            self.process(@name)
            self.read_yaml(File.join(base, '_layouts'), 'project.html')
            self.data['project_name'] = project['name']

            self.data['file'] = project['file']
        end
    end

    class ProjectGenerator < Generator
        safe true

        def generate(site)
            dir = site.config['projects_dir'] || 'Projects'
            site.config['projects'].each do |project|
                site.pages << ProjectPage.new(site, site.source, dir, project)
            end
        end
    end
end

This creates the necessary html pages, but now I still need to be able to "include" files from my custom Project folder. So I created an liquid tag that does that.

module Jekyll
    class InputProjectTag < Liquid::Tag

        def initialize(tag_name, text, tokens)
            super
            @text = text;
        end

        def render(context)
            site = context.environments.first['site']

            #set your project dir.
            dir = 'Projects'
            if defined? site.projects_dir
                dir = site.projects_dir
            end
            source = site['source']

            #render the variable to the markdown filename
            content = Liquid::Template.parse(@text).render(context.environments.first)
            path = File.join(File.join(source, dir), content);

            input = IO.read(path.strip())

            #render the markdown file with the variables of the project.
            return Liquid::Template.parse(input).render(context)

        end
    end
end

Liquid::Template.register_tag('include_project', Jekyll::InputProjectTag)

The only downside of this is that with Jekyll --watch new projects will not appear on the web page, It seems the global config file is not watched, or maybe its something entirely different. I will look into this later down the line but for now I just wanted to share these modules.