2011年12月24日土曜日

【翻訳】速くなったのはいいとして、Bundler 1.1 の他の新機能は?

Pat Shaughnessyさんの "Besides being faster, what else is new in Bundler 1.1?" を翻訳しました。
元記事はこちら: http://patshaughnessy.net/2011/11/5/besides-being-faster-what-else-is-new-in-bundler-1-1
(翻訳の公開は本人より許諾済みです)

翻訳の間違い等があればブログコメントやTwitter(@oshow)などで遠慮無くご指摘ください。

2011年12月発売の WEB+DB PRESS Vol.66 には Bundler の解説記事が載っているそうです。
「Bundler1.1 ではなく Bundler 自体を知りたい」という人は、そちらを手にとってみてはいかがでしょうか。

速くなったのはいいとして、
Bundler 1.1 の他の新機能は?


2011年11月5日
by Pat Shaughnessy

 3週間前に私は、Bundler 1.0 と比べなぜ Bundler 1.1 は速くなるのかを説明した。あれは明らかに最も重要な新機能だ。しかしそれ以外にも新しいコマンドとオプションが Bundler チームによって 1.1 に実装されており、gem のより良い管理や、開発用マシンとサーバにインストールした gem を選別するのを助けてくれる。Bundler 1.1 の新しいコマンドはアップグレード可能な gem を教えてくれたり、もう必要なくなった gem を削除して綺麗にしたりしてくれることだろう。

今日は bundle outdatedbundle cleanbundle install --standalone のそれぞれを、使用例と共に駆け足で見ていくことにしたい。Bundler を 1.1 へアップグレードすれば速度が改善したことにはすぐ気づくだろうが、Bundler 1.1 の新しいコマンドがどのような物か学ぶための時間も取って欲しい。きっと、gem をうまく整理できるようになるはずだ。

Bundle outdated


 bundle outdated は Bundler 1.1 で導入された最も便利かつ重要な新コマンドであり、バンドルされた gem の中から古くなったものを判別してくれる――つまり、もっと新しいバージョンが入手可能かどうか調べてくれる。例えば、Bundler 1.0 から 1.1.rc へアップグレードして、私が去年から放置していたちょいと古めの Rails 3.0.7 アプリで bundle outdated を実行してみよう。

$ gem install bundler --pre
Successfully installed bundler-1.1.rc
1 gem installed
Installing ri documentation for bundler-1.1.rc...
Installing RDoc documentation for bundler-1.1.rc...

$ cd /path/to/an/old/rails/app

$ bundle outdated
Fetching gem metadata from http://rubygems.org/........

Outdated gems included in the bundle:
  * rake (0.9.2.2 > 0.8.7)
  * activesupport (3.1.1 > 3.0.7)
  * builder (3.0.0 > 2.1.2)
  * i18n (0.6.0 > 0.5.0)
  * activemodel (3.1.1 > 3.0.7)
  * erubis (2.7.0 > 2.6.6)
  * rack (1.3.5 > 1.2.4)
  * rack-mount (0.8.3 > 0.6.14)
  * rack-test (0.6.1 > 0.5.7)
  * actionpack (3.1.1 > 3.0.7)
  * mail (2.3.0 > 2.2.19)
  * actionmailer (3.1.1 > 3.0.7)
  * arel (2.2.1 > 2.0.10)
  * activerecord (3.1.1 > 3.0.7)
  * activeresource (3.1.1 > 3.0.7)
  * aws-ses (0.4.3 > 0.3.2)
  * ffi (1.0.10 > 1.0.9)
  * railties (3.1.1 > 3.0.7)
  * rails (3.1.1 > 3.0.7)
  * mysql2 (0.3.7 > 0.2.13)

まず最初に "Fetching gem metadate…" というメッセージと共に Bundler は RubyGems.org の API へ接続し、最新の gem 依存情報を取得する。これについての詳しい話は3週間前の私の記事を見て欲しい。次に Bundler は古くなった gem をリストアップする。言い換えれば、これら gem には新しいバージョンが存在し、今試しに bundle update を実行すればアップデートされるはずの物たちだ。この例では Rails 3.0.7 の gem が古くなっているのがわかる。今は Rails 3.1.1 が使えるようになっているからだ(訳注:執筆時点ではそれが最新)。また同時に昨年からの間に rake の新バージョンがリリースされた事や、mysql2 やその他 Rails 3.1.1 関連の gem が新しくなっているのがわかる。

