5月の読書

5月の読書メーター
読んだ本の数:7冊
読んだページ数:1456ページ

Firefox 3 Hacks Mozillaテクノロジ徹底活用テクニックFirefox 3 Hacks Mozillaテクノロジ徹底活用テクニック
読了日:05月31日 著者:江村 秀之,池田 譲治,下田 洋志,松澤 太郎,dynamis
ダチョウ力 愛する鳥を「救世主」に変えた博士の愉快な研究生活ダチョウ力 愛する鳥を「救世主」に変えた博士の愉快な研究生活
読了日:05月22日 著者:塚本 康浩
アジャイルプラクティス 達人プログラマに学ぶ現場開発者の習慣アジャイルプラクティス 達人プログラマに学ぶ現場開発者の習慣
単体テストの自動化が足りていないことに気づけた
読了日:05月20日 著者:Venkat Subramaniam,Andy Hunt
プロダクティブ・プログラマ -プログラマのための生産性向上術プロダクティブ・プログラマ -プログラマのための生産性向上術
Composed Method は役に立ちそうだ
読了日:05月18日 著者:Neal Ford
ジパング 40 (モーニングKC)ジパング 40 (モーニングKC)
読了日:05月05日 著者:かわぐち かいじ
ジパング 39 (39) (モーニングKC)ジパング 39 (39) (モーニングKC)
読了日:05月02日 著者:かわぐち かいじ
ジパング 38 (38) (モーニングKC)ジパング 38 (38) (モーニングKC)
読了日:05月02日 著者:かわぐち かいじ

読書メーター

RSS Parser の Listener について

前回 REXML::Document.parse_stream を調べたことから、tag_start、tag_end などが解析時に主な処理をしていることが分かりました。
そのため今回は tag_start から Rss オブジェクトが作られるところを解析したいと思います。

Rss オブジェクトはどのように作られるか?

parser.rb にある ListenerMixin の tag_start メソッドを見てみます。かなり端折ると下記のようになります。
Rss オブジェクトが作られる前に注目するため最初の if 節で何か行われると思われます。ここでしていることは、initial_start_#{local} というメソッドを呼び出しているのですが、ここで local は要素名を指しています。そのため rss 要素であれば、initial_start_rss メソッドが呼び出されるということを意味しています。ということは feed 要素であれば initial_start_feed メソッドが呼ばれることになります。そのためコード上は RSS2.0 なのか、Atom なのかを気にしなくてもよくなっています。Rubyメタプログラミング恐るべし。。。

    def tag_start(name, attributes)
      ...
      prefix, local = split_name(name)
        ...
        if @rss.nil? and respond_to?("initial_start_#{local}", true)
          __send__("initial_start_#{local}", local, prefix, attrs, ns.dup)
        elsif respond_to?("start_#{local}", true)
          __send__("start_#{local}", local, prefix, attrs, ns.dup)
        else
          start_else_element(local, prefix, attrs, ns.dup)
        end
        ...
    end

では、initial_start_rss はどこで定義されているかというと、下記の通り 0.9.rb にあります。ここで Rss オブジェクトが生成されていることが分かります。

    def initial_start_rss(tag_name, prefix, attrs, ns)
      ...
      @rss = Rss.new(attrs['version'], @version, @encoding, @standalone)
      ...
      @last_element = @rss
      ...
    end

簡単ではありますが何となく、Rss オブジェクト作成過程までは理解できました。
では次にその他の要素はどのように生成されるのか理解したいと思います。。。
うーん、わからん。。。start_else_element ってのが怪しいとは思うんですが、一体何をしているのやら。どれが重要なコードなのかも分からない状態です。

古いバージョンがヒント。

あまりにも分からないので、恐らく重要な部分だけ書かれていたであろう初期のバージョンのコードを見てみることにします。
下記サイトより RSS Parser Lib の 0.0.4 を取得してきます。
http://www.cozmixng.org/~kou/download/
ソースコード量も少ないし、目論見通り Ruby をあまり理解していない私でも何とか理解できそうな感じです。
ソースコード読解時には古いコードというのも役に立つということが分かりました(もちろんこの限りではないですが)。

Channel や他の子要素はどこで作られるのか?

0.0.4 をベースに見てみたところ、やはり大きな流れは 0.2.3 でも変わらないようです。
まず基点は tag_start メソッド。これは敢えて言う必要も無いですね。
次に start_else_element メソッドへ処理がわたります。ここでは @last_element に格納されているオブジェクトの Class から、要素名に合致する Class を取得しています(const_get の部分)。例えば Channel 要素の tag_start であれば、@last_element には Rss オブジェクトが格納されているため、Rss Class より const_get することにより要素名に合致する Channel クラスを取得することができます。これが entry 要素であった場合は、Rss Class には Entry クラスが定義されていないためエラーとなります。クラス構造がそのままパースするときの正当性確認に使われているようです。すごい。
ここで const_get により取得された Class は、start_have_something_element メソッドへと渡されます。その後 setup_next_element メソッドにて klass.new されることでオブジェクトが生成されるようになっています。
その他の要素についても、上記で説明したようにクラス構造がそのまま解析のための情報となっているため、クラス構造を辿ることでオブジェクトの生成がされていくことになります。そのため start_else_element 中には Rss や Channel などに関する記載は全く無くとも Rss オブジェクトは無事生成されることとなります。これは Atom でも同様となります(atom.rb を参照)。かなり頭の良いやり方だと思います、すばらしいです。

