はじめに

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に進みましょう。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

まず、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はこのファイルを使うことでライブラリのインストール先を決定しています。


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS