ツールを使いたいだけの人のための bundler 入門 (例: vagrant + veewee)

## 目次

0. なぜ bundler?
1. ruby を入れておく
2. rubygems を入れておく
3. bundler を入れておく
4. Gemfile を作る
5. bundle install で gem をインストールする
6. bundle exec で使う
7. まとめ
8. bundle update で更新する
9. bundle clean で古い gem を削除する

## 0. なぜ bundler?

普通に `gem install veewee` でインストールしようとすると、依存関係の解決で出来るだけ最新を入れようとするだけで、うまく試行錯誤してくれず、うまく解決出来ない状態になると `Gem::DependencyError` で諦めてしまいます。
たとえば今最新の veewee をインストールしようとすると以下のように失敗します。

```:実行例
%  gem install veewee
ERROR:  While executing gem ... (Gem::DependencyError)
    Unable to resolve dependencies: gherkin requires json (>= 1.7.6)
```

このような場合でも `bundler` を使うと一部のバージョンを下げるなど、解決出来る組み合わせがあれば、うまく解決してくれます。 (`bundler` を使った場合の例は後で出てきます。)

## 1. ruby を入れておく

まず ruby をインストールしておきます。
Mac などでは OS に標準で入っている ruby をそのまま使っても大丈夫だと思います。

### 元に戻すには?

インストールに使った方法によるので一概には言えません。

## 2. rubygems を入れておく

1.8.7 なら rubygems を入れておきます。
これも OS などに標準で入っていれば、それを使えば大丈夫です。

### 元に戻すには?

ruby 自体をまるごと入れ直すのが楽だと思います。

## 3. bundler を入れておく

`gem install bundler` で bundler を入れておきます。
この後、使用するコマンドは `bundle` ですが、 gem の名前としては `bundler` と `r` が付いているのに注意してください。

### 元に戻すには?

`gem uninstall bundler` でインストール前に戻せます。

### Tips: システムをよごしたくない場合

`GEM_HOME` 環境変数を設定しているとそこにインストール出来るので、システムをよごさずに試すことが出来ます。
`bundle` コマンドは `$GEM_HOME/bin` に入るので `PATH` も通しておく必要があります。

```:実行例
export GEM_HOME=/tmp/gem
export PATH=$GEM_HOME/bin:$PATH
```

## 4. Gemfile を作る

ここからは vagrant + veewee を使うディレクトリを作って、その中で作業をしていきます。
説明のための例なので `/tmp/` 以下に作成していますが、普通は `$HOME` 以下に作ると良いと思います。

1. `bundle init` でカレントディレクトリに `Gemfile` のひな形を作成します。
2. エディタなどで `Gemfile` に使いたい gem を追加します。

```:実行例
% mkdir /tmp/veewee
% cd /tmp/veewee
% bundle init
Writing new Gemfile to /private/tmp/veewee/Gemfile
% cat Gemfile
# A sample Gemfile
source "https://rubygems.org"

# gem "rails"
% echo 'gem "vagrant"' >> Gemfile
% echo 'gem "veewee"' >> Gemfile
% cat Gemfile
# A sample Gemfile
source "https://rubygems.org"

# gem "rails"
gem "vagrant"
gem "veewee"
```

### 元に戻すには?

作業用に作ったディレクトリを削除するだけです。
この後も作業ディレクトリの中に閉じ込めている限りは同じです。

## 5. bundle install で gem をインストールする

`bundle install` で gem をインストールします。

* `--path` オプションを使うと、指定した場所に gem がインストールされるので、不要になったときにまとめて削除出来て便利です。
* `bundle install` に指定したオプションの情報が `.bundle/config` に保存されていて、この後の `bundle` の実行時に使われます。
* `Gemfile.lock` にどのバージョンを要求されてどのバージョンを使う (インストールした) のかなどの情報が保存されています。
* 今後のために `git` などでバージョン管理するなら `.bundle/` や `vendor/bundle` は `.gitignore` で無視して、 `Gemfile` と `Gemfile.lock` をバージョン管理するのがおすすめです。

```:実行例
% bundle install --path vendor/bundle
% bundle install --path vendor/bundle
Fetching gem metadata from https://rubygems.org/........
Fetching gem metadata from https://rubygems.org/..
Resolving dependencies...
Installing rake (10.0.3)
Installing libxml-ruby (2.6.0)
Installing CFPropertyList (2.0.17)
Installing Platform (0.4.0)
Installing ansi (1.3.0)
Installing archive-tar-minitar (0.5.2)
Installing builder (3.2.0)
Installing ffi (1.4.0)
Installing childprocess (0.3.8)
Installing diff-lcs (1.2.1)
Installing json (1.5.5)
Installing gherkin (2.11.5)
Installing cucumber (1.2.1)
Installing erubis (2.7.0)
Installing excon (0.19.3)
Installing fission (0.4.0)
Installing formatador (0.2.4)
Installing mime-types (1.21)
Installing multi_json (1.6.1)
Installing net-ssh (2.2.2)
Installing net-scp (1.0.4)
Installing nokogiri (1.5.6)
Installing ruby-hmac (0.4.0)
Installing fog (1.9.0)
Installing posix-spawn (0.3.6)
Installing grit (2.5.0)
Installing highline (1.6.15)
Installing i18n (0.6.4)
Installing log4r (1.1.10)
Installing open4 (1.3.0)
Installing popen4 (0.1.2)
Installing progressbar (0.20.0)
Installing rspec-core (2.13.0)
Installing rspec-expectations (2.13.0)
Installing rspec-mocks (2.13.0)
Installing rspec (2.13.0)
Installing ruby-vnc (1.0.1)
Installing thor (0.17.0)
Installing vagrant (1.0.6)
Installing veewee (0.3.7)
Using bundler (1.3.0)
Your bundle is complete! It was installed into ./vendor/bundle
```

