As part of my Google Summer of Code project I’ll be refactoring Railties, the internal engine of Ruby on Rails. When a Rails application launches, there is a complicated process of configuration and initialization that sets the Rails web server.
Before you can actually set up this web server, there are quite a few things that need to be configured. Railties are the engines for reading these configurations, creating an application, and connecting different parts of Rails.
The Railtie Class
All major components of Rails are created as a subclass of Rails::Railtie
. For example, Active Record (which handles query syntax to a database) and Action Controller (which handles the base controller logic) are both Railties.
So what does Rails::Railtie
do?
Well, to figure out the function of Railtie
, we can go look at the source code. Finding the definition isn’t too tricky. You can clone the Ruby on Rails source code from github. My primary method for finding class definitions is by using ack-grep, a convenient command line tool for searching through files. I searched for the following:
ack-grep "class Railtie"
This gives us the file “railties/lib/rails/railtie.rb” which defines Railtie
. A casual perusal of the file tells us a couple of things:
Railtie
is abstract. This means that it is not intended for direct instantiation. This is pretty clear because there is an error which is thrown inside of theinitialize
method whenever the class attempting to be initialized is found in theABSTRACT_RAILTIES
constant.Railtie
serves mostly as an API that should be subclassed. This observation comes directly from the observation thatRailtie
is abstract. Abstract classes usually define methods which should be overwritten in subclasses. The documentation also gives us a good clue, because it provides examples where one subclassesRails::Railtie
and configures that subclass.Railtie
has four main methods. These arerake_tasks
,console
,runner
, andgenerators
. For each of these methods, a block is added to a list of previously defined blocks. Here we can seeRailtie
’s true functionality: providing a way to configure anything which subclassesRails::Railtie
.Railtie
is class based. Notice that thenew
method is private. What this means is thatRailtie
cannot be instantiated unless a subclass makes thenew
method public. This is one of the most interesting design decisions made by the Rails developers. In particular, it means that each subclass ofRailtie
operates without any instances.
Class Based Design
The last item I mentioned was the class based design of Railtie
. To get a better sense of what this means, let’s look at an example of how one would create a Railtie
. Since the new
method is private, you can’t do the following:
Rails::Railtie.new
This would throw a runtime error saying that new
is private. What is done instead to define a Railtie
is this:
class MyNewRailtie < Rails::Railtie
initializer "new_initialization_behavior" do
puts "Hello!"
end
end
Something subtle is happening here. The new_initialization_behavior
initializer is being defined on the class instead of on any particular instance of MyNewRailtie
. Normally this would mean that all instances would have access to this initializer, but since you can’t instantiate MyNewRailtie
, we don’t really worry about that.
Instead, a singleton instance is created behind the scenes whenever you want to change the configuration of MyNewRailtie
. Let’s examine the following code:
class AnotherNewRailtie < Rails::Railtie
config.before_configuration do
puts "This runs before the initializers run"
end
end
We’re changing the configuration of AnotherNewRailtie
, but we’re doing this by accessing the singleton of the AnotherNewRailtie
class. In the code, the singleton is called the instance
. It’s defined as follows:
def instance
@instance ||= new
end
Thus, this method grabs either the cached object in @instance
or creates a new AnotherNewRailtie
object and sets @instance
to this new object.
All config
calls on the class are sent to the instance, which will then configure the class. When you call AnotherNewRailtie.config
, you will obtain the configuration from the singleton instance
, with the before_configuration
hook that we defined above.
Discussion
Although the class based design of Railtie
gives you a simple syntax for configuring subclasses of Railtie
and referencing a particular subclass (just get it’s constant), it does run into some problems.
The biggest problem is that you can’t easily create multiple Rails::Application
instances. The Rails::Application
class defines all of the backend logic for a Rails application, but you can’t make multiple applications unless you create multiple subclasses (which isn’t easy to do programatically) or unless you make the new
method public (which destroys the API nature of Railtie
).
My GSOC project will be attempting to fix some of these things, so stay tuned!