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

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

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

rss - Ruby - Feedzirra and updates - Stack Overflow

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

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

何を調べるのか?

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

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

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

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

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

 1# feed.rb
 2
 3    def self.fetch_and_parse(urls, options = {})
 4      # ...
 5
 6      url_queue.slice!(0, 30).each do |url|
 7        add_url_to_multi(multi, url, url_queue, responses, options)
 8      end
 9      # ... 
10    end 
11
12    def self.add_url_to_multi(multi, url, url_queue, responses, options) 
13
14      # ... 
15        klass = determine_feed_parser_for_xml(xml)
16      # ... 
17    end
18
19    def self.determine_feed_parser_for_xml(xml)
20      start_of_doc = xml.slice(0, 2000)
21      feed_classes.detect {|klass| klass.able_to_parse?(start_of_doc)}
22    end 

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

更新のとき

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

1feed = Feedzirra::Parser::RSS.new
2# ... 
3
4Feedzirra::Feed.update(feed)  

このupdateを追ってみます。

 1    def self.update(feeds, options = {})
 2      # ...
 3      feed_queue.slice!(0, 30).each do |feed|
 4        add_feed_to_multi(multi, feed, feed_queue, responses, options)
 5      end
 6      # ...
 7    end
 8
 9    def self.add_feed_to_multi(multi, feed, feed_queue, responses, options) 
10      # ...
11        updated_feed = Feed.parse(c.body_str)
12      # ...
13    end
14
15    def self.parse(xml)
16      if parser = determine_feed_parser_for_xml(xml)
17        parser.parse(xml)
18      else
19        raise NoParserAvailable.new("No valid parser for XML.")
20      end
21    end   

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

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

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