The return of the blogroll

Posted by Toby Wed, 13 Aug 2008 15:03:00 GMT

I’ve bought the blogroll back since it disappeared during the upgrade. It’s now generated from my google reader opml feed which I export and run though some ruby which means it’s going to be up-to-date more frequently.

Here is the code that takes the opml and outputs the html fragment:

#!/usr/bin/ruby

require 'rexml/Document'
require 'pp.rb'

class FeedData
  attr_accessor :name, :url, :category

  def initialize( name, url, category )
    ( @name, @url, @category ) = name, url, category
  end
end

class FeedDataList
  def initialize()
    @array = Array::new
  end

  def add( name, url, category )
    tmpFeed = FeedData::new( name, url, category )
    @array.push( tmpFeed )
  end

  def eachCategory
    catArray = Array::new
    @array.each do |feed|
      if !catArray.member?( feed.category ) then
        catArray.push( feed.category )
      end
    end

    catArray.sort.each do |result|
      yield result
    end
  end

  def eachItemInCategory( category )
    @array.each do |feed|
      if feed.category == category then
        yield feed
      end
    end
  end
end

def parse_opml( opml_node, feeds, parents_names=[] )
  opml_node.elements.each('outline') do |element|
    if element.elements.size != 0 then
      feeds = parse_opml( element, feeds, parents_names + [ element.attributes[ 'text' ] ] )
    end
    if element.attributes['xmlUrl'] then
      feeds.add( element.attributes['title'], element.attributes['htmlUrl'], parents_names.last )
    end
  end

  return feeds
end

opml = REXML::Document.new( STDIN )
feeds = FeedDataList::new
feeds = parse_opml( opml.elements['opml/body'], feeds )

feeds.eachCategory do |category|
  print "<br /><strong>#{category}</strong> "
  feeds.eachItemInCategory( category ) do |item|
    print "<a href=\"#{item.url}\">#{item.name}</a> "
  end
  print "\n"
end

To make it work:

$ ./opml2html.rb < <opml file>

I got the inspiration for the recursive opml parsing code from the Dekstop blog, so thanks are due to them!