Step by Step Ruby on Rails

Ruby on Railsで実際にWebサイトを構築する手順をまとめています。

Bundlerの概要、使用方法

Bundlerの概要、使用方法をまとめました。

目次
(1)Bundlerの目的、概要
(2)Gemfileの設定
(3)各コマンドの説明
(4)Gemアップデートの例
(5)デプロイ
(6)GitリポジトリからGemを導入(リモート)

(1)Bundlerの目的、概要

・Bundlerは、開発、ステージング、プロダクション環境に渡ってコードを簡単に共有できるように設計されている。

・共有だけならGitHubでも事足りるが、Bundlerは依存性の管理も簡単に行える。

・依存性の設定はGemfileファイルに記述する。

1)概要

source 'http://rubygems.org'

gem 'rails', '4.1.5'
gem 'sqlite3'
gem 'sass-rails', '~> 4.0.3'
  :
(設定の意味)
・Bundlerは、sourceで設定されているURLにアクセスしてgemを探す。
・gemで始まる行で依存性を定義する。

●bundle installを実行

・依存性をチェックしながらインストールする。

・Bundlerは、rubygems.orgにアクセスし、定義されているgemとそのgem自身が依存しているgemをインストールする。

・インストールが完了するとすべてのgemとそのバージョンをGemfile.lockファイルに記述する。

2)バージョンの管理の注意点

・Gemfile.lockファイルによって、開発アプリのコードとサードパーティのコードの両方をチェックイン時点の状態でパッケージとしてまとめる事ができる。

・Gemfileによって管理しているgemのバージョンはある範囲をもってバージョンを指定しているので、いつも完全に同じバージョンのgemで構成される事を保障しない。

・次の時点でbundle installを行ったとしても、必要な依存性が満たされている場合は、インストールプロセスはスキップされる。

・"bundle package"コマンドを実行すると必要なgemがvender/cacheディレクトリにダウンロードされる。
 これによってBundlerはインターネットに接続しなくても実行できる。
 ただし、ソースコントロールリポジトリのサイズが増加するため非推奨。

3)開発アプリの共有

・別の開発者または別のマシンから開発アプリをチェックアウトすると開発アプリを最後に更新した時点と同じバージョン構成(Gemfile.lock内に記録されているバージョン)にする事ができる。

・"bundle install"を実行すると、bundlerはGemfile.lockファイルを見つけ、依存性解決のステップを省略する。
 その代わりに、元の開発マシンで使用していたので同じGemのリストをインストールする。

・利用者は、依存性によってどのバージョンのGemが必要が推測する必要がなくなる。

・サードパーティのアプリがバージョンアップされたとしても、最終更新時のバージョンが維持される。


(2)Gemfileの設定

1)Gemfileでのバージョン指定方法

下記リリースを持つgemを例に考えます。

V2.1.0 ベースライン
V2.2.0 新機能追加
V2.2.1 いくつかのバグを除去
V2.2.2 Streamlined your code
V2.3.0 新機能追加、後方互換性あり
V3.0.0 インターフェース変更。V2.x対応コードは動作不可の可能性あり

①楽観的なバージョン指定

開発アプリをV2.2.0で動作確認。V.2.1.0では必要な機能が使用できない可能性がある場合の指定例

gem 'library', '>= 2.2.0'

・V2.2.0以上のすべてのバージョンを対象範囲にしています。

・インターフェースが変更されたV3.0.0も対象範囲に含まれます。
"楽観的"なバージョン指定といえます。

②悲観的なバージョン指定

インタフェースが変更されるV3.0.0以降では、不具合が起きる可能性があるため、V3.0.0以降にはバージョンアップさせたくない場合。

gem 'library', '>= 2.2.0', '< 3.0'

上記指定は、下記のように記述する事ができます。

gem 'library', '~> 2.2'

※'~> 2.2'は、下限は'=2.2.0'で、上限は末尾の数字が桁上がりする未満'< 3.0'と解釈されます。

'~> 2.2.0'と指定した場合は、末尾の数字が桁が上がりした値未満なので'< 2.3.0'と解釈されます。

