bundle packageでgemをアプリケーションに含める


編集リクエスト求む。

# 発端

1. Ruby アプリケーションを EC2 上で動かしたい。AutoScaling でスケールもしたい。
2. AMI を作れば起動が早いけど AMI の運用したくない。
3. 素の Amazon Linux から環境構築しても起動が早くなればいい。
4. bundle package 使ってみよう。

# 手順

前提として `/home/ec2-user/app/` 以下で作業を行っている。
また、bundler 1.7.6 がインストール済み。

## gemをアプリケーションに含める

**1. 使用する gem のバージョンを決め Gemfile にその旨を記述する**

 ```ruby:Gemfile
 source "https://rubygems.org"
 
 gem "aws-sdk-core", "~> 2.0.9"
     :
 ```

**2. gem のインストール先を設定する**

 ```bash:.bundle/config
 $ bundle config --local path vendor/bundle
 $ bundle config
 Settings are listed in order of priority. The top value will be used.
path
Set for your local app (/home/ec2-user/app/.bundle/config): "vendor/bundle"
 $ cat .bundle/config
 ---
 BUNDLE_PATH: vendor/bundle
 ```

**3. `bundle package` する**
`vendor/bundle` にgemがインストール	され `vendor/cache` に .gem ファイルがキャッシュされる。
Gemfileに :gem, :path, .gem が記述してある場合に、それも対象にするため `--all` を付けている。 bundler2.0 からはデフォルト。

 ```bash
 $ bundle package --all
 Fetching gem metadata from https://rubygems.org/............
 Resolving dependencies...
     :
 Installing aws-sdk-core 2.0.9
     :
 Using bundler 1.7.6
 Your bundle is complete!
 It was installed into ./vendor/bundle
 Updating files in vendor/cache
     :
   * aws-sdk-core-2.0.9.gem
     :
 ```

**4. `vendor/cache` ディレクトリと `.bundle/config` ファイルをバージョン管理対象とする**
`vendor/bundle` はいらないので管理対象外。

 ```:.gitignore
 /vendor/bundle/
 !/vendor/cache/*.gem
 ```

## パッケージした gem を更新する

「パッケージした」って日本語どうなのか・・・
ここでは aws-sdk-core 2.0.5 がインストールされている状態で 2.0.9 にアップデートする。
`.bundle/config` は設定済みの体。

**1. `bundle update` する**

`vendor/bundle` を更新した後 `vendor/cache` の .gem ファイルも更新。不要になった .gem が削除される。

 ```bash
 $ bundle update
 Fetching gem metadata from https://rubygems.org/...
 Resolving dependencies...
 Using builder 3.2.2
     :
 Installing jmespath 1.0.1
     :
 Installing aws-sdk-core 2.0.9 (was 2.0.5)
 Using bundler 1.7.6
 Updating files in vendor/cache
   * jmespath-1.0.1.gem
   * aws-sdk-core-2.0.9.gem
 Removing outdated .gem files from vendor/cache
   * jamespath-0.5.1.gem
   * aws-sdk-core-2.0.5.gem
 Your bundle is updated!
```

**2. commit する**
バージョン管理しているならコミットする。

## gemをインストールする

gem をパッケージしたアプリケーションを利用する時に gem をインストールする手順。

**1. `bundle install` する**
`--local` を付けると rubygems.org へのアクセスもなくなりインストール自体はとても早くなる。

 ```bash
 $ time bundle install --local
 Resolving dependencies...
 Installing builder 3.2.2
 Installing multi_json 1.10.1
 Installing jmespath 1.0.1
 Installing multi_xml 0.5.5
 Installing aws-sdk-core 2.0.9
 Using bundler 1.7.6
 Updating files in vendor/cache
 Your bundle is complete!
 It was installed into ./vendor/bundle
 
 real	0m0.440s
 user	0m0.348s
 sys	0m0.064s
 ```

ただし、

> $ bundle help install
> ... Note that if a more appropriate platform-specific gem exists on rubygems.org, it will not be found.

らしいので注意。パッケージした環境と利用する環境が同じなら問題ないと思う。

# 問題点

## native extension の問題

nokogiri のようなネイティブエクステンションをパッケージしてもインストール時に結局ビルドが必要になるので遅い。
完全に環境を固定できるなら `vendor/bundle` ごとバージョン管理したほうがいいんだろうか。

## platform の問題

`bundle help package` を見ると nokogiri を例に説明がある。
ruby プラットフォーム (C Ruby) じゃなくて java プラットフォーム (JRuby) だったら bundler は rubygems.org チェックしちゃうよ。
`--local` 付ければチェックはスキップするけど動かないよ。ということ、だと、思う。