-
(Theme Music)
-
This talk is about how Bundler works
-
How does Bundler work?
-
This is an interesting question.
-
We'll talk about it for a while.
-
This talk is a brief history of
-
dependency management in Ruby,
-
Not Synced
a discussion of how libraries and
-
Not Synced
shared code works now and in the past
-
Not Synced
because how it works now is directly a result
-
Not Synced
of how it used to work in the past
-
Not Synced
and trying to fix problems that happened then.
-
Not Synced
Before we get started, let me introduce myself:
-
Not Synced
My name is Andre Arko, I'm @indirect on all social media
-
Not Synced
that's my avatar, maybe you've seen me on
-
Not Synced
a webpage somewhere. As my day job
-
Not Synced
I work at Cloud City Development doing
-
Not Synced
Ruby, Rails, Ember, and web consulting.
-
Not Synced
We do web and mobile development
-
Not Synced
and I mostly do architectural consulting
-
Not Synced
and Senior Developer pairing and training.
-
Not Synced
Talk to me if you're company is interested.
-
Not Synced
I also founded Ruby Together, a non-profit,
-
Not Synced
it's like npm incorporate without the venture capital.
-
Not Synced
Ruby Together is a trade association that
-
Not Synced
takes money from companies and people who
-
Not Synced
use Ruby and Bundler and RubyGems and all
-
Not Synced
of the public infrastructure that Rubyists use
-
Not Synced
and pays for developers to work on that
-
Not Synced
so that RubyGems.org stays up, and so
-
Not Synced
people can have gems, which is pretty cool.
-
Not Synced
As part of my work for Ruby Together I work as
-
Not Synced
lead of the Bundler team. I've been working on
-
Not Synced
Bundler since before 1.0 came out, and I've
-
Not Synced
been team lead for the last four years.
-
Not Synced
Using Ruby code written by other developers,
-
Not Synced
nowadays this is actually really easy,
-
Not Synced
you add a line to your Gemfile,
-
Not Synced
you go to your terminal and run
-
Not Synced
bundle install, and you start using it.
-
Not Synced
Pretty cool, that's really easy.
-
Not Synced
The thing that I've noticed, talking to people
-
Not Synced
who use Bundler and think it's awesome
-
Not Synced
is that, it's not actually clear what just happened.
-
Not Synced
Based on the text printed out by bundle install
-
Not Synced
it seems like something got downloaded
-
Not Synced
and something got installed, but it's not clear.
-
Not Synced
It's not clear what got downloaded or
-
Not Synced
installed, or where it happened.
-
Not Synced
What exactly happened there?
-
Not Synced
Nobody is really sure.
-
Not Synced
How does just putting a line in your Gemfile
-
Not Synced
mean you can just start using somebody else's code?
-
Not Synced
To explain that, we'll need a little bit of history.
-
Not Synced
We're going to back in a time.
-
Not Synced
I'm going to give you a tour from the beginning of sharing
-
Not Synced
code in Ruby up until now.
-
Not Synced
And hopefully by the end of it you'll understand
-
Not Synced
why things work the way they do now.
-
Not Synced
I'm going to start talking about require,
-
Not Synced
which came with the very first version of Ruby ever, in 1994.
-
Not Synced
And then talk about setup.rb from 2000,
-
Not Synced
and then RubyGems from 2003, and Bundler from 2009.
-
Not Synced
And that's what we're still using today.
-
Not Synced
The require method has been around since
-
Not Synced
1994, with the very first version of Ruby.
-
Not Synced
What I should say is that it's been there
-
Not Synced
since at least 1997, since that's
-
Not Synced
the oldest version controlled Ruby we have.
-
Not Synced
It was probably there before that though.
-
Not Synced
Require can be broken down into even
-
Not Synced
smaller concepts. Using code from
-
Not Synced
a file is basically the same as inserting
-
Not Synced
that code and having Ruby run it
-
Not Synced
as if you'd just written it in the file.
-
Not Synced
It's actually possible to implement it yourself,
-
Not Synced
with a one-line function.
-
Not Synced
This function says; I have a file
-
Not Synced
name and I want to require it,
-
Not Synced
and you read the file in the memory
-
Not Synced
into a string and you pass the
-
Not Synced
string to eval, and Ruby runs it
-
Not Synced
and it's just like you typed that code yourself.
-
Not Synced
There are problems with this.
-
Not Synced
Require doesn't work this way in real life.
-
Not Synced
I'm sure it's totally fine that this will
-
Not Synced
run that same piece of code over and over
-
Not Synced
if you require it over and over, you like
-
Not Synced
having lots and lots of constants that keep
-
Not Synced
getting redefined, I'm sure it's totally fine.
-
Not Synced
Working around that, is pretty straightforward.
-
Not Synced
Just keep track of what you've required in an Array
-
Not Synced
and not require something again if it' been required.
-
Not Synced
As you can see here,
-
Not Synced
you set up an Array, you check
-
Not Synced
to see if the Array already contains
-
Not Synced
the filename that just got passed in,
-
Not Synced
and if hasn't been required,
-
Not Synced
do the same thing we did before,
-
Not Synced
read the file in, pass it to eval,
-
Not Synced
and then add it to the array,
-
Not Synced
so it's not required again later.
-
Not Synced
In fact, this exactly what Ruby does,
-
Not Synced
but written in C not in Ruby.
-
Not Synced
There is a LOADED_FEATURES global variable,
-
Not Synced
and it's an Array, and it contains a list
-
Not Synced
of all the required files.
-
Not Synced
If you want to know if you've required something yet,
-
Not Synced
check the LOADED_FEATURES array.
-
Not Synced
There is one more problem with this,
-
Not Synced
it only works when you pass in absolute paths.
-
Not Synced
I'm sure you don't mind you typing the
-
Not Synced
full path from wherever you are to
-
Not Synced
exactly wherever the file you want to require is.
-
Not Synced
I'm sure that's fine too.
-
Not Synced
The easiest way to allow requires that
-
Not Synced
aren't absolute is to just treat
-
Not Synced
all requires as if they're relative
-
Not Synced
to the path where you started
-
Not Synced
the Ruby program. And that's easy,
-
Not Synced
but that doesn't help a lot if you
-
Not Synced
want to require Ruby files from different places.
-
Not Synced
Say you have a folder full of a library
-
Not Synced
you wrote and folder full of an application you wrote
-
Not Synced
and you want to use a library from the app, you can't,
-
Not Synced
because writing relative paths from wherever
-
Not Synced
you started the Ruby program would be terrible.
-
Not Synced
Instead we create an Array that holds the
-
Not Synced
list of paths we want to load we want to load
-
Not Synced
Ruby files from, in a burst of creativity
-
Not Synced
I'm just going to call that variable the
-
Not Synced
LOAD_PATH, and here's an implementation.
-
Not Synced
If you put something in the LOAD_PATH Array,
-
Not Synced
you can then pass a relative path to any directory
-
Not Synced
that's in the LOAD_PATH Array, and
-
Not Synced
it will look for the file.
-
Not Synced
If you require "foo", it will look for a file
-
Not Synced
named "foo" inside any of the LOAD_PATH directories,
-
Not Synced
and if the first one we find searching
-
Not Synced
the LOAD_PATH in order from first to last,
-
Not Synced
we will require that one.
-
Not Synced
Coincidentally, this is exactly what
-
Not Synced
Ruby does, there is a global variable
-
Not Synced
named LOAD_PATH, and if you put
-
Not Synced
a string that contains a path to a directory
-
Not Synced
in it, Ruby will look in that directory
-
Not Synced
whenever you require something for a file
-
Not Synced
with that name.
-
Not Synced
You can totally use the LOAD_PATH to require
-
Not Synced
files from somewhere else while you're working with them.
-
Not Synced
Of course, the LOADPATH, and LOADED_FEATURES
-
Not Synced
can both be combined, but that didn't
-
Not Synced
fit on a single slide, so I'll leave that
-
Not Synced
as an exercise to the listener.
-
Not Synced
It's pretty straightforward to be honest.
-
Not Synced
Load paths are pretty cool.
-
Not Synced
They allow us to load Ruby directories
-
Not Synced
even if they're spread across multiple places.
-
Not Synced
At this point, we could even
-
Not Synced
have automatically, at the start of every script,
-
Not Synced
the directory that holds the standard library,
-
Not Synced
to the load path, and then all of the
-
Not Synced
files that are pretty of the Ruby standard library,
-
Not Synced
like Net::HTTP, Set, the cool thing that
-
Not Synced
come with Ruby, could just be available for
-
Not Synced
require automatically and you wouldn't have
-
Not Synced
to worry about putting them in the
-
Not Synced
load path yourself. That's exactly
-
Not Synced
what Ruby does, the standard library
-
Not Synced
starts on the load path when Ruby starts.
-
Not Synced
It's pretty great. This was cool, and
-
Not Synced
for several years, this was enough.
-
Not Synced
People just added things to the load path.
-
Not Synced
Or wrote scripts that added things to the
-
Not Synced
load path before requiring things before their
-
Not Synced
actual script happened.
-
Not Synced
The thing that got tedius out just having
-
Not Synced
load paths, is that if you want to get code from
-
Not Synced
someone else, you have to find that code,
-
Not Synced
download it, put it somewhere, remember where,
-
Not Synced
put it in the load path, and then require it.
-
Not Synced
This was tedious.
-
Not Synced
Setup.rb happened next.
-
Not Synced
Around the year 2000 everyone is still
-
Not Synced
installing share Ruby code by hand.
-
Not Synced
That wasn't so much fun.
-
Not Synced
A Japanese Ruby developer, Minero Aoki,
-
Not Synced
wrote setup.rb, and amazingly,
-
Not Synced
even though this was created in 2000,
-
Not Synced
setup.rb is still around on the Internet.
-
Not Synced
The website for this developer is,
-
Not Synced
i.loveruby.net, which is pretty cool,
-
Not Synced
and you can even download setup.rb, but
-
Not Synced
to be honest, it hasn't been updated since 2005,
-
Not Synced
so I'm not sure it's super helpful to you.
-
Not Synced
How did setup.rb work?
-
Not Synced
At it's core it mimicked the classic
-
Not Synced
UNIX installation pattern,
-
Not Synced
downloading a piece of software,
-
Not Synced
decompressing it, and then running
-
Not Synced
configure make, and make install,
-
Not Synced
so setup.rb kind of copied that for Ruby.
-
Not Synced
You would run ruby setup.rb setup,
-
Not Synced
ruby setup.rb config, ruby setup.rb install
-
Not Synced
setup.rb would copy all the Ruby files,
-
Not Synced
there was a specific directory structure,
-
Not Synced
kind of like a Gem today, with
-
Not Synced
library files, and bin files you could run as programs,
-
Not Synced
and support files, and setup.rb would
-
Not Synced
copy all of those files into a directory
-
Not Synced
that was already in the load path called,
-
Not Synced
site ruby, and that was the ruby files
-
Not Synced
you had installed that were specific
-
Not Synced
to your computer.
-
Not Synced
After setup.rb, using Ruby libraries
-
Not Synced
was much easier than it had been.
-
Not Synced
You could find a library online,
-
Not Synced
download it, you had to untar it by hand,
-
Not Synced
and run ruby setup.rb all by hand,
-
Not Synced
but then it was all installed, and no more
-
Not Synced
manual copying, no more having to
-
Not Synced
manage all these files.
-
Not Synced
Everything was in the load path,
-
Not Synced
you could just require it after setup.rb ran.
-
Not Synced
After a little while, some of the
-
Not Synced
shortcomings of this scheme became apparent, too.
-
Not Synced
There were no versions for any libraries,
-
Not Synced
and after you run setup.rb there's not even
-
Not Synced
a way to tell what version you have, unless
-
Not Synced
you write it down, or the library author
-
Not Synced
was really nice, and put the version into
-
Not Synced
the code somehow. There was no way
-
Not Synced
to uninstall, everything thrown into
-
Not Synced
the same directory. You'd run setup.rb
-
Not Synced
for 5 different Ruby libraries and
-
Not Synced
now all of their files are in one directory.
-
Not Synced
Good luck figuring out which ones belongs to which.
-
Not Synced
If you delete the wrong one, too bad.
-
Not Synced
Upgrading was super fun, if there was
-
Not Synced
a new version of the library, which
-
Not Synced
good luck finding that out, you
-
Not Synced
have to remember the website you got
-
Not Synced
it from in the first place.
-
Not Synced
I hope you write all these down.
-
Not Synced
I hope you've written down every
-
Not Synced
website you've ever downloaded Ruby from.
-
Not Synced
You have to go back to that website,
-
Not Synced
remember which version you have, which
-
Not Synced
as I said before, there's nothing there unless
-
Not Synced
you wrote it down.
-
Not Synced
And then you have to download the
-
Not Synced
tarball with the new version, and
-
Not Synced
decompress it, and CD into it and
-
Not Synced
run ruby setup.rb on it all, and
-
Not Synced
hope that the new version didn't delete
-
Not Synced
any files because the old files are still there.
-
Not Synced
This was tedious, it was really tedious.
-
Not Synced
People frequently had no idea what
-
Not Synced
was actually happening with their libraries.
-
Not Synced
It was not uncommon for people to be like
-
Not Synced
"Oh this doesn't work, I'll just fix it
-
Not Synced
in my site ruby directory, ok everything
-
Not Synced
is great now"
-
Not Synced
Super awesome.
-
Not Synced
At some point, some people were like
-
Not Synced
this is not great. What if you could just
-
Not Synced
gem install. That would be cool.
-
Not Synced
And so in 2003, RubyGems came to the rescue.
-
Not Synced
And fixed all of the problems with setup.rb
-
Not Synced
that were known. You could check
-
Not Synced
to see if a library existed by running gem list,
-
Not Synced
install a gem by gem install, uninstall gems.
-
Not Synced
RubyGems kept each of these libraries in different directories.
-
Not Synced
You knew which libraries you had, and how to uninstall
-
Not Synced
and install new versions, all with one command.
-
Not Synced
No having to find it on the internet somewhere,
-
Not Synced
download, and unpack it, setup.rb it.
-
Not Synced
And RubyGems had another super cool trick
-
Not Synced
up it's sleeves -- versions.
-
Not Synced
RubyGems actually kept each version of each
-
Not Synced
gem in a different place. You could install
-
Not Synced
multiple versions of the same library.
-
Not Synced
And they could all be in your Ruby
-
Not Synced
because they didn't all go into one giant folder,
-
Not Synced
they went into their own separate folders.
-
Not Synced
Folders for rails 4.1, 4.2, and 5.0.
-
Not Synced
To make this work, because require doesn't
-
Not Synced
support versioning, inherently,
-
Not Synced
RubyGems added a gem method that
-
Not Synced
let's you say, I need version 1.0 of rack,
-
Not Synced
and RubyGems will check to make sure it's
-
Not Synced
installed, put that directory, just the one
-
Not Synced
with rack 1.0 into your load path.
-
Not Synced
So when you run require "rack" you'll
-
Not Synced
get rack 1.0, it's pretty cool.
-
Not Synced
Calling the gem method, told RubyGems
-
Not Synced
you wanted to manipulate the load path to
-
Not Synced
load exactly the version you knew your
-
Not Synced
code wanted to talk to.
-
Not Synced
It was pretty useful.
-
Not Synced
RubyGems also has a way to support
-
Not Synced
versioning even in commands that
-
Not Synced
come with gems. The rack gem
-
Not Synced
comes with the rackup command, and
-
Not Synced
if you have multiple versions of rack installed,
-
Not Synced
the rack command could run any of those versions.
-
Not Synced
RubyGems defaults to the newest version you have
-
Not Synced
installed, hoping the newest is the right one.
-
Not Synced
But if that's not, RubyGems checks the first
-
Not Synced
argument to the command for something
-
Not Synced
with underscores on either sides,
-
Not Synced
it takes that as the version number
-
Not Synced
that you want to use.
-
Not Synced
In the above example, we're running
-
Not Synced
rackup from rack version 1.2.2, and only 1.2.2.
-
Not Synced
If you don't have that version installed, RubyGems will
-
Not Synced
make you install that version first.
-
Not Synced
RubyGems was really, really successful.
-
Not Synced
Ruby grew in popularity a lot, but RubyGems
-
Not Synced
made sharing Ruby code grow a lot.
-
Not Synced
Present day we have 100,000 gems, with 1,000,000 versions.
-
Not Synced
That's a lot of shared Ruby code.
-
Not Synced
You probably knew this was coming,
-
Not Synced
but as cool as RubyGems is, it still had
-
Not Synced
some problems. If you have multiple
-
Not Synced
applications that all use RubyGems to load
-
Not Synced
their dependencies, this can be problematic.
-
Not Synced
It's hard to coordinate across multiple applications
-
Not Synced
because, each installation of Ruby itself just has
-
Not Synced
a set of gems. If you ran gem install, now
-
Not Synced
there are all these gems.
-
Not Synced
If one developer runs gem install "foo" and
-
Not Synced
starts using "foo" in their application,
-
Not Synced
commits that code and checks it in,
-
Not Synced
and the next person checks it out
-
Not Synced
and tries to run the application,
-
Not Synced
it's going to explode, because it doesn't
-
Not Synced
know what foo is, you need to fix that.
-
Not Synced
It led to an area of pure manual dependency management.
-
Not Synced
Start a new job, hooray!
-
Not Synced
This literally happened to me in 2008.
-
Not Synced
New job, welcome to the team, here's
-
Not Synced
your cool new laptop, we
-
Not Synced
except you to have the application
-
Not Synced
running by next week.
-
Not Synced
It actually took me only 3 and a half days,
-
Not Synced
working overtime on this. It was amazing.
-
Not Synced
[Audience Laughs]
-
Not Synced
To figure out which gems to run gem install,
-
Not Synced
I looked in the README and there
-
Not Synced
was a list.
-
Not Synced
And I installed all of them.
-
Not Synced
But clearly there was some that people
-
Not Synced
forgot to put in the README, and
-
Not Synced
then it kind of worked, but I wasn't
-
Not Synced
able to get images working. And then
-
Not Synced
some other developer was like,
-
Not Synced
you need to install imagemagick,
-
Not Synced
this was before homebrew. It was terrifying.
-
Not Synced
To try and fix this problem,
-
Not Synced
of do we just put the gems in the README?
-
Not Synced
How do we know if we have
-
Not Synced
written everything in the README?
-
Not Synced
"I don't know? Try it?"
-
Not Synced
Of course, you'd need a new machine
-
Not Synced
to try it on, because after 3 years
-
Not Synced
of using Ruby you generally have
-
Not Synced
installed every gem, and you have
-
Not Synced
no idea what's important and what's not.
-
Not Synced
It's terrible.
-
Not Synced
People started to work on tools to help this problem.
-
Not Synced
Rails added config.gem, this is Rails 2.3, 2.4 era.
-
Not Synced
You would put all the gems you need in application.rb
-
Not Synced
This was super helpful if you needed
-
Not Synced
to know for sure this was the
-
Not Synced
master list of all the gems
-
Not Synced
you needed in your application, but
-
Not Synced
you could only access that list when
-
Not Synced
Rails was already loaded.
-
Not Synced
It was pretty bad.
-
Not Synced
Because RubyGems automatically uses
-
Not Synced
the newest version of each gem, just having
-
Not Synced
an older version installed, didn't mean it
-
Not Synced
would be used. And if you install
-
Not Synced
some gem a month after the other person did,
-
Not Synced
maybe there's a new version? You would
-
Not Synced
just get the new version automatically.
-
Not Synced
This is also totally a real-life experience that happened to me in 2009.
-
Not Synced
Debug a production server that just randomly throws exceptions.
-
Not Synced
For three days.
-
Not Synced
The other production servers are fine.
-
Not Synced
We can't reproduce this problem on a single developer laptop.
-
Not Synced
What is going on? This is so weird.
-
Not Synced
After 3 days I finally thought to look at
-
Not Synced
the output from the gemlist for the entire production
-
Not Synced
machine and I was like, oh this production
-
Not Synced
server has gem version 1.1.3 and every
-
Not Synced
other production server and developer laptop has 1.1.4.
-
Not Synced
That was the problem.
-
Not Synced
There was a bug and only that server
-
Not Synced
had this problem.
-
Not Synced
And then, like I was saying, about Rails versions,
-
Not Synced
you could gem install rails, be happy,
-
Not Synced
make a new app, run your server,
-
Not Synced
everything is great. And then
-
Not Synced
you switch to another application
-
Not Synced
that already existed, didn't get
-
Not Synced
written to use that version of rails,
-
Not Synced
got writen to use some older version of rails.
-
Not Synced
You're like, "Okay, let's go!"
-
Not Synced
"Boom", because you didn't have
-
Not Synced
the right version of Rails.
-
Not Synced
If you put your rails version in the rails config
-
Not Synced
rails would complain you had the wrong version,
-
Not Synced
but rails had to be successfully started up to
-
Not Synced
tell you that you had the wrong version, so
-
Not Synced
it didn't actually help.
-
Not Synced
Ultimately, it was a significant part of my job
-
Not Synced
to figure this shit out by hand, and it sucked.
-
Not Synced
Depending on what you did on your team,
-
Not Synced
some people on my team at the time spent
-
Not Synced
a quarter or a third of their time
-
Not Synced
doing nothing but figuring out and fixing
-
Not Synced
dependency management issues.
-
Not Synced
And I felt really, really bad for them.
-
Not Synced
Sometimes it was me and I felt really bad for me.
-
Not Synced
Then there's one more, even after,
-
Not Synced
you've done all of this by hand management,
-
Not Synced
there's one more problem that RubyGems has
-
Not Synced
that is another reason why bundler was created.
-
Not Synced
Activation Errors, they happen in ruby gems
-
Not Synced
when you load an application and start by
-
Not Synced
requiring gems, ruby gems will load the newest
-
Not Synced
versions of those gems that it can.
-
Not Synced
Sometimes a gem's dependents need
-
Not Synced
other gems, that need other gems,
-
Not Synced
and you'll get the newest version of the
-
Not Synced
child gem. And later you'll say, I also
-
Not Synced
need this gem, but that gem won't work with the other.
-
Not Synced
So how common can this be really?
-
Not Synced
Unfortunately, it was super common.
-
Not Synced
Not like happens to you every day common,
-
Not Synced
but like happens you two or three times a year
-
Not Synced
and when it does you basically tear
-
Not Synced
all your hair out, delete your entire
-
Not Synced
ruby install, uninstall and reinstall all your gems,
-
Not Synced
because figuring out exactly which combo
-
Not Synced
of installed gems was causing this
-
Not Synced
problem was a total nightmare.
-
Not Synced
This is a real-life activation error.
-
Not Synced
I salvaged this from a presentation I gave in 2010
-
Not Synced
about why Bundler exists.
-
Not Synced
This is a rails app, it's loading, and
-
Not Synced
rails of course depends on ActionPack, this
-
Not Synced
was the Rails 2.3 era, ActionPack depends on Rack,
-
Not Synced
Rack is a gem that helps Rails talk to web servers.
-
Not Synced
And thin, which is a web server, also depends on rack.
-
Not Synced
So, rack is how rails talks to thin, how thin
-
Not Synced
talks to rails, but there's a problem.
-
Not Synced
thin is perfectly happy to use rack 1.1, which makes some
-
Not Synced
changes to how rack works.
-
Not Synced
ActionPack is not happy to use rack 1.1, and
-
Not Synced
can only use rack 1.0. And so
-
Not Synced
when you run your server, it loads thin
-
Not Synced
first because thin is the server.
-
Not Synced
And thin gets to work trying to load the rails app
-
Not Synced
and your rails app says "I can't use that rack, sorry"
-
Not Synced
The reason this happens is runtime resolution.
-
Not Synced
RubyGems figures out which versions
-
Not Synced
of which gems of which gems it should load.
-
Not Synced
After RubyGems is already running.
-
Not Synced
You say, "Hey I need a thing", and
-
Not Synced
it's like "Okay, this version might work".
-
Not Synced
And if later on you say,
-
Not Synced
"I need a thing that doesn't work with things you've already done"
-
Not Synced
RubyGems just has to be like, can't fix that.
-
Not Synced
The fix for this problem is to figure out all the versions
-
Not Synced
before you run your application.
-
Not Synced
You have to know the versions you're going
-
Not Synced
to use are all versions that can work together.
-
Not Synced
Resolving things at install time,
-
Not Synced
knowing you're installing versions that work together.
-
Not Synced
How do we make sure all the versions we're
-
Not Synced
installing work together?
-
Not Synced
That's actually where Bundler comes in.
-
Not Synced
Before Bundler, the process of figuring out
-
Not Synced
which gems would work together
-
Not Synced
was done entirely by hand and it
-
Not Synced
consisted of gem uninstall,
-
Not Synced
gem install a slighty older version, does rails start up yet?
-
Not Synced
Repeat the process.
-
Not Synced
When the exception stopped you knew you'd won.
-
Not Synced
Unsurprisingly, computers are faster at this than people.
-
Not Synced
Computers are also good and accurate at trying
-
Not Synced
many, many, many options until one works.
-
Not Synced
This is what Bundler does.
-
Not Synced
Bundler figures out the entire list of every gem
-
Not Synced
and every version of every gem that
-
Not Synced
you need, but that also all
-
Not Synced
work together with one another.
-
Not Synced
This is called Dependency Graph Resolution,
-
Not Synced
and there's an entire academic literature about this.
-
Not Synced
It's kind of well-known hard problem, it's
-
Not Synced
part of the set of problems called NP complete,
-
Not Synced
and the totally fantastic thing, and I say
-
Not Synced
this as a person who has to fix Bundler
-
Not Synced
when it doesn't work, in theory, you can construct
-
Not Synced
a set of gems in a gemfile such that
-
Not Synced
it is not possible to find a set of gems that
-
Not Synced
work together until after the heat death of the universe.
-
Not Synced
[Audience Laughs]
-
Not Synced
Most of the time we don't have that long to wait.
-
Not Synced
We use a lot of tricks, shortcuts, and heuristics
-
Not Synced
to figure out which gems to try first and
-
Not Synced
hopefully finish before you've drunk
-
Not Synced
that cup of coffee or whatever.
-
Not Synced
We have a large built-up set of tricks over the years
-
Not Synced
and most Gemfiles resolve in less than 10 seconds.
-
Not Synced
Which is pretty cool, considering the upper bound
-
Not Synced
on that is practically infinity.
-
Not Synced
After finding versions that work together
-
Not Synced
because this problem was really hard,
-
Not Synced
and we don't want to do this over and over.
-
Not Synced
Bundler writes down the exact versions of every gem
-
Not Synced
that did all work together, so they can be reused
-
Not Synced
by other people who are also interested in running
-
Not Synced
your application. That file is called Gemfile.lock.
-
Not Synced
Shows which gems to be installed,
-
Not Synced
the versions to install, and as a bonus
-
Not Synced
the lock file is what makes it possible
-
Not Synced
to install the exact same version of every
-
Not Synced
gem on every machine that's running this application.
-
Not Synced
That means when you develop on your laptop
-
Not Synced
you get whatever version of the gem that was
-
Not Synced
newest when you were developing because run
-
Not Synced
bundle install and got newest version by default.
-
Not Synced
Because of the lock file, when you put
-
Not Synced
that on your production server, you're guaranteed
-
Not Synced
to have the same versions. And you won't
-
Not Synced
have to spend 3 days figuring out why
-
Not Synced
that production server doesn't quite
-
Not Synced
work all of the time.
-
Not Synced
It's pretty great.
-
Not Synced
Fundamentally, the core of bundler consist of two steps.
-
Not Synced
bundle install, and bundle exec.
-
Not Synced
The steps for bundle install are simple.
-
Not Synced
They're totally understandable in plain english
-
Not Synced
It fits on a single slide, which is great.
-
Not Synced
I edited this slide for ten minutes deleting words.
-
Not Synced
So the steps are:
-
Not Synced
1. Read the Gemfile
-
Not Synced
2.Ask RubyGems.org for a list of all the gems we need
-
Not Synced
3. Find versions of those gems both allowed by Gemfile
-
Not Synced
4. Once found, write all those down in lock and install them all.
-
Not Synced
And that's how bundle install works.
-
Not Synced
BundleInstall uses RubyGems under the covers
-
Not Synced
to the installation, and so every
-
Not Synced
bundle is it's own little rubygems isolated install.
-
Not Synced
Every application has it's own rubygems thanks to bundler.
-
Not Synced
The next step is bundle exec.
-
Not Synced
This is how we use that applications dedicated ruby gems
-
Not Synced
instead of the one with whatever in it
-
Not Synced
because you ran gem install last year.
-
Not Synced
The way bundle exec works is:
-
Not Synced
1. Reads the Gemfile, and lock if it's there.
-
Not Synced
2a. Use locked gems if possible OR
-
Not Synced
2b. Find versions that work together like install would.
-
Not Synced
except bundle exec doesn't do any installing.
-
Not Synced
3. Deletes any existing gems in the LOAD_PATH
-
Not Synced
4. Adds the exact gem at the exact version at the load path.
-
Not Synced
That's it. That's all bundle exec does.
-
Not Synced
Once all the gems work together, and
-
Not Synced
there exact versions are in the load path
-
Not Synced
your application is happy. There is no
-
Not Synced
activation errors, all your requires succeed, I hope.
-
Not Synced
Everything is pretty great.
-
Not Synced
As I think I promised in the abstract for this talk,
-
Not Synced
here's a bundle exec removing pro tip.
-
Not Synced
I don't really like typing bundle exec, I find it
-
Not Synced
really annoying, but bundler provides a way
-
Not Synced
to not have to type it all the time.
-
Not Synced
And it's to create programs that map to
-
Not Synced
ruby gems installation that
-
Not Synced
belongs to that application.
-
Not Synced
You can use the binstubs command,
-
Not Synced
bundle binstubs [some gem]
-
Not Synced
and it will create, in the bin directory,
-
Not Synced
a program for that gem, that only
-
Not Synced
runs the exact version that belongs to
-
Not Synced
that application. So if you have
-
Not Synced
rspec in your rails app, you can have
-
Not Synced
bin/rspec that will only load the rspec
-
Not Synced
for your app. This way you can have
-
Not Synced
bin/rspec refer to rspec 3, and this application
-
Not Synced
can have rspec 2. Rails has started to do this.
-
Not Synced
Rails 4 ships with bin/rails bin/rake that are scoped
-
Not Synced
so when you run bin/rails, you get the exact
-
Not Synced
rails version for this application and not another one.
-
Not Synced
When you run bin/rake you get the exact version of rake.
-
Not Synced
Pretty cool, no more bundle exec.
-
Not Synced
If everyone did this, you can check in these binstubs
-
Not Synced
so you can take bin/rspec, but it in git,
-
Not Synced
and it'll be mapped to that application forever,
-
Not Synced
so no one would have bundle exec
-
Not Synced
ever again if everyone did this.
-
Not Synced
Now we bundle install, all our gems
-
Not Synced
show up. We have versions
-
Not Synced
dedicated for individual applications.
-
Not Synced
But, as you probably sensed a problem
-
Not Synced
going through history, that wasn't actually
-
Not Synced
the end. There are still problems
-
Not Synced
that show up after bundler came out.
-
Not Synced
The biggest problem that was left was
-
Not Synced
running bundle install, took forever.
-
Not Synced
If you lived a long time from the United States
-
Not Synced
it took a really long time.
-
Not Synced
I talked to some developers in South Africa
-
Not Synced
when I went there to give a talk
-
Not Synced
and they told me about how running
-
Not Synced
bundle install means they literally get
-
Not Synced
up to start making a cup of coffee
-
Not Synced
that they can finish before bundle install does.
-
Not Synced
To try and speed things up, bundler 1.1
-
Not Synced
created a completely different
-
Not Synced
way to get information from rubygems about gems.
-
Not Synced
And that sped things up by 50%, a big win.
-
Not Synced
We keep working on this, bundler 1.9 just
-
Not Synced
came out this month. There's a bunch more
-
Not Synced
improvements we're working on.
-
Not Synced
If you're interested in following along with that,
-
Not Synced
the bundler websites has news annoucements
-
Not Synced
at bundler.io, and twitter we're also @bundlerio.
-
Not Synced
Having said all of this, if you use Bundler,
-
Not Synced
I would totally love to have your help working on it.
-
Not Synced
It's an open source project.
-
Not Synced
We've dedicated a lot of time to making it easy
-
Not Synced
for people who don't know how to do open source
-
Not Synced
to help with Bundler, and to start working on Bundler,
-
Not Synced
and to get into open source that way.
-
Not Synced
It's a project at Github.com/bundler/bundler.
-
Not Synced
If you're interested but don't know where to start
-
Not Synced
email the bundler team at team@bundler.io
-
Not Synced
and we'll get you set up.
-
Not Synced
On the other hand, if you
-
Not Synced
have a job that means you have money,
-
Not Synced
but not time, join Ruby Together, and give
-
Not Synced
us money, and we'll work on Bundler, and it'll be
-
Not Synced
better. As RubyTogether grows, we will also be
-
Not Synced
tackling bigger community issues.
-
Not Synced
We want to add easy to use gem mirrors so you
-
Not Synced
don't have to go all the way to rubygems.org
-
Not Synced
for your office or data center, we want to
-
Not Synced
add better public benchmarks. There's a project
-
Not Synced
calling ruby-bench that's starting to do that,
-
Not Synced
and we'd really like to expand it.
-
Not Synced
There's a bunch of other things
-
Not Synced
that RubyTogether is working on that are cool
-
Not Synced
If you want Bundler or RubyTogether stickers
-
Not Synced
I have a giant pile, so find me later.
-
Not Synced
That's it.
-
Not Synced
[Audience Applause]