Mike Slinn

Installing Rubocop As a Development Dependency

Published 2022-03-06. Last modified 2022-03-15.
Time to read: 7 minutes.

This page is part of the 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:

Shell
$ 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:

Shell
$ 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).

Shell
$ 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.

Shell
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:

  1. Local config (<project_root>/.bundle/config or $BUNDLE_APP_CONFIG/config)
  2. Environmental variables (ENV)
  3. Global config (~/.bundle/config)
  4. Bundler default config

The above documentation contained this explanation of the --local option:

Executing 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:

Shell
$ 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.

Shell
$ 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:

Shell
$ bundle install

In case you are curious, the contents of the file that was created a second ago are:

Shell
$ 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.

Shell
$ bundle

Let’s look at the settings for bundle config.

Shell
$ 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.

Shell
$ 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_development_dependency. 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.

Gemfile
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:

Shell
$ bundle config set --local with production
$ bundle install

You could also use an environment variable:

Shell
$ 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.

Shell
$ bundle config --delete with
$ bundle

Option 2

Only delete local with settings.

Shell
$ bundle config set --local with ''
$ bundle

Option 3

Ignore all configuration settings.

Shell
$ 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).

jekyll_auto_redirect.gemspec
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:

Shell
$ bundle exec rubocop

Where is that GEM?

In theory there is no difference between theory and practice – in practice there is.

  – 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:

Shell
$ 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:

Shell
$ rubocop _1.18.0_ --version
1.18.0
$ rubocop _1.26.0_ --version 1.26.0

Eventually I came up with this magic setting:

.vscode/settings.json
{
  "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.

Shell
$ 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.

Shell
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.

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.