Bundlerの原理

もし我々がお勧めするワークフローを知りたいだけで、 原理について特に気にならないのであれば、概要までスキップしていただいても構いません。

Bundlerの目的と原理

始めに、あなたのアプリケーションのルート・ディレクトリのGemfileと呼ばれるファイルに依存性を宣言します。 これは次のようになります。

source 'https://rubygems.org'

gem 'rails', '4.1.0.rc2'
gem 'rack-cache'
gem 'nokogiri', '~> 1.6.1'

Gemfileは幾つかの事を主張します。 始めに、BundlerはGemfile内に宣言されたhttp://rubygems.orgからGemを探すべきと主張しています。 もし、プライベートなGemサーバーからGemを取得する必要がある場合は、 このデフォルトのGem供給元をそれ用に上書きすることが可能です。

次に、幾つかの依存関係を宣言しています。

  • railsのバージョンは4.1.0.rc2であること。
  • rack-cacheのバージョンは任意であること。
  • nokogiriのバージョンは>= 1.6.1であるが、< 1.7.0であること。

依存性の宣言をしたら、それらを取得するようにBundlerに指示を出します。

$ bundle install # 省略して bundle とすることも可能です。

Bundlerはrubygems.orgに接続し(他に宣言されていれば、それにも)、 あなたが指定した条件を満たすのに必要な全てのGemのリストを見つけ出します。 あなたのGemfile内の全てのgemも、それぞれが自身の依存関係を持つため(そして、更にそれらの幾つがまた依存関係を持ち)、 上記のGemfileのbundle install実行は、かなりの数のGemをインストールします。

$ bundle install
Fetching gem metadata from https://rubygems.org/.........
Fetching additional metadata from https://rubygems.org/..
Resolving dependencies...
Using rake 10.3.1
Using json 1.8.1
Installing minitest 5.3.3
Installing i18n 0.6.9
Installing thread_safe 0.3.3
Installing builder 3.2.2
Installing rack 1.5.2
Installing erubis 2.7.0
Installing mime-types 1.25.1
Using bundler 1.6.2
Installing polyglot 0.3.4
Installing arel 5.0.1.20140414130214
Installing hike 1.2.3
Installing mini_portile 0.5.3
Installing multi_json 1.9.3
Installing thor 0.19.1
Installing tilt 1.4.1
Installing tzinfo 1.1.0
Installing rack-test 0.6.2
Installing rack-cache 1.2
Installing treetop 1.4.15
Installing sprockets 2.12.1
Installing activesupport 4.1.0.rc2
Installing mail 2.5.4
Installing actionview 4.1.0.rc2
Installing activemodel 4.1.0.rc2
Installing actionpack 4.1.0.rc2
Installing activerecord 4.1.0.rc2
Installing actionmailer 4.1.0.rc2
Installing sprockets-rails 2.0.1
Installing railties 4.1.0.rc2
Installing rails 4.1.0.rc2
Installing nokogiri 1.6.1
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.

必要なGemのうちの幾つかが既にインストールされていれば、Bundlerはそれを使用します。 システムへ必要なGemをインストールした後は、 Bundlerはインストールした全てのGemとバージョンをGemfile.lockに書き込みます。

Bundlerを使用するためのアプリケーションの設定

Bundlerは、Gemfile内の全てのGem(と、全ての依存関係)を、Rubyが見つけることが出来るのかを確かめます。 もし、あなたのアプリケーションがRails3+であれば、デフォルトで既にBundlerを実行するのに必要なコードが含まれています。 もしRailsのバージョンが2.3であれば、 Setting up Bundler in Rails 2.3を参照してください。

その他のアプリケーションのフレームワーク(Sinatraのような)では、 必要とするGemを試す前にBundlerの設定が必要になります。 アプリケーションが読み込む最初のファイルの先頭に(Sinatraであれば、require 'sinatra'を呼び出すファイル)、 下記のコードを埋め込んでください。

require 'rubygems'
require 'bundler/setup'

これは自動的にあなたのGemfileを発見し、 Gefmifle内の全てのGemがRubyで使用できるようにしてくれます。 (技術的な観点から言えば、"読み込みパス"にそれらのGemが設定された事になります) require 'rubygems'に対して、拡張機能が加えられたと考えても良いかもしれません。(翻訳に自信なし)

