Understanding ruby load, require, gems, bundler and rails autoloading from the bottom up

cstack
cstack
May 30, 2017 · 10 min read
puts("foo.rb loaded!")$FOO = 2
> load('/Users/cstack/foo.rb')
foo.rb loaded!
=> true
> $FOO
=> 2

What does `load` do?

# foo.rb$FOO_GLOBAL_VARIABLE = 2class FooClass; endFOO_CONSTANT = 3def foo_method; endfoo_local_variable = 4
> load('/Users/cstack/foo.rb')
=> true
> $FOO_GLOBAL_VARIABLE
=> 2
> FooClass
=> FooClass
> FOO_CONSTANT
=> 3
> foo_method
=> nil
> foo_local_variable
NameError: undefined local variable or method `foo_local_variable'

Calling `load` multiple times

# foo.rbputs("foo.rb loaded!")
FOO_CONSTANT = 3
> load('/Users/cstack/foo.rb')
foo.rb loaded!
=> true
> load('/Users/cstack/foo.rb')
foo.rb loaded!
/Users/cstack/foo.rb:2: warning: already initialized constant FOO_CONSTANT
=> true

Calling `load` with relative paths

> load('./foo.rb')
foo.rb loaded!
=> true
> load('./foo.rb')
LoadError: cannot load such file -- foo.rb
> load('./foo.rb')
foo.rb loaded!
=> true
> Dir.chdir('..')
=> 0
> load('./foo.rb')
LoadError: cannot load such file -- foo.rb

$LOAD_PATH

> $LOAD_PATH.push("/Users/cstack")> load('foo.rb')
foo.rb loaded!
=> true
> Dir.chdir("/Users/cstack")
=> 0
> load('foo.rb')
foo.rb loaded!
=> true

What does `require` do?

> $LOAD_PATH.push('/Users/cstack')
=> ["/Users/cstack"]
> require('foo.rb')
foo.rb loaded!
=> true
> require('foo.rb')
=> false
> $LOAD_PATH.push('/Users/cstack')
=> ["/Users/cstack"]
> require('foo')
foo.rb loaded!
=> true
> File.exists?('foo.rb')
=> true
> require('foo')
LoadError: cannot load such file -- foo

What does `require_relative` do?

# foo.rb
puts("foo.rb loaded!")
load('bar.rb')
# bar.rb
puts("bar.rb loaded!")
> load('/Users/cstack/foo.rb')
foo.rb loaded!
LoadError: cannot load such file -- bar.rb
# foo.rb
puts("foo.rb loaded!")
require_relative('bar.rb')
> load('/Users/cstack/foo.rb')
foo.rb loaded!
bar.rb loaded!
=> true

What are gems?

~ gem which json
/Users/cstack/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/json.rb

How do you require gems?

> load('/Users/cstack/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/json.rb')
=> true
> JSON
=> JSON
> puts $LOADED_FEATURES
...
/Users/cstack/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/rubygems.rb
...
> puts $LOAD_PATH.grep(/json/) # json is initially not in $LOAD_PATH
=> nil
> JSON # JSON is initially not loaded
NameError: uninitialized constant JSON

> require('json') # RubyGems searches through your installed gems
=> true
> puts $LOAD_PATH.grep(/json/) # RubyGems adds entries to $LOAD_PATH
/Users/cstack/.rvm/gems/ruby-2.3.1/gems/json-2.1.0/lib
/Users/cstack/.rvm/gems/ruby-2.3.1/extensions/x86_64-darwin-16/2.3.0/json-2.1.0
=> nil
> JSON # Now all the code from the json gem is loaded
=> JSON

How does all the code in a gem get loaded?

How do you install a gem?

~ gem install json
Fetching: json-2.1.0.gem (140800B)
Building native extensions. This could take a while...
Successfully installed json-2.1.0
1 gem installed
...
- INSTALLATION DIRECTORY: /Users/cstack/.rvm/gems/ruby-2.3.1
...
...
- REMOTE SOURCES:
- https://rubygems.org/
...

What if one gem requires another gem?

~ gem install domain_name
Fetching: unf_ext-0.0.7.4.gem (100%)
Building native extensions. This could take a while...
Successfully installed unf_ext-0.0.7.4
Fetching: unf-0.1.4.gem (100%)
Successfully installed unf-0.1.4
Fetching: domain_name-0.5.20170404.gem (100%)
Successfully installed domain_name-0.5.20170404
Parsing documentation for unf_ext-0.0.7.4
Installing ri documentation for unf_ext-0.0.7.4
Parsing documentation for unf-0.1.4
Installing ri documentation for unf-0.1.4
Parsing documentation for domain_name-0.5.20170404
Installing ri documentation for domain_name-0.5.20170404
Done installing documentation for unf_ext, unf, domain_name after 6 seconds
3 gems installed

Gem Versions

~ gem list domain_name*** LOCAL GEMS ***domain_name (0.5.20170404)
~ gem install domain_name -v 0.5.20160826
Fetching: domain_name-0.5.20160826.gem (100%)
Successfully installed domain_name-0.5.20160826
Parsing documentation for domain_name-0.5.20160826
Installing ri documentation for domain_name-0.5.20160826
Done installing documentation for domain_name after 3 seconds
1 gem installed
~ gem list domain_name*** LOCAL GEMS ***domain_name (0.5.20170404, 0.5.20160826)
~ gem which domain_name
/usr/local/rvm/gems/ruby-2.2.1/gems/domain_name-0.5.20170404/lib/domain_name.rb

How do you require an older version of a gem?

> gem('domain_name', '0.5.20160826')
=> true
> puts $LOAD_PATH.first(4) # domain_name and dependencies
/usr/local/rvm/gems/ruby-2.2.1/gems/unf_ext-0.0.7.4/lib
/usr/local/rvm/gems/ruby-2.2.1/extensions/x86-linux/2.2.0/unf_ext-0.0.7.4
/usr/local/rvm/gems/ruby-2.2.1/gems/unf-0.1.4/lib
/usr/local/rvm/gems/ruby-2.2.1/gems/domain_name-0.5.20160826/lib
=> nil
> require('domain_name')
=> true

What does “activating a gem spec” mean?

What is Bundler?

# Gemfile
gem 'domain_name'
# Gemfile.lock
GEM
specs:
domain_name (0.5.20170404)
unf (>= 0.0.5, < 1.0.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.4)
PLATFORMS
ruby
DEPENDENCIES
domain_name
BUNDLED WITH
1.10.6

What does `bundle exec` do?

~ gem list domain_name*** LOCAL GEMS ***domain_name (0.5.20170404, 0.5.20160826)
# Gemfile
gem 'domain_name', '0.5.20160826'
# Gemfile.lock
GEM
specs:
domain_name (0.5.20160826)
unf (>= 0.0.5, < 1.0.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.4)
PLATFORMS
ruby
DEPENDENCIES
domain_name (= 0.5.20160826)
BUNDLED WITH
1.10.6
# foo.rb
require('domain_name')
~ ruby foo.rb
loaded '0.5.20170404' !
~ bundle exec ruby foo.rb
loaded '0.5.20160826' !

How does Rails load all my gems?

ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)require 'bundler/setup' # Set up gems listed in the Gemfile
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env)

What does the `:require => false` option do in a Gemfile?

Why don’t I have to `require` most constants in Rails?

# app/controllers/posts_controller.rbclass PostsController < ApplicationController
def index
@posts = Post.all
end
end

Further Reading

cstack

Written by

cstack

Writing codez @Square. Previously @Twitter. Graduated from University of Michigan. My heart is as big as a car.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade