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 の仕業です。
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 --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 で修正された事を教えていただきました。
-I
オプションでパスを指定したんですね。
取り急ぎ、v1.6.3 を使って rails c
が起動できる事が確認できました。