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

How Does Bundler Work, Anyway? (RubyConf 2015)

André Arko
November 17, 2015

How Does Bundler Work, Anyway? (RubyConf 2015)

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

November 17, 2015
Tweet

More Decks by André Arko

Other Decks in Technology

Transcript

  1. how does bundler
    work, anyway?

    View Slide

  2. how does bundler
    work, anyway?

    View Slide

  3. a brief (?) history of
    dependency management

    View Slide

  4. André Arko
    @indirect

    View Slide

  5. View Slide

  6. therubyway.io

    View Slide

  7. View Slide

  8. View Slide

  9. let’s share
    ruby code

    View Slide

  10. Gemfile
    gem “foo”

    View Slide

  11. Terminal
    $ bundle install

    View Slide

  12. Terminal
    $ bundle exec foo

    View Slide

  13. done!
    pretty cool, right?

    View Slide

  14. but…
    what just
    happened?

    View Slide

  15. iiiiit’s
    History

    Time

    View Slide

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

    View Slide

  17. require
    it loads the codes

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  21. probably fine too
    only absolute paths?

    View Slide

  22. $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 Slide

  23. load paths are
    ⛄ pretty ❄ cool

    View Slide

  24. but now we need…
    setup.rb

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  28. maybe a little bit
    incredibly tedious

    View Slide

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

    View Slide

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

    View Slide

  31. gem "rack", "1.0"
    require "rack"

    View Slide

  32. $ rackup _1.2.2_ -p 3000

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  37. “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 Slide

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

    View Slide

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

    View Slide

  40. $ 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 Slide

  41. the moral
    runtime resolution
    install-time resolution

    View Slide

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

    View Slide

  43. View Slide

  44. 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 Slide

  45. 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 Slide

  46. 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide