Heroku で FeedNormalizer を使って嵌った

RSS をパースするのに feed-normalizer を使っていたんだけど、Heroku 上で

require "open-uri"
require "feed-normalizer"

feedlink = "http://tnakamura.hatenablog.com/feed"
feed = FeedNormalizer::FeedNormalizer.parse(open(feedlink))
feed.title #=> nil
feed.url #=> nil

を実行したら title と url が nil を返してしまう。RSS をパースしたときはちゃんと取得できたんで、Atom をパースしたときこの問題に遭遇する模様。

ローカルでは取得できるのに、Heroku では取得できなくて嵌った。上記のサンプルを heroku run rails console で実行して発覚。対処方法は現在調査中だけど、ここまで調べるだけでも結構時間使ったんで、忘れないうちにメモっておこう。

追記1

feed.parser #=> "SimpleRSS"

を実行して、内部で使っているパーサーが SimpleRSS ということが分かった。SimpleRSS がちゃんとパースできていない可能性が高い。

追記2

SimpleRSS は parse の中で

tag_cleaned = clean_tag(tag)
instance_variable_set("@#{ tag_cleaned }", clean_content(tag, $2, $3))
self.class.send(:attr_reader, tag_cleaned)

という風に、ATOM のタグと属性をもとに、動的にインスタンス変数とメソッドを作成していた。

そして、Heroku では

self.class.send(:attr_reader, tag_cleaned)

で動的に作成したアクセサが private メソッドになってしまっていた。試しに Heroku で

class Hoge; end
h = Hoge.new("foo")

h.instance_variable_set(:@age, 20) #=> 20
h.class.send(:attr_reader, "age")
h.age #=> NoMethodError: private method `age' called for #<Hoge:0x0000000726ad70 @age=20>

を実行したら、動的に追加したアクセサが private メソッドになっているのを確認できた。ちなみに、ローカル環境で実行すると public メソッドになっていた。

title や author が private なメソッドなので、FeedNormalizer は title や author を取り出せない。その結果、nil が返していたようだ。

追記3

Feedzirra は Heroku でも動いたんで、RSS/Atom パーサーは次から Feedzirra を使おう。