Not too long ago, I learned about bundler. It’s a solution that allows you to have multiple versions of ruby gems installed, specific to the project you’re working on, without affecting the globally installed ruby gems. I’m far from an expert but I hope I can help explain it a bit. To get started, install the bundler gem – it’s the one and only global gem we’ll need to install. You’ll see the dependencies installed as well if you do not have them:
[root@build02 ~]# gem install bundler Fetching: bundler-1.10.6.gem (100%) Successfully installed bundler-1.10.6 Installing ri documentation for bundler-1.10.6 1 gem installed
Now, clone a ruby project with a Gemfile. I’ve chosen puppet-retrospec:
[rnelson0@build02 git]$ git clone firstname.lastname@example.org:nwops/puppet-retrospec.git Initialized empty Git repository in /home/rnelson0/git/puppet-retrospec/.git/ remote: Counting objects: 3246, done. remote: Compressing objects: 100% (100/100), done. remote: Total 3246 (delta 45), reused 0 (delta 0), pack-reused 3127 Receiving objects: 100% (3246/3246), 2.32 MiB | 2.67 MiB/s, done. Resolving deltas: 100% (829/829), done. [rnelson0@build02 git]$ cd puppet-retrospec/ [rnelson0@build02 puppet-retrospec:master]$
Try and run rake and you’ll notice it’s missing a dependency:
[rnelson0@build02 puppet-retrospec:master]$ rake -T Could not find addressable-2.3.8 in any of the sources Run `bundle install` to install missing gems
You can run bundle install on its own, but I prefer to give it a path for the install and to generally avoid system_tests groups. This can be pretty quick for some projects, or take a really long time for anything that includes #&!*# nokogiri (seriously, you’ll learn to hate that one):
[rnelson0@build02 puppet-retrospec:master]$ bundle install --path vendor --without system_tests Fetching gem metadata from http://rubygems.org/.......... Fetching version metadata from http://rubygems.org/... Fetching dependency metadata from http://rubygems.org/.. Resolving dependencies... Installing rake 10.4.2 Installing CFPropertyList 2.2.8 Installing addressable 2.3.8 Installing awesome_print 1.6.1 Installing builder 3.2.2 Using bundler 1.10.6 Installing coderay 1.1.0 Installing thread_safe 0.3.5 Installing descendants_tracker 0.0.4 Installing diff-lcs 1.2.5 Installing facter 2.4.4 Installing fakefs 0.6.7 Installing multipart-post 2.0.0 Installing faraday 0.9.2 Installing git 22.214.171.124 Installing hashie 3.4.2 Installing multi_json 1.11.2 Installing mini_portile 0.6.2 Installing nokogiri 126.96.36.199 with native extensions Installing jwt 1.5.1 Installing multi_xml 0.5.5 Installing rack 1.6.4 Installing oauth2 1.0.0 Installing github_api 0.12.4 Installing json_pure 1.8.2 Installing hiera 1.3.4 Installing highline 1.7.8 Installing json 1.8.3 with native extensions Installing rdoc 3.12.2 Installing jeweler 2.0.1 Installing method_source 0.8.2 Installing slop 3.6.0 Installing pry 0.10.2 Using puppet 3.7.3 from source at vendor/gems/puppet-3.7.3 Installing trollop 2.1.2 Installing retrospec 0.4.0 Installing rspec-support 3.3.0 Installing rspec-core 3.3.2 Installing rspec-expectations 3.3.1 Installing rspec-mocks 3.3.2 Installing rspec 3.3.0 Installing yard 0.8.7.6 Bundle complete! 11 Gemfile dependencies, 42 gems now installed. Gems in the group system_tests were not installed. Bundled gems are installed into ./vendor. Post-install message from rdoc: Depending on your version of ruby, you may need to install ruby rdoc/ri data: <= 1.8.6 : unsupported = 1.8.7 : gem install rdoc-data; rdoc-data --install = 1.9.1 : gem install rdoc-data; rdoc-data --install >= 1.9.2 : nothing to do! Yay!
All of the files were installed to vendor/. There are also files created in .bundle/ and a Gemfile.lock file that tracks the bundler installed gem version-. You’ll most likely want to add those two directories to your .gitignore or you’ll see this all the time:
[rnelson0@build02 puppet-retrospec:master±]$ git status # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # vendor/ruby/ nothing added to commit but untracked files present (use "git add" to track)
Now that the gems are installed, you can see and make use of them with bundle exec <command>. Let’s compare the system gems to the bundler gems:
[rnelson0@build02 puppet-retrospec:master±]$ gem list | wc 78 159 1492 [rnelson0@build02 puppet-retrospec:master±]$ bundle exec gem list | wc 41 82 712
Finally, let’s take a quick look at how bundler knows what to install. I mentioned a Gemfile.lock tracking file earlier, and it’s based on Gemfile. Here’s what the retrospec project’s looks like:
[rnelson0@build02 puppet-retrospec:master±]$ cat Gemfile source "http://rubygems.org" gem 'trollop' gem 'retrospec', "~> 0.4" gem 'awesome_print' # Include everything needed to run rake, tests, features, etc. group :development do gem "rspec", "~> 3.2" gem 'puppet', '3.7.3', :path => 'vendor/gems/puppet-3.7.3' gem "yard", "~> 0.7" gem "rdoc", "~> 3.12" gem "bundler", "~> 1.0" gem "jeweler" gem 'pry' gem "fakefs", :require => "fakefs/safe" end
Gems by themselves (trollop) will install the latest version. Gems with a version will install a version matching the pattern. ‘1.0’, ‘>=1.3’ probably look familiar, but what does that ‘~> 0.4’ do? It means “Greater than 0.4 but less than 1.0.” If it specifies major.minor.revision, like ‘~> 1.3.2’, it means “Greater than 1.3.2 but less than 1.4.” The path option means to use a gem expanded in the specified location on disk. The require I still don’t understand. From the docs, “If a gem’s main file is different than the gem name, specify how to require it. Specify
:require => false to prevent bundler from requiring the gem, but still install it and maintain dependencies.” I guess I’ll find out when I need to know.
I hope this brief intro is helpful to others new to bundler!