我应该使用 `bundle exec` 还是 Rails' binstubs?

Should I use `bundle exec` or Rails' binstubs?

假设我有一个 Rails 应用程序,其中安装了 bundler 的 gems。我应该使用 bundle exec rails 还是 bin/rails?我应该使用 bundle exec rake 还是 bin/rake?有什么不同吗?哪一个比另一个好?

Bundle exec 是一个 Bundler 命令。

您应该在所有情况下都使用 bundle exec

bundle-exec - Execute a command in the context of the bundle

可在此处找到更多信息:http://bundler.io/v1.15/bundle_exec.html

bin/rails 可能会起作用,但前提是所有必需的 gems/executables 都存在于您的系统上且不在捆绑范围内。

简而言之,如果您在系统上安装了所有 gem(例如,全局),bin/rails 将起作用(但可能会产生冲突)。但是,如果您仅将它们安装在捆绑包的范围内,它们可能无法工作。

bundle exec 确保使用 Gemfile 中的 gem 及其版本。

tl;dr 没有特别的区别,但如果让我选择,我会使用 bin/rails

几乎没有区别。让我们看看。

DISABLE_SPRING=1 bin/rails --version:

bin/rails: require_relative '../config/boot'
config/boot: require 'bundler/setup'
bundler/setup: Bundler.setup
Bundler.setup: definition.validate_runtime!
Bundler.definition: Definition.build
Bundler::Definition.build: Dsl.evaluate
Bundler::Dsl.evaluate: builder.eval_gemfile
Bundler::Dsl#eval_gemfile: instance_eval

require 'bundler/setup' 之后尝试 gem 'rails', 'x.y.z' 结果:

*** Gem::LoadError Exception: can't activate rails (= x.y.z), already activated rails-5.1.3. Make sure all dependencies are added to Gemfile.

有了 bundle exec rails --version,我们最终得到 运行 bin/rails 无论如何:

~/.gem/ruby/x.y.z/bin/rails: load Gem.activate_bin_path('railties', 'rails', version)
exe/rails: require 'rails/cli'
rails/cli: Rails::AppLoader.exec_app
Rails::AppLoader.exec_app: `exec RUBY, 'bin/rails', *ARGV

此外,请注意可以在 last file:

中找到的消息

Beginning in Rails 4, Rails ships with a rails binstub at ./bin/rails that should be used instead of the Bundler-generated rails binstub.

所以,归根结底,没有什么区别。但考虑到 Rails 经历了运送自己的 binstub 的麻烦这一事实,我更倾向于 bin/rails 替代方案。此外,它的自动完成效果更好。

并且,

App executables now live in the bin/ directory: bin/bundle, bin/rails, bin/rake. Run rake rails:update:bin to add these executables to your own app. script/rails is gone from new apps.

Running executables within your app ensures they use your app's Ruby version and its bundled gems, and it ensures your production deployment tools only need to execute a single script. No more having to carefully cd to the app dir and run bundle exec ....

Rather than treating bin/ as a junk drawer for generated "binstubs", bundler 1.3 adds support for generating stubs for just the executables you actually use: bundle binstubs unicorn generates bin/unicorn. Add that executable to git and version it just like any other app code.

https://github.com/rails/rails/blob/4-0-stable/railties/CHANGELOG.md

bundle exec 只是确保在 运行 命令时 Gemfile 中的所有 gem 都已安装(全局或在用户空间或在项目的 vendor/bundle 目录中)。 https://bundler.io/man/bundle-exec.1.html (especifically https://bundler.io/man/bundle-exec.1.html#BUNDLE-INSTALL-BINSTUBS 部分有更详细的解释):

Bundle Install --binstubs

If you use the --binstubs flag in bundle install, Bundler will automatically create a directory (which defaults to app_root/bin) containing all of the executables available from gems in the bundle.

After using --binstubs, bin/rspec spec/my_spec.rb is identical to bundle exec rspec spec/my_spec.rb.

Binstubs 可能还包括其他自定义设置,例如加载 spring gem,它会预加载并加速 Rails 应用程序。