これの便利な所は、Bundler がダウンロードしてインストールする gem を表示するが、実際には実行しないことだ。これにより gem のリストを調査し、アップデートしたい gem を選択する自由が与えられる。この例では、私は新しい mysql2 が欲しいが、Rails 3.1.1 にはアップグレードしたくない。このような場合、bundle update mysql2 を実行することができる。Bundler 1.0 では、おそらく単に bundle update を実行して全ての gem をアップデートし、後はうまくいくように願うしかなかっただろう。もしも何か問題――テストが失敗するとかバージョンの衝突とか――があったら、git を使って最初からやり直しする事になる。

デフォルトでは bundle outdated は出力に開発版バージョンの gem を含めることはない。実リリース版バージョンのうち、新しいバージョンだけを列挙する。開発版バージョンも入手可能か調べたいなら、--pre オプションを以下のように追加すればよい。

$ bundle outdated --pre
Fetching gem metadata from http://rubygems.org/........

Outdated gems included in the bundle (including pre-releases):
  * rake (0.9.3.beta.1 > 0.8.7)
  * activesupport (3.1.1 > 3.0.10)
  * builder (3.0.0 > 2.1.2)
  * i18n (0.6.0 > 0.5.0)
  * activemodel (3.1.1 > 3.0.10)
  * erubis (2.7.0 > 2.6.6)
  * rack (1.3.5 > 1.2.4)
  * rack-mount (0.8.3 > 0.6.14)
  * rack-test (0.6.1 > 0.5.7)
  * actionpack (3.1.1 > 3.0.10)
  * mail (2.3.0 > 2.2.19)
  * actionmailer (3.1.1 > 3.0.10)
  * arel (2.2.1 > 2.0.10)
  * activerecord (3.1.1 > 3.0.10)
  * activeresource (3.1.1 > 3.0.10)
  * aws-ses (0.4.3 > 0.3.2)
  * ffi (1.0.10 > 1.0.9)
  * thor (0.15.0.rc2 > 0.14.6)
  * railties (3.1.1 > 3.0.10)
  * rails (3.1.1 > 3.0.10)
  * delayed_job (3.0.0.pre2 > 2.1.4)
  * haml (3.2.0.alpha.8 > 3.1.3)
  * mysql2 (0.3.7 > 0.2.13)
  * ruby-debug-base (0.10.5.rc1 > 0.10.4)
  * ruby-debug (0.10.5.rc1 > 0.10.4)

これで、新しい "alpha.8" バージョンの haml や "pre2" バージョンの delayed_job など、その他インストール可能な開発版バージョンを知ることができる。

私見だが、--pre オプションは単に気のきいている機能ってだけじゃなく、各 gem にどんなリリース計画があり、自分のアプリケーションにどう影響があるかを注視するための素晴らしい方法に思える。Bundler 1.1 は、たくさんググったり RubyGems.org を探しまわらなきゃ見つからない、価値ある知識を与えてくれている。各 gem 作者が開発中のコードが自分のアプリに必要な物かどうかは別にしても、開発版の gem にアップグレードしてテストの失敗やバグを発見することで、haml や delayed_job やその他のプロジェクトへもっと簡単にコントリビュートする事ができるようになるわけだ。

bundle install --path のおさらい


 Bundler 1.1 の新機能の説明を続ける前に、Bundler 1.0 で導入された、あまり認知されていないオプションについておさらいしよう。bundle install を実行すると普通、Bundler は新しい gem を gem install の時と同じ場所へダウンロードしてインストールする。「システムの gem」というやつに混ざるわけだ。この場所は Ruby をインストールしたディレクトリの中にあり、RVM を使っている自分のラップトップではこうなる (Ruby 1.8.7):"/Users/pat/.rvm/rubies/ruby-1.8.7-p352/lib/ruby/gems/1.8/gems"

