Skip to content

Commit

Permalink
Adds code comments
Browse files Browse the repository at this point in the history
  • Loading branch information
fxn committed Feb 15, 2025
1 parent c74ddf7 commit 85a9b97
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 3 deletions.
29 changes: 29 additions & 0 deletions lib/zeitwerk/cref/map.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
# frozen_string_literal: true

# This class emulates a hash table whose keys are of type Zeitwerk::Cref.
#
# It is a synchronized hash of hashes. The first one, stored in `@map`, is keyed
# on class and module object IDs. Then, each one of them stores a hash table
# keyed on constant names, where we finally store the values.
#
# For example, if we store values 0, 1, and 2 for the crefs that would
# correspond to `M::X`, `M::Y`, and `N::Z`, the map will look like this:
#
# { M => { X: 0, :Y => 1 }, N => { Z: 2 } }
#
# Why not use simple hash tables of type Hash[Module, Symbol]? Because class and
# module objects are not guaranteed to be hashable, the `hash` method may have
# been overridden:
#
# /~https://github.com/fxn/zeitwerk/issues/188
#
# Another option would be to make crefs hashable. I tried with hash code
#
# real_mod_hash(mod) ^ cname.hash
#
# and the matching eql?, but that was about 1.8x slower than a hash keyed by
# class and module names.
#
# The gem used hashes keyed by class and module names, but that felt like an
# unnecessary dependency on said names, our natural objects are the crefs.
#
# On the other hand, an unsynchronized hash based on constant paths is 1.6x
# slower than this map.
class Zeitwerk::Cref::Map # :nodoc: all
def initialize
@map = {}
Expand Down
12 changes: 9 additions & 3 deletions lib/zeitwerk/loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ class Loader
# A shadowed file is a file managed by this loader that is ignored when
# setting autoloads because its matching constant is already taken.
#
# This private set is populated as we descend. For example, if the loader
# has only scanned the top-level, `shadowed_files` does not have shadowed
# files that may exist deep in the project tree yet.
# This private set is populated lazily, as we descend. For example, if the
# loader has only scanned the top-level, `shadowed_files` does not have the
# shadowed files that may exist deep in the project tree.
#
# @sig Set[String]
attr_reader :shadowed_files
Expand Down Expand Up @@ -325,6 +325,9 @@ def cpath_expected_at(path)
# Says if the given constant path would be unloaded on reload. This
# predicate returns `false` if reloading is disabled.
#
# This is an undocumented method that I wrote to help transition from the
# classic autoloader in Rails. Its usage was removed from Rails in 7.0.
#
# @sig (String) -> bool
def unloadable_cpath?(cpath)
unloadable_cpaths.include?(cpath)
Expand All @@ -333,6 +336,9 @@ def unloadable_cpath?(cpath)
# Returns an array with the constant paths that would be unloaded on reload.
# This predicate returns an empty array if reloading is disabled.
#
# This is an undocumented method that I wrote to help transition from the
# classic autoloader in Rails. Its usage was removed from Rails in 7.0.
#
# @sig () -> Array[String]
def unloadable_cpaths
to_unload.values.map(&:path)
Expand Down

0 comments on commit 85a9b97

Please sign in to comment.