Rails 4.1以降のコンソールコマンドは必ず bin/ を付けなきゃいけないの？


## はじめに：Springが起動するコマンドはいったいどれだ！？

Rails 4.1からはアプリケーションpreloaderとして[Spring](https://github.com/rails/spring)が標準で組みこまれています。
Springを使うとバックグラウンドでRailsサーバーが動いている状態になるので、`rails g` やrakeコマンドが素早く起動します。

一般に、Springを使う場合は `bin/rails g` や `bin/rake -T` のように、`bin/` を付けるように言われていますが、これは必須なんでしょうか？
`bin/` を付けずに実行すると、何が起きるんでしょうか？

というわけでちょっと調べてみました。

### 対象バージョン

- Rails 4.1または4.2

## railsコマンドの場合：bin/ を付けなくても大丈夫！

まず最初に、`bin/rails c` と `rails c` を実行して、Springのstatusがどう変わるか確認してみました。

```
$ bin/spring status
Spring is not running.

$ bin/rails c    
Loading development environment (Rails 4.2.0)
irb(main):001:0> exit
$ bin/spring status
Spring is running:

37179 spring server | cb | started 6 secs ago  
37180 spring app    | cb | started 6 secs ago | development mode    
$ bin/spring stop  
Spring stopped.

$ rails c          
Loading development environment (Rails 4.2.0)
irb(main):001:0> exit
$ bin/spring status
Spring is running:

37318 spring server | cb | started 12 secs ago  
37319 spring app    | cb | started 12 secs ago | development mode 
```

上記の実行結果を見てもらうとわかりますが、なんと **`rails c` は `bin/` を付けなくてもSpringが起動します。**
これは `rails g` や `rails r` (`rails runner`)でも同様です。 

ただし、`rails s` や `rails new` では常にSpringは起動しません。

### なんで bin/ がいらないの？

Railsはプロジェクトルートの `bin` ディレクトリにある `rails` ファイル（実行可能ファイル）を優先的に起動する仕様になっているからです。

よって、`bin/rails`（実行可能ファイル）がSpringを使うようになっていれば、`bin/` を付けなくてもSpringが起動することになります。

このあたりの挙動を詳しく知りたい方は、以下のコードを読んでみてください。

https://github.com/rails/rails/blob/4-2-stable/railties/lib/rails/app_rails_loader.rb

## rakeコマンドの場合：bin/ が必要

同様に、今度は `bin/rake -T` と `rake -T`、さらに `bundle exec rake -T` を比較してみました。

```
$ bin/spring status
Spring is not running.

$ bin/rake -T
rake about                              # List versions of all Rails frameworks and the environment
# 省略
$ bin/spring status
Spring is running:

37693 spring server | cb | started 5 secs ago  
37694 spring app    | cb | started 5 secs ago | development mode    
$ bin/spring stop  
Spring stopped.

$ rake -T          
rake about                              # List versions of all Rails frameworks and the environment
# 省略
$ bin/spring status
Spring is not running.

$ bundle exec rake -T
rake about                              # List versions of all Rails frameworks and the environment
# 省略
$ bin/spring status  
Spring is not running.
```

はい、上の実行例の通り、 **rakeコマンドは `bin/` を付けないとSpringが起動しません。**
Railsのように `bin` ディレクトリの下を見にいこうとしないので、`bin/` を付けない限り、素の `rake` コマンドが実行されます。
つまり、`rake -T` や `bundle exec rake -T` ではSpringを使わないので、Railsが起動するまで待つ必要があります。


### rspec コマンドも bin/ が必要

同じ理由でRSpecをSpring経由で実行する場合は `bin/rspec` にする必要があります。

なお、RSpecをSpring経由で実行したい場合は、[spring-commands-rspec](https://github.com/jonleighton/spring-commands-rspec)を使って事前にセットアップしておく必要があります。

```ruby
# Gemfile
group :development do
  gem 'spring-commands-rspec'
end
```

```bash
$ bundle install
$ bundle exec spring binstub rspec
```

### 余談："rake -T" と "bundle exec rake -T" は同じ？

上のrakeコマンドの例では `bin/rake -T` と `rake -T` 、`bundle exec rake -T` の3種類を比較しました。
`bin/` を付けないとSpringが起動しないことはわかりましたが、`rake -T` と `bundle exec rake -T` には何か違いがあるのでしょうか？

はい、違いはあります。

`bundle exec` を付けないと、通常、マシンにインストールされている最新バージョンのgemが実行されます。
なので、場合によっては意図しないバージョンが起動したり、gem同士の依存関係を正しく解決できなかったりする場合があります。

なので、 **RailsのようにBundlerを使うプロジェクト（`Gemfile`が存在するプロジェクト）では、`bundle exec` または `bin/` を付けて実行するのが望ましいです。**

これはrakeコマンドに限らず、rspecコマンドやspringコマンドにも同じことが言えます。

## spring と bin/spring の挙動の違いにちょっと注意

上のセクションでは「`bundle exec` を付けないと意図しないバージョンが起動することがある」と書きましたが、Springにおいてはそれが原因でわかりづらい挙動の違いが出る場合があります。

以下のコマンドの実行例を見てください。

```
$ spring -v
Spring version 1.2.0
$ bin/spring -v
Spring version 1.1.2

$ spring status
Spring is not running.
$ bin/spring status
Spring is not running.

$ bin/rake -T
rake about                              # List versions of all Rails frameworks and the environment
# 省略

$ spring status    
Spring is not running.

$ bin/spring status
Spring is running:

38771 spring server | cb | started 11 secs ago  
38772 spring app    | cb | started 11 secs ago | development mode    
```

上の例では、マシンにインストールされている最新のSpringのバージョンは1.2.0で、プロジェクトのBundlerが使っているバージョンが1.1.2になっています。

**Springはサーバーとコマンドのバージョンが異なると、`spring status` や `spring stop` を実行したときに、実行中のSpringサーバーをちゃんと認識してくれない場合があります。**

上の例では `spring status` は "Spring is not running." を返していますが、`bin/spring status` は "Spring is running:" を返しています。

こうなると、「あれ、Spring動いてないの？」という勘違いが発生したり、Springをちゃんと停止できなかったりする問題が出てくるので、注意が必要です。

## まとめ：Rails 4.1以降は bin/ を付けて実行する癖を付けよう

というわけで、結論としては **Rails 4.1以降でSpringを使いたい場合は、とりあえず `bin/` を付けておくのがベスト** だと思います。

railsコマンドだけは `bin/` が不要ですが、付けていても実害はないので、細かい違いを覚えるのが面倒であれば「とりあえず `bin/` を付ける」と考えておけばOKでしょう。

`bin/` を付けておけば、Spring経由で高速にコマンドが実行されますし、Bundlerがgemのバージョンや依存関係も正しく処理してくれるので、通常の開発ワークフローであればベストな選択になるはずです。

### 補足1. 既存のRailsプロジェクトをアップグレードした場合はSpringのセットアップが必要

Rails 4.1以降で`rails new`した場合は最初からSpringが有効になっていますが、Rails 4.0以前のプロジェクトからアップグレードした場合は別途セットアップが必要です。

```ruby
# Gemfile
group :development do
  gem 'spring'
end
```

```bash
$ bundle install
$ bundle exec spring binstub --all
```

また、SpringはRails 3.2や4.0でも使えるみたいです。

詳しくはSpringのREADMEを参照してください。

https://github.com/rails/spring

### 補足2. direnvを使って bin/ の入力を省略する

[direnv](https://github.com/zimbatm/direnv)というツールを使うと、自動的にプロジェクトルートの `bin` の下にPATHを通すことができます。
つまり、`bin/` を入力しなくても優先的に `bin` の下にある実行可能ファイルを呼び出してくれるようになります。

direnvの使い方は@kompiroさんのこちらの記事が参考になります。

[direnvを使おう](http://qiita.com/kompiro/items/5fc46089247a56243a62)

なお、Powを使っている場合はエラーを避けるため、`.envrc` をこんな感じで書く必要があります。

```bash
# .envrc
# See https://github.com/basecamp/pow/issues/73#issuecomment-48735353
if [ "$(type -t direnv_load)" = 'function' ]; then
  PATH_add bin
fi
```

### 補足3. もっと詳しく理解したい場合は binstub の仕組みを勉強しよう

今回説明した話はRubyやRailsの **binstub** の仕組みに関連しています。
「なんでこうなるのか、もっと詳しい理由が知りたい」という人は、binstubの仕組みを勉強するのが良いでしょう。
そうすればどういう理屈で各コマンドが動作しているのか理解できるはずです。

binstubについては、@tanaka51さんのこちらの記事が大変参考になります。

[binstub について翻訳してみた](http://qiita.com/tanaka51/items/bc22c1c364202d3cce4e)

僕も「binstubって名前は知ってるけど、仕組みはよくわかってない」という状態でしたが、今回の調査でかなり詳しくなることができました。