In last post we discussed the initialization process when a Rails application starts. And we disucssed in config/environment.rb it calls Rails.application.initialize!
to initialize the application. And in this blog we will have a detailed examination what has been done in this method.
Let’s have a look what’s in that method,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
we can see that it just calls the run_initializers
method, this method is actually an instance method in a module called Rails::Initializable. The Rails::Application has following hirarchy,
At the bottom is our Sample::Application class defined in config/application.rb. Its super class is Rails::Application which inherits from Rails::Engine. And Rails::Engine’s parent is Rails::Railtie. The Rails::Railtie includes the module Rails::Initializable. The run_initializers
method is defined in that module. The Railtie is to manage the initialization and configuration of each module. In Rails each module such as Active Record, Active Support has its own Railtie class which inherits from Rails::Railtie. After this blog you should understand the main functionality of the Railtie.
Let’s have a look at the module Rails::Initializable. This module provides a set of methods to manage the order of initialization.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
If a class includes this module, it can call the class method initializer
to register a initializer to a class instance variable @initializers
. This method accepts three parameters: the name, an option hash which you can define the order of the initializer, and a block, this block accepts one parameter which is normally our Rails.application
instance.
Let’s check the definition of the Initializer class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
In the initialize
method, it accepts a context object, notice that in Rails::Initializable, the initializer
class method pass context as nil. But later a initializer could call bind
to generate a new initializer with the context bind to an object. So if a class includes Rails::Initializable, its @initializers
class instance variable contains a list of initializer templates. And these initializer templates could be instantiated by bind to a context object. And then pay attention that when a class includes Rails::Initializable module, it also has a initializers
instance method, this method actually gets all initializer templates from itself and its ancestors and pass itself as the context.
Let’s write some classes to verify how Rails::Initializable works. I created a file parent.rb in app/models which has following contents,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
This class Parent is a normal class which includes Rails::Initializable. Two initializers are defined: config2 and config1. Although we defined config1 after config2, but we passed before: 'config2'
when initialize config1.
Now let’s open a rails console. The Parent should have a class method which returns all initializers. Let’s print the names of the initializers.
1 2 3 4 |
|
And the initializers in the class act as a template, their instance variable @context is nil. Let’s verify that by printing the @context instance variable, we can see they are nil.
1 2 |
|
Now let’s create an instance of Parent. We can see that the instance also has an instance method called initializers. Let’s print the name,
1 2 3 4 |
|
Just now we said that the initializers in the class are like a template, and for the initializers in the object instance, we can think that the initializers are instantiated by setting the @context instance variable. We can verify that by printing the instance variable of all initializers.
1 2 |
|
We can see that the @context has been set to the parent object.
And if we call the run_initializers method,
1 2 3 4 |
|
Notice two things here, first the config1 is executed before the config2. And secondly notice why we can call the config1 and config2 methods in the initializer block? Because when the initializers are run, the self object in the block is set to the @context instance variable. Since our context object is parent, and config1 and config2 are instance methods of this object, of course we can call it.
Now let’s create a subclass Child1 which inherits from Parent, and in this class it defines another initializer whose name is config_in_child1.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Let’s restart our console and try to create an instance of Child1 and call run_initializers.
1 2 3 4 5 6 7 8 9 |
|
We can see that when an instance run its initializers, it will run the initializers in its ancestors first, from top to bottom. Here the two initializers config1 and config2 are executed first, then it’s config_in_child1 in Child1 class.
Now let’s create another class Child2 also has an initializer, similar as Child1.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
And then let’s create one App class which combines one instance of Child1 and Child2.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
This class has two instance variables @child1 and @child2, which is an instance of Child1 and Child2 separately. This class also includes Rails::Initializable, but it overrides the initializers method to return the intializers of both @child1 and @child2. Now let’s open a new rails console, and create one instance of App and call run_initializers
.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
We can see that when we run app.run_initializers
, it runs all initializers in @child1 and @child2. And also the order is maintained. All config1 initializers are executed before config2 initializers.
The Rails::Application class also overrides the initializers method like the App class above. It maintains a list of Railties and run the Rails.application.initialize!
method is called, it run all Railties’ initializers.
And we could open a rails console to display all initializers and its associated binding context.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
This is a very long list. Actually by default there is 108 initializers. If you are interested you can check the Rails source code to see which initializer does what. For example, the :set_load_path is to set the $LOAD_PATH global. So each engine has a chance to modify the load path.
So after this blog you should have a general idea what Rails has done when it calls Rails.application.initialize!