2)グループ設定

環境に応じて有効にしたいGemがある場合は、グループにまとめて設定することが出来ます。

●設定例

例えば、開発環境ではSQLiteを使用し、プロダクション環境ではPostgresを使用するといった場合は、下記のように設定します。

(Gemfileの設定)
group :development do
  gem 'sqlite3'
end

group :production do
  gem 'pg'
end

(開発環境でインストールする場合)
$ bundle install --without production

●Bundler.requireでの指定方法について

どのグループか自動的にrequireされるかBundler.requireで指定する事ができます。

①特定のグループ内のGemをインクルードする場合

例)
Bundler.require(:default, :development)

注)特にグループ名を明示していないgemは、defaultグループ

②defaultグループに現在のRails環境と同じ名前のグループ内のGemを加えてインクルードする場合

Bundler.require(:default, Rails.env)


(3)各コマンドの説明

1)bundle install

・--systemオプションがデフォルトで指定されていて、$BUNDLE_PATH または $GEM_HOMEで指定されたシステムロケーションにインストールされる。

・gemをインストールする際、バンドラーは、vendor/cacheディレクトリとシステム用のgemをチェックする。
 もしgemがキャッシュになくインストールされていなければ、バンドラーはGemfileで指定されたソースからgemのインストールを試みる。

●システムロケーション(デフォルト)以外の場所にインストール

例)vendor/bundleディレクトリにインストール

bundle install --path vendor/bundle

●特定のグループをインストール対象から外す

例)developmentとtestグループを外してインストール

bundle install --without development test

●プロダクションサーバーにインストール

bundle install --deployment

2)bundle updateの概要

書式
bundle update [GEM] [--local] [--source=SOURCE]

--localオプション
 リモートからgemを入手せず、キャッシュされたgemを使用する。

--sourceオプション
 特別に指定したソースをアップデートする。

・指定したgem(指定しない場合はすべてのgem)をアップデートし、Gemfile.lockに記録されているすでにインストール済みのGemの状態については無視する。

●すべてのGemをアップデート

・bundle update

インストール済みのGemは無視し、すべての依存性をチェックしなおして、利用できるgemの最新の状態にアップデートする。

※bundle installとの違い

一番最初に実行する際は、Gemfile内のgemとそれに依存するgemをインストールする。そして実際にインストールしたバージョンは、Gemfile.lockに記録する。

次にbundle installを実行する際は、バンドラーは依存性解決を省略し、前回インストール時と同じgemをインストールする。

Gemfile.lockをバージョンコントロールにチェックインし、他のマシンに適用し、bundle installを実行すると同じgemをインストール出来る。

●特定のgemをアップデートする

・Gemfile内の一つのgemをアップデートし、それ以外のgemはGemfile.lockに記録されたバージョンのままにしたい場合は、下記のようにgemを指定して実行する。

bundle update パッケージ名

この場合は、指定したパッケージと依存するパッケージのみがアップデートされ、それ以外はそのまま維持される。

●複数のGemで依存性が重複している場合

次の例を基に考える
 thinは、rack >= 1.0に依存。
 rack-perftools-profilerは、rack ~> 1.0に依存。

・bundle update thinを実行した場合

バンドラーは、thinと依存しているrackをアップデートする。rack-perftools-profilerもrackに依存しているが、それについては考慮されない。

・上記で、rackをアップデートしたくない場合は、Gemfileを手動でthinのバージョンを記述し、bundle installを実行する。

●推奨のワークフロー

①最初にGemfile作成、bundle install実行

②Gemfile.lockをバージョンコントロールに追加

$ git add Gemfile.lock

③インストール

・他の開発マシンにリポジトリをチェックアウトし、インストールする場合

$ bundle install

・デプロイメントマシンにインストールする場合

$ bundle install --deployment

・Gemfileに新しいバージョンや更新バージョンを記載した場合

$ bundle install

④更新後のGemfile.lockをバージョンコントロールに追加

$ git add Gemfile.lock