### 元に戻すには?

作業ディレクトリを削除するだけです。
`bundle install` の時に `--path` を指定し忘れて ruby のシステム側に gem がインストールされてしまった場合は `Installing veewee (0.3.7)` なら `gem uninstall -v 0.3.7 veewee` のように個別に頑張ってアンインストールしてください。
Debian の apt の autoremove のような便利な機能はありません。

## 6. bundle exec で使う

`vagrant` や `veewee` などのコマンドの前に `bundle exec` を付けて実行します。
この方法ならサブディレクトリを作成して、その中でも同様の方法で実行出来ます。
`vagrant` は Vagrant Project ごとのサブディレクトリを作って使うと良いと思います。

```:実行例
% bundle exec vagrant
Usage: vagrant [-v] [-h] command [<args>]

    -v, --version                    Print the version and exit.
    -h, --help                       Print this help.

Available subcommands:
     basebox
     box
     destroy
     gem
     halt
     init
     package
     provision
     reload
     resume
     ssh
     ssh-config
     status
     suspend
     up

For help on any individual command run `vagrant COMMAND -h`
% bundle exec veewee
Tasks:
  veewee add_share    # Adds a Share to the Guest
  veewee fusion       # Subcommand for Vmware fusion
  veewee help [TASK]  # Describe available tasks or one specific task
  veewee kvm          # Subcommand for KVM
  veewee parallels    # Subcommand for Parallels
  veewee vbox         # Subcommand for VirtualBox
  veewee version      # Prints the Veewee version information
```

### Tips: bundle exec を省略するには?

`bundle install` の時に `--binstubs` というオプションを使うと省略出来るようになりますが、 `vagrant` のようにサブディレクトリでも同様に使いたい場合には向いていないと思ったため、詳しい説明は省略します。

## 7. まとめ

`bundler` を使って `gem install` より賢く gem をインストールする方法、インストールした gem を簡単に削除出来る方法を紹介しました。
以上で使うまでは出来るようになったと思います。
続いてインストール後に使うコマンドのうち知っておくと良さそうなものをいくつか紹介しておきます。

## 8. bundle update で更新する

`bundle update` で `Gemfile` の記述を満たす最新の gem に更新してくれます。
以下では上の例でインストールされたのと同じバージョンの json だけインストールしてみて、それを最新にアップデートしています。
例では、まず `gem "json", "1.5.5"` で古いバージョンを指定してインストールしています。
新しいバージョンがうまく動かないなどの理由で古いバージョンが使いたい時に必要になるかもしれません。

```:実行例
% mkdir x
% cd x
% bundle init
 Writing new Gemfile to /tmp/x/Gemfile
% echo 'gem "json", "1.5.5"' >> Gemfile
% cat Gemfile
# A sample Gemfile
source "https://rubygems.org"

# gem "rails"
gem "json", "1.5.5"
%  bundle install --path vendor/bundle
Fetching gem metadata from https://rubygems.org/..
Resolving dependencies...
Installing json (1.5.5) with native extensions
Using bundler (1.2.4)
Your bundle is complete! It was installed into ./vendor/bundle
% echo .bundle > .gitignore
% echo vendor/bundle >> .gitignore
% git init
Initialized empty Git repository in /tmp/x/.git/
% git add -A
%  git commit -m "Initial commit"
[master (root-commit) db09b6e] Initial commit
 3 files changed, 17 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Gemfile
 create mode 100644 Gemfile.lock
% editor Gemfile
% cat Gemfile
source "https://rubygems.org"
gem "json"
% bundle update
Fetching gem metadata from https://rubygems.org/..
Resolving dependencies...
Installing json (1.7.7) with native extensions
Using bundler (1.2.4)
Your bundle is updated! Use `bundle show [gemname]` to see where a bundled gem is installed.
diff --git a/Gemfile b/Gemfile
index 3b67429..b439d83 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,4 +2,4 @@
 source "https://rubygems.org"
 
 # gem "rails"
-gem "json", "1.5.5"
+gem "json"
diff --git a/Gemfile.lock b/Gemfile.lock
index 1acd703..02f7a20 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,10 +1,10 @@
 GEM
   remote: https://rubygems.org/
   specs:
-    json (1.5.5)
+    json (1.7.7)
 
 PLATFORMS
   ruby
 
 DEPENDENCIES
-  json (= 1.5.5)
+  json
```

## 9. bundle clean で古い gem を削除する

`bundle update` や `Gemfile` の変更などで使われなくなった gem は `bundle clean` で削除出来ます。
さきほどの json の例の後だと次のようになります。

```:実行例
% bundle clean
Removing json (1.5.5)
```