gem管理の新標準ツール"Bundler"のTips

Bundlerは、rubygemsのラッパーです。Rails3のgem管理に採用されています。
Bundlerを使うことで、

  • このgem、手元にはインストールされているのに、本番環境にインストールされてない!
  • 開発環境にgemを入れまくったら、プロジェクトにどのgemが必要なのか分からなくなった!

みたいな問題を解決できます。
詳しくは公式サイトをご覧ください。

今日はそんなBundlerについて色々分かってきたので、Tipsをいくつか紹介したいと思います。

前提知識

この記事は、このへんが理解出来ていることが前提です。

Bundlerは1.0系以降を使おう

2010/07/03現在、bundlerには0.9系(stable)と1.0系(beta)があります。
インタフェースや挙動が少々変わっているので、特に理由がなければ1.0系をおすすめします。
インストール時に --pre オプションを付けることで、最新版がインストールできます。(執筆時点では1.0.0.beta2)

$ gem install bundler --pre

ちなみにこの記事は、bundler-1.0.0.beta2を前提に書かれています。

Bundlerで入れたgemをシステムのgemと分離しておきたい

$ gem list

とやって出てくるgemは、デフォルトでは $GEM_HOME という環境変数の指す場所にインストールされています*1。僕の場合は、

$ echo $GEM_HOME
/home/mirakui/.rvm/gems/ruby-1.9.2-head

でした。
通常、 bundle install コマンドを使うと、 Gemfile に書かれたgemは $GEM_HOME 以下にインストールされます。つまり、システムのgemとごっちゃになってしまいます。
プロジェクトに必要なgemは、$GEM_HOMEではなく、そのプロジェクトのディレクトリに入れておきたいですよね*2。その場合は、以下のようにします。

$ bundle install [DIR]

これで、gemはDIR以下に展開されるので、$GEM_HOMEは汚染されません。ちなみに、おすすめは公式サイトにもある通り

$ bundle install vendor/bundle

です。
なお、このようにディレクトリを設定して bundle install すると、次回以降はディレクトリを指定しなくてもそのディレクトリが使われます。この設定はBundlerが生成する .bundler/config というファイルに書き出されています。

$ cat .bundle/config
--- 
BUNDLE_DISABLE_SHARED_GEMS: "1"
BUNDLE_WITHOUT: ""
BUNDLE_PATH: vendor/bundle

BUNDLE_PATHの部分がそれです。BundlerはBUNDLE_PATH以下をgemとして読み書きします。この.bundle/configファイルがある場合は、

$ bundle install

と打つだけで、.bundle/configに書かれているオプションを指定したのと同じ効果を得られます。むしろ bundle install は

$ bundle

と省略することもできます。

システムに同じgemが入っていたらそちらが使われる

たとえば、 $GEM_HOME に nokogiri が入っている場合、Gemfileにnokogiriを指定してbundle installしても、インストールはスキップされて、 $GEM_HOME のnokogiriが使われます。
これを回避する場合は、--disable-shared-gems オプションをつけましょう。

$ bundle install vendor/bundle --disable-shared-gems

公式サイトにも書いてあるこのコマンドが、最終的なオススメのbundle installです。

Bundlerが入れたgemを使ってrubyを実行したい

以下のようにbundle execを使うと、BUNDLE_PATH以下のgemを使って、スクリプトを実行できます。

$ bundle exec ruby your-script.rb

irbの場合は、以下のようなコマンドが用意されています。

$ bundle console

これは、 bundle exec irb と同等です(多分)。

コンパイルオプションの指定が必要なgemをbundlerで入れたい

残念ながら1.0.0.beta2現在、Gemfileに、gemのコンパイルオプションを指定する機能は付いていないようです。
なので、以下のような手順で代替します。以下の例は、「--with-ruby-include」を指定してruby-debug19をビルドする方法です。

$ cat Gemfile
  :
gem 'ruby-debug19'

$ bundle install vendor/bundle --disable-shared # ruby-debug19のコンパイルエラー
$ env GEM_HOME=`pwd`/vendor/bundle gem install ruby-debug19 -- --with-ruby-include=$rvm_ruby_src_path # bundle install の前にvendor/bundleにgemを入れておく
$ bundle install # 成功

cronからrvmとbundlerを使いたい

cronでrubyスクリプトを実行したら環境変数が間違っててうまく実行できなかった、というのは誰しもが通る道だと思います。
RVMを使っていれば、$HOME/.rvm/scripts/rvm を実行(source)することで、いい具合に環境変数を読み込んでくれます。

 * * * * * source $HOME/.rvm/scripts/rvm; rvm your-ruby-version; cd /path-to-project/; bundle exec ruby your-script.rb

最後に

bundlerについて、まだ分かってないことがいくつかあるので、これらが分かったらTipsその2を書きたいと思います。

  • Bundler1.0から、bundle install時に自動的にbundle lockされるようになったが、bundle installのたびに毎回Gemfile.lockが更新されてしまうので実質的にlockができなくなってるような気がする
  • 同様に、bundle install でGemfile.lockがあってもgemがアップデートされてしまうので、bundle install と bundle update の差がなくなってる気がする
  • 同様に、bundle installで毎回sourceをフェッチして更新の有無を調べてるので、bundle packageで作られるvendor/cache以下のgemがあんまり意味なくなってる気がする

*1:user-installの場合

*2:個人の感想であり実際の効果とは異なる場合があります