Rubyパッケージの伝統的なインストーラとして青木さんのsetup.rbがあります。著名なRubyのパッケージ管理ツールであるRubyGemsでさえ、インストールする方法は
ruby setup.rb
です。Ruby on Railsを理解する、前にRubyGemsを理解する、前にsetup.rbを理解してみましょう。その前にRubyを理解する必要があるんじゃないかって?それはすでに青木さんの大著があるので小生の出る幕はありません。
ではまず、ruby setup.rbとした時に何が起こるかを見てみましょう。実際のコードでは例外処理があるのですが見やすさのためにそこら辺のコードは省きます。
if $0 == __FILE__ ToplevelInstaller.invoke end
$0は実行中のファイル名、__FILE__は現在のソースファイル名です。この2つは似ているようで違います。論より証拠です。次の2つのスクリプトがあったとします。
foo.rb
puts "$0?t:#{$0}" puts "__FILE__:#{__FILE__}" if $0 == __FILE__ puts 'foo.rb' end
bar.rb
require 'foo' puts "$0?t:#{$0}" puts "__FILE__:#{__FILE__}"
foo.rbを実行すると以下のように表示されます。
$ ruby foo.rb $0 :foo.rb __FILE__:foo.rb foo.rb
bar.rbを実行すると以下のように表示されます。
$ ruby bar.rb $0 :bar.rb __FILE__:./foo.rb $0 :bar.rb __FILE__:bar.rb
$0は変わらないけど__FILE__は変わります。__FILE__はRubyインタプリタが今まさに読んでいるファイルです。一方、$0はRubyインタプリタ起動時に指定されたスクリプトです。
さて、以上の説明およびfoo.rbの実行結果を見ていただくとご理解いただけると思いますが、
if $0 == __FILE__ 何とか end
というのはスクリプトが直接実行されたときに実行したいコード(ライブラリとして呼ばれたときは実行して欲しくないコード)を入れておくためのイディオムです。
前置きが長くなってしまいましたが、起動時に呼ばれるToplevelInstaller.invokeに進みましょう。ToplevelInstaller.invokeは以下のようになっています。
def ToplevelInstaller.invoke config = ConfigTable.new(load_rbconfig()) config.load_standard_entries config.load_multipackage_entries if multipackage? config.fixup klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) klass.new(File.dirname($0), config).invoke end
一行ずつ読んでいきましょう。
まず、ToplevelInstaller.load_rbconfigです。
def ToplevelInstaller.load_rbconfig if arg = ARGV.detect {|arg| /?A--rbconfig=/ =~ arg } ARGV.delete(arg) load File.expand_path(arg.split(/=/, 2)[1]) $".push 'rbconfig.rb' else require 'rbconfig' end ::Config::CONFIG end
まあそんな感じかなというところですが注目すべきは以下の一行です。
load File.expand_path(arg.split(/=/, 2)[1])
というか解説したいところが2つあるので二行に分けましょう。
path = arg.split(/=/, 2)[1] load File.expand_path(path)
一行目、普通ならsplit(/=/)とだけしてしまいそうですがオプション第2引数で分割数を2に限定しています。これにより、
--rb-config=a=b.rb
としていても適切にa=b.rbが読み込まれます。そんな変なファイル指定するなというところですがこういう細かい配慮がされていると好感が持てます。
次に二行目、File.expand_pathを使って相対パスを絶対パスにしています。これにより、a.rbと指定した場合にスクリプト検索パス中にa.rbがあった場合に誤って読まれてしまうということがなくなります。広く使ってもらおうというコードはこういう配慮をすべきだなと痛感させられます。
rbconfig.rbって何?という方のために書いておくとrbconfig.rbとはRubyインストール時に生成されるファイルでライブラリのディレクトリはどこかということが書いてあります。setup.rbはこのファイルを使うことでライブラリのインストール先を決定しています。