これであなたのコードがRubyで利用可能となり、必要なGemをrequireすることが出来ます。 例えば、require 'sinatra'とする事が可能です。 もし多くの依存性を持つ場合、"Gemfile内の全てのGemをrequire"したいと考えるかもしれません。 これをするために、require 'bundler/setup'のすぐ後に下記のコードを配置します。

Bundler.require(:default)

前述したGefileを例とすると、この行は実際には次のように評価されます。

require 'rails'
require 'rack-cache'
require 'nokogiri'

もしかしたら、あなたはrack-cacheのrequireの正しい指定方法が、require 'rack-cache'では無く、 require 'rack/cache'であることに気づいていいるかもしれません。 Bundlerにrequire 'rack/cache'を使用することを伝えるために、Gemfileを更新してください。

source 'https://rubygems.org'

gem 'rails', '4.1.0.rc2'
gem 'rack-cache', require: 'rack/cache'
gem 'nokogiri', '~> 1.6.1'

このような小さなGemfileであれば、Bundler.requireの説明をスキップして、 手動でgemをrequireするようにアドバイスしたいところです。 (特に、Gemfile内の:require指定子(ディレクティブ)が必要なケースもあるため) 規模の大きいGemfileでは、Bundler.requireを使用することで、 必要なGemの詰め込みの繰り返しをスキップすることが可能になります。

バージョン管理のコードを確認

アプリケーション開発後は、GemfileGemfile.lockのスナップショット(その瞬間のファイル)を一緒に、 アプリケーションにチェックインします。 これであなたのリポジトリはアプリケーションが動作していたことを最後に確認した時に使用していた、 全てのGemの実際のバージョンの記録を持つことになります。 ここで例に出したGemfileに記述されているGem(それぞれバージョン固定の制限が異なる)は3つだけですが、 実際にはかなりの数のGemにアプリケーションが依存しており、 暗黙的にrequireされる依存性のあるGemの全てについても考慮しておく必要があることを忘れないで下さい。

重要:Gemfile.lockは、あなたのアプリケーションが全て動作していることを、あなたが最後に確認した、 あなた自身のコードとサードパーティ製のコードを1つのパッケージにしてくれます。 Gemfileに書かれる依存関係のあるサードパーティ製のコードのバージョン指定は、 同一である事を保証するものではありません。 何故なら、通常それには依存するGemのバージョンが範囲指定で宣言されているからです。

再び同じマシン上でbundle installを実行すると、 Bundlerは必要とされる全ての依存性が既に存在している事を確認し、インストール処理をスキップします。

.bundleディレクトリ、及びその中のファイルをチェックインしないで下さい。 これらのファイルは個別のマシン毎に指定され、 bundle installの実行する際のインストールオプションを固定するのに使用されます。

もしbundle packが実行されると、 あなたのBundleに必要とされるGem(ただし、gitのGemでは無い)はvendor/cacheにダウロードされます。 もしソースの制御下で、そのフォルダに必要とする全てのGemが提供され、確認することが出来れば、 Bundlerはインターネット(またはRubygemsサーバー)への接続無しで実行できるようになります。 これは任意の手順であり、リポジトリ管理のソースコードの容量が増える傾向があり、 推奨されるものではありません。

他の開発者とアプリケーションを共有する

あなたの協同開発者(または別のマシン)が、あなたのコードをチェックアウトする際に、 あなたのマシン上で最後に開発を行った(Gemfile.lock内の)サードパーティ製の正確なバージョンが取得されます。 **彼ら**がbundle installを実行すると、 BundlerはGefile.lockを見つけ、依存性解決の手順はスキップします。 代わりに、あなたの元のマシン上で使用されていたものと同じGemを全てインストールします。

言い換えれば、あなたはインストールすべき依存性のバージョンについて考える必要は無いということです。 先程、例として使用していたrack-cacheがrack >= 0.4に依存すると宣言されていたとしても、 我々はそれがrack 1.5.2で動作することが保証されていることを把握しています。 もしRack開発チームがrack 1.5.3をリリースしていたとしても、 Bundlerは常に1.5.2をインストールし、動作するGemの正確なバージョンを把握することが出来ます。 これは、常に全てのマシンが正確に同じバージョンのサードパーティ製のコードを実行するため、 アプリケーション開発者が抱えるメンテンナンスによる負荷を軽減することが出来ます。

依存性の更新

