RSS を YAML に変換する#2

の続きです。nuna さんがコメントで色々教えてくれました。

  • バージョンが異なるフィードを、バージョンを意識せずに parse するには require を複数書くとよい (http://www.cozmixng.org/~rwiki/?cmd=view;name=RSS+Parser%3A%3ATutorial.ja にマニュアルあり。)
  • YAML にするときにオブジェクトをそのまま渡すと、YAML で復元した側がそのオブジェクトのクラスを必要とするようになってしまう。これを回避するには RSS オブジェクトを Hash にすると良い。
  • Hash にするには RSS::RDF に to_hash などを追加定義するとよい

とのことです。そこで、昨日のコードを書き替えてみました。

  • require で rss/1.0 に加えて rss/2.0 も追加したので、RSS 2.0 を parse できるようになりました。
  • to_hash メソッドの追加は RSS::Hashable というモジュールを使って Mixin でやってみました。RSS::RDF (1.0 用のクラス) と RSS::Rss に全く同じメソッドを追加する必要があったので。
#!/usr/local/bin/ruby

require 'open-uri'
require 'rss/1.0'
require 'rss/2.0'
require 'yaml'

module RSS::Hashable
  def to_hash
    {
      :channel => {
        :title       => self.channel.title,
        :link        => self.channel.link,
        :description => self.channel.description
      },
      :items => self.items.map do |item|
        {
          :title       => item.title,
          :link        => item.link,
          :description => item.description
        }
      end
    }
  end
end

class RSS::RDF
  include RSS::Hashable
end

class RSS::Rss
  include RSS::Hashable
end

class RSS2YAML
  def initialize(url)
    @url = url
  end

  def to_hash
    rss = self.parse_rss(@url)
    rss.to_hash
  end

  def to_yaml
    self.to_hash.to_yaml
  end

  def get_remote(url)
    begin
      return Kernel.open(@url).read
    rescue OpenURI::HTTPError
      return nil
    end
  end

  def parse_rss(url)
    content = self.get_remote(url)
    if content
      begin 
        return RSS::Parser::parse(content)
      rescue RSS::InvalidRSSError
        return nil
      end
    end
  end

  public :to_yaml
  protected :get_remote, :parse_rss
end

if ARGV.size != 1
  puts "Usage: ruby rss2yaml.rb <url>"
  exit
end

puts RSS2YAML.new(ARGV.shift).to_yaml

これで出力の yamlruby に依存しなくなりました。nuna さんありがとうございました。

RSS::Hashable の中身がちょっと Perl チックな書き方なのですが、こういうものでしょうか。Ruby は既存のクラスにメソッドを追加したりモジュールを Mixin したりするのが楽でいいですね。

ところで「たのしい Ruby」には普通は"モジュールの中では self は使いません" という記述があるのですが、Mixin 用モジュールでは使わないと書けないようなきもしますがどうなんでしょう。