Bundler の動作で疑問に思っていたことが理解できた

## Bundlerの動作に疑問を持ったきっかけ

数か月前の話になりますが、@jnchitoさんの以下のQiita記事を読ませていただいて `--path vendor/bundle` について改めて考える機会がありました。

> bundle install時に--path vendor/bundleを付ける必要性は本当にあるのか、もう一度よく考えてみよう
> https://qiita.com/jnchito/items/99b1dbea1767a5095d85

この記事で特に心動かされたのは、(`--path vendor/bundle`を付けても付けなくても)どっちでもいいなら付けない方を好む理由として挙げられていた**「systemにインストールするのがBundlerのデフォルトで、pathを指定するのがオプションだから」**という一文でした。確かにデフォルト設定で問題が起きないならデフォルトのまま使うに越したことがないなぁと思います。


ただ、gemをグローバルにインストールした場合で1個だけ疑問に思った動作がありました。

上記記事にもありますが、**「グローバルにインストールされる」というのはすなわち、Bundlerを使ってインストールしたgemが開発マシン内のどこでも使える状態になる（bundle installしたプロジェクト以外の場所でも使える）こと**を意味します。

どこでも使えるとは、例えば静的解析ツール[RuboCop](https://github.com/rubocop-hq/rubocop)を`bundle install`でグローバルにインストールすれば、どこのディレクトリにいようが`rubocop`コマンドを実行できる！ということですが、自分の環境ではそのような動作にはならなかったのです…。

```shell
$ echo "source 'https://rubygems.org'\n\ngem 'rubocop'" >>| Gemfile
$ bundle install
$ bundle exec rubocop -v # bundle execを付けるともちろん大丈夫
0.75.0
$ rubocop -v # bundle execを付けないとコマンドが見つからない…
zsh: command not found: rubocop
```

これについて、何故そうなるのかやっと理解できました。

## 先に結論

- **rbenv**を使用している環境の場合、`bundle install`しただけでは`shims`ディレクトリが更新されない

  > 参考
  > https://github.com/rbenv/rbenv/pull/638#issuecomment-59375024

- `rbenv rehash`を明示的に行うことで、`shims`ディレクトリが更新されて、どこのディレクトリにいようが`rubocop`コマンドを実行できる！
- **rbenv**を使用していない環境(aptで直接インストール等)であれば、今回の事象はそもそも起きない

## 自分の環境

- Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-65-generic x86_64)
- rbenv 1.1.2-4-g577f046
- ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
- Bundler version 2.0.2

## 色々試したこと

### `gem install rubocop`したらどうなのか？？

そもそもBundlerを経由しなかった場合どうなるのか知りたかったので試してみました。

```shell
$ gem install rubocop
$ rubocop -v # ちゃんとコマンドが見つかる
0.75.0
```

大丈夫そうです。ということはBundlerが原因…と思いましたが、そういえば**rbenv**を当たり前のように使っていたので、**rbenvを使わなかったらどうなるのか**試してみました。

### `sudo apt install ruby`で入れたRubyだとどうなのか？？

厳密にはrbenvでインストールするrubyバージョンと合わせるべきですが、今回は横着しました…。
rbenvを使用していないまっさらな環境で以下を実行します。

```shell
$ sudo apt install -y ruby
$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]
$ sudo gem install bundler
$ echo "source 'https://rubygems.org'\n\ngem 'rubocop'" >>| Gemfile
$ bundle install # 本件と関係ないエラーが出る場合があるが、ruby-dev等 足りないライブラリをインストールすると解消する
$ bundle exec rubocop -v # bundle execを付けるともちろん大丈夫
0.75.0
$ rubocop -v # bundle execを付けなくても大丈夫！
0.75.0
```

なるほど、ということはrubyのバージョンに因る問題か、rbenvに何かあるのかな？

…詳細は書きませんが、rbenvでruby 2.5.1をインストールして試しても同様だったので、**rbenv**に何かありそうです。

## rbenvの仕組み

[rbenvのGitHubリポジトリ](https://github.com/rbenv/rbenv#understanding-shims)を読むと、rbenvの`shims`について記述があります。
rbenvは`PATH`の先頭に`shims`ディレクトリを挿入することで、rubyのバージョンを変更した際のコマンド切替をよしなに制御してくれています。

> その他参考
> rbenvの使い方と仕組みについて
> https://qiita.com/Kodak_tmo/items/73147ed4f0eec54d6e94

古い記事だと`rbenv install`実行後は`shims`を更新する為に`rbenv rehash`を実行すること！と書いてあったりするのですが、
[5年ほど前のPull requests](https://github.com/rbenv/rbenv/pull/638)で`rbenv rehash`を自動で処理するように変更されています。
なので、普段`rbenv rehash`を実行することはあんまりないんじゃないかなー…と思います。(違っていたらごめんなさい…)

今回の件、なんとなく`shims`が更新されてないんじゃない？と思ったので明示的に`rbenv rehash`したらどうだろうと思い試してみました。

```shell
$ echo "source 'https://rubygems.org'\n\ngem 'rubocop'" >>| Gemfile
$ bundle install
$ bundle exec rubocop -v # bundle execを付けるともちろん大丈夫
0.75.0
$ rubocop -v # bundle execを付けないとコマンドが見つからない…
zsh: command not found: rubocop
$ rbenv rehash # shimsディレクトリを更新すると…
$ rubocop -v # bundle execを付けなくても大丈夫！
0.75.0
```

思っていた動作になりました！

## Pull requestsをよくよく読んだら書いてあった

[先ほどの`rbenv rehash`が不要になったPull requests](https://github.com/rbenv/rbenv/pull/638#issuecomment-59375024)をよくよく読んだら書いてありました！
`bundle install`した際 複数回`rbenv rehash`が実行されるのを防ぐ為にそうなっているとのこと。Bundlerの並列性の為に`rbenv rehash`を一回だけ処理するのは難しかったとのことです。

そもそもBundlerを使用している時点で`bundle exec`を付けてコマンド実行すると思うので実用上何も問題は起きないと思います。ですが、引っかかっていた些細な疑問が解決して良かったです！