⑤bundle installでコンフリクトが発生し、Gemfile内で変更したGemを手動でアップデートする場合

$ bundle update パッケージ名

⑥すべてのgemをGemfile内に記述されているgemにマッチする利用可能な最新のバージョンへアップデートする場合

$ bundle update


3)bundle package

書式
bundle package [--no-prune] [--all]

・パッケージコマンドは、バンドル内の使用しているgemファイルをvender/cacheディレクトリにコピーしてキャッシュする。

・その後、bundle installを実行すると、バンドラーはrubygems.org上のgemよりキャッシュ上のgemを優先して使用する。

・このvender/cacheディレクトリをバージョンコントロールのリポジトリにチェックインすると、別のマシンにチェックアウトしてそのままの構成でインストールすることができる。

・デフォルトでは、bundle package実行後bundle installを実行するとバンドラーはrubygems.org に接続し、プラットフォーム固有のgemがvendor/cacheディレクトリ内に存在するかどうかチェックする。

この振る舞いを避ける場合は、
bundle install --local
を実行する。

・キャッシュする際、古いgemを削除したくない場合は、
bundle package --no-prune
を実行する。

・:gitや:pathで指定したgemも含めてキャッシュしたい場合は、
bundle package --all
を実行する。


(4)Gemアップデートの例

1)依存性がある場合のアップデートの例

下記例を基にします。

(希望のアップデート)
・rails3.0.0最終版にアップデートしたい。
・目的としているgemのアップデートのみ希望し、それ以外のすべての依存性を解決し、最新版にアップデートする事は望まない。

(各gemの依存性)
・rails3.0.0.rcは、actionpack3.0.0.rcに依存
・actionpack3.0.0.rcは、rack~>1.2.1( >= 1.2.1かつ< 1.3.0を意味する)に依存。
・rack-cacheは、rack >= 0.4に依存。
・rails3.0.0最終版は、rack~>1.2.1に依存。
・rails3.0.0リリース後、Rackチームがrack 1.2.2をリリース

●バンドラーによるアップデートの挙動

・rackの最新版であるrack1.2.2にアップデートしてもrails3.0.0とrack-cacheの要件を満たす。

しかし、rack-chaceのアップデートは特に求めていない。rack1.2.2と互換性がないかもしれないから。

・上記のような問題を避けるため、バンドラーはgemをアップデートする際、他のgemが依存している場合、そのgemの依存性をアップデートしない。

・上の例では、rack-cacheはrackと依存しているので、バンドラーは、rackをアップデートしない。

この対応によって、railsをアップデート時rack-cacheの互換性不一致による異常を防ぐ事ができる。

rails3.0.0とactionpack3.0.0は、rack1.2.1と互換性があるので、バンドラーは、rackをアップデートせずそのままにする。

rack-cacheは、rack1.2.2にアップデートする事による互換性の不一致にさらされる心配がなくなる。

●アップデート方法

・rails3.0.0.rcからrails3.0.0にアップデートしたい場合

Gemfileに  gem 'rails', '3.0.0'
と記述し、bundle installを実行する。

●bundle installコマンドについて

・"bundle install"コマンドはいつも保守的なアップデートを行うので、Gemfileに明示的に修正されていないものについてはアップデートを行わない。

・これは、もしGemfile内のrack-cacheの記述を変更していない場合、バンドラーは、rack-cacheとそれに依存しているrackを独立して変更しないユニットととして扱う。

・もしrails3.0.0がrack-cacheと互換性が無くなる場合、バンドラーは、Gemfile.lockと更新したGemfileの間で不一致だとレポートする。

・Gemfileを更新し、システム内にすでに必要な依存するgemが導入されている場合、バンドラーは透過的にGemfile.lockを更新し、アプリケーションを起動する。

・例えば、mysqlをGemfileに追加した場合、すでにMySQLがシステム内に導入されている場合は、"bundle install"を実行しなくてもアプリを起動する事ができる。そして、バンドラーは、Gemfile.lockに最新の正常状態のスナップショットを維持する。