もちろん、ある時点でアプリケーションが依存する特定のGemの依存性のバージョンを更新したいと考えるようになるかもしれません。 例えば、あなたはRailsを4.1.0 finalに更新したくなるかもしれません。 重要なことですが、あなたが1つの依存関係を更新しているからといって、 あなたが全ての依存関係を再解決し、全ての最新バージョンを使用したいという意味では無いはずです。 ここでの例は3つの依存性しかありませんが、それでも全てを更新しようとすると複雑な状態になる可能性があります。

実例で説明すると、rails 4.1.0.rc2のGemは、 rack ~> 1.5.2(>= 1.5.2 且つ < 1.6.0 という意味)に依存した、 actionpack 4.1.0.rc2のGemに依存している、といった具合です。 rack-cacheのGemはrack >= 0.4に依存しています。 仮にrails 4.1.0 finalのGemもrack ~> 1.5.2に依存しており、 rails 4.1.0のリリース以降、Rackチームがrack 1.5.3をリリースしたとしましょう。

もし単純にRailsを更新するためにGemを全て更新しようとすると、 rails 4.1.0rack-cacheの両方の依存関係を満たすrack 1.5.3を取得します。 ただし、我々はrack 1.5.3と互換性がないかもしれないrack-cacheの更新を明確に求めることをしていません。(どんな理由であれ) そして、rack 1.5.2からrack 1.5.3への更新はおそらく何も問題は起こらないでしょうが、 同様の事をして大事になることもあるかもしれません。 ([1]参照)

この問題を避けるために、あなたがGemを更新する際に、Bundlerは他のGemがそのGemに未だ依存しているのであれば、 そのGemの依存性を更新しません。 この例であれば、rack-cacheはまだrackに依存しているため、 BundlerはrackのGemを更新しません。 これはRailsの更新の際に、不注意でrack-cacheを壊してしまわないことを保証します。 rails 4.1.0actionpack 4.1.0依存関係は、 依然としてrack 1.5.2との互換性を持つため、 Bundlerはそれを単独で残し、rack 1.5.3との非互換性と直面しても、 rack-cacheは動作し続けます。

元々はrails 4.1.0.rc2への依存を宣言していたため、 もしrails 4.1.0へ更新したいのであれば、 Gemfilegem 'rails', '4.1.0'に更新して、それを実行します。

$ bundle install

前述したとおり、bundle installコマンドは常に保守的な更新を行い、 あなたがGemfile内で明確に変更していないGem(またはその依存性)の更新を拒絶します。 これは、もしあなたがGemfile内でrack-cacheを修正しなければ、 Bundlerは、それと、その依存性(rack)を単一のもの、修正できない集まり、として取り扱う事を意味します。 もしrails 4.1.0rack-cacheと互換性が無い場合、 Bundlerは依存性のスナップショット(Gemfile.lock)と更新されたGemfileとの間に、 衝突があることを報告します。

もしあなたがGemfileを更新し、システムが既に必要とされる依存性を全て持つ場合、 アプリケーションの起動時に、Bundlerはあなたに意識させること無くGemfile.lockを更新します。 例えば、もしGemfilemysqlを追加し、既にそれがシステム内にインストールされていた場合、 bundle installの実行無しにアプリケーションを起動する事が可能で、 BundlerはGemfile.lockスナップショットに、前回正常起動時("last known good")の構成を引き継ぎます。

最小の依存性(データベースドライバ、wirbleruby-debug)でのGemの追加または更新時は、 手動で行うことが可能です。 もし、大規模な依存性(例えばRails)または多くのGemへの依存(rack)を伴うGemの更新をする場合、それはおそらく失敗するでしょう。 もし意識させないで行われるGemfile.lockの更新が失敗すると、アプリケーションの起動は失敗し、 Bundlerはbundle installの実行を指示するエラーを出力します。

Gemfileを変更せずに、Gemを更新

時に、Gemfileの修正無しに依存性の更新行いたいというケースがあるでしょう。 例えば、あなたはrack-cacheを最新バージョンに更新したいと考えるかもしれません。 何故なら、Gemfile内にrack-cacheのバージョン指定を宣言していないため、 rack-cacheの最新バージョンを定期的に取得したいと考えているかもしれません。 これを行うには、bundle updateコマンドを使用します。

$ bundle update rack-cache

