gem と require_gem の挙動の違いについて

Ruby には RubyGems というライブラリマネージャが標準的に利用されています。RubyGemsperl でいうCPANのような存在です。この機構によってライブラリのダウンロードからインストールまでがコマンド一発で済むという非常に便利な存在となっています。

実際のライブラリのロードには require_gem メソッドや require メソッドを利用していましたが、RubyGems が 1.0.0 になった時点で require_gem は gem メソッドに置き換えられました。このことは有名のようですが、挙動の違いについては情報はどうも見かけませんでした。自分が見落としているのかもしれませんが、せっかく調べたので書いておきます。

RubyGems には、ロードするライブラリのバージョンを指定することが可能である、という特性のひとつがあります。

これは 1.0.0 未満のバージョンでは以下のようにして可能でした。

require_gem 'hoge', '=1.0.0'

こうすると、指定したバージョンのライブラリをロードしてくれていました。システム的に新しいライブラリを入れても、過去のバージョンをそのまま利用できる(批判もあるらしいですが)便利な機能でした。

さて、前述のとおり 1.x 系になってからは require_gem は gem に置き換えられました。以下のようにする必要があります。

gem 'hoge', '=1.0.0'

ただし、名前だけでなく挙動も変わっています。上記の例では require_gem しただけでライブラリのロードを行ってくれましたが、1.x でこれは行ってくれません。gem を行った後にもう一度 require を行う必要があります。

また、もうひとつ嵌り易い…というか自分ははまった問題点が。 gem の引数名で指定するライブラリ名と require で指定する引数名が違う場合があるのです。

具体的には、 gem ではシェルコマンドの `gem list` で表示されるパッケージ名を指定し、require では gem のパッケージが展開された配下にあるファイル名を指定する必要があります。これは今後改善されていくと思います。

以下の例では irb を利用し、RubyGems 0.9.5 と 1.1.1 との違いをみていきます。 require 'rubygems' は実行済みの例だと思って下さい。gem 0.9.5 で表示される "Warning: require_gem is obsolete. Use gem instead." という表示も省いて記載します。

require_gem と gem の挙動の違い

0.9.5 では以下の記述でライブラリのロードが可能でした。

irb(main):001:0> require_gem 'activerecord'
=> true
irb(main):002:0> ActiveRecord
=> ActiveRecord

1.1.1 ではどうでしょうか。

irb(main):001:0> gem 'activerecord', '=1.15.6'
=> true
irb(main):002:0> ActiveRecord
NameError: uninitialized constant ActiveRecord
from (irb):2

この通り、gem から true が返されているにも関わらず、ActiveRecord 定数を参照できません。前の版ではこれで reuqire されていましたが、1.1.1 は $LOAD_PATH に ActiveRecord ライブラリのパスを追加するだけのようです。改めて require する必要があります。

irb(main):001:0> gem 'activerecord', '=1.15.6'
=> true
irb(main):002:0> require 'activerecord'
LoadError: no such file to load -- activerecord
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
from (irb):2

失敗してしまいました。ただし、エラーメッセージが変わっています。gem_original_require というメソッドからエラーが吐かれています。

require 'rubygems' した時点で、もともとの require は gem_original_require とリネームされます。RubyGems は独自の require を定義します。

RubyGems の require はライブラリ名を引数に受取り、そのライブラリがまだ gem によって $LOAD_PATH にライブラリパスが追加されていない場合は、対象ライブラリの最新版のパスを $LOAD_PATH に追加します。実際この機構は gem と同じで、内部的には Gem.activate メソッドが担当しています。

require はこの動作を行った後、 gem_original_require(=rubyのもともとのrequire)を呼び出して、ライブラリを実際にロードします。

gem_original_require で LoadError が出るということは、そんなファイルないよ、と言われてしまっているという事です。

まずは上記までの操作ののち、$LOAD_PATH を表示してみます。

irb(main):003:0> $LOAD_PATH
=> ["/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/bin", "/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib", "/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.6/bin", "/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.6/lib", "/usr/lib/ruby/site_ruby/1.8", "/usr/lib/ruby/site_ruby/1.8/i386-linux", "/usr/lib/ruby/site_ruby", "/usr/lib/site_ruby/1.8", "/usr/lib/site_ruby/1.8/i386-linux", "/usr/lib/site_ruby", "/usr/lib/ruby/1.8", "/usr/lib/ruby/1.8/i386-linux", "."]

ActiveRecord のパスは正しく追加されています。実際にそのディレクトリを見てみます。 bin ディレクトリはそもそも存在しないので lib のみ ls してみます。

$ ls /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.6/lib
active_record active_record.rb

ファイルとしてロード可能なものは 'active_record.rb' しか存在しません。これだと当然 require 'activerecord' ではロードできません。ファイル名が違っていますから(笑)。つまり、require 'active_record' (または require 'active_record.rb')でなくてはなりません。

最終的に、以下の形で正しくロードできます。

irb(main):001:0> gem 'activerecord', '=1.15.6'
=> true
irb(main):002:0> require 'active_record'
=> true
irb(main):003:0> ActiveRecord
=> ActiveRecord

gem 'activerecord' によって /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.6/lib が $LOAD_PATH に追加され、require 'active_record' によって$LOAD_PATH 配下にある active_record.rb がロードされるということです。

なぜこういう仕様なのか?

require_gem がライブラリのロードまで一気にやってしまうのに対して、gem は宣言的です。宣言とロード(動作)を分離する狙いかもしれません。

require と gem の名前が食い違う件は頂けませんが、これは今後解消されていくかと思います。たとえば、ActiveRecord 2.x では以下の対処がされています。

$ ls /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.0/lib
active_record active_record.rb activerecord.rb
$ cat /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.0/lib/activerecord.rb
require 'active_record'