しかし、--path オプションを渡せば gem を好きなディレクトリにインストールするよう Bundler に指示できる。例えば以下のように。

$ bundle install --path vendor/bundle

上記のコマンドで、新しい gem は "vendor/bundle/ruby/1.8/gems" のようなディレクトリにインストールされるはずだ。ほとんどの人はこのオプションに気づいておらず、というのも普通はさほど役に立たないからだ。自分のコンピュータ上でやる通常の開発作業では、システムへ gem をインストールしても大抵問題はない。しかし --path オプションが役に立つようなケースというのも存在し、特にサーバ上などではそうだ。例えば以下のようなケースが考えられる。
  • システムの gem ディレクトリへの書き込み権限がない。
  • Bundler を Capistrano と一緒に使っていて、このオプションが自動的に有効化されている。
  • Bundler を使ったアプリケーションと、そうでないアプリケーションがある。
  • 同一サーバ上の各アプリケーションにバンドルされている gem が、それぞれ独立して隔離されていることを絶対確実にしたい。
一旦バンドルのパスをこの方法で設定したなら、それは永続的な設定として .bundle/config ファイルへ保存される。これにより、このアプリケーション開発時に将来実行する bundle コマンド はいつも同じパスを使ってバンドルすることが保証される。この値を確認したり、他の全てのコンフィグ設定を見るには bundle config コマンドを使う。

$ bundle config
Settings are listed in order of priority. The top value will be used.

disable_shared_gems
  Set for your local app (/path/to/an/old/rails/app/.bundle/config): "1"

path
  Set for your local app (/path/to/an/old/rails/app/.bundle/config): "vendor/bundle"

パスの設定を削除して gem のインストール先をシステムへ戻すなら、--system オプションを使おう。

$ bundle install --system

--path が設定されている場合に自動的に
古い gem を片付ける


 Bundler 1.1 のその他のステキ機能は、古くて使っていない gem をマシンから自動的に取り除く機能だ…ただし、--path を使ってバンドルのパスを設定していた場合だけだが。もし bundle install --path をせずに普通にシステムの方へ gem をインストールしていたら、未使用の gem を一掃してはくれない。なぜなら、システム上の他のアプリケーションがまだそれを使っているかもしれないからだ。

ではどういう風に動作するか見ていこう。さきほどに引き続き私の古い Rails 3.0.7 アプリを例にするが、あれを Rails 3.0.7 から Rails 3.0.10 へアップグレードすることに決めたとしよう。まず最初に Gemfile をこう編集する。

gem 'rails', '3.0.10'

それから、bundle update を実行する。

$ bundle update rails
Fetching gem metadata from http://rubygems.org/........
Using rake (0.8.7)
Using abstract (1.0.0)
Installing activesupport (3.0.10)
Using builder (2.1.2)
Using i18n (0.5.0)
Installing activemodel (3.0.10)

...etc...

Using rspec-rails (2.6.0)
Using ruby-debug-base (0.10.4)
Using ruby-debug (0.10.4)
Removing actionmailer (3.0.7)
Removing actionpack (3.0.7)
Removing activemodel (3.0.7)
Removing activerecord (3.0.7)
Removing activeresource (3.0.7)
Removing activesupport (3.0.7)
Removing rails (3.0.7)
Removing railties (3.0.7)
Your bundle is updated! Use `bundle show [gemname]` to see where a bundled gem is installed.

ここで分かるのは、Bundler は Rails 3.0.10 のための新しい gem(activesupport、activerecord、他)をインストールすると同時に、もう使っていない古い gem を自動的に削除してくれたってことだ! 古い gem を一掃することはディスク領域の節約にもなるし、gem 置き場の整備維持に一役買ってくれる。
定期的に gem を新しいバージョンにしている内に散らかってしまうのを、防いでくれるんだ。

Bundle clean


 それ以外にも bundle clean を実行することで、使っていない gem を自分から削除する事も出来る。--path オプションをセットしてさえいれば bundle installbundle update の時に自動的に綺麗にしてくれるので、普通はこのコマンドを実行する機会はないだろう。

--path を設定しておらずシステムの gem 領域を使っている場合でも、bundle clean を使うことで、現在のバンドルでは未使用の gem を全てシステムから削除することは可能だ。

$ bundle clean
Can only use bundle clean when --path is set or --force is set

ただしそのままコマンドを打つだけでは、削除できない。上の結果のように、gem を意図せず削除することを回避してくれている。もし本当に実行したいなら --force オプションを使うわけだが…気をつけよう、あなたのマシンで以下を試してはいけない。

$ bundle clean --force
Removing actionmailer (3.1.1)
Removing actionpack (3.1.1)
Removing activemodel (3.1.1)
Removing activerecord (3.1.1)
Removing activeresource (3.1.1)
Removing activesupport (3.1.1)
Removing arel (2.2.1)

etc...

お分かりのように、私のラップトップにある Rails 3.1.1 アプリはたった今すべて壊れてしまった! 予想通り、Bundler は今対象としているアプリケーションのバンドルには含まれていない gem を全て削除してしまった。この場合、今いじっていたアプリケーションは Rails 3.0.10 をバンドルしていたのだから、それには無関係な gem(Rails 3.1.1)を削除したというわけだ。

bundle clean --force は、しばらくの間一つの Ruby アプリケーションしか触らない予定だと分かっていて、gem を整理してディスクスペースを節約したい時は便利だと思われる。システムの gem を綺麗にしておくことは、Bundler を使っていないレガシー Rails アプリケーションが気づかない内にシステムに入れている gem にうっかり依存してしまわないようにするのにも役立つ。

Bundle install --standalone


 これも Bundler 1.1 での新しいオプションで、Bundler がインストールされていないサーバやその他マシンでも動作するようなバンドルを作成させてくれる。以下のように実行される。

$ bundle install --standalone
Using rake (0.8.7) 
Using abstract (1.0.0) 
Using activesupport (3.0.9) 
Using builder (2.1.2) 

... etc...

Using rspec (2.7.0) 
Using rspec-rails (2.7.0) 
Using ruby-debug-base (0.10.4) 
Using ruby-debug (0.10.4) 
Your bundle is complete! It was installed into ./bundle

このオプションにより bundle install --path ./bundle を実行した時に作成されるのと同じ、バンドル内容を格納したローカルディレクトリがもたらされる。ただし、"bundle/bundler/setup.rb" という追加ファイルが作成される点が違う。

$ find bundle | more
bundle
bundle/bundler
bundle/bundler/setup.rb
bundle/ruby
bundle/ruby/1.8
bundle/ruby/1.8/bin
bundle/ruby/1.8/bin/cdiff

... etc...

このファイルの中身を見てみると、バンドルに含まれる各 gem の lib ディレクトリをロードパスに追加するコードが入っているのが分かる。

path = File.expand_path('..', __FILE__)
$:.unshift File.expand_path("#{path}/../ruby/1.8/gems/rake-0.8.7/lib")
$:.unshift File.expand_path("#{path}/../ruby/1.8/gems/abstract-1.0.0/lib")
$:.unshift File.expand_path("#{path}/../ruby/1.8/gems/activesupport-3.0.10/lib")
$:.unshift File.expand_path("#{path}/../ruby/1.8/gems/builder-2.1.2/lib")
$:.unshift File.expand_path("#{path}/../ruby/1.8/gems/i18n-0.5.0/lib")
$:.unshift File.expand_path("#{path}/../ruby/1.8/gems/activemodel-3.0.10/lib")

これにより、Bundler がインストールされていないサーバマシンでもアプリケーションを実行させることが出来る。またこれを見ると、普段 Bundler が実際には何をしているかが感じ取れると思う。Bundler は Gemfile に基づいたロードパスを構築するのが仕事なのだ。

待て待て、まだまだあるぞ


 その他、マイナーチェンジやバグフィックスなど今回で紹介しきれないたくさんのものが Bundler 1.1 には含まれている。ChangeLog を調べたり、http://gembundler.com にある新しいバージョンのドキュメントを見てみるといいだろう。

関連記事:【翻訳】なぜ Bundler 1.1 は速くなるのか

0 件のコメント:

コメントを投稿

フォロワー