aboutsummaryrefslogtreecommitdiffstats
path: root/guides/source/constant_autoloading_and_reloading.md
diff options
context:
space:
mode:
authorXavier Noria <fxn@hashref.com>2014-12-15 14:06:16 +0100
committerXavier Noria <fxn@hashref.com>2014-12-15 14:06:16 +0100
commitc82321dcb72e73b4bfb62b817cca027cee1449b8 (patch)
tree981c0776337a69ae823f1b99a791aee02a98b764 /guides/source/constant_autoloading_and_reloading.md
parentdb1e3f6de27453eff8b58ac222fe465e72db32d3 (diff)
downloadrails-c82321dcb72e73b4bfb62b817cca027cee1449b8.tar.gz
rails-c82321dcb72e73b4bfb62b817cca027cee1449b8.tar.bz2
rails-c82321dcb72e73b4bfb62b817cca027cee1449b8.zip
autoloading guide: documents nesting
Diffstat (limited to 'guides/source/constant_autoloading_and_reloading.md')
-rw-r--r--guides/source/constant_autoloading_and_reloading.md222
1 files changed, 160 insertions, 62 deletions
diff --git a/guides/source/constant_autoloading_and_reloading.md b/guides/source/constant_autoloading_and_reloading.md
index 1a33a5b55f..62dd1ac234 100644
--- a/guides/source/constant_autoloading_and_reloading.md
+++ b/guides/source/constant_autoloading_and_reloading.md
@@ -64,95 +64,114 @@ end
This guide documents how that works.
-Vocabulary
-----------
-
-### Parent Namespaces
+Constants Refresher
+-------------------
-Given a string with a constant path we define its *parent namespace* to be the
-string that results from removing its rightmost segment.
+While constants are trivial in most programming languages, they are a rich
+topic in Ruby.
-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 "".
+It is beyond the scope of this guide to document Ruby constants, but we are
+nevertheless going to highlight a few key topics. Truly grasping the following
+sections is instrumental to understanding constant autoloading and reloading.
-The interpretation of a parent namespace when thinking about classes and modules
-is tricky though. Let's consider a module M named "A::B":
+### Nesting
-* The parent namespace, "A", may not reflect nesting at a given spot.
+Class and module definitions can be nested to create namespaces:
-* The constant `A` may no longer exist, some code could have removed it from
-`Object`.
+```ruby
+module XML
+ class SAXParser
+ # (1)
+ end
+end
+```
-* 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.
+The *nesting* at any given place is the collection of enclosing nested class and
+module objects outwards. For example, in the previous example, the nesting at
+(1) is
-* In such case, it could even happen that the reassigned `A` held a new class or
-module called also "A"!
+```ruby
+[XML::SAXParser, XML]
+```
-* 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".
+It is important to understand that the nesting is composed of class and module
+*objects*, it has nothing to do with the constants used to access them, and is
+also unrelated to their names.
-The idea of a parent namespace is at the core of the autoloading algorithms
-and helps explain and understand their motivation intuitively, 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.
+For instance, while this definition is similar to the previous one:
-### Loading Mechanism
+```ruby
+class XML::SAXParser
+ # (2)
+end
+```
-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.
+the nesting in (2) is different, `XML` does not belong to it:
-`Kernel#load` allows Rails to execute files more than once if [constant
-reloading](#constant-reloading) is enabled.
+```ruby
+[XML::SAXParser]
+```
-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.
+We can see in this example that the name of a class or module that belongs to a
+certaing nesting does not necessarily correlate with the namespaces at the spot.
+Even more, they are totally independent, take for instance
-Autoloading Availability
-------------------------
+```ruby
+module X::Y
+ module A::B
+ # (3)
+ end
+end
+```
-Rails is always able to autoload provided its environment is in place. For
-example the `runner` command autoloads:
+The nesting in (3) is composed of two module objects:
-```
-$ bin/rails runner 'p User.column_names'
-["id", "email", "created_at", "updated_at"]
+```ruby
+[A::B, X::Y]
```
-The console autoloads, the test suite autoloads, and of course the application
-autoloads.
+So, it not only doesn't end in `A`, which does not even belong to the nesting,
+but it also contains `X::Y`, which is independent from `A::B`.
-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 during eager loading.
+The nesting is an internal stack maintained by the interpreter, and it gets
+modified according to these rules:
-For example, given
+* The class object following a `class` keyword gets pushed when its body is
+executed, and popped after it.
-```ruby
-class BeachHouse < House
-end
-```
+* The module object following a `module` keyword gets pushed when its body is
+executed, and popped after it.
-if `House` is still unknown when `app/models/beach_house.rb` is being eager
-loaded, Rails autoloads it.
+* When a singleton class is opened with `class << object`, the singleton class
+gets pushed when the body is executed, and popped after it.
+* When any of the `*_eval` family of methods is called using a string argument,
+the singleton class of the receiver is pushed to the nesting of the eval'ed
+code.
-Constants Refresher
--------------------
+It is interesting to observe that **no** block gets a modified nesting. In
+particular the blocks that may be passed to `Class.new` and `Module.new` do not
+get the class or module being defined pushed to their nesting. That's one of the
+differences between defining classes and modules in one way or another.
-While constants are trivial in most programming languages, they are a rich
-topic in Ruby.
+The nesting at any given place can be inspected with `Module.nesting`.
+
+At any given point, the nesting can be empty, let's use *cref* to refer to the
+first element of the nesting if it is not empty, or `Object` otherwise. Without
+getting too much into the details, the resolution algorithm for relative
+constant references goes like this:
+
+1. First, if the nesting is not empty it looks for the constant in its elements
+and in order, ingoring their ancestors.
+
+2. If not found, then it walks up the ancestor chain of the cref.
+
+3. If not found, `const_missing` is invoked on the cref.
+
+Rails autoloading **does not emulate this algorithm**, but its starting point is
+the name of the constant to be autoloaded, and the cref.
-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
@@ -256,6 +275,85 @@ class and module objects, constant names, and value objects assiociated to them
in constant tables.
+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 is at the core of the autoloading algorithms
+and helps explain and understand their motivation intuitively, 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 during eager loading.
+
+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.
+
+
autoload_paths
--------------