bundler 1.11.0 の変更で spring が起動しない件について

`rails c` で以下の様な例外が発生して起動しない事があります。これは、spring が子プロセスを立ち上げるところで発生する例外です。

```
/Users/koshigoe/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load such file -- bundler/setup (LoadError)
        from /Users/koshigoe/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/commands.rb:33:inv `<module:Spring>'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/commands.rb:4:in `<top (required)>'
        from /Users/koshigoe/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from /Users/koshigoe/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/application.rb:77:in `preload'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/application.rb:143:in `serve'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/application.rb:131:in `block in run'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/application.rb:125:in `loop'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/application.rb:125:in `run'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/application/boot.rb:18:in `<top (required)>'
        from /Users/koshigoe/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from /Users/koshigoe/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from -e:1:in `<main>'
```

bundler の変更によって、 `Bundler.with_clean_env` のスコープ(ブロック)では `ENV['RUBYLIB']` から bundler のパス(path/to/bundler-x.y.z/lib) が取り除かれる様になりました。なお、bundler のパスが `ENV['RUBYLIB']` に追加されているのは bundler の仕業です。

- [Clean RUBYLIB as well in `with_clean_env` · bundler/bundler@42b5072](https://github.com/bundler/bundler/commit/42b507218fd910feb2a9756bc08dc41181a9d1c4)

spring では、子プロセスが `require 'bundler/setup'` する場面があります。この子プロセスは `Bundler.with_clean_env` の中で `Process.spawn` されて生まれています。

ここで、bundler を `gem install` でインストールし、アプリケーションが依存する gem を `bundle install --path vendor/bundle` でインストールしている場合を考えます(`BUNDLE_DISABLE_SHARED_GEMS: '1'`)。bundler 管理下の `GEM_HOME` (vendor/bundle) には bundler がいないため、vendor/bundle しか見ない場合は `require 'bundle/setup'` が `LoadError` となってしまいます。

さて、vendor/bundle に bundler があれば例外は避けられるのでしょうか？

```
$ GEM_HOME=vendor/bundle gem install bundler --no-document
$ bundle exec rails c
Running via Spring preloader in process 45717
Loading development environment (Rails 4.2.5)
(001): >>
```

予想通り、 `LoadError` 例外は発生しなくなりました。

spring 側の変更で対応する場合はどうするのが良いのでしょうか。乱暴ですが `ENV['RUBYLIB']` に path/to/bundler-x.y.z/lib を追加してしまうのが簡単でしょう。`Bundler.with_clean_env` で取り除いたものを戻すというあたりが気持ち悪いですね。

```diff
diff --git a/lib/spring/application_manager.rb b/lib/spring/application_manager.rb
index 8b34b9f..2699ba2 100644
--- a/lib/spring/application_manager.rb
+++ b/lib/spring/application_manager.rb
@@ -92,13 +92,18 @@ module Spring
     def start_child(preload = false)
       @child, child_socket = UNIXSocket.pair
 
+      bundler_lib_path = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR).grep(%r{/bundler-\d[^/]*/lib\z}).first
       Bundler.with_clean_env do
+        rubylib = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR)
+        rubylib.unshift bundler_lib_path if bundler_lib_path
+
         @pid = Process.spawn(
           {
             "RAILS_ENV"           => app_env,
             "RACK_ENV"            => app_env,
             "SPRING_ORIGINAL_ENV" => JSON.dump(Spring::ORIGINAL_ENV),
-            "SPRING_PRELOAD"      => preload ? "1" : "0"
+            "SPRING_PRELOAD"      => preload ? "1" : "0",
+            "RUBYLIB"             => rubylib.join(File::PATH_SEPARATOR)
           },
           "ruby",
           "-I", File.expand_path("../..", __FILE__),
```

spring 側で `require 'bundler/setup'` しているという事は、spring は bundler に依存しているという事だと思ってしまうのですが、RUNTIME DEPENDENCIES に bundler がないので、自分は何かを見落としている様な気がしています。

テストが書ければ PR を作ってみようかとも思うのですが、spring 本体のテストコードでは、テストコード(テスト用アプリ)が依存する gem を `test/apps/gems/2.2.3/gems/` 以下に bundler も含めて配置する仕組みになっています。bundler とアプリケーションの依存 gem を分けて配置する構成のテストを書ける気がしません。

以下、まとめ。

- 敗北を認めよう
- bundler 1.10.6 を使ってれば大丈夫
- bundler 1.11.x でも vendor/bundle に入れちゃえば大丈夫(`--path vendor/bundle`)
- 多分 `bundler install --system` という選択肢もある


2016/02/07 追記
----

コメントにて、v1.6.3 で修正された事を教えていただきました。

- [Comparing v1.6.2...v1.6.3 · rails/spring](https://github.com/rails/spring/compare/v1.6.2...v1.6.3)

`-I` オプションでパスを指定したんですね。

取り急ぎ、v1.6.3 を使って `rails c` が起動できる事が確認できました。