How Does Bundler Work, Anyway?
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Part Number | 6 | |
Number of Parts | 94 | |
Author | ||
License | CC Attribution - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/30668 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
RailsConf 20156 / 94
1
4
7
8
9
10
11
13
14
16
17
19
21
24
25
29
30
33
34
35
36
37
39
40
42
47
48
49
50
51
53
54
55
58
59
61
62
64
65
66
67
68
70
71
77
79
81
82
85
86
88
92
94
00:00
Web pageMultiplication signCodeDivisorBitData managementWordCategory of beingLibrary (computing)ResultantAvatar (2009 film)Computer animation
01:05
Mobile WebComputer architectureSoftware developerInformation technology consultingProcess (computing)Wave packetWeb 2.0NumberAssociative propertyMereologyDialectXMLComputer animation
01:59
CodeSoftware developerPoint (geometry)MereologyBitShared memoryRevision controlMultiplication signInstallation artComputer fileLine (geometry)Radical (chemistry)Computer animation
03:45
CodeComputer fileLibrary (computing)Point (geometry)Computer programmingMereologySet (mathematics)Structural loadDirectory serviceInternetworkingMultiplicationScripting languageState of matterSoftware developerNumbering schemeParticle systemRevision controlMessage passingFamilyWebsiteCore dumpInheritance (object-oriented programming)Classical physicsPattern languageHelixSoftwareImplementationFitness functionConfiguration spaceSlide ruleLatent heatData structureOrder (biology)String (computer science)TrailLogical constantElectronic mailing listVariable (mathematics)WordOnline helpCartesian coordinate systemTheory of relativityBit rateGraphics tabletDifferent (Kate Ryan album)Goodness of fit1 (number)SubsetData compressionFunctional (mathematics)Line (geometry)Semiconductor memoryRight angleReal numberType theoryVideo gameWritingWeightStandard deviationAuthorizationBinary fileComputer animation
13:33
Inheritance (object-oriented programming)Installation artLibrary (computing)DialectCodeDirectory serviceDifferent (Kate Ryan album)Presentation of a groupRevision controlCartesian coordinate systemVirtual machineSet (mathematics)Software developerShared memoryError messageMedical imagingData managementLink (knot theory)Configuration spaceInternetworkingStructural loadMultiplicationPurchasingElectronic mailing listComputer fileTrailParameter (computer programming)NumberSolid geometryDefault (computer science)LaptopRight angleMobile appComputer animation
20:01
Software developerSingle-precision floating-point formatFunction (mathematics)LaptopEntire functionMultiplication signElectronic mailing listVirtual machineProduct (business)Server (computing)Revision controlInstallation artCartesian coordinate systemRootArithmetic meanReal numberDebuggerVideo gameException handlingCombinational logicData recoveryGroup actionPresentation of a groupNeuroinformatikWeb 2.0Process (computing)BitStandard deviationRow (database)Structural loadMathematicsLine (geometry)Configuration spaceMereologyFigurate numberDialectError messageImage resolutionMultilateration3 (number)Data managementAreaQuicksortExistenceComputer configurationRight angleComputer animationLecture/Conference
26:28
Set (mathematics)Entire functionImage resolutionGraph (mathematics)MereologyNP-hardFigurate numberTheoryRevision controlComputer fileElectronic mailing listNP-completeUniverse (mathematics)Engineering drawing
27:19
Revision controlBinary fileInstallation artProduct (business)Structural loadCartesian coordinate systemServer (computing)Computer fileSoftware developerPattern languageArithmetic meanCore dumpElectronic mailing listSlide ruleProjective planeMultiplication signProcess (computing)TwitterBenchmarkHeuristicType theoryKeyboard shortcutError messageSet (mathematics)Computer programmingInformationOnline helpWebsiteInheritance (object-oriented programming)Open sourceDirectory serviceVirtual machineDefault (computer science)Covering spaceMoment (mathematics)Endliche ModelltheorieIncidence algebraData storage deviceAreaDialectOffice suiteData centerWordOpen setSystem callSubset2 (number)Group actionBit rateWave packetLaptopRight angleMappingQuicksortReal numberTheoryCuboidDirection (geometry)Computer animation
Transcript: English(auto-generated)
00:12
So, this talk is about how Bundler works. How does Bundler work?
00:21
This is an interesting question. We'll talk about it for a while. So, this talk is kind of a brief, hopefully brief, history of dependency management in Ruby and kind of a discussion of how libraries and shared code works. It's both how it's worked in the past and how it works now
00:41
because how it works now is kind of directly a result of how it used to work in the past and trying to fix problems that happened back then. So, before we get started, let me introduce myself. My name is Andrej Arko. I am indirect on pretty much all the internet things.
01:00
That's my avatar. Maybe you have seen me on a web page somewhere. As my day job, I work at Cloud City Development doing Ruby and Rails and web and Ember consulting. We do web and mobile development and I mostly do architectural consulting and senior developer pairing and training.
01:21
If that's something that your company's into, talk to me later. The other company that I founded is called Ruby Together and it's a nonprofit. It's kind of like NPM Incorporated but without the venture capital. Ruby Together is a trade association that takes money from companies and people
01:42
who use Ruby and who use Bundler and RubyGems and all of the public infrastructure that everyone who uses Ruby uses and pays for developers to work on that stuff so that RubyGems.org stays up and so that people keep being able to have gems, which is pretty cool. As part of my work for Ruby Together,
02:01
I work as lead of the Bundler team. I've been working on Bundler since before 1.0 came out and I've been team lead for the last four years. So, using Ruby code written by other developers. Nowadays, this is actually really easy.
02:21
You add a line to your gem file, gem foo. You go to your terminal and you run bundle install. You start using it. That was actually it. Pretty cool. That's really easy. The thing that I've noticed talking to people who use Bundler and think that it's awesome
02:42
is that it's not actually clear what just happened. Based on the text printed out by bundle install, it seems like something probably got downloaded and something probably got installed but it's not totally clear what got downloaded. It's not clear what got installed. It's not clear where any of that happened and it's not clear what exactly happened there.
03:04
Nobody's really sure. How does just putting a line in your gem file mean that you can start using somebody else's code? So, to explain that, we'll need a little bit of history. We're going to go back in time and I'm going to give you a little tour from the beginning of sharing code in Ruby up until now.
03:23
And hopefully by the end of it, you'll understand why things work the way they do now. So, I'm going to start talking about Require, which came with the very first version of Ruby ever in 1994, and then talk about SetupRB, which came out in 2000, and then RubyGems, which came out in 2003,
03:40
and then Bundler, which came out in 2009, and that's what we're still using today. So, Require. The Require method has been around since 1994 with the very, very first version of Ruby that actually came out. Or, I guess I should say, I'm sure that Require has been around since at least 1997
04:00
because that's the oldest version of Ruby that we have in version control still. It was probably there before that, though. But Require can be broken down into even smaller concepts. So, using code from a file is basically the same as just inserting that code and having Ruby run it like you had just written it in the file.
04:20
So, it's actually possible to implement Require yourself with just a one-line function. You say, hey, I have a file name and I want to require it, and you read the file and the memory into a string, and then you pass the string to eval, and Ruby runs it, and it's just like you typed that code yourself, and it got run. So, there's some problems with this.
04:42
This is not how Require works in real life. I'm sure it's totally fine that this will run that same piece of code over and over and over if you require it over and over and over. You like having lots and lots of constants that keep getting redefined. I'm sure it's totally fine. So, working around that is actually also pretty straightforward.
05:01
You can just keep track of what you've required in an array and not require something again if it's already been required. As you can see here, you set up an array. You check to see if the array already contains the file name that just got passed in, and then if it hasn't already been required,
05:21
do the same thing we were doing before. Read the file in, pass it to eval, and then add it to the array so that you won't require it again later. In fact, this is exactly what Ruby does, albeit written in C and not in Ruby. There is a loaded features global variable, and it's an array, and it contains a list of all of the things that you've required in the past.
05:42
So, if you ever wanted to know if you've required something yet, you can actually just check the loaded features array. So, there's one more problem with this, which is that right now it only works if you pass it absolute paths. I'm sure you don't mind typing the full path from wherever you are to exactly where the file
06:00
that you want to require is. I'm sure that's fine, too. So, the easiest way to allow requires that aren't absolute is to just treat all requires as if they're relative to the path where you started the Ruby program. And that's easy, but that doesn't help a lot
06:20
if you want to require Ruby files from different places. So, say you have a folder full of this library you wrote and a folder full of this application you wrote and you want to use the library from that application, you can't, like, writing relative paths from wherever you started the Ruby program would be terrible. So, instead, we can create an array
06:42
that holds the list of paths that we want to load Ruby files from. In a burst of creativity, I'm just gonna call that variable the loadPath, and here's an implementation of a loadPath. If you put something in the loadPath array,
07:01
you can then pass a relative path to any directory that's in the loadPath array and will look for the file, hey, is there a file, so if you require foo, we'll say, hey, is there a file named foo inside any of the loadPath directories, and the first one that we find, searching the loadPath in order from first to last,
07:22
will require that one. Coincidentally, this is exactly what Ruby does. There is a global variable named loadPath, and if you put a string that contains a path to a directory in it, Ruby, whenever you require something, will look in that directory to see if there's a file with that name.
07:41
So you can totally use the loadPath to require files from somewhere else while you're working with them. And of course, the loadPath and loaded features can both be combined, but that code didn't fit on a single slide, so I'll leave that as an exercise to the listener.
08:00
It's pretty straightforward, to be honest. So loadPaths are pretty cool. They allow us to load Ruby directories, even if they're spread across multiple places. At this point, we could even have, like, automatically, at the start of every script, we could add the directory that holds the standard library to the loadPath, and then all of the files
08:23
that are part of the Ruby standard library, like net HTTP, you know, all of the set, all of those cool things that come with Ruby, could just be available for require automatically, and you wouldn't have to worry about putting them in the loadPath yourself. That is exactly what Ruby does. The standard library starts on the loadPath
08:41
whenever you start Ruby. It's pretty great. So this was cool, and for several years, this was enough. People just added things to the loadPath a lot, or wrote scripts that added things to the loadPath before requiring things before their actual script happened.
09:01
The thing that got super tedious about just having loadPaths is that if you want to get code from someone else, you have to find that code, download that code, put it in somewhere, remember where that somewhere is, put that somewhere in the loadPath, and then require it. It was pretty tedious. Sorry. So the next thing that happened was setup-rb.
09:24
So we're totally caught up to the state-of-the-art in Ruby libraries around the year 2000. Everyone's still installing shared Ruby code by hand, CP, CP, and that wasn't so much fun. So a Japanese Ruby developer named Minoru Aoki
09:42
wrote setup-rb, and amazingly, even though this was created in the year 2000, setup-rb is still around on the internet. The website for this developer is i.loveruby.net, which is pretty cool, and you can even download setup-rb,
10:00
although to be perfectly honest, it hasn't been updated since 2005, so I'm not sure it's super helpful to you. So how did setup-rb work? Well, you... At its core, setup-rb kind of mimicked the classic Unix installation pattern of downloading a piece of software, decompressing it,
10:22
and then running configure-make-make-install, and so setup-rb kind of copied that for Ruby, and you would run setup-rb setup, setup-rb config, setup-rb install, and what would happen is setup-rb would copy all of the Ruby files, and there was a specific directory structure,
10:42
kind of like a gem today, where you would have library files and bin files that you could run as programs and support files, and setup-rb would copy all of those files into a directory that was already in the load path called site-ruby, and that was like the Ruby files that you had installed that were specific to your computer,
11:03
and so after setup-rb, using Ruby libraries was actually much easier than it had been. You could find a cool library on the Internet, you could download that cool library, you had to untar that cool library by hand, and then you had to run Ruby setup-rb all by hand, but then, hey, it was all installed.
11:22
No more manual copying, no more, you know, like having to manage all these files, and everything was in the load path. You could just require it as soon as you ran setup-rb. It was pretty cool. So after a little while, some of the shortcomings of this scheme became apparent too.
11:40
There's no versions for any of these libraries, and after you run setup-rb, there's not even a way to tell what version you have unless you write it down or unless the library author was really nice and put the version into the code somehow, and there's no way to uninstall.
12:01
Everything just gets thrown into the same directory, so you run setup-rb for five different Ruby libraries, and now all of their files are just in one directory. Good luck figuring out which ones belong to which, because if you delete the wrong one, too bad. And then upgrading. Upgrading was super fun.
12:22
If there was a new version of the library, which, good luck finding that out, right? You had to remember the website where you got it from in the first place. I hope you write all these down. I hope you've written down every website you've ever downloaded Ruby from. You have to go back to that website, and you have to remember which version you have, which, as I said before, there's nothing there unless you wrote it down.
12:42
And then you have to download the tarball with the new version, and decompress it, and cd into it, and run Ruby setup-rb all, and hope that the new version didn't delete any files, because the old files are still there, and you just went over the top of them. Yeah.
13:00
So, overall, this was, this probably sounds a little tedious. It was really tedious. People frequently kind of had no idea what was actually happening with their libraries, and it was actually not uncommon for people to be like, oh, this doesn't work, I'm just gonna fix it in my site Ruby directory.
13:20
Okay, everything's great. Right, yeah, super awesome. So, at some point, some people were like, hey, this isn't actually that great. What if you could just gem install? That would be cool. And so, in 2003, RubyGems came to the rescue
13:42
and kind of fixed all of the problems with setup-rb that were known. You could check to see if a library existed by just running gem list. You could install a gem just by running gem install. You could uninstall a gem, super great, by running gem uninstall. And RubyGems kept each of these libraries
14:02
in different directories, so that you knew which libraries you had, and you knew how to uninstall those libraries, and you knew how to install new versions of those libraries, and it was all with a single command. There was none of this like, find it on the internet somewhere, download it, unpack it, setup-rb it. And RubyGems had another super cool trick up its sleeve,
14:20
which was versions. RubyGems actually kept each version of each gem in a different place. You could install multiple versions of the same library, and they could all be in your Ruby, because they didn't all go into one giant folder, they all went into their own separate folders. So there was a folder for Rails 4.1,
14:40
Rails 4.2, Rails 5. This was pretty cool. So to make this actually work, because required doesn't support versioning inherently, RubyGems added a gem method that lets you say, hey, I know that I have, or I don't really care whether it's installed or not,
15:00
I need version 1.0 of Rack, and RubyGems will check to make sure it's installed, and then put that directory, just the one with Rack 1.0, into your load path. So then when you run require rack, you get Rack 1.0. It was pretty cool. And so calling the gem method told RubyGems
15:22
that you wanted to manipulate the load path to load exactly the version that you knew that your code wanted to talk to. It was pretty useful. RubyGems also has a way to support versioning even in commands that come with gems. So like the Rack gem comes with the rackup command,
15:40
and if you have multiple versions of Rack installed, the rackup command could run any of those versions. So RubyGems defaults to the newest version that you have installed, hoping that the newest version is the right one, but if that's not the right one, RubyGems actually checks the first argument to the command to see if there's something with underscores on either side of it, and it thinks that that will be
16:02
the version number that you want. So in this example, we're running rackup from Rack version 1.2.2, and only version 1.2.2. If you don't have version 1.2.2 installed, RubyGems will be like, hey, sorry, I couldn't find that version. You need to install it first. RubyGems was really, really successful.
16:24
Ruby grew in popularity a lot, but RubyGems made Ruby libraries and sharing code grow in popularity a lot. Present day, we have about 100,000 different gems and about a million different versions of those 100,000 gems.
16:42
That is a lot of shared Ruby code, and that is super cool. So, and you probably knew this was coming, as cool as RubyGems is, it still has some problems. If you have multiple applications that all use RubyGems to load their dependencies,
17:02
this can be problematic. It's really hard to coordinate across multiple applications because every, the way RubyGems works, every machine, or technically every individual version of Ruby, but like each installation of Ruby itself just has a set of gems, right?
17:21
Like you ran gem install, and now there's all these gems. And so if one developer runs gem install foo and starts using foo in their application, and then like commits that code and checks it in, and the next person checks it out and tries to run the application, it's going to explode with like,
17:40
I don't know what foo is. Where is it? You need to fix that for me. And so it led to an era of basically pure manual dependency management. Starting a new job, hooray! This, no joke, this literally happened to me in 2008. New job, welcome to the team, here's your cool new laptop.
18:03
We expect you to have the app running by next week. It actually took me, and I was totally working overtime on this, I think it only took me three and a half days. It was amazing.
18:21
To figure out which gems to run gem install, I looked in the readme and there was a list, and I installed all of them, and then clearly there were some that some people had just kind of forgotten to put in the readme, and then it kind of worked, but then I wasn't able to get images working, and then some other developer was like,
18:41
oh yeah, you have to install image magic. This was before homebrew. It was really, really terrifying. To try and fix this problem of, do we just put the gems in the readme? How do we even know if we have written everything in the readme?
19:01
I don't know, try it. And of course you had to get a new machine to try it on because some person, after three years of using Ruby, you've just gem installed everything and you have no idea what is important and what isn't important, and yeah, it's terrible. So people started working on tools to help this problem. Rails actually added this thing called config.gem,
19:23
and this is like Rails 2.3 era, 2.2 era, where you would say, hey Rails, I need this gem, and you would put it inside your application rb file, and that was super helpful if you needed to know for sure that this was the master list of all the gems
19:41
that you needed in your application, but you could only access that list if Rails was already loaded. So if you upgrade Rails over here, it was pretty bad.
20:00
So because Ruby gems automatically uses the newest version of each gem, just having an older version installed didn't mean that it would be used, and if you install some gem a month after the other person did, maybe there's a new version. You just get the new version automatically.
20:20
This is also totally a real-life experience that happened to me in 2009. Debug a production server that just throws exceptions sometimes for three days. The other production servers are fine. Can't reproduce this problem on a single developer laptop. Like, what is even going on? This is so weird.
20:41
After three days, I finally thought to look at the output from gem list for the entire production machine, and I was like, oh, this production server has gem version 1.1.3, and every other production server and every developer laptop has gem version 1.1.4, and that was the problem.
21:00
There was a bug, and only that server had this problem. And then, like I was saying about Rails versions, you could gem install Rails, be happy, make a new app, run your server, everything's great, and then you switch to another application that already existed, didn't get written to use that version of Rails,
21:22
got ready to use some older version of Rails, and you're like, okay, let's go. Because you just didn't have the right version of Rails, and there was no way to, like, if you put the Rails version in your config.gem line inside your application RB, then Rails would complain that you had the wrong version of Rails,
21:41
but Rails had to have successfully started up to tell you that you had the wrong version of Rails, so it didn't actually help. And ultimately, like, it was actually a significant part of my job as a Ruby developer to, like, figure this shit out by hand, and it sucked. Depending on what exactly you did on the team,
22:02
some people on my team at the time spent like a quarter or a third of their time doing nothing but figuring out and fixing dependency management issues, and it was really, really, like, I felt really bad for them. Sometimes it was me, and I felt really bad for me. And then there's one more,
22:21
even after you have done all of this by-hand management, there's one more problem that RubyGems has that is another reason why Bundler was created, and that is activation errors, which, so, an activation error
22:41
is what happens in RubyGems when you load an application, and you start by saying, hey, I need this gem. Hey, I need this gem. Hey, I need this other gem. And so RubyGems will load the newest version of those gems that it can, and so sometimes you'll say, hey, I need this gem,
23:01
and then this gem will need that gem, and then that gem will need this gem, and you'll get the newest version of that child gem, and then later you'll say, oh, and I also need this gem, and that gem won't work with that gem. So how common can this be, really? Well, unfortunately, it was super common.
23:23
Not like happens to you every day common, but like happens to you maybe two or three times a year, and when it happens, you basically tear all your hair out, delete your entire Ruby install, and reinstall Ruby and start installing gems again because figuring out exactly which combination of installed gems was causing this problem was just a total nightmare.
23:43
So this is a real-life activation error. I salvaged this from a presentation that I gave in 2010 about why Bundler exists. So this is a Rails app, and it's loading, and Rails, of course, depends on ActionPack. This is the Rails 2.3 era.
24:01
ActionPack depends on Rack. Rack is a gem that helps Rails talk to web servers, and Thin, which is a web server, also depends on Rack. So Rack is how Rails talks to Thin, how Thin talks to Rails, but there's a problem. Thin is perfectly happy to use Rack 1.1,
24:21
which makes some changes to how Rack works. ActionPack, on the other hand, is not happy to use Rack 1.1 and can only use Rack 1.0. And so when you run your server, your server, of course, loads Thin first because Thin is the server, and then Thin gets to work trying to load up your Rails application, and your Rails application says,
24:41
I can't actually use that Rack, sorry, the end. So the conclusion here is that, so the reason that these activation errors would happen is that RubyGems does what we call runtime resolution, which is RubyGems figures out which versions
25:01
of which gems it should load after RubyGems is already running, and you say, hey, I need a thing, and it's like, okay, I think this version works, and at some point, if later on, you say, hey, I need a thing that doesn't work with things that you've already done, RubyGems just has to be like, well, can't fix that.
25:21
And so the fix for this problem is to figure out all of the versions before you run your application. You have to know that the versions that you're gonna use are all versions that can work together with one another. And so resolving things when you, like at install time, which is when you install
25:41
all of the gems, know that you're installing versions that work together. So hang on a second, you're probably saying, how do we make sure that all of the versions that we're installing work together? Well, that's actually where Bundler comes in. Before Bundler, the process of figuring out which gems would work together was done entirely by hand, and it consisted of
26:01
gem uninstall, gem install slightly older version, does Rails start up yet? Gem uninstall, gem install slightly older version, does Rails start up yet? And when the exception stopped, you knew you'd won. Unsurprisingly, computers are a little bit faster at this process than people, and computers are also really good and accurate
26:23
at trying many, many, many, many, many options until one of them actually works. So this is what Bundler does. You, yeah, I know. Bundler figures out the entire list of every gem and every version of every gem that you need,
26:40
but that also all work together with one another. This is called dependency graph resolution, and there's an entire academic literature about dependency graph resolution, and it's kind of a well-known hard problem. It's part of the set of problems called NP-complete, and the totally fantastic thing,
27:01
and I say this as a person who has to fix Bundler when it doesn't work, is that in theory, you can construct a set of gems and a gem file such that it is not possible to find a set of gems that work together until after the heat death of the universe. Most of the time, we don't have that long to wait,
27:22
and so we use a lot of tricks and shortcuts and heuristics to try and figure out which gems to try first and hopefully actually finish before you've drunk that cup of coffee or whatever. So we have this pretty large built-up set of tricks over the years, and most gem files actually resolve in less than 10 seconds, which is pretty cool considering that the upper bound on that
27:41
is practically infinity. So after finding versions that work together, because this problem is really hard and we don't want to have to keep doing it over and over and over, Bundler writes down the exact versions of every gem that did all work together so that they can be reused by other people who are also interested in running your application.
28:04
So that file is called the gemfile.lock. This is a little snippet of a gemfile.lock showing you which gems need to be installed, which versions of those gems need to be installed, and as a bonus, the lock file is what makes it possible to install the exact same version of every gem
28:22
on every machine that's running this application. That means that when you develop on your laptop, you get whatever version of the gem was newest when you were developing because you ran, you know, bundle install and you got the newest version by default or whatever. But because of the lock file, when you go to put that on your production server, you're completely guaranteed
28:41
that you will also have version 1.1.4 of Rovnits and you won't have to spend three days figuring out why that production server doesn't quite work all the time. It's pretty great. So, fundamentally, like, the core of Bundler consists of two steps,
29:01
bundle install and bundle exec. So the steps for bundle install are actually pretty simple. They're totally understandable in plain English that fits on a single slide, which is great. I edited this slide for maybe ten minutes deleting words. So the steps to bundle install are read the gem file,
29:21
ask RubyGems.org for a list of all of the gems that we're gonna need, find versions of those gems that are both allowed by the gem file because you can sometimes say, I only want this version or I only want that version or I only want versions greater than that version kind of thing. And then once you've found versions that all work together because you checked,
29:41
write all of those versions down in the lock and then install every version until every gem that's in the lock is installed. And that's how bundle install works. Bundle install actually uses RubyGems under the covers to do the installation, and so every bundle is its own little
30:00
RubyGems isolated install. Every application has its own RubyGems, thanks to Bundler. And then the next step is bundle exec, which is how we use that application's dedicated little RubyGems instead of the one that just has whatever in it because you ran gem install last year. So the way bundle exec works is it reads the gem file
30:21
and it reads the lock if the lock's there. It uses the lock gems if the lock file is there, and if the lock file isn't there, it finds versions that all work together just like install would, except bundle exec doesn't do any installing. It just says, oh, do I already have versions that all work together? They do. Cool. And then bundle exec deletes any gems
30:41
that are already in the load path, because sometimes that happens before bundle loads, and then it adds the exact gem at the exact version that you need to the load path so you can use it, which is pretty great. That's it. That's all bundle exec does. Once your gems, all the gems that actually work together and their exact versions are in the load path, your application just goes on its way and it's happy.
31:02
There's no activation errors. All your requires actually succeed, I hope. Everything's pretty great. So as I think I promised in the abstract for this talk, here's a bundle exec removing protip. I don't really like typing bundle exec.
31:21
I find it really annoying, but Bundler provides a way to not have to type bundle exec all the time, and it is to create programs that map to the little copy, like the Ruby gem installation that belongs just to that application. You can use the bin stubs command, bundle bin stubs, some gem, and it will create in the bin directory
31:43
a program for that gem that only runs the exact version that belongs to that application. So if you have RSpec in your Rails app, you can have bin RSpec that will only run the RSpec for your app, and in this way, you can have bin RSpec refer to RSpec 3 in this application and have bin RSpec refer to RSpec 2 in that application.
32:02
No exec required. It's pretty great. Rails has actually started to do this very thing, and Rails 4 ships with bin Rails and bin Rake that are scoped. Like, so when you run bin Rails, you get the exact Rails version for that application and not this application, and when you run bin Rake, you get the exact version of Rake for that application and not this application.
32:23
Pretty cool. No more bundle exec. If everyone did this, and you can check in these bin stubs, right? So you can take bin RSpec, and you can put it in Git, and it will be mapped to just that application forever, so no one would ever have to bundle exec ever again if everyone did this. Pretty cool.
32:41
So, now we bundle install. All our gems show up. We have versions that are dedicated to each individual application, but as you probably sensed a pattern going through history, that wasn't actually the end. There are still problems that show up after Bundler came out.
33:00
The biggest problem that was left was that running bundle install just took a really long time, and if you lived really far away from the US, it took a really long time. I talked to some developers in South Africa when I went there to give a talk, and they told me about how running bundle install means that they literally get up to start making themselves a cup of coffee that they can finish
33:20
before bundle install finishes. So, to try and speed things up, Bundler 1.1 created a completely new way to get information from RubyGems about gems, and that sped things up by around 50%, which was a pretty big win. We keep working on this. Bundler 1.9 just came out
33:41
this month. There's a bunch more improvements that we're still working on. Bundler will keep getting better. If you're interested in following along with that, the Bundler website has news announcements at Bundler.io, and on Twitter, we're also at Bundler.io. So, having said all of this, if you use Bundler, I
34:01
would totally love to have your help working on Bundler. It's an open source project. We're super... We have dedicated a lot of time to making it easy for people who don't know how to do open source to help with Bundler and to start working on Bundler and to kind of get into open source that way.
34:21
It's a project on GitHub at Bundler Bundler. It's on Twitter. If you are interested but don't really know where to start, you can totally email the Bundler team at team at Bundler io and we'll get you set up. On the other hand, if you have a job that means
34:41
you have money but not time, join Ruby Together and give us money and we'll work on Bundler and it'll be better. As Ruby Together grows, we're also going to be tackling bigger community issues. We want to add easy to use gem mirrors so that you don't have to go all the way to RubyGems.org for your office
35:00
or for your data center. We want to add better public benchmarks. There's a project called RubyBench that's starting to do that and we'd really like to expand it. There's a bunch of other things that Ruby Together is working on that'll be totally cool. If you want Bundler or Ruby Together stickers, I have a giant pile so find me later. That's it.