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:
Railtieis 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 theinitializemethod whenever the class attempting to be initialized is found in theABSTRACT_RAILTIESconstant.Railtieserves mostly as an API that should be subclassed. This observation comes directly from the observation thatRailtieis 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::Railtieand configures that subclass.Railtiehas 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.Railtieis class based. Notice that thenewmethod is private. What this means is thatRailtiecannot be instantiated unless a subclass makes thenewmethod public. This is one of the most interesting design decisions made by the Rails developers. In particular, it means that each subclass ofRailtieoperates 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!