Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
Z

zeitwerk

> 编程语言
Open source

Efficient and thread-safe code loader for Ruby

2.1K stars0 likes1 views
WebsiteGitHub

About

Efficient and thread-safe code loader for Ruby

Zeitwerk

  • Introduction
  • Synopsis
  • File structure
    • The idea: File paths match constant paths
    • Inner simple constants
    • Root directories and root namespaces
      • The default root namespace is Object
      • Custom root namespaces
      • Nested root directories
    • Implicit namespaces
    • Explicit namespaces
      • Explicit namespaces defined in ordinary files
      • Explicit namespaces defined in nsfiles
    • Collapsing directories
    • Testing compliance
  • Usage
    • Setup
      • Generic
      • for_gem
      • for_gem_extension
    • Autoloading
    • Eager loading
      • Eager load exclusions
      • Eager load directories
      • Eager load namespaces
      • Eager load namespaces shared by several loaders
      • Global eager load
    • Loading individual files
    • Reloading
      • Configuration and usage
      • Thread-safety
    • Inflection
      • Zeitwerk::Inflector
      • Zeitwerk::GemInflector
      • Zeitwerk::NullInflector
      • Custom inflector
    • Callbacks
      • The on_setup callback
      • The on_load callback
      • The on_unload callback
      • Technical details
    • Logging
      • Loader tag
    • Ignoring parts of the project
      • Use case: Files that do not follow the conventions
      • Use case: The adapter pattern
      • Use case: Test files mixed with implementation files
    • Shadowed files
    • Beware of circular dependencies
    • Reopening third-party namespaces
    • Introspection
      • Zeitwerk::Loader#dirs
      • Autoloaded Constants
      • Zeitwerk::Loader#cpath_expected_at
      • Zeitwerk::Loader#all_expected_cpaths
    • Encodings
    • Rules of thumb
  • Pronunciation
  • Supported Ruby versions
  • Testing
  • Motivation
    • Kernel#require is brittle
    • Rails autoloading was brittle
  • Awards
  • Thanks
  • License

Introduction

Zeitwerk is an efficient and thread-safe code loader for Ruby.

Given a conventional file structure, Zeitwerk is capable of loading your project's classes and modules on demand (autoloading) or upfront (eager loading). You don't need to write require calls for your own files; instead, you can streamline your programming by knowing that your classes and modules are available everywhere. This feature is efficient, thread-safe, and aligns with Ruby's semantics for constants.

Zeitwerk also supports code reloading, which can be useful during web application development. However, coordination is required to reload in a thread-safe manner. The documentation below explains how to achieve this.

The gem is designed to allow any project, gem dependency, or application to have its own independent loader. Multiple loaders can coexist in the same process, each managing its own project tree and operating independently of each other. Each loader has its own configuration, inflector, and optional logger.

Internally, Zeitwerk exclusively uses absolute file names when issuing require calls, eliminating the need for costly file system lookups in $LOAD_PATH. Technically, the directories managed by Zeitwerk don't even need to be in $LOAD_PATH.

Furthermore, Zeitwerk performs a single scan of the project tree at most, lazily descending into subdirectories only when their namespaces are used.

Synopsis

Main interface for gems:

ruby
# lib/my_gem.rb (main file)

require 'zeitwerk'
loader = Zeitwerk::Loader.for_gem
loader.setup # ready!

module MyGem
  # ...
end

loader.eager_load # optionally

Main generic interface:

ruby
loader = Zeitwerk::Loader.new
loader.push_dir(...)
loader.setup # ready!

The loader variable can go out of scope. Zeitwerk keeps a registry with all of them, and so the object won't be garbage collected.

You can reload if you want to:

ruby
loader = Zeitwerk::Loader.new
loader.push_dir(...)
loader.enable_reloading # you need to opt-in before setup
loader.setup
...
loader.reload

and you can eager load all the code:

ruby
loader.eager_load

It is also possible to broadcast eager_load to all instances:

ruby
Zeitwerk::Loader.eager_load_all

File structure

The idea: File paths match constant paths

For Zeitwerk to work with your file structure, simply name files and directories after the classes and modules they define:

lib/my_gem.rb         -> MyGem
lib/my_gem/foo.rb     -> MyGem::Foo
lib/my_gem/bar_baz.rb -> MyGem::BarBaz
lib/my_gem/woo/zoo.rb -> MyGem::Woo::Zoo

You can fine-tune this behavior by collapsing directories or ignoring specific parts of the project, but that is the main idea.

Inner simple constants

While a simple constant like HttpCrawler::MAX_RETRIES can be defined in its own file:

ruby
# http_crawler/max_retries.rb
HttpCrawler::MAX_RETRIES = 10

that is not required, you can also define it the regular way:

ruby
# http_crawler.rb
class HttpCrawler
  MAX_RETRIES = 10
end

The first example needs a custom inflection rule:

ruby
loader.inflector.inflect('max_retries' => 'MAX_RETRIES')

Otherwise, Zeitwerk would expect the file to define MaxRetries.

In the second example, no custom rule is needed.

Root directories and root namespaces

Every directory configured with push_dir is called a root directory, and they represent root namespaces.

The default root namespace is Object

By default, the namespace associated to a root directory is the top-level one: Object.

For example, given

ruby
loader.push_dir("#{__dir__}/models")
loader.push_dir("#{__dir__}/serializers")

these are the expected classes and modules being defined by these files:

models/user.rb                 -> User
serializers/user_serializer.rb -> UserSerializer

Custom root namespaces

Although Object is the most common root namespace, you have the flexibility to associate a different one with a specific root directory. The push_dir method accepts a non-anonymous class or module object as the optional namespace keyword argument.

For example, given:

ruby
require 'active_job'
require 'active_job/queue_adapters'
loader.push_dir("#{__dir__}/adapters", namespace: ActiveJob::QueueAdapters)

a file defining ActiveJob::QueueAdapters::MyQueueAdapter does not need the conventional parent directories, you can (and have to) store the file directly below adapters:

adapters/my_queue_adapter.rb -> ActiveJob::QueueAdapters::MyQueueAdapter

Please note that the provided root namespace must be non-reloadable, while allowing autoloaded constants within that namespace to be reloadable. This means that if you associate the app/api directory with an existing Api module, the module itself should not be reloadable. However, if the project defines and autoloads the Api::Deliveries class, that class can be reloaded.

Nested root directories

Root directories are recommended not to be nested; however, Zeitwerk provides support for nested root directories since in frameworks like Rails, both app/models and app/models/concerns belong to the autoload paths.

Zeitwerk identifies nested root directories and treats them as independent roots. In the given example, concerns is not considered a namespace within app/models. For instance, consider the following file:

app/models/concerns/geolocatable.rb

should define Geolocatable, not Concerns::Geolocatable.

Implicit namespaces

If a namespace consists only of a simple module without any code, there is no need to explicitly define it in a separate file. Zeitwerk automatically creates modules on your behalf for directories without a corresponding Ruby file.

For instance, suppose a project includes an admin directory:

app/controllers/admin/users_controller.rb -> Admin::UsersController

and does not have a file called admin.rb, Zeitwerk automatically creates an Admin module on your behalf the first time Admin is used.

To trigger this behavior, the directory must contain non-ignored Ruby files with the ".rb" extension, either directly or recursively. Otherwise, the directory is ignored. This condition is reevaluated during reloads.

Explicit namespaces

Classes and modules that act as namespaces can also be explicitly defined in a file. This can be done with ordinary files named after the corresponding constant path, or with special namespace files, or nsfiles for short.

Explicit namespaces defined in ordinary files

Let's consider:

app/models/hotel.rb         -> Hotel
app/models/hotel/pricing.rb -> Hotel::Pricing

Since there is a file app/models/hotel.rb and also a directory app/models/hotel, Zeitwerk realizes Hotel is a namespace that is defined in app/models/hotel.rb.

In order to realize this, the directory or directories conforming the namespace do not need to be next to the file, as in the example, they could be in some other root directory.

The classes and modules from an explicit namespace are already available in the body of the class or module that defines it:

ruby
class Hotel < ApplicationRecord
  include Pricing # works
  ...
end

When autoloaded, Zeitwerk verifies the expected constant (Hotel in the example) stores a class or module object. If it doesn't, Zeitwerk::Error is raised.

Explicit namespaces defined in nsfiles

If the

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Ruby

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言