Jekyll Excerpt Plugin
When you run a blog, you often don't want the entire contents of each post to appear on the front page. You'd rather have just a small excerpt of each post show, and then have a link that says something like "Read More" which points to the actual post. While I was setting up this blog, I couldn't find any nice way to make jekyll do this, so I wrote a short plugin that provides this functionality.
Here's the code:
module JekyllPlugin
class Excerpt < Liquid::Block
def render(context)
# Get the current post's post object
id = context["page"]["id"]
posts = context.registers[:site].posts
post = posts [posts.index {|post| post.id == id}]
# Put the block contents into the post's excerpt_tag field,
# and also return those contents
post.data["excerpt_tag"] = super
end
end
end
Liquid::Template.register_tag('excerpt', JekyllPlugin::Excerpt)
Using the plugin is simple.
Just copy the code into a .rb file in your blog's _plugins directory.
Then, surround the text you want to excerpt in {% excerpt %}
and {% endexcerpt %}
tags.
After that, you can simply use post.excerpt_tag
to get the contents of the excerpt tags. (Replace post
with whatever your variable's name is while you're iterating through site.posts
or paginator.posts
)
As an example, on my index page I do something along these lines:
{% for post in site.posts %}
{% if post.excerpt_tag %}
{{ post.excerpt_tag | markdownify }}
{% else %}
{{ post.content }}
{% endif %}
{% endfor %}
The if block is there so whenever I don't define an excerpt for a particular post, it simply shows the entire post.
Note that the markdownify
part is one of jekyll's liquid extensions.
The plugin doesn't capture the html of your post, it captures the text of the post before being converted by the markdown engine.
Because of this, we need to filter post.excerpt_tag
through the markdown converter before including the excerpt on the page.
(Use the textilize
filter instead if you want to use textile files instead of markdown files.)
One last usage note: If you access your post through the page
variable in the liquid template, the excerpt_tag
variable won't be available.
In other words, page.excerpt_tag
is always going to be undefined.
I suppose that the plugin's code deserves some explanation.
The first few lines of render()
retrieve the post's object.
You can access the page's variables easily by simply using context["page"]
, but if you try to modify them, the modifications will only be local to the current page.
Instead, we want the changes to be global, just as if we had specified the variable in the YAML Front Matter.
To do this, we get the site's global posts hash, and then simply iterate through the posts looking for one that matches the ID in context["page"]["id"]
.
The object we get is an instance of Jekyll::Post
.
The last line of render()
simply modifies post.data
, which is where the variables from the YAML Front Matter live.
After that, accessing post.excerpt_tag
from the liquid template yields the desired value on all of the pages (including the index, which is where we need it).
Update (2013-06-14):
Recent versions of jekyll started treating the variable excerpt
specially, which caused problems.
Renaming the variable to anything else will solve these issues.
I therefore have renamed the variable to excerpt_tag
above.
Update (2013-09-04):
Jekyll 1.1 includes a class named Excerpt
.
Because I had previously (and foolishly) placed my class in the Jekyll
module, this caused conflicts.
This was fixed in the code above by moving the class to a module named JekyllPlugin
.