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:

Gravl RSS Feed running in RSSOwl

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…