gem, bundler と pip, venv の比較

Ruby や Python でできたツールの README には、よくインストール方法として `gem install redcarpet` とか `pip install pygments` どとシンプルなコマンドが書かれています。

ですが、このインストール方法をみたときに、Rubyに慣れてない人は「どうせ gem install してもなんかよくわからないエラーがでて、愚痴ったら bundler 使わないほうが悪いみたいに言われるんだろ」とか、Pythonに慣れてない人は「これどこにインストールするの？ bundler みたいなのないの？」と思うかもしれません。

少なくとも私は gem install でインストールするツールを敬遠していました。でも、「これだから gem は…」と愚痴るのは違うだろう、 Rubyist は pip install に同じ思いを持ってるだろうと思ったので、私のような人のためにこの記事を書くことにしました。 Ruby 側のツールに対する理解が間違っているかもしれないので、間違っていたら編集リクエストをください。


## `gem install` と `pip install`

どちらもサードパーティーのパッケージをインストールするために使います。

通常は Ruby だと `/usr/local/lib/ruby/gems/2.3.0`, Python だと `/usr/local/lib/python3.5/site-packages/` のような、ユーザー非依存の場所にインストールしようとします。

`gem install --user-install <パッケージ名>` や `pip install --user <パッケージ名>` すると、 `~/.gem/ruby/2.3.0` や `~/.local/lib/python3.5` (maxOS で Framework になっている場合 `~/Library/Python/3.5/lib/python/site-packages`) のような場所にインストールします。

> Note: 
> Ubuntu で `apt install python3-pip` でインストールできる pip は、 sudo を付けてない場合は自動で --user でインストールするようにカスタマイズされています。

## `bundle install` と `pip install -r`

どちらも、定義されたパッケージの一覧をインストールするのに使います。

bundler の場合、 `bundle install` すると (`Gemfile.lock` が無い場合や `Gemfile` が更新されていた場合は) `Gemfile` をもとにパッケージをインストールして、実際にインストールしたパッケージ (Gemfile に含まれない依存パッケージも含む) とバージョンを記述した `Gemfile.lock` を生成します。 `Gemfile.lock` が既にある場合はそちらをもとにインストールすることで、違う環境でも全く同じバージョンのライブラリをインストールすることができます。

pip の場合も、 `pip install -r requirements.txt` すると `requirements.txt` に書いてあるルールにもとづいてパッケージをインストールします。バージョンをロックしたい場合、 `pip freeze > requirements.lock` すると現在インストールされている全てのライブラリとバージョンを書き出せるので、それを `pip install -r requirements.lock` することができます。

bundler が `Gemfile.lock` を自動で生成しバージョン管理に含めることを推奨しているのに対して、 pip は自動で lock ファイルを作りませんし、特定のやりかたを推奨してもいません。
WebアプリをCIとプロダクション環境で同じようにデプロイしたい場合は bundler と同じ方針を取りますし、ライブラリを開発するプロジェクトで、開発に利用するツールをインストールするだけなら、 `requirements.txt` だけあれば十分です。OSSのフレームワークやライブラリのリポジトリには `Gemfile.lock` 相当のものがないのはそのためです。


## bundler の `--path` と venv

インストールするライブラリを Ruby や Python 全体で共有するのではなく、アプリケーション固有で管理したい場合に、 `bundle install --path=vendor/bundle` や `python3 -m venv /path/to/python` を使います。
これらは目的は同じなのですが、実現方法が異なるので注意が必要です。

`bundle install --path` は、指定したディレクトリ配下にパッケージをインストールするだけです。
そのままではそのディレクトリ配下のパッケージが使えませんが、 `bundle exec コマンド` すると使えるようになります。

一方、 `python3 -m venv /path/to/python` は、 `/path/to/python` に新しく Python をインストールしたかのように振る舞う「仮想環境」を作ります。この仮想環境はサードパーティーのパッケージは分離されているものの、標準ライブラリは元になった Python の物を使うので、 bundler の path と同程度に軽量です。

venv は仮想環境を作るだけなので、 `bundle install --path` の用にインストールまでするには、その環境の pip を使って `pip install -r requirements.txt` する必要があります。
このように仮想環境内のコマンドを実行する場合、 `bundle exec` のようなコマンドを使うのではなく、 `/path/to/python/bin` にあるコマンドを実行することになります。それにはいくつかの方法があります。

* フルパスでコマンドを実行する: `/path/to/python/bin/pip install -r requiremnts.txt`
* PATH の最初に追記する: `export PATH=/path/to/python/bin:$PATH; pip install -r requirements.txt`
* `activate` というヘルパースクリプトを使う: `source /path/to/python/bin/activate; pip install -r requirements.txt`

`activate` スクリプトは主に3つのことをします。

* PATH に仮想環境の bin を追加する
* VIRTUAL_ENV という環境変数を設定する (vim や emacs などの外部ツールが現在利用中の仮想環境を見つけられる)
* PATH などを元に戻す `deactivate` コマンドを定義する


## さいごに

この記事ではどちらが優れているかを論じることはしません。

片方に慣れているともう片方で同じやり方ができないために面倒に感じがちなので、慣れてない方を dis りたくなっても我慢しましょう。