まとめ

というわけで何回かに渡って、RubyRSS Parser Library ソースコード読解をしてきましたが、一通りの流れを掴んだ所で読解については終わりにしたいと思います。一通り理解してみて Ruby に少し詳しくなった気がしますので、今後何かしらプログラミングへ役立てて行きたいと思います。

Command Prompt Explorer Bar インストール

Command Prompt Explorer Bar というかなり便利そうな Tool があったのでインストールしてみました。
エクスプローラ上にコマンドプロンプトが表示されるという優れもの。きっかけは以下の本を読んだことです。
http://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%83%80%E3%82%AF%E3%83%86%E3%82%A3%E3%83%96%E3%83%BB%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E-%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E7%94%9F%E7%94%A3%E6%80%A7%E5%90%91%E4%B8%8A%E8%A1%93-Neal-Ford/dp/4873114020/ref=sr_1_1?ie=UTF8&s=books&qid=1242144197&sr=8-1


インストールは簡単で下記サイトへ行き、Binary ファイルを落としてインストールするのみです。
http://www.codeproject.com/csharp/CommandBar.asp
# ただし codeproject へメンバー登録する必要があるようです。


これは便利です。普段今いるディレクトリでコマンドプロンプト使いたいなと思うことは良くあります。
例えば Windows の検索ではなく grep 使いたいときとか。
そんなときにこれがあればエクスプローラ開きつつ Ctrl+M することで、コマンドプロンプトが現在のディレクトリとなってエクスプローラ下部に表示されます。すばらしい。

REXML::StreamParser の使い方

REXML Parser について簡単に調べてみます。
lib/rss/rexmlparser.rb にて REXML は下記のように使われています。

        REXML::Document.parse_stream(@rss, @listener)

これはストリーム型と呼ばれるパース方法で、パース後に tree が作られるのではなく、逐次 Listener へ解析結果が通知されるような形となります(SAX 型もたぶん一緒)。
よって調べるべきは Listener の方で、Listener では REXML::StreamListener を include していますので、これを調べれば使い方はある程度わかりそうです。
ということで下記サイトで調べてみました(須藤功平さんに感謝)。
http://pub.cozmixng.org/~kou/rexml-doc-ja/
目立つところでは REXML::StreamListener クラスは tag_start や tag_end といった通知系メソッドが定義されています。これらはサイトに記載がありますように、前者は要素(タグ)が現れたときに呼ばれ、後者は終了要素(タグ)が現れると呼ばれます。これがストリーム方式と言われている部分です。
# それ以外にもメソッドがありますが、何となくどんなことをしているのか判別できればよいためここではこれ以上深入りはしません。
では、試しに使ってみたいと思います。ネタは例によってこのサイトの RSS データにします。Listener を作って、それを REXML::Document.parse_stream に渡すだけです。tag_start, tag_end をオーバーライドして rss, channel, item 要素だった場合に Print するようにしてみました。

#!ruby

require 'rexml/document'
require 'rexml/streamlistener'
require 'rss'

url = "http://d.hatena.ne.jp/bazz/rss2"
rss_cont = open(url) { |u| u.read }

class RSSListener
  include REXML::StreamListener
  def tag_start(tag, attrs)
    case tag
    when "rss"
      s = ""
    when "channel"
      s = "  "
    when "item"
      s = "    "
    else
      return
    end
    print s + tag + "\n"
  end

  def tag_end(tag)
    case tag
    when "rss"
      s = ""
    when "channel"
      s = "  "
    when "item"
      s = "    "
    else
      return
    end
    print s + "/" + tag + "\n"
  end
end

listener = RSSListener.new
REXML::Document.parse_stream(rss_cont, listener)

結果は以下のようになります。

rss
  channel
    item
    /item
    item
    /item
    item
    /item
    item
    /item
    item
    /item
    item
    /item
    item
    /item
    item
    /item
  /channel
/rss

私でも、ものの数分でできてしまいました。Ruby 恐るべし。。。
では、REXML::StreamParser の使い方がわかったところで今日はおしまいにします。
RSS Lib でも、REXML::StreamParser を継承することで、rss 要素が始まったときや、channel 要素が始まったときなどに何らかの処理をすることで RSS 解析結果を蓄積していると考えられます。

参考サイト

REXML API ドキュメントの日本語版
http://pub.cozmixng.org/~kou/rexml-doc-ja/

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 が何者か見ていこうと思います。
ここまでのクラス関係を絵にしてみるとこんな感じですね▽

