はじめに
Everyday RailsでRSpecの学習をしていたところ、rspec binstubという言葉が出てきました。
$ bundle binstubs rspec-core
上記コマンドにより、bin/rspecファイルが作成され、今までbundle exec rspec
というコマンドがbin/rpsec
のみで実行できるようになる。
と教材では書かれていたのですが、あまり理解出来なかったので掘り下げようと思います。
bundle exec
とは
まずは周辺知識から掘り下げたいと思います。
bundle exec
を使用することにより、Gemfile内のgemのバージョンに基づいてコマンドを実行できる。
bundle exec
をつけないと、システム共通のライブラリ保存場所にあるgemを使用するため、エラーが起きる場合がある。
bundle installしたgemの保存先
`~/.rbenv/versions/*/lib/ruby/gems/*/gems/インストールしたgem
binstubとは
例えばgem install rspec-core
をした時、Rubygemsは以下のものを用意してくれる。
- rubyバージョン/bin/rspec (RubyGems によって作られた binstub)
- rubyバージョン/lib/ruby/gems/*/gems/rspec-core-XX.YY/exe/rspec (オリジナル)
上記1は、上記2のファイルをラップするために作られたbinstub
そして、rbenvのコマンドであるrspecを実行した時、
shims → binstub → オリジナルの順にパスを探して実行する。
rspecのbinstubを作成
$ bundler binstubs rspec-core
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'rspec' is installed as part of a gem, and
# this file is here to facilitate running it.
#
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
bundle_binstub = File.expand_path("bundle", __dir__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems"
require "bundler/setup"
load Gem.bin_path("rspec-core", "rspec")
一番下の行のload Gem.bin_path("rspec-core", "rspec")
によって、bin/rspec
コマンドが呼ばれた際に、rspec-core
ディレクトリのrspec
実行ファイルが呼び出される?仕組みだと思います。
これにより、今まではbundle exec rspec
コマンドでオリジナルのrspec実行ファイルを実行していたが、bin/rspec
だけでオリジナルのrspec実行ファイルを呼び出すことができるようになっているのだと思います。
- また、./binのパスを通せば
rspec
コマンドのみで実行できます
$ export PATH="./bin:$PATH"