< Return to Video

RailsConf 2015 - How Does Bundler Work, Anyway?

  • 0:00 - 0:09
    (Theme Music)
  • 0:13 - 0:17
    This talk is about how Bundler works
  • 0:18 - 0:21
    How does Bundler work?
  • 0:22 - 0:25
    This is an interesting question.
  • 0:25 - 0:28
    We'll talk about it for a while.
  • 0:29 - 0:30
    This talk is a brief history of
  • 0:32 - 0:36
    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]
Title:
RailsConf 2015 - How Does Bundler Work, Anyway?
Description:

more » « less
Video Language:
English
Duration:
35:39

English subtitles

Incomplete

Revisions