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 は速くなるのか

【翻訳】なぜ Bundler 1.1 は速くなるのか

Pat Shaughnessyさんの "Why Bundler 1.1 will be much faster" を翻訳しました。
元記事はこちら: http://patshaughnessy.net/2011/10/14/why-bundler-1-1-will-be-much-faster
(翻訳の公開と画像の使用は本人より許諾済みです)

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

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

なぜ Bundler 1.1 は速くなるのか


2011年10月15日
by Pat Shaughnessy

 ここ一年ほどの間で Rails 3 アプリケーションを作っていた人ならば、bundle installbundle update に長い長い時間がかかるのが珍しくないことに気づいていた事と思う。ターミナルが30秒かそれ以上の間ハングしたように見え、その後 "Fetching source index for http://rubygems.org/." なんて文字が表示される。さてここで、あなたに良いニュースがある。明敏なる Bundler & RubyGems.org チームはこれに対する解決策を考えだし、来たる新バージョンの Bundler ではそれが物凄く高速化されたのだ! 今日は、如何にして Bundler 1.1 がこんなにも速くなったのかを見ていきたいと思う――つまり RubyGems & Bundler チーム達の、「巧速化」ぶりを見ていこう。

2011年11月追記:11月15日の Boston.rb の集まりで、この話題に関するプレゼンをしたら盛況だった。Bundler 1.1 についてもっと知りたい人は、その時の動画を見たりスライドのダウンロードが可能だ。

なぜ Budler 1.0 が遅かったのか


 Bundler 1.1 を見ていく前に、なぜ Bundler 1.0 はあんなにも遅かったのだろうか? "Fetching source index..." と表示される時は何が起こっていて、そしてなぜそれに長時間かかったのか? Nick Quaranto はこれについて、2011年1月に良いまとめ記事を書いてくれた。詳細を知りたいなら、その記事を見てみるといいだろう。Nick の説明によると、Bundler を走らせる度に起きているのは、RubyGems.org から全 gem のリストをダウンロードしてそれを処理に通すという作業らしい。RubyGems.org には数万個の gem があるわけだから、とても、とても時間がかかる。Nick は同時に、私がこれから続く節以降で説明しようとしている解決法も示唆してくれた。

なぜこんなことをしているのかと言えば、Bundler の仕事はどの gem をあなたのアプリケーションに含めるべきかを決定する事――「バンドル」する事――であり、その決定は、ある gem がどの gem に依存しているかに基づいているからだ。だから、全ての gem の依存関係情報をダウンロードする必要があるのだ。この全ての情報をダウンロードして処理するのには、30秒かそれ以上かかる(ネットワーク環境と CPU にも左右されるが)。

Bundler 1.1 は本当に速くなったのか?


 新バージョンの Bundler が速くなったのかを調べるのに一番いい方法は、ただインストールして試すだけだ。(訳注:執筆・翻訳時点では Bundler 1.1 が正式リリース前であるため、インストールするために --pre を付けている)

$ 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/my/favorite/rails/app
$ bundle update
Fetching gem metadata from http://rubygems.org/.........
Using rake (0.9.2)
Using multi_json (1.0.3)
Using activesupport (3.1.1)
Using builder (3.0.0)

etc...

Using sass-rails (3.1.4)
Using sqlite3 (1.3.4)
Using uglifier (1.0.3)
Your bundle is updated! Use `bundle show [gemname]` to see where a bundled gem is installed.

もし上記を試したなら、2つの劇的な違いに気づくはずだ。

  1. ずううっと速くなっている。私の場合たった4秒だった。Bundler 1.0 では30秒以上かかった――目覚しい進歩だ!
  2. "Fetching source index..." の表示と長い沈黙の代わりに、"Fetching gem metadata from http://rubygems.org/........." の表示と共にドットが徐々に増えていき、なんだか spec を走らせているように思えた。

しかし、これは実際どう動いているのだろう? そして "Fetching gem metadate…" は何を意味しているのか? ドットが徐々に増えるのは何によってなのか? より詳しく見てみようじゃないか。

RubyGems.org の API


 何が起こっているかのヒントを得るために、bundle update--verbose オプションを付けてもう一度試してみよう。

$ bundle update --verbose
Fetching gem metadata from http://rubygems.org/
Query List: ["rails", "sqlite3", "json", "sass-rails", "coffee-rails", "uglifier", "jquery-rails"]
Query Gemcutter Dependency Endpoint API: rails sqlite3 json sass-rails coffee-rails uglifier jquery-rails
Fetching from: http://rubygems.org/api/v1/dependencies?gems=rails,sqlite3,json,sass-rails,coffee-rails,uglifier,jquery-rails
HTTP Success
Query List: ["bundler", "railties", "actionmailer", "activeresource", "activerecord", "actionpack", "activesupport", "rake", "actionwebservice", "ffi", "sprockets", "tilt", "sass", "coffee-script", "multi_json", "execjs", "therubyracer", "thor"]
Query Gemcutter Dependency Endpoint API: bundler railties actionmailer activeresource activerecord actionpack activesupport rake actionwebservice ffi sprockets tilt sass coffee-script multi_json execjs therubyracer thor
Fetching from: http://rubygems.org/api/v1/dependencies?gems=bundler,railties,actionmailer,activeresource,activerecord,actionpack,activesupport,rake,actionwebservice,ffi,sprockets,tilt,sass,coffee-script,multi_json,execjs,therubyracer,thor
HTTP Success

etc...

おっと、これで Bundler 1.1 がどう動いているかわかりそうだ。短いリストで指定された gem の依存関係情報を得るために、RubyGems.org が提供する HTTP の API を呼んでいる。source index 全体をダウンロードはしないわけだ。どれかの URL をブラウザに貼り付けてみれば、指定された gem の依存関係を表す、JSON に似た何やら複雑なフォーマットのデータが返されるのがわかるだろう。これは実は JSON ではなく、Ruby の Marshal ライブラリが生成した文字列だ。

RubyGems.org の API が動作する感覚をつかむために、HTTParty と Marshal を使って、指定した gem の依存関係を表示する簡単なスクリプトを書いてみた。

require 'rubygems'
require 'httparty'

 class RubyGemsApi
  include HTTParty
  base_uri 'rubygems.org'

  def self.info_for(gems)
    res = get('/api/v1/dependencies', :query => { :gems => gems })
    Marshal.load(res)
  end

  def self.display_info_for(gems)
    info_for(gems).each do |info|
      puts "#{info[:name]} version #{info[:number]} dependencies: #{info[:dependencies].inspect}"
    end
  end
end

RubyGemsApi.display_info_for(ARGV[0])

例として私の大好きな gem である "uglifier" に対してこれを走らせてみる。

$ ruby parse_rubygems_api.rb uglifier
uglifier version 1.0.3 dependencies: [["multi_json", ">= 1.0.2"], ["execjs", ">= 0.3.0"]]
uglifier version 1.0.2 dependencies: [["multi_json", ">= 1.0.2"], ["execjs", ">= 0.3.0"]]
uglifier version 1.0.1 dependencies: [["multi_json", ">= 1.0.2"], ["execjs", ">= 0.3.0"]]
uglifier version 1.0.0 dependencies: [["multi_json", ">= 1.0.2"], ["execjs", ">= 0.3.0"]]
uglifier version 0.5.4 dependencies: [["multi_json", ">= 1.0.2"], ["execjs", ">= 0.3.0"]]
uglifier version 0.5.3 dependencies: [["multi_json", ">= 1.0.2"], ["execjs", ">= 0.3.0"]]
uglifier version 0.5.2 dependencies: [["multi_json", ">= 0"], ["execjs", ">= 0.3.0"]]
uglifier version 0.5.1 dependencies: [["json", ">= 0"], ["execjs", ">= 0"]]
uglifier version 0.5.0 dependencies: [["json", ">= 0"], ["execjs", "~> 0.1.0"]]
uglifier version 0.4.0 dependencies: [["therubyracer", "~> 0.8.0"]]
uglifier version 0.3.0 dependencies: [["therubyracer", ">= 0.8.0"]]
uglifier version 0.2.0 dependencies: []
uglifier version 0.1.1 dependencies: []
uglifier version 0.1.0 dependencies: []

レスポンスには最新版だけじゃなく、gem の各バージョン毎の依存関係が含まれているのに気づいて欲しい。これが必要なのは、Bundler の依存解決アルゴリズムが gem の古いバージョンを使う可能性があるからだ(バンドルしている他の gem の内容により、動作が違ってくる)。

gem 名をコンマで区切ったリストを指定することもでき、例えば上の方で --verbose を指定した時のリストを使えばこうだ。

$ ruby parse_rubygems_api.rb rails,sqlite3,json,sass-rails,coffee-rails,uglifier,jquery-rails

この HTTP API の呼び出しは非常に速い――1秒未満だ――うえに、Bundler が欲する情報を全て提供し、それ以上のことは何もしない。

依存 gem をダウンロードする
Bundler 1.1 のアルゴリズムを視覚化する


 さっきと同じで、簡単な Gemfile を使ってみよう(この Gemfile は3週間前の How does Bundler bundle で使ったものだ)。

source 'http://rubygems.org'
gem 'uglifier’

そして、この Gemfile の内容を表現するシンプルな図を描こう。つまり、一つの gem だけだ。



次に、この Gemfile のあるディレクトリで bundle update --verbose を実行し、出力を見てみる。ただし今度は、出力テキストの合間に図を挟み込み、Bundler の依存 gem 取得アルゴリズムが実際に何をしているかを示そう。

$ bundle update --verbose
Fetching gem metadata from http://rubygems.org/



Query List: ["uglifier"]
Query Gemcutter Dependency Endpoint API: uglifier
Fetching from: http://rubygems.org/api/v1/dependencies?gems=uglifier
HTTP Success

ここで最初に起こっているのは、Gemfile の中に唯一入っている gem である "uglifier" の依存関係を決定するための HTTP リクエストだ。この HTTP リクエストの結果は、上にある parse_rubygems_api.rb スクリプトの出力でわかる。uglifier のそれぞれ違うバージョンの中に出てくる、4つの gem だ。



最新の uglifier は json、execjs、multi_json に依存していて、ある古いバージョンでは "therubyracer" gem に依存している。

その次に Bundler がする事は、2回目の HTTP リクエストを RubyGems.org へ送り、これら4つの gem の依存関係を要求することだ。



Query List: ["multi_json", "execjs", "json", "therubyracer"]
Query Gemcutter Dependency Endpoint API: multi_json execjs json therubyracer
Fetching from: http://rubygems.org/api/v1/dependencies?gems=multi_json,execjs,json,therubyracer
HTTP Success

興味があるなら、parse_rubygems_api.rb を実行してこのリクエストの結果を見ることも出来る。以下は RubyGems.org が返す結果の図だ。



今度は "execjs" gem がいくつかのバージョンの "multi_json" へ依存している事と、"therubyracer" が "libv8" に依存していることがわかる。そして Bundler は RubyGems.org への3回目の HTTP リクエスト送信へと続き、libv8 の依存関係を得る。このリクエストに multi_json は含まれない。なぜなら、既にその情報は持っているからだ。



Query List: ["libv8"]
Query Gemcutter Dependency Endpoint API: libv8
Fetching from: http://rubygems.org/api/v1/dependencies?gems=libv8
HTTP Success

今度は RubyGems.org が空のセットを返す。libv8 は一つも依存する gem を持たないということだ。



Query List: []
Unmet Dependencies:

これで Bundler は必要な全依存情報を手に入れたので依存解決アルゴリズムの実行へと進み、そして最後に、たった今新しくなったバンドルに含まれている gem を列挙する。

Using multi_json (1.0.3) from /Users/pat/.rvm/gems/ruby-1.8.7-p352/specifications/multi_json-1.0.3.gemspec
Using execjs (1.2.9) from /Users/pat/.rvm/gems/ruby-1.8.7-p352/specifications/execjs-1.2.9.gemspec
Using uglifier (1.0.3) from /Users/pat/.rvm/gems/ruby-1.8.7-p352/specifications/uglifier-1.0.3.gemspec
Using bundler (1.1.rc) from /Users/pat/.rvm/gems/ruby-1.8.7-p352/specifications/bundler-1.1.rc.gemspec


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

フォロワー