aboutsummaryrefslogtreecommitdiffstats
path: root/guides/source/constant_autoloading_and_reloading.md
diff options
context:
space:
mode:
authorXavier Noria <fxn@hashref.com>2014-12-15 00:57:17 +0100
committerXavier Noria <fxn@hashref.com>2014-12-15 00:59:15 +0100
commit2bdaaa1f63ed51a29d9eebbce60722059c59e6cc (patch)
tree2912dc6cda4a733142ced18f1310dbc31eea4912 /guides/source/constant_autoloading_and_reloading.md
parent870519395f4adfcfe35054b76d3db24662871569 (diff)
downloadrails-2bdaaa1f63ed51a29d9eebbce60722059c59e6cc.tar.gz
rails-2bdaaa1f63ed51a29d9eebbce60722059c59e6cc.tar.bz2
rails-2bdaaa1f63ed51a29d9eebbce60722059c59e6cc.zip
new guide: constant autoloading and reloading
Diffstat (limited to 'guides/source/constant_autoloading_and_reloading.md')
-rw-r--r--guides/source/constant_autoloading_and_reloading.md1071
1 files changed, 1071 insertions, 0 deletions
diff --git a/guides/source/constant_autoloading_and_reloading.md b/guides/source/constant_autoloading_and_reloading.md
new file mode 100644
index 0000000000..de75b4fab7
--- /dev/null
+++ b/guides/source/constant_autoloading_and_reloading.md
@@ -0,0 +1,1071 @@
+Constant Autoloading and Reloading
+==================================
+
+This guide documents how constant autoloading and reloading works.
+
+After reading this guide, you will know:
+
+* Key aspects of Ruby constants
+
+* What is `autoload_paths`
+
+* How constant autoloading works
+
+* What is `require_dependency`
+
+* How constant reloading works
+
+* Solutions to common autoloading gotchas
+
+--------------------------------------------------------------------------------
+
+
+Introduction
+------------
+
+Ruby on Rails allows applications to be written as if all their code was
+preloaded.
+
+For example, in a normal Ruby program a class like the following controller
+would need to load its dependencies:
+
+```ruby
+require 'application_controller'
+require 'post'
+
+class PostsController < ApplicationController
+ def index
+ @posts = Post.all
+ end
+end
+```
+
+Our Rubyist instinct quickly sees some redundancy in there: If classes were
+defined in files matching their name, couldn't their loading maybe be automated
+somehow? We could save scanning the file for dependencies, which is brittle.
+
+Moreover, `Kernel#require` loads files once, but development is much more smooth
+if code gets refreshed when it changes without restarting the server. It would
+be nice to be able to use `Kernel#load` in development, and `Kernel#require` in
+production.
+
+Indeed, those features are provided by Ruby on Rails, where we just write this:
+
+```ruby
+class PostsController < ApplicationController
+ def index
+ @posts = Post.all
+ end
+end
+```
+
+This guide documents how that works.
+
+
+Vocabulary
+----------
+
+### Parent Namespaces
+
+Given a string with a constant path we define its *parent namespace* to be the
+string that results from removing its rightmost segment.
+
+For example, the parent namespace of the string "A::B::C" is the string "A::B",
+the parent namespace of "A::B" is "A", and the parent namespace of "A" is "".
+
+The interpretation of a parent namespace when thinking about classes and modules
+is tricky though. Let's consider a module M named "A::B":
+
+* The parent namespace, "A", may not reflect nesting at a given spot.
+
+* The constant `A` may no longer exist, some code could have removed it from
+`Object`.
+
+* If `A` exists, the class or module that was originally in `A` may not be there
+anymore. For example, if after a constant removal there was another constant
+assignment there would generally be a different object in there.
+
+* In such case, it could even happen that the reassigned `A` held a new class or
+module called also "A"!
+
+* In the previous scenarios M would no longer be reachable through `A::B` but
+the module object itself could still be alive somewhere and its name would
+still be "A::B".
+
+The idea of a parent namespace it's at the core of the autoloading algorithms
+and helps explain and understand intuitively their motivation, but as you see
+that metaphor leaks easily. Given an edge case to reason about, take always into
+account the by "parent namespace" the guide means exactly that specific string
+derivation.
+
+### Loading Mechanism
+
+Rails autoloads files with `Kerne#load` when `config.cache_classes` is false,
+the default in development mode, and with `Kernel#require` otherwise, the
+default in production mode.
+
+`Kernel#load` allows Rails to execute files more than once if [constant
+reloading](#constant-reloading) is enabled.
+
+This guide uses the word "load" freely to mean a given file is interpreted, but
+the actual mechanism can be `Kernel#load` or `Kernel#require` depending on that
+flag.
+
+
+Autoloading Availability
+------------------------
+
+Rails is always able to autoload provided its environment is in place. For
+example the `runner` command autoloads:
+
+```
+$ bin/rails runner 'p User.column_names'
+["id", "email", "created_at", "updated_at"]
+```
+
+The console autoloads, the test suite autoloads, and of course the application
+autoloads.
+
+By default, Rails eager loads the application files when it boots in production
+mode, so most of the autoloading going on in development does not happen. But
+autoloading may still be triggered because at top-level constants not yet loaded
+may be referenced.
+
+For example, given
+
+```ruby
+class BeachHouse < House
+end
+```
+
+if `House` is still unknown when `app/models/beach_house.rb` is being eager
+loaded, Rails autoloads it.
+
+
+Constants Refresher
+-------------------
+
+While constants are trivial in most programming languages, they are a rich
+topic in Ruby.
+
+It is beyond the scope of this guide to document Ruby constants, but we are
+nevertheless going to highlight a couple of key topics. Truly grasping the
+following two sections is instrumental to understanding constant autoloading and
+reloading.
+
+### Class and Module Definitions are Constant Assignments
+
+Let's suppose the following snippet creates a class (rather than reopening it):
+
+```ruby
+class C
+end
+```
+
+Ruby creates a constant `C` in `Object` and stores in that constant a class
+object. The name of the class instance is "C", a string, named after the
+constant.
+
+That is,
+
+```ruby
+class Project < ActiveRecord::Base
+end
+```
+
+performs a constant assignment equivalent to
+
+```ruby
+Project = Class.new(ActiveRecord::Base)
+```
+
+Similarly, module creation using the `module` keyword:
+
+```ruby
+module Admin
+end
+```
+
+performs a constant assignment equivalent to
+
+```ruby
+Admin = Module.new
+```
+
+WARNING. The execution context of a block passed to `Class.new` or `Module.new`
+is not entirely equivalent to the one of the body of the definitions using the
+`class` and `module` keywords. But as far as this guide concerns, both idioms
+perform the same constant assignment.
+
+Thus, when one informally says "the `String` class", that really means: the
+class object the interpreter creates and stores in a constant called "String" in
+the class object stored in the `Object` constant. `String` is otherwise an
+ordinary Ruby constant and everything related to constants applies to it,
+resolution algorithms, etc.
+
+Similarly, in the controller
+
+```ruby
+class PostsController < ApplicationController
+ def index
+ @posts = Post.all
+ end
+end
+```
+
+`Post` is not syntax for a class. Rather, `Post` is a regular Ruby constant. If
+all is good, the constant evaluates to an object that responds to `all`.
+
+That is why we talk about *constant autoloading*, Rails has the ability to load
+constants on the fly.
+
+### Constants are Stored in Modules
+
+Constants belong to modules in a very literal sense. Classes and modules have
+a constant table, think of it as a hash table.
+
+Let's analyze an example to really understand what that means. While in a
+casual setting some abuses of language are customary, the exposition is going
+to be exact here for didactic purposes.
+
+Let's consider the following module definition:
+
+```ruby
+module Colors
+ RED = '0xff0000'
+end
+```
+
+First, when the `module` keyword is processed the interpreter creates a new
+entry in the constant table of the class object stored in the `Object` constant.
+Said entry associates the string "Colors" to a newly created module object.
+Furthermore, the interpreter sets the name of the new module object to be the
+string "Colors".
+
+Later, when the body of the module definition is interpreted, a new entry is
+created in the constant table of the module object stored in the `Colors`
+constant. That entry maps the string "RED" to the string "0xff0000".
+
+In particular, `Colors::RED` is totally unrelated to any other `RED` constant
+that may live in any other class or module object. If there were any, they
+would have separate entries in their respective constant tables.
+
+Put special attention in the previous paragraphs to the distinction between
+class and module objects, constant names as strings, and value objects
+assiociated to them in constant tables.
+
+
+autoload_paths
+--------------
+
+As you probably know, when `require` gets a relative file name:
+
+```ruby
+require 'erb'
+```
+
+Ruby looks for the file in the directories listed in `$LOAD_PATH`. That is, Ruby
+iterates over all its directories and for each one of them checks whether they
+have a file called "erb.rb", or "erb.so", or "erb.o", or "erb.dll". If it finds
+any of them, the interpreter loads it and ends the search. Otherwise, it tries
+again in the next directory of the list. If the list gets exhausted, `LoadError`
+is raised.
+
+We are going to cover how constant autoloading works in more detail later, but
+the idea is that when a constant like `Post` is hit and missing, if there's a
+*post.rb* file for example in *app/models* Rails is going to find it, evaluate
+it, and have `Post` defined as a side-effect.
+
+Alright, Rails has a collection of directories similar to `$LOAD_PATH` in which
+to lookup that *post.rb*. That collection is called `autoload_paths` and by
+default it contains:
+
+* All subdirectories of `app` in the application and engines. For example,
+ `app/controllers`. They do not need to be the default ones, any custom
+ directories like `app/workers` belong automatically to `autoload_paths`.
+
+* Any existing second level directories called `app/*/concerns` in the
+ application and engines.
+
+* The directory `test/mailers/previews`.
+
+Also, this collection is configurable via `config.autoload_paths`. For example,
+`lib` was in the list years ago, but no longer is. An application can opt-in
+throwing this to `config/application.rb`:
+
+```ruby
+config.autoload_paths += "#{Rails.root}/lib"
+```
+
+The value of `autoload_paths` can be inspected. In a just generated application
+it is (edited):
+
+```
+$ bin/rails r 'puts ActiveSupport::Dependencies.autoload_paths'
+.../app/assets
+.../app/controllers
+.../app/helpers
+.../app/mailers
+.../app/models
+.../app/controllers/concerns
+.../app/models/concerns
+.../test/mailers/previews
+```
+
+INFO. `autoload_paths` is computed and cached during the initialization process.
+The application needs to be restarted to reflect any changes in the directory
+structure.
+
+
+Autoloading Algorithms
+----------------------
+
+### Relative References
+
+A relative constant may appear in several places, for example, in
+
+```ruby
+class PostsController < ApplicationController
+ def index
+ @posts = Post.all
+ end
+end
+```
+
+all three constants are relative.
+
+#### Constants after the `class` and `module` Keywords
+
+Ruby performs a lookup for the constant that follows a `class` or `module`
+keyword because it needs to know if the class or module is going to be created
+or reopened.
+
+If the constant is not defined at that point it is not considered to be a
+missing constant, autoloading is **not** triggered.
+
+So, in the previous example, if `PostsController` is not defined when the file
+is interpreted Rails autoloading is not going to be triggered, Ruby will just
+define the controller.
+
+#### Top-Level Constants
+
+On the contrary, if `ApplicationController` is unknown, an autoload is going to
+be attempted by Rails.
+
+In order to load `ApplicationController`, Rails iterates over `autoload_paths`.
+First checks if `app/assets/application_controller.rb` exists. If it does not,
+which is normally the case, it continues and finds
+`app/controllers/application_controller.rb`.
+
+If the file defines the constant `ApplicationController` all is fine, otherwise
+`LoadError` is raised:
+
+```
+unable to autoload constant ApplicationController, expected
+<full path to application_controller.rb> to define it (LoadError)
+```
+
+INFO. Rails does not require the value of autoloaded constants to be a class or
+module object. For example, if the file `app/models/max_clients.rb` defines
+`MAX_CLIENTS = 100` autoloading `MAX_CLIENTS` works just fine.
+
+#### Namespaces
+
+Autoloading `ApplicationController` looks directly under the directories of
+`autoload_paths` because the nesting in that spot is empty. The situation of
+`Post` is different, the nesting in that line is `[PostsController]` and support
+for namespaces comes into play.
+
+The basic idea is that given
+
+```
+module Admin
+ class BaseController < ApplicationController
+ @@all_roles = Role.all
+ end
+end
+```
+
+to autoload `Role` we are going to check if it is defined in the current or
+parent namespaces, one at a time. So, conceptually we want to try to autoload
+any of
+
+```
+Admin::BaseController::Role
+Admin::Role
+Role
+```
+
+in that order. That's the idea. To do so, Rails looks in `autoload_paths`
+respectively for file names like these:
+
+```
+admin/base_controller/role.rb
+admin/role.rb
+role.rb
+```
+
+modulus some additional directory lookups we are going to cover soon.
+
+INFO. 'Constant::Name'.underscore gives the relative path without extension of
+the file name where `Constant::Name` is expected to be defined.
+
+Let's see how does Rails autoload the `Post` constant in the `PostsController`
+above assuming the application has a `Post` model defined in
+`app/models/post.rb`.
+
+First it checks for `posts_controller/post.rb` in `autoload_paths`:
+
+```
+app/assets/posts_controller/post.rb
+app/controllers/posts_controller/post.rb
+app/helpers/posts_controller/post.rb
+...
+test/mailers/previews/posts_controller/post.rb
+```
+
+Since the lookup is exhausted without success, a similar search for a directory
+is performed, we are going to see why in the next section:
+
+```
+app/assets/posts_controller/post
+app/controllers/posts_controller/post
+app/helpers/posts_controller/post
+...
+test/mailers/previews/posts_controller/post
+```
+
+If all those attempts fail, then Rails starts the lookup again in the parent
+namespace. In this case only the top-level remains:
+
+```
+app/assets/post.rb
+app/controllers/post.rb
+app/helpers/post.rb
+app/mailers/post.rb
+app/models/post.rb
+```
+
+A matching file is found in `app/models/post.rb`. The lookup stops there and the
+file is loaded. If the file actually defines `Post` all is fine, otherwise
+`LoadError` is raised.
+
+### Qualified References
+
+When a qualified constant is missing Rails does not look for it in the parent
+namespaces. But there's a caveat: Unfortunately, when a constant is missing
+Rails is not able to say if the trigger was a relative or qualified reference.
+
+For example, consider
+
+```ruby
+module Amin
+ User
+end
+```
+
+and
+
+```ruby
+Admin::User
+```
+
+If `User` is missing, in either case all Rails knows is that a constant called
+"User" was missing in a module called "Admin".
+
+If there is a top-level `User` Ruby would resolve it in the former example, but
+wouldn't in the latter. In general, Rails does not emulate the Ruby constant
+resolution algorithms, but in this case it tries using the following heuristic:
+
+> If none of the parent namespaces of the class or module has the missing
+> constant then Rails assumes the reference is relative. Otherwise qualified.
+
+For example, if this code triggers autoloading
+
+```ruby
+Admin::User
+```
+
+and the `User` constant is already present in `Object`, it is not possible that
+the situation is
+
+```ruby
+module Amin
+ User
+end
+```
+
+because otherwise Ruby would have resolved `User` and no autoloading would have
+been triggered in the first place. Thus, Rails assumes a qualified reference and
+considers the file `admin/user.rb` and directory `admin/user` to be the only
+valid options.
+
+In practice this works quite well as long as the nesting matches all parent
+namespaces respectively and the constants that make the rule apply are known at
+that time.
+
+But since autoloading happens on demand, if the top-level `User` by chance was
+not yet loaded then Rails has no way to know whether `Amin::User` should load it
+or raise `NameError`.
+
+These kind of name conflicts are rare in practice, but in case there's one
+`require_dependency` provides a solution by making sure the constant needed to
+trigger the heuristic is defined in the conflicting place.
+
+### Automatic Modules
+
+When a module acts as a namespace, Rails does not require the application to
+defines a file for it, a directory matching the namespace is enough.
+
+Suppose an application has a backoffice whose controllers are stored in
+`app/controllers/admin`. If the `Admin` module is not yet loaded when
+`Admin::UsersController` is hit, Rails needs first to autoload the constant
+`Admin`.
+
+If `autoload_paths` has a file called `admin.rb` Rails is going to load that
+one, but if there's no such file and a directory called `admin` is found, Rails
+creates an empty module and assigns it to the constant `Admin` on the fly.
+
+### Generic Procedure
+
+The procedure to autoload constant `C` in an arbitrary situation is:
+
+```
+if the nesting is empty
+ let ns = ''
+else
+ let M = nesting.first
+
+ if M is anonymous
+ let ns = ''
+ else
+ let ns = M.name
+ end
+end
+
+loop do
+ # Look for a regular file.
+ for dir in autoload_paths
+ if the file "#{dir}/#{ns.underscore}/c.rb" exists
+ load/require "#{dir}/#{ns.underscore}/c.rb"
+
+ if C is now defined
+ return
+ else
+ raise LoadError
+ end
+ end
+ end
+
+ # Look for an automatic module.
+ for dir in autoload_paths
+ if the directory "#{dir}/#{ns.underscore}/c" exists
+ if ns is an empty string
+ let C = Module.new in Object and return
+ else
+ let C = Module.new in ns.constantize and return
+ end
+ end
+ end
+
+ if ns is empty
+ # We reached the top-level without finding the constant.
+ raise NameError
+ else
+ if C exists in any of the parent namespaces
+ # Qualified constants heuristic.
+ raise NameError
+ else
+ # Try again in the parent namespace.
+ let ns = the parent namespace of ns and retry
+ end
+ end
+end
+```
+
+
+require_dependency
+------------------
+
+Constant autoloading is triggered on demand and therefore code that uses a
+certain constant may have it already defined or may trigger an autoload. That
+depends on the execution path and it may vary between runs.
+
+There are times, however, in which you want to make sure a certain constant is
+known when the execution reaches some code. `require_dependency` provides a way
+to load a file using the current [loading mechanism](#loading-mechanism), and
+keeping track of constants defined in that file as if they were autoloaded to
+have them reloaded as needed.
+
+`require_dependency` is rarely needed, but see a couple of use-cases in
+[Autoloading and STI](#autoloading-and-sti) and [When Constants aren't
+Triggered](#when-constants-aren-t-missed).
+
+WARNING. Unlike autoloading, `require_dependency` does not expect the file to
+define any particular constant. Exploiting this behavior would be a bad practice
+though, file and constant paths should match.
+
+
+Constant Reloading
+------------------
+
+When `config.cache_classes` is false Rails is able to reload autoloaded
+constants.
+
+For example, in you're in a console session and edit some file behind the
+scenes, the code can be reloaded with the `reload!` command:
+
+```
+> reload!
+```
+
+When the application runs, code is reloaded when something relevant to this
+logic changes. In order to do that, Rails monitors a number of things:
+
+* `config/routes.rb`.
+
+* Locales.
+
+* Ruby files under `autoload_paths`.
+
+* `db/schema.rb` and `db/structure.sql`.
+
+If anything in there changes, there is a middleware that detects it and reloads
+the code.
+
+Autoloading keeps track of autoloaded constants. Reloading is implemented by
+removing them all from their respective classes and modules using
+`Module#remove_const`. That way, when the code goes on, those constants are
+going to be unkown again, and files reloaded on demand.
+
+INFO. This is an all-or-nothing operation, Rails does not attempt to reload only
+what changed since dependencies between classes makes that really tricky.
+Instead, everything is wiped.
+
+
+Common Gotchas
+--------------
+
+### Nesting and Qualified Constants
+
+Let's consider
+
+```ruby
+module Admin
+ class UsersController < ApplicationController
+ def index
+ @users = User.all
+ end
+ end
+end
+```
+
+and
+
+```ruby
+class Admin::UsersController < ApplicationController
+ def index
+ @users = User.all
+ end
+end
+```
+
+If Ruby resolves `User` in the former case it checks whether there's a `User`
+constant in the `Admin` module. It does not in the latter case, because `Admin`
+does not belong to the nesting.
+
+Unfortunately autoloading does not know the nesting in the spot where the
+constant was missing and so it is not able to act as Ruby would. In particular,
+if `Admin::User` is autoloadable, it will get autoloaded in either case.
+
+Albeit qualified constants with `class` and `module` keywords may technically
+work with autoloading in some cases, it is preferrable to use relative constants
+instead:
+
+```ruby
+module Admin
+ class UsersController < ApplicationController
+ def index
+ @users = User.all
+ end
+ end
+end
+```
+
+### Autoloading and STI
+
+STI (Single Table Inheritance) is a feature of Active Record that easies storing
+records that belong to a hierarchy of classes in one single table. The API of
+such models is aware of the hierarchy and encapsulates some common needs. For
+example, given these classes:
+
+```ruby
+# app/models/polygon.rb
+class Polygon < ActiveRecord::Base
+end
+
+# app/models/triangle.rb
+class Triangle < Polygon
+end
+
+# app/models/rectangle.rb
+class Rectangle < Polygon
+end
+```
+
+`Triangle.create` creates a row that represents a triangle, and
+`Rectangle.create` creates a row that represents a rectangle. If `id` is the ID
+of an existing record, `Polygon.find(id)` returns an object of the correct type.
+
+Methods that perform operations on collections are also aware of the hierarchy.
+For example, `Polygon.all` returns all the records of the table, because all
+rectangles and triangles are polygons. Active Record takes care of returning
+instances of their corresponding class in the result set.
+
+When Active Record does this, it autoloads constants as needed. For example, if
+the class of `Polygon.first` is `Rectangle` and it has not yet been loaded,
+Active Record autoloads it and the record is fetched and correctly instantiated,
+transparently.
+
+All good, but if instead of performing queries based on the root class we need
+to work on some subclass, then things get interesting.
+
+While working with `Polygon` you do not need to be aware of all its descendants,
+because anything in the table is by definition a polygon, but when working with
+subclasses Active Record needs to be able to enumerate the types it is looking
+for. Let’s see an example.
+
+`Rectangle.all` should return all the rectangles in the "polygons" table. In
+particular, no triangle should be fetched. To accomplish this, Active Record
+constraints the query to rows whose type column is “Rectangle”:
+
+```sql
+SELECT "polygons".* FROM "polygons"
+WHERE "polygons"."type" IN ("Rectangle")
+```
+
+That works, but let’s introduce now a child of `Rectangle`:
+
+```ruby
+# app/models/square.rb
+class Square < Rectangle
+end
+```
+
+`Rectangle.all` should return rectangles **and** squares, the query should
+become
+
+```sql
+SELECT "polygons".* FROM "polygons"
+WHERE "polygons"."type" IN ("Rectangle", "Square")
+```
+
+But there’s a subtle caveat here: How does Active Record know that the class
+`Square` exists at all?
+
+Even if the file `app/models/square.rb` exists and defines the `Square` class,
+if no code yet used that class, `Rectangle.all` issues the query
+
+```sql
+SELECT "polygons".* FROM "polygons"
+WHERE "polygons"."type" IN ("Rectangle")
+```
+
+That is not a bug in Active Record, as we saw above the query does include all
+*known* descendants of `Rectangle`.
+
+A way to ensure this works correctly regardless of the order of execution is to
+load the leaves of the tree by hand at the bottom of the file that defines the
+root class:
+
+```ruby
+# app/models/polygon.rb
+class Polygon < ActiveRecord::Base
+end
+require_dependency ‘square’
+```
+
+Only the leaves that are **at least grandchildren** have to be loaded that way.
+Direct subclasses do not need to be preloaded, and if the hierarchy is deeper
+intermediate superclasses will be autoloaded recursively from the bottom because
+their constant will appear in the definitions.
+
+### Autoloading and `require`
+
+Files defining constants that should be autoloaded should never be loaded with
+`require`:
+
+```ruby
+require 'user' # DO NOT DO THIS
+
+class UsersController < ApplicationController
+ ...
+end
+```
+
+If some part of the application autoloads the `User` constant before, then the
+application will interpret `app/models/user.rb` twice in development mode.
+
+As we saw before, in development mode autoloading uses `Kernel#load` by default.
+Since `load` does not store the name of the interpreted file in
+`$LOADED_FEATURES` (`$"`) `require` executes, again, `app/models/user.rb`.
+
+On the other hand, if `app/controllers/users_controllers.rb` happens to be
+evaluated before `User` is autoloaded then dependencies won’t mark `User` as an
+autoloaded constant, and therefore changes to `app/models/user.rb` won’t be
+updated in development mode.
+
+Just follow the flow and use constant autoloading always, never mix autoloading
+and `require`. As a last resort, if some file absolutely needs to load a certain
+file by hand use `require_dependency` to play nice with constant autoloading.
+This option is rarely needed though in practice.
+
+Of course, using `require` in autoloaded files to load ordinary 3rd party
+libraries is fine, and Rails is able to distinguish their constants, so they are
+not marked as autoloaded.
+
+### Autoloading and Initializers
+
+Consider this assignment in `config/initializers/set_auth_service.rb`:
+
+```ruby
+AUTH_SERVICE = Rails.env.production? ? RealAuthService : MockedAuthService
+```
+
+The purpose of this setup would be that the application code uses always
+`AUTH_SERVICE` and that constant holds the proper class for the runtime
+environment. In development mode `MockedAuthService` gets autoloaded when the
+initializer is run. Let’s suppose we do some requests, change the implementation
+of `MockedAuthService`, and hit the application again. To our surprise the
+changes are not reflected. Why?
+
+As we saw earlier, Rails wipes autoloaded constants by removing them from their
+containers using `remove_const`. But the object the constant holds may remain
+stored somewhere else. Constant removal can’t do anything about that.
+
+That is precisely the case in this example. `AUTH_SERVICE` stores the original
+class object which is perfectly functional regardless of the fact that there is
+no longer a constant in `Object` that matches its class name. The class object
+is independent of the constants it may or may not be stored in.
+
+The following code summarizes the situation:
+
+```ruby
+class C
+ def quack
+ 'quack!'
+ end
+end
+
+X = C
+Object.instance_eval { remove_const(:C) }
+X.new.quack # => quack!
+X.name # => C
+C # => uninitialized constant C (NameError)
+```
+
+Because of that, it is not a good idea to autoload constants on application
+initialization.
+
+In the case above we could for instance implement a dynamic access point that
+returns something that depends on the environment:
+
+```ruby
+class AuthService
+ if Rails.env.production?
+ def self.instance
+ RealAuthService
+ end
+ else
+ def self.instance
+ MockedAuthService
+ end
+ end
+end
+```
+
+and have the application use `AuthService.instance` instead of `AUTH_SERVICE`.
+The code in that `AuthService` would be loaded on demand and be
+autoload-friendly.
+
+### `require_dependency` and Initializers
+
+As we saw before, `require_dependency` loads files in a autoloading-friendly
+way. Normally, though, such a call does not make sense in an initializer.
+
+`require_dependency` provides a way to ensure a certain constant is defined at
+some point regardless of the execution path, and one could think about doing
+some calls in an initialzer to make sure certain constants are loaded upfront,
+for example as an attempt to address the gotcha with STIs.
+
+Problem is, in development mode all autoloaded constants are wiped on a
+subsequent request as soon as there is some relevant change in the file system.
+When that happens the application is in the very same situation the initializer
+wanted to avoid!
+
+Calls to `require_dependency` have to be strategically written in autoloaded
+spots.
+
+### When Constants aren't Missed
+
+Let’s imagine that a Rails application has an `Image` model, and a subclass
+`Hotel::Image`:
+
+```ruby
+# app/models/image.rb
+class Image
+end
+
+# app/models/hotel/image.rb
+module Hotel
+ class Image < Image
+ end
+end
+```
+
+No matter which file is interpreted first, `app/models/hotel/image.rb` is
+well-defined.
+
+Now consider a third file with this apparently harmless code:
+
+```ruby
+# app/models/hotel/poster.rb
+module Hotel
+ class Poster < Image
+ end
+end
+```
+
+The intention is to subclass `Hotel::Image`, but which is actually the
+superclass of `Hotel::Poster`? Well, it depends on the order of execution of the
+files:
+
+1. If neither `app/models/image.rb` nor `app/models/hotel/image.rb` have been
+loaded at that point, the superclass is `Hotel::Image` because Rails is told
+`Hotel` is missing a constant called "Image" and loads
+`app/models/hotel/image.rb`. Good.
+
+2. If `app/models/hotel/image.rb` has been loaded at that point, the superclass
+is `Hotel::Image` because Ruby is able to resolve the constant. Good.
+
+3. Lastly, if only `app/models/image.rb` has been loaded so far, the superclass
+is `Image`. Gotcha!
+
+The last scenario (3) may be surprising. Why isn't `Hotel::Image` autoloaded?
+
+Constant autoloading cannot happen at that point because Ruby is able to
+resolve `Image` as a top-level constant, in consequence autoloading is not
+triggered.
+
+Most of the time, these kind of ambiguities can be resolved using qualified
+constants. In this case we would write
+
+```ruby
+module Hotel
+ class Poster < Hotel::Image
+ end
+end
+```
+
+That class definition now is robust. No matter which files have been
+previously loaded, we know for certain that the superclass is unambiguously
+set.
+
+It is interesting to note here that fix works because `Hotel` is a module, and
+`Hotel::Image` won’t look for `Image` in `Object` as it would if `Hotel` was a
+class (because `Object` would be among its ancestors). If `Hotel` was class we
+would resort to loading `Hotel::Image` with `require_dependency`. Furthermore,
+with that solution the qualified name would no longer be necessary.
+
+### Autoloading within Singleton Classes
+
+Let’s suppose we have these class definitions:
+
+```ruby
+# app/models/hotel/services.rb
+module Hotel
+ class Services
+ end
+end
+
+# app/models/hotel/geo_location.rb
+module Hotel
+ class GeoLocation
+ class << self
+ Services
+ end
+ end
+end
+```
+
+If `Hotel::Services` is known by the time `Hotel::GeoLocation` is being loaded,
+everything works because `Hotel` belongs to the nesting when the singleton class
+of `Hotel::GeoLocation` is opened, and thus Ruby itself is able to resolve the
+constant.
+
+But if `Hotel::Services` is not known and we rely on autoloading for the
+`Services` constant in `Hotel::GeoLocation`, Rails is not able to find
+`Hotel::Services`. The application raises `NameError`.
+
+The reason is that autoloading is triggered for the singleton class, which is
+anonymous, and as we saw before, Rails only checks the top-level namespace in
+that edge case.
+
+An easy solution to this caveat is to qualify the constant:
+
+```ruby
+module Hotel
+ class GeoLocation
+ class << self
+ Hotel::Services
+ end
+ end
+end
+```
+
+### Autoloading in `BasicObject`
+
+Direct descendants of `BasicObject` do not have `Object` among their ancestors
+and cannot resolve top-level constants:
+
+```ruby
+class C < BasicObject
+ String # NameError: uninitialized constant C::String
+end
+```
+
+When autoloading is involved that plot has a twist. Let's consider:
+
+```ruby
+class C < BasicObject
+ def user
+ User # WRONG
+ end
+end
+```
+
+Since Rails checks the top-level namespace `User` gets autoloaded just fine the
+first time the `user` method is invoked. You only get the exception if the
+`User` constant is known at that point, in particular in a *second* call to
+`user`:
+
+```
+c = C.new
+c.user # surprisingly fine, User
+c.user # NameError: uninitialized constant C::User
+```
+
+because it detects a parent namespace already has the constant.
+
+As with pure Ruby, within the body of a direct descendant of `BasicObject` use
+always absolute constant paths:
+
+```ruby
+class C < BasicObject
+ ::String # RIGHT
+
+ def user
+ ::User # RIGHT
+ end
+end
+```