ぴよログ

↓に移転したのでこっちは更新されません、多分。

Feedzirraのupdateメソッドを読み解く

移転しました →

少し前にRSSフィードをパースするgem、Feedzirraの紹介記事で次のようなことを書きました。

そうするとupdateメソッドが使えるようになります。このような流れで更新された記事だけを取り出すことができるようになりました。

なお、Feedzirra::Parser::RSS以外にもFeedzirra::Parser::Atomなどのパーサーがあるのですが、違うフォーマットでもいい感じにやってくれるから大丈夫!という旨がこちらのコメントで確認できます。

rss - Ruby - Feedzirra and updates - Stack Overflow

ソース読めばわかるんでしょうがまだ読んでいないという。今度読みます。

なので、早速ソースコードを読んでみました。

何を調べるのか?

整理すると、フィードの更新の際に用意するパーサーオブジェクトがFeedzirra::Parser::RSSであろうがFeedzirra::Parser::Atomだろうが、どんなフォーマットのフィードでも更新できるらしいが、それはなぜかというところです。

そもそも新規の取得時は?

新規取得のときはこのような使い方をします。

feed = Feedzirra::Feed.fetch_and_parse(feed_urls.first)  

このfetch_and_parseが何をしているのかを追ってみます。

# feed.rb

    def self.fetch_and_parse(urls, options = {})
      # ...

      url_queue.slice!(0, 30).each do |url|
        add_url_to_multi(multi, url, url_queue, responses, options)
      end
      # ... 
    end 

    def self.add_url_to_multi(multi, url, url_queue, responses, options) 

      # ... 
        klass = determine_feed_parser_for_xml(xml)
      # ... 
    end

    def self.determine_feed_parser_for_xml(xml)
      start_of_doc = xml.slice(0, 2000)
      feed_classes.detect {|klass| klass.able_to_parse?(start_of_doc)}
    end 

お!どのパーサー使うか、というような話がでてきました。fetch_and_parseの奥でパース直前にフィードのXMLからパーサーを特定するようなロジックが入っていました。このdetermine_feed_parser_for_xmlRSSフィードのフォーマットに応じてFeedzirra::Parser::RSSFeedzirra::Parser::Atomなどが返ってくるようです。

更新のとき

更新のときはこう使います。

feed = Feedzirra::Parser::RSS.new
# ... 

Feedzirra::Feed.update(feed)  

このupdateを追ってみます。

    def self.update(feeds, options = {})
      # ...
      feed_queue.slice!(0, 30).each do |feed|
        add_feed_to_multi(multi, feed, feed_queue, responses, options)
      end
      # ...
    end

    def self.add_feed_to_multi(multi, feed, feed_queue, responses, options) 
      # ...
        updated_feed = Feed.parse(c.body_str)
      # ...
    end

    def self.parse(xml)
      if parser = determine_feed_parser_for_xml(xml)
        parser.parse(xml)
      else
        raise NoParserAvailable.new("No valid parser for XML.")
      end
    end   

ここでもまたdetermin_feed_parser_for_xmlが呼ばれてた!

そういうわけで、どのクラスを渡しても実際のパース前に適切なロジックが選択されることに間違いないということがわかりました。

よかった。すっきりしました。