How Does Rake Initialise a Rails App?

Rake is apparently simple, used all over the place, and a bit fiddly. Whenever you run a rake task, the entire rails application is initialised in the background. But how? HOW?!

  1.  It’s not the namespace you’re using. Arbitrary namespaces all initialise the rails app.
  2. Therefore, somehow, somewhere, something is happening in some parent task that everything is pulling in. But what?

Well, there’s no specific mechanism in rake that I’m aware of that arbitrary tasks inherit from. So it’s either the case that there’s a mechanism I don’t know about, or it’s the case that the rake tool has been monkeypatched somehow.

The case for rake being monkeypatched seems promising. How does rake -vT know to look in lib/tasks for extra tasks? Rails configuration, that’s how. So, how do you configure rake this way? I want to start by working out where a standard rake task is defined, so I try:

rake --where db:migrate

From here, I can see that railties is responsible for the db tasks. I also try:

rake --where log:clear

and it’s clear again that railties is coming into play. I’ve got a vague idea from the name, and my own reading, that railties is some kind of glue for rails. So, let’s dig a little deeper. I’ve opened my text editor to the location of the railties gem (it’s in the path given out by rake –where log:clear), and there’s a readme. It confirms that railsties is a glue gem.

I search for all files beginning with Rake, just because I’ve got to start somewhere, and find a rakefile in lib/rails/generators/rails/app/templates/ which has the line <%= app_const %>.load_tasks. This reads like an erb template, and I’m not specifically interested save that it gives me a clue: presumably, tasks are discovered by rails using a load_tasks method. Searching for “def load_tasks” in railties reveals:


# Load the application and its railties tasks and invoke the registered hooks.
# Check Rails::Railtie.rake_tasks for more info.
def load_tasks(app=self)
initialize_tasks
super
self
end

I can buy that initialize_tasks is going to be where I need to start. I search for def initialize_tasks and find:

def initialize_tasks #:nodoc:
self.class.rake_tasks do
require "rails/tasks"
task :environment do
$rails_rake_task = true
require_environment!
end
end
end

require_environment!? What does that sound like it does?


def require_environment! #:nodoc:
environment = paths["config/environment"].existent.first
require environment if environment
end

The namespace we’re in right now is “class Application < Engine”.

I can buy that this is where the environment is getting pulled in for _something_. I don’t know whether it’s the current app I’m working on, some rails-y magic or whatever. However, this doesn’t specifically help me out right now, because I’m trying to prevent rails loading for a rake task. rake task:name will still need to know how to load the rakefile in order to make any progress, which requires that the rails app has loaded. I’ve got a little further on the path to accomplishing my goal, but I’m not quite there.

 

Of course, I could just be overcomplicating things and there’s simply a Rakefile in rails root. And this slurps in the application config. Curses!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s