Published 2022-03-06.
Last modified 2022-03-15.
Time to read: 7 minutes.
ruby
collection.
Rubocop is a code analyzer for Ruby. Jekyll and Jekyll plugins are written in Ruby. Rubocop is provided as a Ruby gem. This article discusses Rubocop in general, and adds extra details regarding Jekyll. If you do not care about Jekyll, just ignore those portions.
I demonstrated Rubocop in the Debugging Jekyll Plugins with an IDE article.
Installation Instructions
The official Rubocop installation instructions
describe the proper way to install Rubocop, and the same approach should be used to install other build tools,
such as rake
, rspec
.
In this article, I will first give some background, then demonstrate various ways of installing build tools as a development dependency.
Runtime Dependencies vs. Development Dependencies
Rubocop is only useful for development and should not be made a runtime dependency.
Rubocop itself has several dependencies,
which will be added to your project as runtime dependencies if you specify Rubocop as a runtime dependency:
parallel
,
parser
(which also has runtime dependencies),
rainbow
,
regexp_parser
,
rexml
,
rubocop-ast
(which also has runtime dependencies),
ruby-progressbar
, and
unicode-display_width
.
Bundler 2.1: Breaking Changes
Gems such as Rubocop are normally managed by bundler
.
The bundler.io
website has a lot of out-of-date information that is not flagged.
Unfortunately, most of the currently available advice on how to configure development dependencies for use with
bundler
also suffers from this problem.
Please notice the current version of bundler
, as of when this article was published:
$ bundler --version Bundler version 2.3.7
Bundler v2.1 happened years ago! Bundler behavior will change when 3.0 is released; do not allow your projects to stop working because you followed misinformation.
Following is the out-of-date command for installing development
dependencies on a development machine.
You have probably read many articles that say similar things.
THOSE ARTICLES ARE WRONG NOW.
These instructions were deprecated when
bundler
2.1
became available on December 15, 2019.
The following still works, but is expected to break when bundler
3.0 is released:
$ bundle install --with development # Do not do this!
Happily, the command-line help information for bundler
is up-to-date.
Here is the voluminous output; I will discuss the highlighted portion next
(scroll the man bundle-install
output to see the yellow highlighted text).
$ man bundle-install BUNDLE-INSTALL(1) BUNDLE-INSTALL(1) NAME bundle-install - Install the dependencies specified in your Gemfile SYNOPSIS bundle install [--binstubs[=DIRECTORY]] [--clean] [--deployment] [--frozen] [--full-index] [--gemfile=GEMFILE] [--jobs=NUMBER] [--lo‐ cal] [--no-cache] [--no-prune] [--path PATH] [--quiet] [--redown‐ load] [--retry=NUMBER] [--shebang] [--standalone[=GROUP[ GROUP...]]] [--system] [--trust-policy=POLICY] [--with=GROUP[ GROUP...]] [--without=GROUP[ GROUP...]] DESCRIPTION Install the gems specified in your Gemfile(5). If this is the first time you run bundle install (and a Gemfile.lock does not exist), Bundler will fetch all remote sources, resolve dependencies and in‐ stall all needed gems. If a Gemfile.lock does exist, and you have not updated your Gem‐ file(5), Bundler will fetch all remote sources, but use the depen‐ dencies specified in the Gemfile.lock instead of resolving dependen‐ cies. If a Gemfile.lock does exist, and you have updated your Gemfile(5), Bundler will use the dependencies in the Gemfile.lock for all gems that you did not update, but will re-resolve the dependencies of gems that you did update. You can find more information about this update process below under CONSERVATIVE UPDATING. OPTIONS The --clean, --deployment, --frozen, --no-prune, --path, --shebang, --system, --without and --with options are deprecated because they only make sense if they are applied to every subsequent bundle in‐ stall run automatically and that requires bundler to silently remem‐ ber them. Since bundler will no longer remember CLI flags in future versions, bundle config (see bundle-config(1)) should be used to ap‐ ply them permanently. --binstubs[=<directory>] Binstubs are scripts that wrap around executables. Bundler creates a small Ruby file (a binstub) that loads Bundler, runs the command, and puts it in bin/. This lets you link the binstub inside of an application to the exact gem version the application needs. Creates a directory (defaults to ~/bin) and places any exe‐ cutables from the gem there. These executables run in Bundler´s context. If used, you might add this directory to your environment´s PATH variable. For instance, if the rails gem comes with a rails executable, this flag will create a bin/rails executable that ensures that all referred dependen‐ cies will be resolved using the bundled gems. --clean On finishing the installation Bundler is going to remove any gems not present in the current Gemfile(5). Don´t worry, gems currently in use will not be removed. This option is deprecated in favor of the clean setting. --deployment In deployment mode, Bundler will ´roll-out´ the bundle for production or CI use. Please check carefully if you want to have this option enabled in your development environment. This option is deprecated in favor of the deployment setting. --redownload Force download every gem, even if the required versions are already available locally. --frozen Do not allow the Gemfile.lock to be updated after this in‐ stall. Exits non-zero if there are going to be changes to the Gemfile.lock. This option is deprecated in favor of the frozen setting. --full-index Bundler will not call Rubygems´ API endpoint (default) but download and cache a (currently big) index file of all gems. Performance can be improved for large bundles that seldom change by enabling this option. --gemfile=<gemfile> The location of the Gemfile(5) which Bundler should use. This defaults to a Gemfile(5) in the current working directory. In general, Bundler will assume that the location of the Gem‐ file(5) is also the project´s root and will try to find Gem‐ file.lock and vendor/cache relative to this location. --jobs=[<number>], -j[<number>] The maximum number of parallel download and install jobs. The default is 1. --local Do not attempt to connect to rubygems.org. Instead, Bundler will use the gems already present in Rubygems´ cache or in vendor/cache. Note that if an appropriate platform-specific gem exists on rubygems.org it will not be found. --no-cache Do not update the cache in vendor/cache with the newly bun‐ dled gems. This does not remove any gems in the cache but keeps the newly bundled gems from being cached during the in‐ stall. --no-prune Don´t remove stale gems from the cache when the installation finishes. This option is deprecated in favor of the no_prune setting. --path=<path> The location to install the specified gems to. This defaults to Rubygems´ setting. Bundler shares this location with Rubygems, gem install ... will have gem installed there, too. Therefore, gems installed without a --path ... setting will show up by calling gem list. Accordingly, gems installed to other locations will not get listed. This option is deprecated in favor of the path setting. --quiet Do not print progress information to the standard output. In‐ stead, Bundler will exit using a status code ($?). --retry=[<number>] Retry failed network or git requests for number times. --shebang=<ruby-executable> Uses the specified ruby executable (usually ruby) to execute the scripts created with --binstubs. In addition, if you use --binstubs together with --shebang jruby these executables will be changed to execute jruby instead. This option is deprecated in favor of the shebang setting. --standalone[=<list>] Makes a bundle that can work without depending on Rubygems or Bundler at runtime. A space separated list of groups to in‐ stall has to be specified. Bundler creates a directory named bundle and installs the bundle there. It also generates a bundle/bundler/setup.rb file to replace Bundler´s own setup in the manner required. Using this option implicitly sets path, which is a [remembered option][REMEMBERED OPTIONS]. --system Installs the gems specified in the bundle to the system´s Rubygems location. This overrides any previous configuration of --path. This option is deprecated in favor of the system setting. --trust-policy=[<policy>] Apply the Rubygems security policy policy, where policy is one of HighSecurity, MediumSecurity, LowSecurity, AlmostNoSe‐ curity, or NoSecurity. For more details, please see the Rubygems signing documentation linked below in SEE ALSO. --with=<list> A space-separated list of groups referencing gems to install. If an optional group is given it is installed. If a group is given that is in the remembered list of groups given to --without, it is removed from that list. This option is deprecated in favor of the with setting. --without=<list> A space-separated list of groups referencing gems to skip during installation. If a group is given that is in the re‐ membered list of groups given to --with, it is removed from that list. This option is deprecated in favor of the without setting. DEPLOYMENT MODE Bundler´s defaults are optimized for development. To switch to de‐ faults optimized for deployment and for CI, use the --deployment flag. Do not activate deployment mode on development machines, as it will cause an error when the Gemfile(5) is modified. 1. A Gemfile.lock is required. To ensure that the same versions of the gems you developed with and tested with are also used in deployments, a Gemfile.lock is required. This is mainly to ensure that you remember to check your Gem‐ file.lock into version control. 2. The Gemfile.lock must be up to date In development, you can modify your Gemfile(5) and re-run bundle install to conservatively update your Gemfile.lock snapshot. In deployment, your Gemfile.lock should be up-to-date with changes made in your Gemfile(5). 3. Gems are installed to vendor/bundle not your default system lo‐ cation In development, it´s convenient to share the gems used in your application with other applications and other scripts that run on the system. In deployment, isolation is a more important default. In addi‐ tion, the user deploying the application may not have permission to install gems to the system, or the web server may not have permission to read them. As a result, bundle install --deployment installs gems to the vendor/bundle directory in the application. This may be overrid‐ den using the --path option. SUDO USAGE By default, Bundler installs gems to the same location as gem in‐ stall. In some cases, that location may not be writable by your Unix user. In that case, Bundler will stage everything in a temporary direc‐ tory, then ask you for your sudo password in order to copy the gems into their system location. From your perspective, this is identical to installing the gems di‐ rectly into the system. You should never use sudo bundle install. This is because several other steps in bundle install must be performed as the current user: • Updating your Gemfile.lock • Updating your vendor/cache, if necessary • Checking out private git repositories using your user´s SSH keys Of these three, the first two could theoretically be performed by chowning the resulting files to $SUDO_USER. The third, however, can only be performed by invoking the git command as the current user. Therefore, git gems are downloaded and installed into ~/.bundle rather than $GEM_HOME or $BUNDLE_PATH. As a result, you should run bundle install as the current user, and Bundler will ask for your password if it is needed to put the gems into their final location. INSTALLING GROUPS By default, bundle install will install all gems in all groups in your Gemfile(5), except those declared for a different platform. However, you can explicitly tell Bundler to skip installing certain groups with the --without option. This option takes a space-sepa‐ rated list of groups. While the --without option will skip installing the gems in the specified groups, it will still download those gems and use them to resolve the dependencies of every gem in your Gemfile(5). This is so that installing a different set of groups on another ma‐ chine (such as a production server) will not change the gems and versions that you have already developed and tested against. Bundler offers a rock-solid guarantee that the third-party code you are running in development and testing is also the third-party code you are running in production. You can choose to exclude some of that code in different environments, but you will never be caught flat-footed by different versions of third-party code being used in different environments. For a simple illustration, consider the following Gemfile(5): source ´https://rubygems.org´ gem ´sinatra´ group :production do gem ´rack-perftools-profiler´ end In this case, sinatra depends on any version of Rack (>= 1.0), while rack-perftools-profiler depends on 1.x (~> 1.0). When you run bundle install --without production in development, we look at the dependencies of rack-perftools-profiler as well. That way, you do not spend all your time developing against Rack 2.0, us‐ ing new APIs unavailable in Rack 1.x, only to have Bundler switch to Rack 1.2 when the production group is used. This should not cause any problems in practice, because we do not attempt to install the gems in the excluded groups, and only evalu‐ ate as part of the dependency resolution process. This also means that you cannot include different versions of the same gem in different groups, because doing so would result in dif‐ ferent sets of dependencies used in development and production. Be‐ cause of the vagaries of the dependency resolution process, this usually affects more than the gems you list in your Gemfile(5), and can (surprisingly) radically change the gems you are using. THE GEMFILE.LOCK When you run bundle install, Bundler will persist the full names and versions of all gems that you used (including dependencies of the gems specified in the Gemfile(5)) into a file called Gemfile.lock. Bundler uses this file in all subsequent calls to bundle install, which guarantees that you always use the same exact code, even as your application moves across machines. Because of the way dependency resolution works, even a seemingly small change (for instance, an update to a point-release of a depen‐ dency of a gem in your Gemfile(5)) can result in radically different gems being needed to satisfy all dependencies. As a result, you SHOULD check your Gemfile.lock into version con‐ trol, in both applications and gems. If you do not, every machine that checks out your repository (including your production server) will resolve all dependencies again, which will result in different versions of third-party code being used if any of the gems in the Gemfile(5) or any of their dependencies have been updated. When Bundler first shipped, the Gemfile.lock was included in the .gitignore file included with generated gems. Over time, however, it became clear that this practice forces the pain of broken dependen‐ cies onto new contributors, while leaving existing contributors po‐ tentially unaware of the problem. Since bundle install is usually the first step towards a contribution, the pain of broken dependen‐ cies would discourage new contributors from contributing. As a re‐ sult, we have revised our guidance for gem authors to now recommend checking in the lock for gems. CONSERVATIVE UPDATING When you make a change to the Gemfile(5) and then run bundle in‐ stall, Bundler will update only the gems that you modified. In other words, if a gem that you did not modify worked before you called bundle install, it will continue to use the exact same ver‐ sions of all dependencies as it used before the update. Let´s take a look at an example. Here´s your original Gemfile(5): source ´https://rubygems.org´ gem ´actionpack´, ´2.3.8´ gem ´activemerchant´ In this case, both actionpack and activemerchant depend on ac‐ tivesupport. The actionpack gem depends on activesupport 2.3.8 and rack ~> 1.1.0, while the activemerchant gem depends on activesupport >= 2.3.2, braintree >= 2.0.0, and builder >= 2.0.0. When the dependencies are first resolved, Bundler will select ac‐ tivesupport 2.3.8, which satisfies the requirements of both gems in your Gemfile(5). Next, you modify your Gemfile(5) to: source ´https://rubygems.org´ gem ´actionpack´, ´3.0.0.rc´ gem ´activemerchant´ The actionpack 3.0.0.rc gem has a number of new dependencies, and updates the activesupport dependency to = 3.0.0.rc and the rack de‐ pendency to ~> 1.2.1. When you run bundle install, Bundler notices that you changed the actionpack gem, but not the activemerchant gem. It evaluates the gems currently being used to satisfy its requirements: activesupport 2.3.8 also used to satisfy a dependency in activemerchant, which is not being updated rack ~> 1.1.0 not currently being used to satisfy another dependency Because you did not explicitly ask to update activemerchant, you would not expect it to suddenly stop working after updating action‐ pack. However, satisfying the new activesupport 3.0.0.rc dependency of actionpack requires updating one of its dependencies. Even though activemerchant declares a very loose dependency that theoretically matches activesupport 3.0.0.rc, Bundler treats gems in your Gemfile(5) that have not changed as an atomic unit together with their dependencies. In this case, the activemerchant dependency is treated as activemerchant 1.7.1 + activesupport 2.3.8, so bundle install will report that it cannot update actionpack. To explicitly update actionpack, including its dependencies which other gems in the Gemfile(5) still depend on, run bundle update ac‐ tionpack (see bundle update(1)). Summary: In general, after making a change to the Gemfile(5) , you should first try to run bundle install, which will guarantee that no other gem in the Gemfile(5) is impacted by the change. If that does not work, run bundle update(1) bundle-update.1.html. SEE ALSO • Gem install docs http://guides.rubygems.org/rubygems-basics/#in‐ stalling-gems • Rubygems signing docs http://guides.rubygems.org/security/ November 2020 BUNDLE-INSTALL(1)
To paraphrase the highlighted passage:
bundler
’s --without
and --with
options are deprecated because they
require bundler
to remember them.
Since bundler
will no longer remember CLI flags in a planned major upgrade, bundle config
should be used instead to apply them permanently.
Let’s look at the man
information for the bundle config
command.
BUNDLE-CONFIG(1) BUNDLE-CONFIG(1)
NAME
bundle-config - Set bundler configuration options
SYNOPSIS
bundle config [list|get|set|unset] [name [value]]
DESCRIPTION
This command allows you to interact with Bundler´s configuration
system.
Bundler loads configuration settings in this order:
1. Local config (<project_root>/.bundle/config or $BUNDLE_APP_CON‐
FIG/config)
2. Environmental variables (ENV)
3. Global config (~/.bundle/config)
4. Bundler default config
Executing bundle config list with will print a list of all bundler
configuration for the current bundle, and where that configuration
was set.
Executing bundle config get <name> will print the value of that con‐
figuration setting, and where it was set.
Executing bundle config set <name> <value> will set that configura‐
tion to the value specified for all bundles executed as the current
user. The configuration will be stored in ~/.bundle/config. If name
already is set, name will be overridden and user will be warned.
Executing bundle config set --global <name> <value> works the same
as above.
Executing bundle config set --local <name> <value> will set that
configuration in the directory for the local application. The con‐
figuration will be stored in <project_root>/.bundle/config. If BUN‐
DLE_APP_CONFIG is set, the configuration will be stored in $BUN‐
DLE_APP_CONFIG/config.
Executing bundle config unset <name> will delete the configuration
in both local and global sources.
Executing bundle config unset --global <name> will delete the con‐
figuration only from the user configuration.
Executing bundle config unset --local <name> <value> will delete the
configuration only from the local application.
Executing bundle with the BUNDLE_IGNORE_CONFIG environment variable
set will cause it to ignore all configuration.
Executing bundle config set --local disable_multisource true up‐
grades the warning about the Gemfile containing multiple primary
sources to an error. Executing bundle config unset disable_multi‐
source downgrades this error to a warning.
REMEMBERING OPTIONS
Flags passed to bundle install or the Bundler runtime, such as
--path foo or --without production, are remembered between commands
and saved to your local application´s configuration (normally,
./.bundle/config).
However, this will be changed in bundler 3, so it´s better not to
rely on this behavior. If these options must be remembered, it´s
better to set them using bundle config (e.g., bundle config set
--local path foo).
The options that can be configured are:
bin Creates a directory (defaults to ~/bin) and place any exe‐
cutables from the gem there. These executables run in
Bundler´s context. If used, you might add this directory to
your environment´s PATH variable. For instance, if the rails
gem comes with a rails executable, this flag will create a
bin/rails executable that ensures that all referred dependen‐
cies will be resolved using the bundled gems.
deployment
In deployment mode, Bundler will ´roll-out´ the bundle for
production use. Please check carefully if you want to have
this option enabled in development or test environments.
path The location to install the specified gems to. This defaults
to Rubygems´ setting. Bundler shares this location with
Rubygems, gem install ... will have gem installed there, too.
Therefore, gems installed without a --path ... setting will
show up by calling gem list. Accordingly, gems installed to
other locations will not get listed.
without
A space-separated list of groups referencing gems to skip
during installation.
with A space-separated list of groups referencing gems to include
during installation.
BUILD OPTIONS
You can use bundle config to give Bundler the flags to pass to the
gem installer every time bundler tries to install a particular gem.
A very common example, the mysql gem, requires Snow Leopard users to
pass configuration flags to gem install to specify where to find the
mysql_config executable.
gem install mysql -- \
--with-mysql-config=/usr/local/mysql/bin/mysql_config
Since the specific location of that executable can change from ma‐
chine to machine, you can specify these flags on a per-machine ba‐
sis.
bundle config set --global build.mysql \
--with-mysql-config=/usr/local/mysql/bin/mysql_config
After running this command, every time bundler needs to install the
mysql gem, it will pass along the flags you specified.
CONFIGURATION KEYS
Configuration keys in bundler have two forms: the canonical form and
the environment variable form.
For instance, passing the --without flag to bundle install(1) bun‐
dle-install.1.html prevents Bundler from installing certain groups
specified in the Gemfile(5). Bundler persists this value in
app/.bundle/config so that calls to Bundler.setup do not try to find
gems from the Gemfile that you didn´t install. Additionally, subse‐
quent calls to bundle install(1) bundle-install.1.html remember this
setting and skip those groups.
The canonical form of this configuration is "without". To convert
the canonical form to the environment variable form, capitalize it,
and prepend BUNDLE_. The environment variable form of "without" is
BUNDLE_WITHOUT.
Any periods in the configuration keys must be replaced with two un‐
derscores when setting it via environment variables. The configura‐
tion key local.rack becomes the environment variable BUNDLE_LO‐
CAL__RACK.
LIST OF AVAILABLE KEYS
The following is a list of all configuration keys and their purpose.
You can learn more about their operation in bundle install(1) bun‐
dle-install.1.html.
• allow_bundler_dependency_conflicts (BUNDLE_ALLOW_BUNDLER_DEPEN‐
DENCY_CONFLICTS): Allow resolving to specifications that have
dependencies on bundler that are incompatible with the running
Bundler version.
• allow_deployment_source_credential_changes (BUNDLE_ALLOW_DEPLOY‐
MENT_SOURCE_CREDENTIAL_CHANGES): When in deployment mode, allow
changing the credentials to a gem´s source. Ex:
https://some.host.com/gems/path/ -> https://user_name:pass‐
word@some.host.com/gems/path
• allow_offline_install (BUNDLE_ALLOW_OFFLINE_INSTALL): Allow
Bundler to use cached data when installing without network ac‐
cess.
• auto_clean_without_path (BUNDLE_AUTO_CLEAN_WITHOUT_PATH): Auto‐
matically run bundle clean after installing when an explicit
path has not been set and Bundler is not installing into the
system gems.
• auto_install (BUNDLE_AUTO_INSTALL): Automatically run bundle in‐
stall when gems are missing.
• bin (BUNDLE_BIN): Install executables from gems in the bundle to
the specified directory. Defaults to false.
• cache_all (BUNDLE_CACHE_ALL): Cache all gems, including path and
git gems. This needs to be explicitly configured on bundler 1
and bundler 2, but will be the default on bundler 3.
• cache_all_platforms (BUNDLE_CACHE_ALL_PLATFORMS): Cache gems for
all platforms.
• cache_path (BUNDLE_CACHE_PATH): The directory that bundler will
place cached gems in when running bundle package, and that
bundler will look in when installing gems. Defaults to ven‐
dor/cache.
• clean (BUNDLE_CLEAN): Whether Bundler should run bundle clean
automatically after bundle install.
• console (BUNDLE_CONSOLE): The console that bundle console
starts. Defaults to irb.
• default_install_uses_path (BUNDLE_DEFAULT_INSTALL_USES_PATH):
Whether a bundle install without an explicit --path argument de‐
faults to installing gems in .bundle.
• deployment (BUNDLE_DEPLOYMENT): Disallow changes to the Gemfile.
When the Gemfile is changed and the lockfile has not been up‐
dated, running Bundler commands will be blocked.
• disable_checksum_validation (BUNDLE_DISABLE_CHECKSUM_VALIDA‐
TION): Allow installing gems even if they do not match the
checksum provided by RubyGems.
• disable_exec_load (BUNDLE_DISABLE_EXEC_LOAD): Stop Bundler from
using load to launch an executable in-process in bundle exec.
• disable_local_branch_check (BUNDLE_DISABLE_LOCAL_BRANCH_CHECK):
Allow Bundler to use a local git override without a branch spec‐
ified in the Gemfile.
• disable_multisource (BUNDLE_DISABLE_MULTISOURCE): When set, Gem‐
files containing multiple sources will produce errors instead of
warnings. Use bundle config unset disable_multisource to unset.
• disable_shared_gems (BUNDLE_DISABLE_SHARED_GEMS): Stop Bundler
from accessing gems installed to RubyGems´ normal location.
• disable_version_check (BUNDLE_DISABLE_VERSION_CHECK): Stop
Bundler from checking if a newer Bundler version is available on
rubygems.org.
• force_ruby_platform (BUNDLE_FORCE_RUBY_PLATFORM): Ignore the
current machine´s platform and install only ruby platform gems.
As a result, gems with native extensions will be compiled from
source.
• frozen (BUNDLE_FROZEN): Disallow changes to the Gemfile. When
the Gemfile is changed and the lockfile has not been updated,
running Bundler commands will be blocked. Defaults to true when
--deployment is used.
• gem.push_key (BUNDLE_GEM__PUSH_KEY): Sets the --key parameter
for gem push when using the rake release command with a private
gemstash server.
• gemfile (BUNDLE_GEMFILE): The name of the file that bundler
should use as the Gemfile. This location of this file also sets
the root of the project, which is used to resolve relative paths
in the Gemfile, among other things. By default, bundler will
search up from the current working directory until it finds a
Gemfile.
• global_gem_cache (BUNDLE_GLOBAL_GEM_CACHE): Whether Bundler
should cache all gems globally, rather than locally to the in‐
stalling Ruby installation.
• ignore_messages (BUNDLE_IGNORE_MESSAGES): When set, no post in‐
stall messages will be printed. To silence a single gem, use dot
notation like ignore_messages.httparty true.
• init_gems_rb (BUNDLE_INIT_GEMS_RB) Generate a gems.rb instead of
a Gemfile when running bundle init.
• jobs (BUNDLE_JOBS): The number of gems Bundler can install in
parallel. Defaults to 1.
• no_install (BUNDLE_NO_INSTALL): Whether bundle package should
skip installing gems.
• no_prune (BUNDLE_NO_PRUNE): Whether Bundler should leave out‐
dated gems unpruned when caching.
• only_update_to_newer_versions (BUNDLE_ONLY_UPDATE_TO_NEWER_VER‐
SIONS): During bundle update, only resolve to newer versions of
the gems in the lockfile.
• path (BUNDLE_PATH): The location on disk where all gems in your
bundle will be located regardless of $GEM_HOME or $GEM_PATH val‐
ues. Bundle gems not found in this location will be installed by
bundle install. Defaults to Gem.dir. When --deployment is used,
defaults to vendor/bundle.
• path.system (BUNDLE_PATH__SYSTEM): Whether Bundler will install
gems into the default system path (Gem.dir).
• path_relative_to_cwd (BUNDLE_PATH_RELATIVE_TO_CWD) Makes --path
relative to the CWD instead of the Gemfile.
• plugins (BUNDLE_PLUGINS): Enable Bundler´s experimental plugin
system.
• prefer_patch (BUNDLE_PREFER_PATCH): Prefer updating only to next
patch version during updates. Makes bundle update calls equiva‐
lent to bundler update --patch.
• print_only_version_number (BUNDLE_PRINT_ONLY_VERSION_NUMBER)
Print only version number from bundler --version.
• redirect (BUNDLE_REDIRECT): The number of redirects allowed for
network requests. Defaults to 5.
• retry (BUNDLE_RETRY): The number of times to retry failed net‐
work requests. Defaults to 3.
• setup_makes_kernel_gem_public (BUNDLE_SETUP_MAKES_KER‐
NEL_GEM_PUBLIC): Have Bundler.setup make the Kernel#gem method
public, even though RubyGems declares it as private.
• shebang (BUNDLE_SHEBANG): The program name that should be in‐
voked for generated binstubs. Defaults to the ruby install name
used to generate the binstub.
• silence_deprecations (BUNDLE_SILENCE_DEPRECATIONS): Whether
Bundler should silence deprecation warnings for behavior that
will be changed in the next major version.
• silence_root_warning (BUNDLE_SILENCE_ROOT_WARNING): Silence the
warning Bundler prints when installing gems as root.
• ssl_ca_cert (BUNDLE_SSL_CA_CERT): Path to a designated CA cer‐
tificate file or folder containing multiple certificates for
trusted CAs in PEM format.
• ssl_client_cert (BUNDLE_SSL_CLIENT_CERT): Path to a designated
file containing a X.509 client certificate and key in PEM for‐
mat.
• ssl_verify_mode (BUNDLE_SSL_VERIFY_MODE): The SSL verification
mode Bundler uses when making HTTPS requests. Defaults to verify
peer.
• suppress_install_using_messages (BUNDLE_SUPPRESS_INSTALL_US‐
ING_MESSAGES): Avoid printing Using ... messages during instal‐
lation when the version of a gem has not changed.
• system_bindir (BUNDLE_SYSTEM_BINDIR): The location where
RubyGems installs binstubs. Defaults to Gem.bindir.
• timeout (BUNDLE_TIMEOUT): The seconds allowed before timing out
for network requests. Defaults to 10.
• unlock_source_unlocks_spec (BUNDLE_UNLOCK_SOURCE_UNLOCKS_SPEC):
Whether running bundle update --source NAME unlocks a gem with
the given name. Defaults to true.
• update_requires_all_flag (BUNDLE_UPDATE_REQUIRES_ALL_FLAG) Re‐
quire passing --all to bundle update when everything should be
updated, and disallow passing no options to bundle update.
• user_agent (BUNDLE_USER_AGENT): The custom user agent fragment
Bundler includes in API requests.
• with (BUNDLE_WITH): A :-separated list of groups whose gems
bundler should install.
• without (BUNDLE_WITHOUT): A :-separated list of groups whose
gems bundler should not install.
In general, you should set these settings per-application by using
the applicable flag to the bundle install(1) bundle-install.1.html
or bundle package(1) bundle-package.1.html command.
You can set them globally either via environment variables or bundle
config, whichever is preferable for your setup. If you use both, en‐
vironment variables will take preference over global settings.
LOCAL GIT REPOS
Bundler also allows you to work against a git repository locally in‐
stead of using the remote version. This can be achieved by setting
up a local override:
bundle config set \
--local local.GEM_NAME /path/to/local/git/repository
For example, in order to use a local Rack repository, a developer
could call:
bundle config set --local local.rack ~/Work/git/rack
Now instead of checking out the remote git repository, the local
override will be used. Similar to a path source, every time the lo‐
cal git repository change, changes will be automatically picked up
by Bundler. This means a commit in the local git repo will update
the revision in the Gemfile.lock to the local git repo revision.
This requires the same attention as git submodules. Before pushing
to the remote, you need to ensure the local override was pushed,
otherwise you may point to a commit that only exists in your local
machine. You´ll also need to CGI escape your usernames and passwords
as well.
Bundler does many checks to ensure a developer won´t work with in‐
valid references. Particularly, we force a developer to specify a
branch in the Gemfile in order to use this feature. If the branch
specified in the Gemfile and the current branch in the local git
repository do not match, Bundler will abort. This ensures that a de‐
veloper is always working against the correct branches, and prevents
accidental locking to a different branch.
Finally, Bundler also ensures that the current revision in the Gem‐
file.lock exists in the local git repository. By doing this, Bundler
forces you to fetch the latest changes in the remotes.
MIRRORS OF GEM SOURCES
Bundler supports overriding gem sources with mirrors. This allows
you to configure rubygems.org as the gem source in your Gemfile
while still using your mirror to fetch gems.
bundle config set --global mirror.SOURCE_URL MIRROR_URL
For example, to use a mirror of rubygems.org hosted at rubygems-mir‐
ror.org:
bundle config set \
--global mirror.http://rubygems.org http://rubygems-mirror.org
Each mirror also provides a fallback timeout setting. If the mirror
does not respond within the fallback timeout, Bundler will try to
use the original server instead of the mirror.
bundle config set --global mirror.SOURCE_URL.fallback_timeout TIMEOUT
For example, to fall back to rubygems.org after 3 seconds:
bundle config set \
--global mirror.https://rubygems.org.fallback_timeout 3
The default fallback timeout is 0.1 seconds, but the setting can
currently only accept whole seconds (for example, 1, 15, or 30).
CREDENTIALS FOR GEM SOURCES
Bundler allows you to configure credentials for any gem source,
which allows you to avoid putting secrets into your Gemfile.
bundle config set --global SOURCE_HOSTNAME USERNAME:PASSWORD
For example, to save the credentials of user claudette for the gem
source at gems.longerous.com, you would run:
bundle config set --global gems.longerous.com claudette:s00pers3krit
Or you can set the credentials as an environment variable like this:
export BUNDLE_GEMS__LONGEROUS__COM="claudette:s00pers3krit"
For gems with a git source with HTTP(S) URL you can specify creden‐
tials like so:
bundle config set \
--global https://github.com/rubygems/rubygems.git \
username:password
Or you can set the credentials as an environment variable like so:
export BUNDLE_GITHUB__COM=username:password
This is especially useful for private repositories on hosts such as
Github, where you can use personal OAuth tokens:
export BUNDLE_GITHUB__COM=abcd0123generatedtoken:x-oauth-basic
CONFIGURE BUNDLER DIRECTORIES
Bundler´s home, config, cache and plugin directories are able to be
configured through environment variables. The default location for
Bundler´s home directory is ~/.bundle, which all directories inherit
from by default. The following outlines the available environment
variables and their default values
BUNDLE_USER_HOME : $HOME/.bundle
BUNDLE_USER_CACHE : $BUNDLE_USER_HOME/cache
BUNDLE_USER_CONFIG : $BUNDLE_USER_HOME/config
BUNDLE_USER_PLUGIN : $BUNDLE_USER_HOME/plugin
November 2020 BUNDLE-CONFIG(1)
The above man
information has a silly error: it states configuration priorities in reverse.
The following is correct.
Bundler loads configuration settings with the following priority, such that the lower priority settings are replaced by higher priority settings:
- Local config (
<project_root>/.bundle/config
or$BUNDLE_APP_CONFIG/config
) - Environmental variables (
ENV
) - Global config (
~/.bundle/config
) - Bundler default config
The above documentation contained this explanation of the --local
option:
bundle config set --local <name> <value>
will set that
configuration in the directory for the local application.
The configuration will be stored in <project_root>/.bundle/config
.
If BUNDLE_APP_CONFIG
is set, the configuration will be stored in $BUNDLE_APP_CONFIG/config
.
The .bundle
directory is specific to each machine and should not be added to source control.
For git, that means it should be added to .gitignore
for each project:
$ echo .bundle/ >> .gitignore
You could also add .bundle/
to your
global .gitignore
.
Example: Persistent Setting
Let’s use the above information to install a gem, including development dependencies.
We will invoke the bundle
command twice.
Immediately below is the first usage of the command, which permanently establishes that the current project
(and only the current project) is to be built using normal production dependencies plus the development
dependencies.
This is a persistent setting; it will affect all future bundle
invocations –
again, for this project only.
You can change this setting at any time, and the new value of the setting will also persist.
$ bundle config set --local with development
The above bundle config
command created .bundle/config
and installed the production dependencies plus the development dependencies.
Alright, we've told bundle
how we want it to behave (for the current project only).
Let's ask bundle
to actually do something:
$ bundle install
In case you are curious, the contents of the file that was created a second ago are:
$ cat .bundle/config --- BUNDLE_WITH: "development"
The default subcommand for bundle
is install
.
That means the above bundle install
command could and should be written as follows.
I say “should” because I follow the principle that default values should never be written.
Reasonable competence in reading code for mainstream languages and packages is assumed.
Generally, I only remind readers of default values once.
$ bundle
Let’s look at the settings for bundle config
.
$ bundle config Settings are listed in order of priority. The top value will be used. gem.changelog Set for the current user (/home/mslinn/.bundle/config): false gem.ci Set for the current user (/home/mslinn/.bundle/config): false gem.coc Set for the current user (/home/mslinn/.bundle/config): false gem.linter Set for the current user (/home/mslinn/.bundle/config): false gem.mit Set for the current user (/home/mslinn/.bundle/config): true gem.test Set for the current user (/home/mslinn/.bundle/config): false with Set for your local app (/mnt/f/work/my_app/.bundle/config): [:development]
Example: Environment Settings
If you do not want to use persistent settings but prefer to apply settings as needed, use environment variables to apply the settings.
$ BUNDLE_WITH="development" bundle install
Specifying Development Dependencies
Update 2024-05-06
Rubocop v 1.44 added a new cop:
Gemspec/DevelopmentDependencies
.
Enforce that development dependencies for a gem are specified in Gemfile
,
rather than in the gemspec using add_
.
Alternatively, using EnforcedStyle: gemspec
, enforce that all dependencies are specified in gemspec,
rather than in Gemfile
.
TODO: Rewrite this section to incorporate the new information.
Going beyond what the official (and horribly misguided) installation instructions say, here are some of the various ways to specify and install Rubocop. The following instructions will install Rubocop so it is not a runtime dependency. This first option is for Ruby gem developers only:
Using Gemfile
This option specifies Rubocop and other development tools as dependencies of the project, but not the gem.
The project Gemfile
would contain require: false
,
which means those gems would not be loaded until the first time they are explicitly required.
This means that you'll have to require it in your code when you need it; however, these are most often used as command-line programs,
and are much less often invoked programmatically, so this is not a problem.
group :development do gem 'rubocop', '>= 1.58.0', require: false gem 'rubocop-jekyll', '>= 0.13.0', require: false gem 'rubocop-md', require: false gem 'rubocop-performance', require: false gem 'rubocop-rake', require: false gem 'rubocop-rspec', require: false gem 'ruby-debug-ide', require: false end
group :test, :development do gem 'bundler', require: false gem 'rake', require: false gem 'rspec', require: false gem 'bundler', require: false gem 'debase', require: false gem 'rake', require: false gem 'rspec', '~> 3.0' end
Installing Default Dependencies
Your project might explicitly define a production
group of dependencies, or, very commonly,
production dependencies might be assumed to be those that remain when development
dependencies are not required.
If your project has an explicit production
group of dependencies, install them like this:
$ bundle config set --local with production
$ bundle install
You could also use an environment variable:
$ BUNDLE_WITH="production" bundle install
Otherwise, if the project does not have an explicit production
group of dependencies,
turn off all dependency groups and install production dependencies like this:
Option 1
Delete all with
settings locally and globally.
$ bundle config --delete with
$ bundle
Option 2
Only delete local with
settings.
$ bundle config set --local with ''
$ bundle
Option 3
Ignore all configuration settings.
$ BUNDLE_IGNORE_CONFIG=true bundle
Jekyll Coding Standards
Rubocop standards for Jekyll are provided by
rubocop-jekyll
,
a gem containing a RuboCop extension that enforces common code style in Jekyll and Jekyll plugins.
Here is an example of its suggestions:
The standards enforced by the default rubocop-jekyll
settings are appropriate for older Ruby projects that have migrated from Ruby 1.8 and earlier.
I am working with a younger code base, and follow more current standards.
Ruby 2.4 is no longer a viable version; Ruby 2.6 or later must be used.
While Ruby 2.7.6 is a better choice,
Ruby 3.0.0 is about three times faster than previous versions,
and Ruby 3.1.0+ is best for Jekyll development.
Install rubocop-jekyll
by placing it in your .gemspec
.
Following is the gemspec
for my jekyll_auto_redirect
Jekyll plugin.
I highlighted all the development dependencies in yellow (scroll down to see them).
require_relative 'lib/jekyll_auto_redirect/version' Gem::Specification.new do |spec| spec.name = 'jekyll_auto_redirect' spec.summary = 'Automatically generate HTTP 301 redirects for pages that move or are deleted on Jekyll site.' spec.version = Jekyll::PageLookup::VERSION spec.authors = ['Mike Slinn'] spec.email = 'mslinn@mslinn.com' spec.homepage = 'https://github.com/mslinn/jekyll_auto_redirect' spec.licenses = ['MIT'] spec.files = `git ls-files -z`.split("\x0") spec.executables = spec.files.grep(%r!^bin/!) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r!^(test|spec|features)/!) spec.require_paths = ['lib'] spec.required_ruby_version = '>= 2.5.0' spec.add_dependency 'jekyll', '>= 4.3.2', '< 5.0' end
Here are the overrides that I use for this Jekyll-powered website:
require: - rubocop-jekyll - rubocop-md - rubocop-performance - rubocop-rake - rubocop-rspec inherit_gem: rubocop-jekyll: .rubocop.yml AllCops: Exclude: - _site/**/* - binstub/**/* - Gemfile* - exe/**/* - jekyll/**/* - vendor/**/* NewCops: enable TargetRubyVersion: 2.6 Gemspec/RequireMFA: Enabled: false Jekyll/NoPutsAllowed: Enabled: false # Some of my Ruby code is plugins, other Ruby code is not, so this rule is a PITA Naming/FileName: Exclude: - _bin/**/* Layout/HashAlignment: EnforcedColonStyle: table EnforcedHashRocketStyle: table Layout/LeadingCommentSpace: Exclude: - _bin/**/* Layout/LineLength: Max: 150 Layout/FirstHashElementIndentation: Enabled: false Layout/MultilineMethodCallIndentation: Enabled: false Metrics/AbcSize: Max: 40 Metrics/BlockLength: Max: 50 Metrics/CyclomaticComplexity: Max: 15 Metrics/MethodLength: Max: 30 Metrics/PerceivedComplexity: Max: 20 Style/Alias: Exclude: - _plugins/symlink_watcher.rb - blog/bin/avImport Style/Documentation: Enabled: false Style/FrozenStringLiteralComment: Enabled: false Style/HashSyntax: EnforcedStyle: ruby19 EnforcedShorthandSyntax: consistent Style/PercentLiteralDelimiters: Enabled: false Style/RegexpLiteral: Enabled: false Style/StringLiterals: Enabled: false Style/StringLiteralsInInterpolation: Enabled: false Style/TrailingCommaInArrayLiteral: Enabled: false Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: comma
Rubocop-jekyll
introduces a few new coding standards, which I applaud.
However, as I explain when
discussing Jekyll logging,
the Jekyll logger is best used by the programmers workin on Jekyll itself, not plugin developers.
The Jekyll/NoPutsAllowed
rule message is currently:
Jekyll/NoPutsAllowed: Avoid using puts to print things. Use Jekyll.logger instead.
One day I hope to change the message to something like:
Jekyll/NoPutsAllowed: Avoid using puts to print things. Use PluginLogger.logger instead.
Driving Rubocop With Bundler
I prefer to use Rubocop using bundler
because bundler
manages multiple versions of gems transparently.
From the top-level directory of a Ruby project, type:
$ bundle exec rubocop
Where is that GEM?
– Yogi Berra
Sometimes you really need to know where a dependent gem is,
and you search a long time for it and struggle to cause it to be invoked.
I experienced this trying to get Visual Studio Code’s Rubocop plugin
to work with version 1.18.x of Rubocop
because the project had dependency conflicts.
You can see my problem below; there are at least two versions of the Rubocop gem installed.
Bundle exec
returns the version that it resolves to,
which is different from the version on the PATH
:
$ rbenv rehash # Just making sure
$ bundle exec rubocop --version 1.18.0
$ rubocop --version 1.26.0
By the way, the following funny syntax is how to specify the execution of a specific version of a gem on the command line. Simply enclose the version string within the underscore characters:
$ rubocop _1.18.0_ --version 1.18.0
$ rubocop _1.26.0_ --version 1.26.0
Eventually I came up with this magic setting:
{ "ruby.rubocop.configFilePath": ".rubocop.yml", "ruby.rubocop.useBundler": true, "ruby.rubocop.executePath": "/home/mslinn/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/rubocop-1.18.4/exe/" }
The value of ruby.rubocop.executePath
must be a directory name, and it must end with a slash, as shown.
A rubocop
executable must reside in that directory.
So how did I discover the directory? As usual, I wrote a bash incantation; this one prompts for the name of the gem to search for and returns the fully qualified directories for the currently active Ruby virtual environment.
$ read -p "Name of gem to find: " GEM; \ find "$( rbenv root )" -regex ".*/exe/$GEM" | sort Name of gem to find: rubocop /home/mslinn/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/rubocop-1.18.4/exe/rubocop /home/mslinn/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/rubocop-1.26.0/exe/rubocop
If you ever need to discover where a gem is and your computer is set up with rbenv
,
simply paste in the incantation, and it will ask you the name of the gem to look for.
read -p "Name of gem to find: " GEM; \ find "$( rbenv root )" -regex ".*/exe/$GEM" | sort
About the Author
I, Mike Slinn, have been working with Ruby for a long time now. Back in 2005, I was the product marketing manager at CodeGear (the company was formerly known as Borland) for their 3rd Rail IDE. 3rd Rail supported Ruby and Ruby on Rails at launch.
In 2006, I co-chaired the Silicon Valley Ruby Conference on behalf of the SD Forum in Silicon Valley. As you can see, I have the t-shirt. I was the sole chairman of the 2007 Silicon Valley Ruby Conference.
Several court cases have come my way over the years in my capacity as a software expert witness. The court cases featured questions about IP misappropriation for Ruby on Rails programs. You can read about my experience as a software expert if that interests you.
I currently enjoy writing Jekyll plugins in Ruby for this website and others, as well as Ruby utilities.