これの続き。
たとえば
$ rurema String
とすると
bitclust の
bitclust/bin/refe.rb String -d db_path
を実行させているってことがわかったので↓
rurema/myrurema/bin/rurema
92 sh "#{bitclust_path/'bin/refe.rb'}" +
93 " #{query} -d #{db_path(ver)}", :silent => true
それでふと refe.rb は "String" などの文字列うけとった後どうしているのだろう?と思っておいかけてみた。
(デバッガーみたいなものを使い倒せる人じゃないので p self;exit を付け足すといった原始的な方法)
bitclust/bin/refe.rb
def _main
refe = BitClust::Searcher.new
BitClust::Searcher をnew して、class Searcher の method parse に "String" という引数を渡しているらしきことがわかったので
class Searcher を探す...
% grep Searcher lib/bitclust
class Searcher は searcher.rb ファイルに納められていることがわかった。
bitclust/lib/bitclust/searcher.rb
20 module BitClust
21
22 class Searcher
public method parse は80行あたりにあった。
81 def parse(argv)
82 @parser.parse! argv
引数としてうけとった argv の中身は
["String", "-d", "/path/to/data/db/1.9.2"]
配列になっていて "/path/to/data/db/1.9.2" は、myrurema でいうところの db_path がわたされていた。
そして条件分岐した後に
refe_mode_check argv
を実行していた。この時 refe_mode_check に渡している argv には ["String"] がおさめられていた。
refe_mode_check をみてみると、(line 118あたり)
@target_type によって条件分岐していて、@target_type が :class だった場合とそうでない場合の処理があった。
でも
% rurema String を実行した場合 @target_type は nil になるので case 中の条件のどれにもあてはまらない。
case がおわった後の箇所から実行されることになる。
(line 135 あたり)
@view = TerminalView.new(compiler,
...略
class TerminalView を new して @view にそれを代入していることがわかった。
それでこのあとの続きはどこにあるのだろうか?
@view には class TerminalView が代入されているだけで、ほかにメソッドが与えられていない。
これまで辿ってきた道を引き返していくほかなさそうだ... 。
最初までもどってみたら続きがあった。
bitclust/bin/refe.rb
def _main
refe = BitClust::Searcher.new
refe.parse ARGV
refe.exec nil, ARGV
要は
1, _main は ローカル変数 refe に class Searcher を代入する。
2, class Searcher のパブリックメソッドである parse を実行し@view にTerminalView.new を代入する。
3, class Searcher のパブリックメソッドである exec を実行。
ということのようだ。
では早速続きにあたる3番目の箇所 class Searcher の method exec をみてみよう。
90 def exec(db, argv)
91 if @listen_url
@listen_url 変数に何か値がはいっていたら spawn_server を、そうでなければ search_pattern を実行する、と。
% rurema String を実行した時 @listen_url は nil なので
line 94 の search_pattern
が実行される。
def search_pattern みてみよう。
200 def search_pattern(db, argv)
201 db ||= new_database()
202 case @target_type || db
203 when :class
...略
214 else
引数として "String" を渡した時は @target_type は nil なので line 214 以降に移動する。移動すると argv のサイズによっての条件分岐が始まる。サイズが 1 の場合は
219 when 1
220 find_class_or_method db, argv[0]
find_class_or_method を実行する...
262 def find_class_or_method(db, pattern)
263 case pattern
pattern の値によって処理を分けている。
pattern には "String" がおさまっている。
db には#<BitClust::MethodDatabase>
"String" という文字列受け取った場合は when /\A[A-Z]/ に該当するのでそれに続く begin以降にジャンプする。
270 when /\A[A-Z]/ # Method name or class name, but class name is better.
271 begin
272 find_class db, pattern
次に探すべきメソッドは find_class 。
find_class
241 def find_class(db, c)
242 @view.show_class db.search_classes(c)
243 end
をーここで @view と出会いました。やっと話がつながった感じがします。
つまり
1, _main は ローカル変数 refe に class Searcher を代入する。
2, class Searcher のパブリックメソッドである parse を実行し@view にTerminalView.new を代入する。
3, class Searcher のパブリックメソッドである exec を実行。
この3番目の処理が line 242line 90 にあたると。
2番目の処理で「あらかじめ」Searcher のインスタンス変数である @view に class TerminalView が代入されているからここ(line 242 )で@view.show_class を使うことができるわけね、と。
一応、確認してみると
def find_class(db, c)
p @view.class, c ; exit
end
% rurema String
BitClust::TerminalView
"String"
ちゃんと TerminalView が代入されている。
ではでは
class TerminalView の methods show_class をみてみよう。
324 def show_class(cs)
325 if cs.size == 1
def show_class は cs をうけとって そのサイズが1だった場合とそうじゃなかった場合とで処理を分岐させている。
% rurema String
の場合、cs は [#<class String>]
% rurema Hash
の場合、cs は [#<class Hash>]
と変化する。@line が false の場合で、csのサイズが1ならば describe_class を実行する。
なので
% rurema String
した時は
line. 329 describe_class cs.first
を実行するのだとわかった。
では def describe_class はどこだろう? line 411 あたりにあった。
describe_class
411 def describe_class(c)
...略
416 puts "#{c.type} #{c.name}#{c.superclass ? " < #{c.superclass.name}" : ''}"
417 p c.class.name; p c; exit
line. 417 を上のようにしてみると
% rurema String
class String < Object
"BitClust::ClassEntry"
#<class String>
と返ってきたのでローカル変数c には ClassEntry が代入されていてそのクラスは type や name といったメソッドを持っているとわかった。
つづいて
417 unless c.included.empty?
...略
423 unless c.source.strip.empty?
とあって c (すなわち BitClust::ClassEntry)のincluded が empty じゃなかったら空行を1行出力し
c.included.each ...
を実行し
同様に、c.source.strip.empty? がempty じゃなかったら空行を1行出力し
@compiler.compile(c.source.strip)
を実行する。
ようだ。
% rurema String
を実行した場合は何行目の処理が実行されるのか?
line 417 のunless c.included.empty? にキャッチされるとわかった。従ってこの部分
419 c.included.each do |mod|
420 puts "include #{mod.name}"
421 end
を実行していることになる。
変数c には class ClassEntry が代入されているので c.included は、ClassEntryクラスの included メソッド。included メソッドはブロックを受け取ることができるのだなぁ、と。
では class ClassEntry がどうなっているかをみてみよう...。 classentry.rb を開いてみると
bitclust/lib/bitclust/classentry.rb
class ClassEntry < Entry
Entry クラスを継承していた。searcher.rb が使っていた included はどこだろ?継承元の Entry の方なのかな?いや無い。てことは ClassEntry が自分でもっている?ないみたい。
うむむ、どうやら何かにくるまれているようだ...。
hash のキーとしてならあるんだけども...
bitclust/lib/bitclust/classentry.rb
77 property :included, '[ClassEntry]'
included のゆくえがわからなくなってしまった。
こういう時は最初に initialize がどうなっているかをみてみよう。
16 class ClassEntry < Entry
... 略
36 init_properties
init_properties って何かな?classentry.rb ファイル中にその実態は無いようだ。
継承元のクラスのファイルを開いてみると
% vim entry.rb
init_properties は module_eval によって定義されていた...。
もう一度頭を整理して考えてみよう。呼び出した側は下記のようになっていて
bitclust/lib/bitclust/searcher.rb
419 c.included.each do |mod|
420 puts "include #{mod.name}"
421 end
ClassEntryクラスのパブリックメソッドの中に included という名前のメソッドはない。
classentry.rb ファイル中に included という名前で存在しているのはハッシュのキーのみ。
最終的なところまで近づいてきた感じがしつつもこの先はちょっと難しそうだな。読み解けるかなぁ...
今日はここまで。
0 件のコメント:
コメントを投稿