Give Marc’s excellent work on the Feeds Plugin, I knew the feed generating work would be pretty straightforward, but there were a few things to learn along the way.
I wanted to mimic Pebble’s feed urls, so a little custom work in UrlMappings would be required. Pebble supports a /rss.xml
, a /atom.xml
, and also the category specific versions (/categories/Groovy/rss.xml
). All very doable:
// feeds for all blog entries "/$blog/$feedtype" { controller = "blog" action = "feeds" constraints { feedtype(inList: ['rss', 'atom']) } } // feeds for individual categories "/$blog/categories/$categoryName/$feedtype" { controller = "blog" action = "feeds" constraints { feedtype(inList: ['rss', 'atom']) } }
Which gives us a nice matcher. Seems the extension (.xml) gets thrown away for matching purposes, but that makes things even easier for me.
Now on to the work of generating the feed itself. I’m using Marc’s builder since it gives me a little more flexibility. I also have to cater for category specific feeds. Here’s my feeds action:
def feeds = { log.debug "Starting feed generation..." def blogId = params.blog def category = params.categoryName def feedtype = params.feedtype def blog = Blog.findByBlogid(blogId) log.debug "Rendering feed for blog $blogId of type $feedtype" if (blog) { def entries = BlogEntry.findAllByBlog(blog, [max: 10, sort: "created", order: "desc"]) if (category) { // filter to supplied category entries = entries.findAll {entry -> entry.tags.find {it.name == category} } } def baseUri = request.scheme + "://" + request.serverName + ":" + request.serverPort + grailsAttributes.getApplicationUri(request) def builder = new feedsplugin.FeedBuilder() def feedTitle = blog.title + (category ? " ($category Related category)" : "") builder.feed(title: feedTitle, link: baseUri + (category ? "/categories/$category" : ""), description: feedTitle) { entries.each() {blogEntry -> entry() { title = blogEntry.title link = blogEntry.toPermalink(baseUri) publishedDate = blogEntry.created content(type: 'text/html', value: blogEntry.body) // return the content } } } def romeFeed = builder.render(feedtype) render(text: romeFeed, contentType: "text/xml", encoding: "UTF-8") } }
Let’s give it a whirl in RSSOwl to see how it looks:
Ok. Still not ideal. Should do the filtering in the query rather than afterwards to ensure I end up with the right number of elements. Will tidy that one up soon.
One of the other little neat tricks that I’m using is developing some handy codecs. Codecs let you attach a handy method to your string objects that you can later take advantage of. I’ve created NiceTitleCodec.groovy and placed it in /grails-app/utils
to help with permalink generation:
// strip all non word chars... class NiceTitleCodec { static encode = { str -> return str.toString().replaceAll("\W", "_") } }
Which means I can then implement BlogEntry.toPermalink() to use title.encodeAsNiceTitle()
very simply:
public String toPermalink(String baseUri) { return baseUri + "/" + blog.blogid + "/" + (created.year + 1900) + "/" + (created.month + 1) + "/" + (created.getDate()) + "/" + title.encodeAsNiceTitle() }
And we’ve got ourselves some nicer looking urls like: /gravel/glen/2007/11/28/Give_me_1K_LOC_and_I_will_move_the_Earth_
. Hmm…. Needs a .html extension to make it look real.
Anyways, that’s feeds pretty much done, what else is on the list…