Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How does Bundler work, anyway? (RubyConf AU 2017)

André Arko
February 10, 2017

How does Bundler work, anyway? (RubyConf AU 2017)

We all use Bundler at some point, and most of us use it every day. But what does it do, exactly? Why do we have to use bundle exec? What's the point of checking in the Gemfile.lock? Why can't we just gem install the gems we need? Join me for a walk through the reasons that Bundler exists, and a guide to what actually happens when you use it. Finally, we'll cover some Bundler "pro tips" that can improve your workflow when developing on multiple applications at once.

André Arko

February 10, 2017
Tweet

More Decks by André Arko

Other Decks in Technology

Transcript

  1. how does bundler
    work, anyway?
    RubyConf AU
    2017

    View full-size slide

  2. how does bundler
    work, anyway?

    View full-size slide

  3. a brief (?) history of
    dependency management

    View full-size slide

  4. André Arko
    @indirect

    View full-size slide

  5. therubyway.io

    View full-size slide

  6. let’s share
    ruby code

    View full-size slide

  7. Gemfile
    gem “foo”

    View full-size slide

  8. Terminal
    $ bundle install

    View full-size slide

  9. Terminal
    $ bundle exec foo

    View full-size slide

  10. done!
    pretty cool, right?

    View full-size slide

  11. but…
    what just
    happened?

    View full-size slide

  12. iiiiit’s
    History

    Time

    View full-size slide

  13. 1994 2000 2003 2009
    dependency timeline
    require setup.rb RubyGems Bundler

    View full-size slide

  14. require
    it loads the codes

    View full-size slide

  15. def require(filename)
    eval File.read(filename)
    end

    View full-size slide

  16. i’m sure it’s fine
    run code twice?

    View full-size slide

  17. $LOADED_FEATURES = []
    def require(filename)
    if $LOADED_FEATURES.include?(filename)
    return true
    end
    eval File.read(filename)
    $LOADED_FEATURES << filename
    end

    View full-size slide

  18. probably fine too
    only absolute paths?

    View full-size slide

  19. $LOAD_PATH = []
    def require(filename)
    full_path = $LOAD_PATH.first do |path|
    File.exist?(File.join(path, filename))
    end
    eval File.read(full_path)
    end

    View full-size slide

  20. load paths are
    ⛄ pretty ❄ cool

    View full-size slide

  21. but now we need…
    setup.rb

    View full-size slide

  22. how do setup.rb?
    ruby setup.rb setup
    ruby setup.rb config
    ruby setup.rb install

    View full-size slide

  23. use ruby libraries
    1. Find a cool library
    2. Download the library
    3. Untar the library
    4. Run `ruby setup.rb all`

    View full-size slide

  24. no versions
    no uninstall
    but it’s fine, I’m sure

    View full-size slide

  25. maybe a little bit
    incredibly tedious

    View full-size slide

  26. what if you could…
    gem install
    gem uninstall
    gem list rails

    View full-size slide

  27. bonus round!
    gem install rails -v 4.1
    gem install rails -v 4.2
    gem install rails -v 5.0

    View full-size slide

  28. gem "rack", "1.0"
    require "rack"

    View full-size slide

  29. $ rackup _1.2.2_ -p 3000

    View full-size slide

  30. so many gems!
    100,000 gems
    1,000,000 versions

    View full-size slide

  31. but gem apps don’t
    play well with others

    View full-size slide

  32. welcome to the team
    here’s your machine
    we expect setting up
    to take a week

    View full-size slide

  33. # config/application.rb
    […]
    config.gem “rack”
    config.gem “rails”
    config.gem “what?”
    […]

    View full-size slide

  34. “why is this broken in production?”
    “dunno, it works on every developer machine”
    3 days of debugging later…
    “oh, look, one production server has frobnitz
    1.1.3, but the others have 1.1.4”
    “welp. that explains those exceptions ”

    View full-size slide

  35. version conflicts
    gem install rails
    rails s
    cd ../other-project
    rails s

    View full-size slide

  36. activation errors
    how common can
    they be, really?

    View full-size slide

  37. $ rails s
    Gem::LoadError: can't activate
    rack (~> 1.0.0., runtime) for
    ["actionpack-2.3.5"], already
    activated rack-1.1.0 for
    ["thin-1.2.7"]
    activation errors

    View full-size slide

  38. the moral
    runtime resolution
    install-time resolution

    View full-size slide

  39. wait…
    how do we resolve
    at install-time?

    View full-size slide

  40. addressable (2.3.7)
    arel (6.0.0)
    bcrypt (3.1.10)
    binding_of_caller (0.7.2)
    debug_inspector (>= 0.0.1)
    builder (3.2.2)
    byebug (3.5.1)
    columnize (~> 0.8)
    debugger-linecache (~> 1.2)
    slop (~> 3.6)
    celluloid (0.16.0)
    Gemfile.lock

    View full-size slide

  41. bundle install
    my old friend
    1. Read the Gemfile (and lock, if it's there)
    2. Ask RubyGems.org for a list of every gem we need
    2. Find gems allowed by the Gemfile that work together
    3. Write down those versions in the lock for future installs
    4. Install gems until every locked gem is installed

    View full-size slide

  42. bundle exec
    everyone’s nemesis
    1. Read the Gemfile (and lock, if it's there)
    2a. Use locked gems if possible OR
    2b. Find versions that work to put in the lock
    3. Remove any existing gems the $LOAD_PATH
    4. Add each gem in the lock to the $LOAD_PATH

    View full-size slide

  43. pro tip
    no more bundle exec!
    $ bundle binstubs rspec-core
    $ bin/rspec
    repeat as needed for other gems

    View full-size slide

  44. was that the end?
    in a word, no.

    View full-size slide

  45. I want YOU
    to contribute
    3
    @bundlerio
    [email protected]

    View full-size slide

  46. or if you’re too busy,
    fund this work.
    http://rubytogether.org/join

    View full-size slide