In the beginning, there was require...

Ff075e16f16f327bee1f233542b8b7fc?s=47 Adam McCrea
November 20, 2019

In the beginning, there was require...

Almost every Ruby program begins with the "require" method, but many of us don't pause to think about what it's doing until it fails us.

What happens when we call "require"? How does Ruby find what we're looking for? Is Bundler somehow involved? What about "require_relative" and "require_dependency"?

This talk will guide beginner and intermediate Rubyists through these foundational concepts that power our Ruby programs, from single-file script to a behemoth Rails 6 app.

Ff075e16f16f327bee1f233542b8b7fc?s=128

Adam McCrea

November 20, 2019
Tweet

Transcript

  1. IN THE BEGINNING THERE WAS REQUIRE

  2. CONFESSION https://pxhere.com/en/photo/458183

  3. class ActiveStorage::Blob < ActiveRecord::Base require_dependency "active_storage/blob/analyzable" require_dependency "active_storage/blob/identifiable" require_dependency "active_storage/blob/representable"

    # ...
  4. CARGO CULTING

  5. 12 YEARS photo by John Nebbia

  6. None
  7. WHO AM I? Adam McCrea @adamlogic You Need A Budget

    (YNAB) Rails Autoscale
  8. DEPENDENCIES standard library Rubygems within a project Photo by Iker

    Urteaga on Unsplash
  9. PHP include 'banana.php';

  10. NODEJS const fs = require("fs"); const banana = require("./banana.js");

  11. ES6 import fs from "fs"; import banana from "./banana.js";

  12. RUBY require require_relative require_dependency load autoload Bundler.require ✨ Rails ✨

  13. None
  14. None
  15. REQUIRE A STANDARD LIBRARY > CSV # NameError (uninitialized constant

    CSV) > require 'csv' # true > CSV # CSV > require 'csv' # false
  16. > Kernel.methods.sort # [ :attr_accessor, # :attr_reader, # :attr_writer, #

    ... # :puts, # :raise, # :require, # :require_relative, # ...
  17. > $LOAD_PATH.inspect # [ "/Users/adam/.asdf/plugins/ruby/rubygems-plugin", # ... # "/Users/adam/.asdf/installs/ruby/2.6.4/lib/ruby/2.6.0", #

    ...
  18. > $LOADED_FEATURES.inspect # [ ... # "/Users/adam/.../ruby/2.6.0/csv/writer.rb", # "/Users/adam/.../ruby/2.6.0/csv/version.rb", #

    "/Users/adam/.../ruby/2.6.0/csv.rb"]
  19. REQUIRE A GEM > Minitest # NameError (uninitialized constant Minitest)

    > require 'minitest' # true > Minitest # Minitest
  20. GEM ACTIVATION > $LOAD_PATH.grep(/minitest/) # [] > require 'minitest' #

    true > $LOAD_PATH.grep(/minitest/) # [".../ruby/2.6.4/lib/ruby/gems/2.6.0/gems/minitest-5.11.3/lib"]
  21. KERNEL#REQUIRE FROM RUBYGEMS Check LOADED_FEATURES Check LOAD_PATH Check for a

    matching installed gem Add gem's lib* dir to LOAD_PATH Resume default require behavior
  22. Gem::Specification.new do |spec| spec.require_path = "lib" ...

  23. REQUIRE WITHIN A PROJECT ~/project ├── main.rb └── lib └──

    example.rb # main.rb require 'example' require 'lib/example'
  24. REQUIRE WITHIN A PROJECT ~/project ├── main.rb └── lib └──

    example.rb # main.rb require '/Users/Adam/project/lib/example'
  25. REQUIRE WITHIN A PROJECT ~/project ├── main.rb └── lib └──

    example.rb # main.rb require './lib/example' $ ruby project/main.rb
  26. ADD TO LOAD PATH ~/project ├── main.rb └── lib └──

    example.rb # main.rb $LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib') require 'example'
  27. ALTER LOAD_PATH AT RUNTIME ~/project ├── main.rb └── lib └──

    example.rb $ ruby -I lib main.rb
  28. ALTER LOAD_PATH AT RUNTIME ~/project ├── main.rb └── test ├──

    test_helper.rb └── example_test.rb # example_test.rb require 'test_helper' $ ruby -I test test/example_test.rb
  29. None
  30. REQUIRE RELATIVE ~/project ├── main.rb └── lib └── example.rb #

    main.rb require './lib/example' require_relative 'lib/example'
  31. None
  32. LOAD > load './lib/example.rb' # true > load './lib/example.rb' #

    true > load './lib/example.rb' # true
  33. None
  34. GEM VERSIONS > require 'minitest' # true $ gem list

    minitest # minitest (5.12.2, 5.11.3) > Minitest::VERSION # "5.12.2"
  35. BUNDLER # Gemfile gem 'minitest', '~> 5.11.0' ❯ bundle install

    # Resolving dependencies... # Using minitest 5.11.3
  36. # Gemfile.lock GEM specs: minitest (5.11.3)

  37. > require 'minitest' # true > Minitest::VERSION # "5.12.2"

  38. > require 'bundler' > Bundler.setup > $LOAD_PATH # [ "/Users/adam/.asdf/plugins/ruby/rubygems-plugin",

    # ".../ruby/2.6.4/lib/ruby/gems/2.6.0/gems/minitest-5.11.3/lib", # ... > require 'minitest' # true > Minitest::VERSION # "5.11.3"
  39. > require 'bundler' > Bundler.require > Minitest::VERSION # "5.11.3"

  40. # Gemfile gem 'minitest', '~> 5.11.0', require: false

  41. None
  42. KERNEL#AUTOLOAD # lib/example.rb class Example puts "Example has loaded" end

    # main.rb $LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib') autoload :Example, 'example' Example # Example has loaded
  43. # rack-2.0.6/lib/rack.rb autoload :Builder, "rack/builder" autoload :BodyProxy, "rack/body_proxy" # ...

    40 more autoloads
  44. None
  45. > defined? User # nil > User.new # #<User:0x00007fd6547a88e8> >

    defined? User # "constant"
  46. class UsersController < ApplicationController def new @user = User.new end

    end
  47. AUTOLOAD_PATHS app/controllers app/models app/* app/*/concerns

  48. $LOAD_PATH != autoload_paths # lib/example.rb class Example end # users_controller.rb

    require 'example'
  49. None
  50. None
  51. # app/models/active_storage/blob.rb class ActiveStorage::Blob < ActiveRecord::Base require_dependency "active_storage/blob/analyzable" require_dependency "active_storage/blob/identifiable"

    require_dependency "active_storage/blob/representable" include Analyzable include Identifiable include Representable # ... end
  52. None
  53. TAKEAWAYS

  54. STANDARD LIBRARIES require 'csv'

  55. GEMS require 'minitest' # OR Bundler.setup require 'minitest' # OR

    Bundler.require
  56. WITHIN A PROJECT (NON-RAILS) require_relative 'lib/example' # OR $LOAD_PATH.unshift File.join(File.dirname(__FILE__),

    'lib') require 'example' # OR autoload :Example, 'example'
  57. WITHIN A PROJECT (RAILS) embrace implicit autoloading expclicitly require files

    in lib
  58. Photo by Laurie-Anne Robert on Unsplash

  59. THANK YOU! Comments/Questions: @adamlogic (or come talk to me) ynab.com

    railsautoscale.com