Require method

15 Jun 2020

require and require_relative

The require method is used to load a file (or gems) on demand. For example:

# require the benchmark-ips gem so we can benchmark code
require 'benchmark-ips'

The require method looks in the $LOAD_PATH to find the file. The file path is expanded then we traverse the directories until there is a match.

NOTE: When we use rubygems in our code, there is an extra step that happens with the require method. In a rails appliction for example, have a look at the config/boot.rb:

# config/boot.rb
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require "bundler/setup" # Set up gems listed in the Gemfile.

Rubygems aliases the Kernel#require method, so we are calling the rubygems require method to find the required library/gem.

There is another method to load code called require_relative. This can be used for code, that is in a relative location to the current file.

require_relative 'subscription'

Ideally we should use require_relative when the file is relative to the current file, it’s much faster than using require. For accessing other libraries, continue to use the require method.

Now that we understand what is happening with require, there are a couple more interesting concepts related to code loading which you may have come across in your rails application.

bootsnap

Bootsnap, authored by the folks at shopify, loads up your application, really fast.

Bootsnap optimizes all the places where you use require in your application and improves the lookup speed by caching the full path for a faster lookup. Bootsnap also does compilation caching, however the focus for this topic is require.

Bootsnap does some path pre-scanning, on app initialization or when modifying the $LOAD_PATH (e.g. adding a new folder to $LOAD_PATH). Absolute locations of all requireable files on the $LOAD_PATH are cached, then require uses that cached copy of requirables. This means we avoid having to check all $LOAD_PATHs for every single require.

Bootsnap is included by default in every rails application, keep in mind for optimal performance bootsnap should be loaded as early as possible. This does not mean that bootsnap should be added to the top of the Gemfile rather, in the application boot process, bootsnap should be right at the top. Here is an example of a rails application, that includes bootsnap right after rubygems.

# config/boot.rb

ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require "bundler/setup" # Set up gems listed in the Gemfile.
require "bootsnap/setup" # Speed up boot time by caching expensive operations.

This file is the first thing called in a rails application, inside the application.rb.

Wherever you can, use bootsnap! For applications that have a big $LOAD_PATH that could massively improve your app boot time.

Zeitwerk

Zeitwerk is an efficient and thread safe code loader for ruby. Your projects classes/modules are loaded on demand (autoloading) or upfront (eager loading). It has been included in rails 6.0 by default.

So what does this mean?

In our rails applications let’s say we have a user.rb model. We want to use that inside the search_controller.rb, a mailer or the console. The user model is autoloaded so we don’t have to add the require 'user' in every class, we use the user model.

That’s the rails magic handled by zeitwerk.

For the keen observer, zeitwerk gem was not used proir to rails 6.0 and yet this behaviour still worked. Zeitwerk is a faster alternative to the classic autoloader, which will soon be deprecated.

Open up a rails console

bin/rails c

We can directly call the user model without a require statement.

User.all_paying_users