クラスメソッドとインスタンスメソッド

昨日の記事では、RSS Lib を使ってみるところまで実行しました。今日はその中で早速気になった部分から見ていきたいと思います。
まず気になったのが下記の部分です。

rss = RSS::Parser.parse(rss_cont, false)

インスタンスを作成する前から(new する前から)、parse メソッドを呼び出しています。インスタンスが無いのに操作できてしまうというのは、どういうことなんでしょうか?
調べてみると RSS::Parser.parse というのは、module RSS の Parser Class にある parse メソッドであることが分かります。場所は lib/rss/parser.rb にあります(以下抜粋)。

  class Parser
  	~省略~
    class << self
  	  ~省略~
      def parse(rss, do_validate=true, ignore_unknown_element=true,
                parser_class=default_parser)
        parser = new(rss, parser_class)
        parser.do_validate = do_validate
        parser.ignore_unknown_element = ignore_unknown_element
        parser.parse
      end
    end
  	~省略~
  end

"class << self" というのが謎です。ググって見ると ruby では "class << インスタンス ... end" で特異メソッドを定義できるそうです。特異メソッドというのはそのインスタンス特有のメソッドという意味となります。ということは上記で行われていることは class Parser の中における self であるため、Parser クラスというオブジェクトを指しています。これは Parser クラスのインスタンスではないことが注意点です。Ruby ではクラス自身もオブジェクトであるというところが、ここで言いたいこととなります。この辺りは言葉では説明が難しいところですが、要するにクラスメソッドを定義しているということになります。そのため Parser.parse という呼び出し方をしていたようです(Parser と書くと Parser クラスオブジェクトを意味する)。
上記より Parser クラスのインスタンスを作成する前でも、クラスメソッドであるため呼び出すことが可能となる。試しに以下のコードを書いて試してみた。

#!ruby

class Hoge
  def printHoge1
    print "Hoge1\n"
  end
  class << self
    def printHoge2
      print "Hoge2\n"
    end
  end
end

begin
  Hoge::printHoge1
rescue => exc
  print "in rescue\n"
  print exc
  print "\n"
end

Hoge::printHoge2

クラスメソッドとインスタンスメソッドを定義。インスタンスメソッドは当然 new してからでないと呼び出せないはずで、クラスメソッドは"クラス名::メソッド名"で呼び出すことが可能。
結果は下記の通り。

in rescue
undefined method `printHoge1' for Hoge:Class
Hoge2

4月の読書

読書メーター便利です。これは続けていけそうな感じ。

4月の読書メーター
読んだ本の数:9冊
読んだページ数:2006ページ

やる気のスイッチ!やる気のスイッチ!
頼ろうとするのではなく役に立とうとすること。まずは小さな仕事からやり始めること。
読了日:04月30日 著者:山崎 拓巳
先を読む頭脳先を読む頭脳
メタ認知
読了日:04月29日 著者:羽生 善治,松原 仁,伊藤 毅志
会社に人生を預けるな リスク・リテラシーを磨く (光文社新書) (光文社新書 393)会社に人生を預けるな リスク・リテラシーを磨く (光文社新書) (光文社新書 393)
上が上がと言っていないで、自分からリスクの発見対処などしていこうと感じさせてくれる一冊でした。
読了日:04月22日 著者:勝間和代
多読術 (ちくまプリマー新書 106)多読術 (ちくまプリマー新書 106)
昔は速読、速読と追い求めていたけど、本(著者)との会話を楽しむといった発想が今までの読書感と異なり新鮮でした。
読了日:04月20日 著者:松岡正剛
本がどんどん読める本 記憶が脳に定着する速習法! (講談社BIZ)本がどんどん読める本 記憶が脳に定着する速習法! (講談社BIZ)
読了日:04月15日 著者:園 善博
サボる技術―10分の1の努力で成果が出せちゃう仕事術サボる技術―10分の1の努力で成果が出せちゃう仕事術
任せること、断ること。自分の力を発揮できる部分に力を注ぐことに繋がると感じた。でも上手くサボるってのは難しい。
読了日:04月14日 著者:松本 幸夫
起きていることはすべて正しい―運を戦略的につかむ勝間式4つの技術起きていることはすべて正しい―運を戦略的につかむ勝間式4つの技術
やりたくないことはやらない。心に響きました。それから嬉しがり過ぎない、悲しがり過ぎないということは今後実践したいところです。
読了日:04月14日 著者:勝間 和代
決弾 最適解を見つける思考の技術決弾 最適解を見つける思考の技術
読了日:04月12日 著者:小飼 弾,山路 達也
小飼弾の 「仕組み」進化論小飼弾の 「仕組み」進化論
仕組み化して空いた時間の使い方が参考になった。もっと頭を使って働くことが大事。
読了日:04月10日 著者:小飼 弾

読書メーター