snax

how to publish feeds with resource_feeder

The resource_feeder plugin, along with simply_helpful, recently snuck into edge Rails, courtesy of DHH. They’re nice. resource_feeder lets a controller return a well-formed RSS or Atom feed for any array of ActiveRecord instances. I don’t really know what simply_helpful does, but resource_feeder requires it, so whatever.

install the plugins

First, check out the plugins as svn:externals so you can keep up to date easily.

Go to whatever folder you keep Camping plugins in, or make a folder, and run:

svn propedit svn:externals .

Add these lines to the file:

resource_feeder http://dev.rubyonrails.org/svn/rails/plugins/resource_feeder
simply_helpful http://dev.rubyonrails.org/svn/rails/plugins/simply_helpful

Now, svn up. You will have the plugins.

require them

Now your Camping application needs to load them. One way to do that is this:

%w[simply_helpful resource_feeder].each do |plugin|
  $LOAD_PATH.unshift("plugins/#{plugin}/lib")
  require plugin
  $LOAD_PATH.shift
end

You also need to add a few support methods somewhere in your application (or in an external require), since this is not Rails.

require 'rubygems'
require_gem 'actionpack'
require_gem 'activesupport'

module ResourceFeeder::Atom
  #emulate Rail's request object with a singleton method on @env
  def request
    class << @env
      def host_with_port
        self['HTTP_HOST']
      end
    end
    @env
  end
end

class String
  def pluralize
      Inflector.pluralize(self)
  end
end

That it. It’s all Ruby in the end, so we can make resource_feeder think it’s operating in a Rails environment even though that’s totally a lie.

usage example

Now we can make a feed controller. Assume our Camping app is named Awesome, and that we want to use Atom and not RSS:

module Awesome::Controllers

  class FeedMe
    include ResourceFeeder::Atom

    def get
      @things_to_eat = ThingsToEat.find(:all, :limit => 15, :order => "created_at DESC")
      @things_to_eat = [ThingsToEat.new(:title=>"No results", :id => 0)] if @things_to_eat.blank?
      @headers['Content-Type'] = "application/xml"
      atom_feed_for @things_to_eat,
        {:feed => {:title => "Things to eat 4 u", # feed title
                    :ttl => 40}, # time-to-live in minutes
         :items => {:title => :title,
                    :pub_date => :created_at}}
    end

That would be it, except we also need some specially named methods to return the canonical url of the feed itself and also of the individual records.


    def awesome_models_thing_to_eats_url
       "http://www.aweso.me/" + R(ViewThingToEat)
    end

    def awesome_models_thing_to_eat_url(obj)
       "http://www.aweso.me/" + R(ViewThingToEat, obj.id)
    end

  end
end

One last thing: your models have to respond to .title and .description methods, which provide the title and body of the feed entry, respectively. Sometimes you can just point this at another field, but sometimes you will want to mess around with formatting and aggregating multiple fields into one, specially for the RSS output.

this bug juice won’t come out of my shirt

In order to be able to feed resource_feeder an empty array, you need to fix two small bugs so that we can make it work with new() records. Currently resource_feeder bombs out if there are no records, which we clearly don’t want if we are making a feed for a dynamic search or something like that, in which case an empty result set is perfectly valid.

Also, we will fix it externally so that we don’t run into merge conflicts in the future when the plugin gets updated.

First, the new record must have an id, even if it’s completely bogus, or resource_feeder complains. So we assign it 0, as above.

Second, resource_feeder likes to retrieve the xmlschema of things even if they are nil. So we will give nil its own special xmlschema:

class NilClass
  def xmlschema; ""; end
end

Of course you knew to do that already :) .

postcript

I’d like to use Remember the Milk as my scheduler now that I don’t have a corporate job anymore. But I want events to find their way wirelessly onto my Treo’s calendar and there isn’t a way to do that. I never use that Palm Desktop thing.

I realize I am probably like one of three people in the universe who would want this.