Generating RSS/Atom Feeds for Individual Tags in a Middleman Blog
My blog can cover a wide range of topics which I organise using tags. How can I provide filtered RSS/Atom feeds so that my readers can subscribe to the tags that interests them?
I recently needed to provide tag filtered feeds for the Unruly Tech site which uses it to aggregate relevant posts from my blog with other blog-enabled developers at Unruly. Previously Tumblr provided this functionality but it was lost when I migrated to Middleman and static hosting.
It's pretty straight forward to do once you get your head around some of the template trickery that Middleman allows and boils down to two steps.
1. Create a feed.xml
Template
Here's an example feed.xml.builder
which is essentially a simpler version of my own Atom feed. It's using the Builder templating engine (hence .builder
) to generate XML so you'll need to add gem "builder"
to your project's Gemfile
and rerun bundle install
.
---
layout: false
---
root_url = "http://jahed.dev"
feed_url = URI.join(root_url, current_page.path)
if defined? tag_name
articles = blog.tags[tag_name]
page_url = URI.join(root_url, blog.controller.tag_pages.link(tag_name))
else
articles = blog.articles
page_url = URI.join(root_url, "#{blog.options.prefix.to_s}/")
end
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.title [tag_name, "Jahed's Blog"].compact.join(" - ")
xml.subtitle [tag_name, "Posts by Jahed"].compact.join(" ")
xml.id page_url
xml.link "href" => page_url
xml.link "href" => feed_url, "rel" => "self"
xml.updated(articles.first.date.to_time.iso8601) unless articles.empty?
xml.author { xml.name "Jahed Ahmed" }
articles[0..5].each do |article|
article_url = URI.join(root_url, article.url)
xml.entry do
xml.title article.title
xml.link "rel" => "alternate", "href" => article_url
xml.id article_url
xml.published article.date.to_time.iso8601
xml.updated article.date.to_time.iso8601
xml.author { xml.name "Jahed Ahmed" }
xml.summary article.summary(500), :type => "html"
xml.content article.body, "type" => "html"
end
end
end
This is essentially checking if a tag_name
local variable has been passed into the template. If so it only shows posts under that tag using Middleman's blog.tag
hash which maps tag names to an array of Articles
.
A few other things to note in the template:
layout: false
- Prevent Middleman from wrapping the template in any layouts you might have defined.
[tag_name, ...].compact.join
- Used to conveniently concatenate the tag name to relevent text when it's available.
URI.join(root_url, ...)
- Used to separate the feed from where it's hosted as some readers rely on absolute URLs to find resources.
You should customise the feed layout to suite your needs. Checkout out Atom's format to see what information you can provide. You can also use the RSS format, though the template will be completely different. There should be some example templates on GitHub/Google.
2. Generate Feeds for each Tag
Now that we have the feed template, we can wire it up with our blog's tags via the config.rb
.
ready do
blog.tags.each_key do |tag_name|
destination_url = "/tags/#{tag_name}/feed.xml"
template_url = "/feed.xml"
proxy destination_url, template_url, :locals => { :tag_name => tag_name }
end
end
Here we're going through every tag and using Middleman's proxy
method to pass in tag_name
and generate a feed using our template.
Pretty self explanatory. This needs to be in the ready
block in the config.rb
so that Middleman Blog has ran and populated blog.tags
.
Also, since feed.xml.builder
is in our project's sources
, Middleman will automaticallt generate a feed.xml
without a tag_name
passed which will generate an unfiltered feed as we defined in our if-else block in the template.
3. You're done!
Just run bundle exec middleman build
and you'll see your feeds built and ready to go. Run them through some feed readers like Firefox's built-in one, Feedly, W3C's Validator and others to make sure everything's valid.