このコマンドは、rack-cacheを更新し、Gemfileによる、その最新バージョンへの依存関係を許可します。 (このケースでは、利用可能な最新のバージョン) これは、その他の依存関係にあるものの変更を行いません。

ただし、もし必要となれば他のGemへの依存関係の更新が行われます。 例えば、もしrack-cacheの最新バージョンが、rack >= 1.5.2への依存性を指定していた場合、 Bundlerはあなたがrackの更新を指示していなくても、rackを1.5.2へ更新します。 もしBundlerがその他のGemの依存関係の更新を必要とする場合は、 それは更新が完了した後にあなたに知らされます。

もし可能な限り最新のバージョンでGemfile内の各Gemを更新したいのであれば、 次のように実行します。

$ bundle update

これはGemfile.lockを無視して、ゼロから依存性を解決します。 もしこれを行うと、git reset --hardと背面のテストスイートが維持されます。 ゼロから全ての依存性を解決することは、驚く結果になる可能性があり、 特に依存性のある多数のサードパーティ製のパッケージを、最後の全ての更新から最新のバージョンに更新する場合は、 そういった事が起こりやすいでしょう。

概要

シンプルなBundlerのワークフロー

  • あなたがRailsアプリケーションを作成する際には、最初からGemfileは存在しているはずです。 その他のアプリケーション(Sinatraのような)では、下記を実行してください。

    $ bundle init
    

    bundle initコマンドは、編集されるための単純なGemfileを作成します。

  • 次にあなたのアプリケーションに依存するGemを追加します。 もしGemが特定のバージョンであることに注意する必要がある場合は、 適切なバージョン制限を含めることでそれを確定して下さい。

    source 'https://rubygems.org'
    
    gem 'sinatra', '~> 1.3.6'
    gem 'rack-cache'
    gem 'rack-bug'
    
  • もしシステムにまだGemがインストールされていなければ、下記を実行してください。

    $ bundle install
    
  • Gemのバージョンを更新するには、まずGemfileを修正します。

    source 'https://rubygems.org'
    
    gem 'sinatra', '~> 1.4.5'
    gem 'rack-cache'
    gem 'rack-bug'
    

    そして、下記を実行します。

    $ bundle install
    
  • もしbundle installGemfileGemfile.lockの間に衝突があると報告してきたら、 下記を実行してください。

    $ bundle update sinatra
    
  • これは単にSinatraのGemを更新するだけでなく、それに依存する全てのもの更新します。

  • Gemfile内の全てのGemを可能な限り最新のものに更新するには、下記のように実行します。

    $ bundle update
    
  • Gemfile.lockに変更があった場合は、それを必ずバージョン管理にチェックインしてください。 こうすることでアプリケーション実行が成功した際に使用された、 サードパーティ製のコード全ての実際のバージョンの履歴を保持することが出来ます。

  • コードをステージング、またはプロダクション環境のサーバーにデプロイする際には、 まずテストを実行(またはローカルの開発サーバーを起動)し、 Gemfile.lockがバージョン管理へチェックインされている事を確認してください。 リモートサーバー上で、次のように実行します。

    $ bundle install --deployment
    

注釈

[1] 例えば、もしrails 4.1.0rack 2.0に依存している場合、 依存性が>= 0.4として宣言されているrack-cacheであれば、その必要性をまだ満たしています。 もちろん、あなたはバージョンの制限が無いrack-cacheに依存する事を馬鹿馬鹿しいと感じるかもしれませんが、 こういったシチュエーションは実際に存在し、プロジェクトは何のバージョンに依存するかを決定する際に、 どうにかしてそれらを見つけてくることがよくあります。(翻訳に自信なし) あまりに依存性の制約を強くし過ぎる(rack =1.5.1)と、 他の互換性のあるプロジェクトで、そのプロジェクトを使用することが困難になります。 あまりに依存性の制約を弱くしすぎる(rack >= 1.0)と、 Rackの新しいリリースがあった際にコードが不具合を起こすかもしれません。 rack ~> 1.5.2のような依存性の使用と、 SemVerによるバージョニングコードに沿った方法は、 これらの問題をほとんど解決しますが、それは普遍的なコンプライアンスに従っている事を想定としています。 Rubygemsには100,000を超えるパッケージがああるため、 実際にはこの想定通りにはいかないことがあるでしょう。

 Back to top

© 2010 - 2017 STUDIO KINGDOM