・もし透過的なアップデートが失敗した場合は、アプリケーションはブートに失敗し、バンドラーは、"bundle install"を実行するように指示するエラーコメントを出力する。

2)Gemfileを変更せずにGemをアップデート

最新のrack-cacheにアップデートしたい場合を例に考えます。
Gemfileに明示的にrack-cacheのバージョンを定義していなかったので、最新バージョンのrack-cache取得したい場合です。

このようにしたい場合、下記のようにコマンドを実行します。

$ bundle update rack-cache

・上記コマンドは、rack-cacheをアップデートし、最新バージョンへの依存性がGemfileで満たされていれば、その依存性もアップデートされます。それ以外の依存性については、変更されません。

・しかし、必要ならばその他のGemの依存性もアップデートされるかもしれません。

例えば、最新のrack-cacheがrack >= 1.2.2の依存性がある場合、たとえrackをアップデートするように指示されていなくてもバンドラーはrackを1.2.2にアップデートします。

バンドラーが他のGemが依存しているGemのアップデートを必要とした場合は、アップデート後に通知される。

・もし、GemfileのすべてのGemを利用可能な最新にアップデートしたい場合は、次のコマンドを実行する。

$ bundle update

・上記コマンドは、Gemfile.lockを無視し、最初から依存性を解決する。

・最初から依存性を解決する場合、前回のアップデート以降、依存するたくさんのサードパーティソフトの最新版がリリースされていると、驚くような結果になることがある。


(5)デプロイ

1)デプロイの概要

・"bundle install"を実行するとき、バンドラーはデフォルトでは、Gemをシステムリポジトリー($GEM_HOME)にインストールする。

これは、"gem list"で確認できる。

この事によって、たくさんのアプリを開発している場合でも、各アプリケーション毎に重複してGemをダウンロードする必要がなく、共有してインストールできる。

・デプロイする場合、デプロイするUnixユーザは、システム用のディレクトリにGemをインストールするアクセス権限がないかもしれないので注意する。

例)PassengerがRubyサブプロセスを"nobody"という制限されたユーザで実行する場合。

・上記に対応するため、バンドラーは、--deploymentオプションを用意している。

2)--deploymentオプション

・システムロケーションにインストールせずに、各アプリケーション内のvendor/bundleディレクトリにインストールする。

・アプリケーション内で(Bundler.setupとBundler.requireと共に)実行するとバンドラーは透過的にこのロケーションを覚えている。

・バンドラーは、たとえすでに存在していてもシステムにインストール済みのGemを使用しない。

・もし、"bundle pack"を実行した場合は、"vendor/cache"ディレクトリにチェックインし、いっさい"git gems"せず、バンドラーはバンドルインストール中にインターネットに接続しない。

・バンドラーは、Gemfile.lockスナップショットを必要とし、無い場合は失敗する。

・バンドラーは、Gemfileの記述が古くなっていたらGemfile.lockを透過的にアップデートしない。

・もし、Capistranoを使う場合は、vendor/bundleディレクトリをshared/vendor_bundleディレクトリにシンボリックリンクし、バンドラーがデプロイ間でインストールしたGemを共有するようにすると、それぞれのアプリでGemを分離するメリットを享受できる。

・バンドラーがvendor/bundleディレクトリをデフォルトとし、デプロイ工程の一部としてバンドルをインストールする事によって、あなたのアプリをチェックアウトしたUnixユーザが、あなたのアプリが必要とするサードパーティコードをまたインストールする事が可能となる。

これは、もしPassenger(又はUnicom)があなたのアプリを見る事ができれば、その依存性も見る事が出来る事を意味する。

・--deploymentフラグをしようする場合、プロダクション環境に導入したコード実際に反映してテストした事を保証するため、Gemfile.lockを最新化する事が必要になる。

bundle checkコマンドを実行すると、事前にあなたのアプリをデプロイする前にGemfile.lockを最新化した事を確認する事が出来る。

もしbundle installを実行した場合、最後にGemfileを変更して以降、正常にあなたのアプリが起動できていて、常にGemfile.lockは最新化されているという事に注意する。

