RSS Lib の Parser について
内部で実際に使っている Parser は以下のどれかとなります。
以下 lib/rss/parser.rb より抜粋。
AVAILABLE_PARSER_LIBRARIES = [ ["rss/xmlparser", :XMLParserParser], ["rss/xmlscanner", :XMLScanParser], ["rss/rexmlparser", :REXMLParser], ]
require によりロードできたものを使用できるパーサとして登録していきます。ちなみに私の環境では irb で試してみたところ、REXML だけロードできるようです。これは REXML が標準の Ruby に含まれているためと思われます。他のものは Ruby 標準としては採用されていないようです(xmlparser および xmlscanner)。
それ以外のパーサを使う場合はインスタンス作成時に引数として ParserClass を渡す必要があります。
私の環境で動く REXML について引き続きコードを読んで行こうと思います。
前回の記事にありますように、RSS::Parser クラスメソッドの parse にて Parser クラスに応じたインスタンスが作成されます。REXML がロードされる私の環境では REXMLParser インスタンスが作成される。作成されたインスタンスの parse メソッドを呼び出すことでパースされ、RSSの情報が取得できるインスタンスが生成されるという構造となっているようです。
後者の RSS 情報を持っているインスタンスについては、後々見ていくとして今回はパース部分を見てみたいと思います。
さて parse メソッドなのですが、REXMLParser クラスを見てみても _parse というメソッドがあるのみで他には見当たりません。ということは他で定義されていることになるのですが、REXMLParser クラスのスーパークラスに当たる BaseParser クラスが怪しそうです。BaseParser クラスを見てみると案の定 parse メソッドがありますが、中では _parse メソッドを呼び出していますが _parse メソッドは見当たりません。これはサブクラスで _parse メソッドを実装する必要があることを意味していると考えられます。Parser クラスを変更することによって独自のパースを行うことができるということだと思います。速さを求めるためのカスタマイズ用とか、XML パーサが乱立していたとかあるんだと思う、たぶん。。。
というわけで、また REXMLParser に戻ってきて _parse を見てみると、中では REXML::Document.parse_stream を呼び出していることがわかります。
class REXMLParser < BaseParser class << self def listener REXMLListener end end private def _parse begin REXML::Document.parse_stream(@rss, @listener) rescue RuntimeError => e raise NotWellFormedError.new{e.message} rescue REXML::ParseException => e context = e.context line = context[0] if context raise NotWellFormedError.new(line){e.message} end end ~省略~ end
REXML は Ruby 標準として含まれている機能ですので、使い方を調べる必要があります。見た感じでは Web から取得した RSS コンテンツ(@rss)と @listener というものを渡して、その後は何もしていないことからここで RSS 情報が作成完了するということになります。名前からはおそらく @listener へタグ情報が通知され、何らかの形で RSS 情報が作成されるのだと推測されます。
今日はここら辺にして、次は @listener が何者か見ていこうと思います。
ここまでのクラス関係を絵にしてみるとこんな感じですね▽