Meta Templates For Github Pages
Whether you know it or not, your Github pages, blog or project pages, get piped through Jekyll. Jekyll’s great at generating a site from templates. But what happens when you need to generate those templates from templates?
I found myself down this path as I wanted to generate multiple tag pages for my blog posts (example page). All these pages had the same content layout but different yaml. Although I could use an include tag for the content, I couldn’t for the yaml. Realizing this I was faced with generating jekyll templates with well .. jekyll. But rather than go through markup-escape hell ie escaping Liquid’s brace tags, I decided to use good ’ol ERB. Here were additional features I wanted while generating with meta-templates:
- commandline ability to generate specific templates, pass options, arguments, etc.
- verbose flag to see pages generated
- local flag to generate pages locally ie for testing purposes
The first was a no-brainer, thor. With it’s dead-easy option and command creation, I’ve used it for several daily tasks. So after coding like a monkey, I came up with these thor tasks. Here is the directory containing all relevant files to these tasks. So let’s try a task:
# Starting from JEKYLL_ROOT, move to the directory containg thor tasks bash> cd _meta # Create tag pages ruby and rails locally bash> thor jekyll:tag_xml_pages ruby rails --verbose --local Created file: _site/tag/ruby.xml Created file: _site/tag/rails.xml # Creates them in jekyll root bash> thor jekyll:tag_xml_pages ruby rails --verbose Created file: cldwalker.github.com/tag/ruby.xml Created file: cldwalker.github.com/tag/rails.xml
Since you may not need to generate tag pages, you may be wondering what can I do with these tasks. Let’s look at a task that all jekyll users can appreciate:
bash> thor jekyll:new_post --verbose thoughts on world peace Created file: cldwalker.github.com/_posts/2009-02-19-thoughts-on-world-peace.textile #If we peer inside the generated file: --- layout: post title: Thoughts On World Peace ---
As you can see the new_post
task generates the filename based on the date and title and also generates a basic skeleton which includes the given title. So let’s examine this thor task:
1 desc "new_post TITLE IS MULTIPLE WORDS", "New jekyll blog post"
2 method_options :editor=>:boolean
3 def new_post(*args)
4 template_file = '_posts/new_post.textile'
5 basename = Date.today.to_s + "-#{args.join('-')}"
6 output_file = template_file.sub('new_post', basename)
7 title = args.map {|e| e.capitalize}.join(' ')
8 result = string_from_template(template_file, :title=>title)
9 output_file = create_output_file(output_file, result)
10 system(ENV['EDITOR'], output_file) if options['editor']
11 end
Line by line:
- Line 1 calls thor’s
desc()
to describe usage and description as well as mark the method as a task. - Line 2 defines a boolean option editor which we’ll use later.
- Line 3 allows the task any number of arguments which it uses as the title.
- Line 4 defines the location of our template file.
- Line 5 calculates the generated file’s basename using the current date and title.
- Line 6 makes the generated location’s file smarter by giving it the same directory structure as the template.
- Line 7 creates the title used in the file simply by capitalizing.
- Line 8 uses my erb helper method to generate a string given the template file and its arguments.
- Line 9 uses another helper method to generate the file given the location and string.
- Line 10 uses the editor option to optionally open the file in our preferred editor.
You may have noticed we didn’t define a verbose option yet I used it in the above example. Since my Thorfile defines verbose and local as global options, we don’t have to explicitly define them. The string_from_template
helper method handles those two options. You may have also noticed that in line 10 there is an options variable even though there is none defined. This comes automagically when defining your options for a thor task.
If others have come up with different solutions to template generators, I’d be interested to know. I’m aware of commandline generators such as rubigen but it seemed easier to just roll my own.
Update: I do all of this with boson nowadays.