3)バンドルされたアプリをデプロイ

・バンドラーを使用しているアプリをデプロイする場合は、デプロイする前に、GemfileとGemfile.lockをバージョンコントロールに追加しておきます。そして、.bundleディレクトリは無視するように設定しておきます。

バージョンコントロールにgitを使用している場合は、下記のように実行します。

$ echo ".bundle\n" >> .gitignore
$ git add Gemfile Gemfile.lock .gitignore
$ git commit -m "Add Bundler support"

4)手動でデプロイ

・下記コマンドを使ってデプロイする。

$ bundle install --deployment

・下記コマンドを実行すると、vendor/bundleディレクトリにインストールされる。

・もし事前にbundle packageを実行している場合は、自動でキャッシュされているgemが使用される。

5)Capistranoを使って自動デプロイ

・deploy.rbファイルに下記記述を追加するのみ。

require 'bundler/capistrano'

・"cap deploy"を実行しているとリモートサーバー上でデプロイに適したオプションで"bundle install"が実行される。

・変更されうるオプションのリストは、capタスクのヘルプで確認できる。

cap -e bundle:install

6)Vladを使って自動デプロイ

・デフォルトのVladタスクが利用できる。利用するには、Vladのdeploy.rbファイルに下記記述を追加する。

require 'bundler/vlad'

・上記を実行すると、"vlad:bundle:install"タスクが利用できるようになる。
 
7)デプロイ後

・バンドル内のgemから実行ファイルを実行する場合は、bundle execを必ず使うようにします。

使用例)
bundle exec rake db:setup

※--binstubsオプションを使って"bundle install"を実行した場合は、実行ファイルのバイナリが作成されているので、"bundle exec"を使用しなくてよい。

8)Heroku

・Herokuへデプロイしている場合は、Gemfileが存在する限り、バンドラーは自動で実行される。

・Gemfile.lockがチェックインされていれば、Herokuは、"bundle install --deployment"を実行する。

・あるグループを"--without"オプションを使ってデプロイ対象からはずしたい場合は、"heroku config"コマンドを使って設定する。

設定例)
heroku config:add BUNDLE_WITHOUT="test development" --app app_name


(6)GitリポジトリからGemを導入(リモート)

・Gitリポジトリもまた、gemの供給場所として使用できる。

・バンドラーは、gitリポジトリーから直接gemを使用する事ができる。

・Rubygemsは、gitからgemを操作することができないので、gitリポジトリからインストールされたgemは"gem list"では表示されない。Bundler.setupを実行すれば見ることができる。

・Gitリポジトリ内に.gemspecファイルが含まれていない場合は、バンドラーが簡単な.gemspecファイルを作成する。

・Gitリポジトリからgemを導入するには、Gemfileに下記のように記述し、そのrootに.gemspecファイルを用意する。

gem 'nokogiri', :git => 'git://github.com/tenderlove/nokogiri.git'

・Gitリポジトリのrootに.gemspecファイルがない場合は、バンドラーが依存性を解決する際に使用すべきバージョンを記述しなければならない。

gem 'deep_merge', '1.0', :git => 'git://github.com/peritor/deep_merge.git'

・複数の.gemspecファイルを含むGitリポジトリがgemのソースとして取り扱われる場合の明示の仕方の例

git 'git://github.com/rails/rails.git' do
 gem 'railties'
 gem 'action_pack'
 gem 'active_model'
end

・Gitリポジトリの特定のref,branch,tagを使用するように指定する場合の例

:git => 'git://github.com/rails/rails.git', :ref => '4aded'
:git => 'git://github.com/rails/rails.git', :branch => '2-3-stable'
:git => 'git://github.com/rails/rails.git', :tag => 'v2.3.5'

gem 'nokogiri', :git => 'git://github.com/tenderlove/nokogiri.git', :ref => '0eec4'

・gemをパブリックなGitHubリポジトリから導入する場合は、下記のように省略して記述できる。

gem 'nokogiri', :github => 'tenderlove/nokogiri'