With Bundler 4, what does `bundle binstubs rspec-core` generate, and how does running `bin/rspec` compare with `bundle exec rspec`?
answer
- a small Ruby file in bin/
- BUNDLE_GEMFILE ||= the project's Gemfile
- require bundler/setup, then load Gem.bin_path
- --all for every gem
- install --binstubs removed in 4
basics
~20 sbundle binstubs rspec-core writes bin/rspec, a small Ruby script that points BUNDLE_GEMFILE at the project, requires bundler/setup and loads rspec's executable, so it runs the locked version like bundle exec. Bundler 4 removed bundle install --binstubs.
solid answer
~40 sA binstub is a generated wrapper for one gem executable. `bundle binstubs rspec-core` writes `bin/rspec` (or into the directory named by the `bin` setting). The file sets `ENV["BUNDLE_GEMFILE"] ||=` to the project's Gemfile, resolved relative to the stub, requires `bundler/setup` and then `load Gem.bin_path("rspec-core", "rspec")`. The result is the same guarantee as `bundle exec rspec`, the locked version and only the bundle's gems, but the file can be run directly, from any directory, and committed so everyone types `bin/rspec`. `--all` generates stubs for every gem in the bundle, `--force` overwrites, `--shebang` changes the interpreter line and `--standalone` makes stubs for a bundle installed with `bundle install --standalone`. In Bundler 4, `bundle install --binstubs` raises an error pointing to `bundle binstubs --all`, and `bundle binstubs --path` is replaced by `bundle config set bin DIR`.
code
bash · 4 linesbundle binstubs rspec-core rake # writes bin/rspec and bin/rake
bin/rspec spec/app_spec.rb # same versions as bundle exec rspec
bundle binstubs --all # a stub for every gem executable
bundle config set bin exe/stubs # Bundler 4: set the directory this waygo deeper
Recall that bundle binstubs writes scripts in bin/ that run a gem's command with the bundle's versions, the same result as bundle exec.
Explain the three lines of a binstub: BUNDLE_GEMFILE relative to the stub, require bundler/setup, load Gem.bin_path, and why that works from any directory.
Standardise on committed binstubs for CI and process managers, regenerate them with --force after upgrades, and replace Bundler 2 install flags in old scripts.
Decide which entry points a team commits and how they are kept consistent across services, trading convenience against stale stubs and framework-owned scripts.
## What a binstub is A **binstub** is a small script that wraps one executable shipped by a gem, so that running it always goes through the project's bundle. RubyGems also writes wrappers for executables, into its global `bin` directory; those activate the newest installed version. Bundler's binstubs activate the **locked** version instead. `bundle binstubs rspec-core` creates `bin/rspec`, because the `rspec` executable lives in the `rspec-core` gem. The generated file, from Bundler's template, looks like this: ```ruby #!/usr/bin/env ruby # frozen_string_literal: true ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) require "rubygems" require "bundler/setup" load Gem.bin_path("rspec-core", "rspec") ``` Three lines do the work: 1. **`BUNDLE_GEMFILE ||=`** points Bundler at the project's Gemfile, computed relative to the stub itself, so the stub works from any directory. `||=` keeps a value already set, which lets a caller choose another Gemfile deliberately. 2. **`require "bundler/setup"`** runs `Bundler.setup`: locked versions, bundle-only gems. 3. **`load Gem.bin_path(...)`** finds the executable inside the bundle and runs it in the same process. ## Binstub or `bundle exec`? | | `bundle exec rspec` | `bin/rspec` | |---|---|---| | Uses locked versions | yes | yes | | Needs the `bundle` command on PATH | yes | no | | Works from a subdirectory | Bundler searches upward for the Gemfile | Gemfile path is baked into the stub | | Startup | loads Bundler's CLI, then the command | loads `bundler/setup`, then the command | | Committed to the repository | nothing to commit | usually committed | Both end up running the executable under `Bundler.setup`. A binstub skips starting Bundler's command-line interface, and gives the team one obvious, versioned entry point: `bin/rspec`, `bin/rake`. Frameworks often generate their own `bin/` scripts along the same lines. ## How the stub finds the right executable The last line, `load Gem.bin_path("rspec-core", "rspec")`, runs after `Bundler.setup`, which replaced RubyGems' executable lookup with a bundle-aware one. Its checks explain several messages you may meet: - If the named gem is not in the bundle, it raises `Gem::Exception`: "can't find executable rspec for gem rspec-core. rspec-core is not currently included in the bundle, perhaps you meant to add it to your Gemfile?" - If another gem in the bundle ships an executable with the same name, Bundler warns which gem's executable it is loading and suggests project-specific binstubs to disambiguate. - Asking for a gem that ships no executables, such as the `rspec` meta-gem, writes nothing; Bundler warns and lists the executables of the gems it depends on (`rspec-core has: rspec`). - `bundle binstubs bundler` is refused with a warning: Bundler's own version is selected by RubyGems, not by a stub. ## Options - `bundle binstubs rspec-core rubocop` - stubs for the listed gems; `bundle binstubs` with no gem prints an error and exits 1. - `--all` - stubs for every gem in the bundle; it cannot be combined with gem names. - `--force` - overwrite existing files, for example stubs from an older template. - `--shebang=jruby` - write a different interpreter name into the `#!` line instead of `ruby`. - `--standalone` - stubs that need neither RubyGems nor Bundler at runtime, for a bundle installed with `bundle install --standalone`. - The `bin` setting (`bundle config set bin exe/stubs`) changes the output directory. ## What changed in Bundler 4 Bundler 2 could generate stubs during installation with `bundle install --binstubs` and remembered the flag for later installs. Bundler 4 dropped every remembered install flag: - `bundle install --binstubs` raises `InvalidOption`: "The --binstubs option has been removed in favor of `bundle binstubs --all`". - `bundle binstubs --path DIR` raises too, telling you to use `bundle config set bin DIR`. The bundle-exec man page in the 4.0.21 tree still describes `bundle install --binstubs`; the CLI source is authoritative, and it rejects the flag. ## Traps - Running a bare `rspec` and assuming it is the stub; the shell finds `bin/rspec` only if `bin` is on `PATH` or you type the path. - Keeping stubs generated for a gem that has since left the Gemfile; running them raises because the gem is no longer in the bundle. - Overwriting a framework's hand-maintained `bin/` script with `--force`.
- Why does the generated stub use `||=` when setting `BUNDLE_GEMFILE`?So a value that is already set wins. If a caller exported `BUNDLE_GEMFILE=gemfiles/rack_3.gemfile` to test against another dependency set, the stub respects it; otherwise it falls back to the project's own Gemfile, computed relative to the stub's location.
- A CI script from the Bundler 2 era runs `bundle install --binstubs`. What happens on Bundler 4, and what replaces it?The install stops with `InvalidOption`: the `--binstubs` option has been removed in favor of `bundle binstubs --all`. Run `bundle install`, then `bundle binstubs --all`, or better, commit the stubs you need so CI does not regenerate them.
saying these in an interview costs you the question
- A binstub runs the newest installed version of the gem
- bin/rspec only works when run from the project root
- bundle install --binstubs still generates stubs in Bundler 4
- Binstubs replace the need for Gemfile.lock
- bundle binstubs rspec creates bin/rspec