Jekyll Extensions -= Pain
UPDATE: jekyll_ext is now a gem and easy to install. See Installation. (Updated on 21/06/2010)
UPDATE: I’ve created a wiki page over at GitHub to list all the extensions that use jekyll_ext. At the moment it’s a bit sparse, but hopefully that will change. Add your creations to the Extensions page. (Updated on 24/03/2010)
If you’ve been using Jekyll for your blog, you no doubt have needed to extend it in some way or another. Like me, you probably have your own forked version of Jekyll where you keep your customizations. This was fine in the beginning, but after a while it became kinda painful:
- You can’t use the Jekyll gem anymore because you’re using your own version. Either make that a gem and use it, or just clone the repo and symlink it. You now have to worry about maintaining your fork when a new release comes out.
- Forget about sharing your extensions with others. Actually, you can, using Git and its branching capabilities, but having to resolve conflicts takes the pleasure away.
Wouldn’t it be great if you could extend Jekyll without creating your own fork?
Wouldn’t it be even better if we could share and reutilize those extensions?
Well, wait no more…
Ladies and Gentlemen, may I present jekyll_ext!
I’ve come up with a solution that leverages Ruby’s metaprogramming superpowers and its Open Class mechanism to give you:
- Extensions that are local to the blog directory, stored under the _extensions dir
- Extensions that can be reutilized and shared
- All this without modifying the Jekyll gem codebase
As a bonus, this solution comes with 3 Aspect-Oriented Programming style helper methods (before, after, and around) to facilitate extension creation and also hide some of the metaprogramming magic code.
Installing jekyll_ext
gem install jekyll_ext
Now you just need to create the directory _extensions in your blog where all your extensions will live. Any .rb file in that directory or subdirectory will automatically be loaded by jekyll_ext.
IMPORTANT: Instead of using the jekyll command, you should now use ejekyll instead (which is just a jekyll wrapper that loads your extensions).
Extending Jekyll with jekyll_ext
jekyll_ext attempts to load the file jekyll_ext.rb located in the _extensions directory of your blog. That file works as an entry point to load all the extensions that will be used when generating the site for your blog.
The idea here is that you can use Ruby’s Open Class mechanism to define new methods in Jekyll classes, or even replace them if need be. You can then use the AOP style helper functions I wrote to intercept method calls and add functionality to existing Jekyll class methods without actually changing the Jekyll code base:
- before can be used when you want to execute some code before a method is invoked.
- after can be used when you want to execute some code after a method is invoked.
- around can be used to control the execution of a method and to change the result returned by that method. With this you can execute code before and after the original method plus change the method’s result.
For a more general overview on how to use these methods you can have a look at the specs.
The extensions I’m using for my blog are also available on GitHub (my_jekyll_extensions) which you can use in your own blog or just as example code to get you started.
Helper Methods in Detail
Here’s a more detailed explanation of how you can actually use the helper methods:
AOP.before(<Class to intercept>, <Name of Method to intercept>)
Executes the block you pass to it before the method you supply is called. Your block receives the following as parameters:- The receiver of the intercepted method (the class’s instance)
- An array with the arguments that were passed to the method
AOP.after(<Class to intercept>, <Name of Method to intercept>)
Executes the block you pass to it after the method you supply is called. Your block receives the following as parameters:- The receiver of the intercepted method (the class’s instance)
- The method’s result
- An array with the arguments that were passed to the method
AOP.around(<Class to intercept>, <Name of Method to intercept>)
Executes the block you pass to it around the method you supply, i.e. you control when the original method gets executed and can change its result. The value returned by the intercepted method will be whatever the last line in the block you supply evaluates to. Your block receives the following as parameters:- The receiver of the intercepted method (the class’s instance)
- The method’s result
- An array with the arguments that were passed to the method
- Lambda/Proc that corresponds to the method that’s been intercepted. You must explicitly invoke the call method on this proc to execute the intercepted method.
- Lambda/Proc that can be used to explicitly abort the method. Whatever you pass when invoking the call method as arguments will be returned by the intercepted method.
Once again, you can use my jekyll extensions to see how all this comes together and allows you to extend Jekyll without touching its codebase.
Hope this makes extending Jekyll less painfull for you as it did for me.
If you have any problems, drop me a comment and I’ll try help you out as best I can.
Happy Jekyll extending!