Skip to content

MR19 Dependency Management With Bundler

Dave Strus edited this page Feb 1, 2016 · 1 revision

Dependency Management With Bundler

In projects that use Bundler, we don't have access to whatever gems just happen to be installed in the local environment. We allow Bundler to install and manage a separate set of gems just for this project. Bundler will make sure that we are using versions of each gem that are compatible with our project, and it will even include the proper versions of the gems that our required gems depend on.

How does it know which gems our project requires? It looks at the Gemfile!

A typical Gemfile includes a list of gems on which a project depends, often specifying version numbers—or a range of version numbers—that the author knows are compatible. Our Gemfile, however, loads the list of dependencies from our gemspec.

Open up mutantcorp-mutant.gemspec and look for the dependencies near the end of the file.

spec.add_development_dependency "bundler", "~> 1.11"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "minitest", "~> 5.0"

We want to add Pry as a dependency. Like the others, it's only a development dependency. Projects can our gem with no trouble without having Pry installed. Let's add Pry to the list. We don't know the version number at the moment, so we'll leave it off.

spec.add_development_dependency "bundler", "~> 1.11"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "minitest", "~> 5.0"
spec.add_development_dependency 'pry'

Now, when Bundle compiles the set of gems required for our project, Pry will be among them. To install the required gems, run bundle install.

$ bundle install
Resolving dependencies...
Using rake 10.5.0
Using bundler 1.11.2
Using coderay 1.1.0
Using method_source 0.8.2
Using minitest 5.8.4
Using mutantcorp-mutant 0.1.0 from source at `.`
Using slop 3.6.0
Using pry 0.10.3
Bundle complete! 5 Gemfile dependencies, 8 gems now installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.

Pry is there! Notice the version number. In my case, it's using version 0.10.3. Let's require at least that version by specifying it in the gemspec.

spec.add_development_dependency "bundler", "~> 1.11"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "minitest", "~> 5.0"
spec.add_development_dependency 'pry', '~> 0.10.3'

The ~> symbol means any version 0.10.3 or greater, but less than 0.11. If we changed it to ~> 0.10, then it could use any version 0.10 or greater, but less than 1.0. As you might expect, removing the ~ character and leaving it as > 0.10.3 simply means any version 0.10.3 or greater.

After adding the version number, run bundle install again to make sure you didn't break anything.

Running bundle install creates a new file (or updates it if it's already there): Gemfile.lock. Gemfile.lock lists the exact versions of every gem that was installed, along with the dependency tree for each gem.

In many projects, you want to include Gemfile.lock in the project's Git repo. When your project is a gem, you do not want to commit Gemfile.lock. It's not important that other projects using your gem have exactly the same versions of its dependencies—just that they have compatible versions, consistent with those listed in your gemspec. Thankfully, the .gitignore file that Bundler generated for us already lists Gemfile.lock as a file to ignore.

Now that we have our bundle, including Pry, installed, let's try running the console again.

$ bin/console
[1] pry(main)>

We have a prompt. Woo-hoo! Let's try using our gem's Mutant class.

[1] pry(main)> m = Mutant.new
NameError: uninitialized constant Mutant
Did you mean?  Mutantcorp

The class is buried inside two modules, so we'll have to be more specific. Pry helpfully suggests Mutantcorp as something that is defined. How lovely! Let's try again.

[2] pry(main)> m = Mutantcorp::Mutant::Mutant.new
=> #<Mutantcorp::Mutant::Mutant:0x007fbbf21e7138>

That's more like it. We have an instance! Play around with it.

[3] pry(main)> m.real_name = 'Gwendolyn'
=> "Gwendolyn"
[4] pry(main)> m.mutant_name = 'Amber Hawk'
=> "Amber Hawk"
[5] pry(main)> m.power = 'Flight'
=> "Flight"
[6] pry(main)> m.attributes
=> {:real_name=>"Gwendolyn", :mutant_name=>"Amber Hawk", :power=>"Flight"}
[7] pry(main)> m.description
=> "Amber Hawk (also known as Gwendolyn) has an incredible power: Flight."

Excellent! It seems like we could remove the class definition from the roster project and use this gem instead!