In a Gemfile, what does putting gems inside `group :test do ... end` declare, and what does `require: false` on a gem change?
answer
- labels, not separate bundles
- ungrouped gems land in :default
- every group resolves into one lockfile
- require: false skips autorequire only
- dash-to-slash retry: rack-test, rack/test
basics
~20 sA group labels gems (ungrouped ones are in :default) so installs and Bundler.setup can include or skip them, yet every group still resolves into one Gemfile.lock. require: false keeps the gem installed and loadable but stops Bundler.require from requiring it automatically.
solid answer
~50 sGroups are labels on dependencies. A gem outside any `group` block or `group:` option belongs to `:default`; `group :development, :test do ... end` tags each gem inside with both names. The labels let an install skip gems (through the `without` setting) and let `Bundler.setup` or `Bundler.require` load a subset, but they never create separate resolutions: all groups are resolved together into one Gemfile.lock, so a gem cannot have one version in `:test` and another in `:development`. `require:` controls autorequire. By default `Bundler.require` requires the gem's name (retrying with dashes turned into slashes, so `rack-test` loads `rack/test`); `require: "path"` names another file; `require: false` requires nothing. The gem is still installed and on the load path, so you require it explicitly where it belongs, as with RuboCop or a stubbing library that should load only in the spec helper.
code
ruby · 5 lines# spec/spec_helper.rb for the API above
require "bundler/setup"
require "rack/test"
require "webmock/rspec" # declared with require: false, loaded here on purpose
require_relative "../app"go deeper
Recall that ungrouped gems are in :default, that group blocks tag gems for test or development use, and that require: false still installs the gem.
Explain that all groups resolve into one lockfile, how Bundler.require picks the file to load, including the dash-to-slash retry, and when require: false is the right choice.
Keep production images lean with the without setting rather than require flags, and use require: false to control load order and boot cost for gems with side effects.
Decide how the team splits groups across environments and when optional groups are worth their setup friction, trading install size against a single tested resolution.
## Groups are labels on dependencies Every `gem` line in a **Gemfile** produces a dependency with a list of **groups**. Bundler assigns them in three ways: - A gem declared outside any group gets the group **`:default`**. - A block, `group :test do ... end`, tags every gem inside it; `group :development, :test do` tags them with both. - An option on one line, `gem "rspec", group: :test` or `groups: [:development, :test]`, does the same for a single gem. Here is the Gemfile of a small JSON API with test-only gems: ```ruby source "https://rubygems.org" gem "sinatra", "~> 4.2" gem "puma" group :development, :test do gem "rubocop", require: false end group :test do gem "rspec", "~> 3.13" gem "rack-test" gem "webmock", require: false end ``` ## What groups are used for Group names matter to two consumers: 1. **Installation.** A production image does not need RSpec. The `without` setting (for example `bundle config set --local without development test`) tells `bundle install` to skip those groups. A group block declared with `optional: true` works the other way round: its gems are skipped unless the `with` setting names it. 2. **Loading.** `Bundler.setup` and `Bundler.require` accept group names, so code can put only some groups on the load path or require only some of them. What groups do **not** do is create separate bundles. The gemfile(5) man page is explicit: on `bundle install` Bundler downloads and evaluates *all* gems to build a single canonical list, so you cannot list different versions of the same gem in different groups. Even a skipped group is still used to resolve versions; that is what guarantees the production server runs the same versions you tested with, minus the groups it left out. ## What `require:` controls The `require:` option only matters to **`Bundler.require`**, the call some applications make at boot to require every gem in the named groups (with no arguments, the `:default` group). For each dependency it decides which file to require: | Declaration | What `Bundler.require` does | |---|---| | `gem "sinatra"` | `require "sinatra"` (the gem's name) | | `gem "rack-test"` | tries `require "rack-test"`; on a `LoadError` for that path it retries `require "rack/test"` | | `gem "redis", require: ["redis/connection/hiredis", "redis"]` | requires each listed file in order | | `gem "byebug", require: true` | same as the default: the gem's name | | `gem "rubocop", require: false` | requires nothing | The dash-to-slash retry exists only for the default: when `require:` is given explicitly and the file fails to load, Bundler raises `Bundler::GemRequireError` instead. ## What `require: false` does not change `require: false` is **not** an install switch. The gem is still resolved, recorded in Gemfile.lock, installed, and placed on the load path by `Bundler.setup`. Only the automatic require is skipped, so this still works in a spec helper: ```ruby require "webmock/rspec" ``` Typical reasons to use it: - **Command-line tools** such as RuboCop, which the application never loads. - **Gems with side effects on load**, such as a library that patches the HTTP client as soon as it is required; you want that only inside the test process, at a moment you choose. - **Gems whose entry file is not the one you need**, when you would rather require a sub-file explicitly. - **Boot time and memory**: every autorequired gem is loaded into every process that calls `Bundler.require`, including consoles and background workers. In an application that never calls `Bundler.require` and simply writes its own `require` lines after `require "bundler/setup"`, the option has no effect at all. ## Traps - Assuming gems in `:test` are absent from Gemfile.lock; every group is locked. - Using `require: false` to keep a gem off production machines; that is the job of the `without` setting. - Expecting `require: "rack/test"` to be necessary for a dashed gem name; the default retry already covers it. - Declaring the same gem in two groups with different requirements, which raises a `GemfileError`; declare it once with `groups: [...]`.
- What does `optional: true` on a group block change?Gems in an optional group are not installed by a plain `bundle install`; they are installed only when the `with` setting names the group. It suits heavy tooling a few developers need, such as profilers, without making every clone install it. The gems are still part of the resolution.
- If `rack-test` is declared with `require: "rack/test"`, is anything gained over the default?Very little. Without the option, `Bundler.require` first tries `require "rack-test"` and, on a `LoadError` for that path, retries with dashes turned into slashes, loading `rack/test`. Naming the path explicitly skips the failed attempt and makes a typo fail loudly with `Bundler::GemRequireError` instead of being silently skipped.
saying these in an interview costs you the question
- Gems in the test group are left out of Gemfile.lock
- require: false means the gem is not installed
- require: false removes the gem from the load path
- A gem outside any group block belongs to no group
- You can lock one version of a gem in :development and another in :test