by Eric

Have you ever thought why ‘rake’ is available in the shell or windows cmd, after "gem install rake"? What if you want write your who gem that could let you say hello to he world from console? Let’s better ask rubygems itself 🙂

But, before we dive into the code, there is a little warm up. The gem specification, which contains the meta date of the gem, have some entries related the executables:

Executables A list of files in the package that are applications.

Bindir The directory containing the application files, if any.

For example:

1: spec.executables << 'rake'

2: spec.bindir = 'bin'

Is tell us to looking up a file named rake, as executable, in ‘bin’ folder. Just as above, we could find rake file(or rake.bat if you are on windows), which start with #!/usr/bin/ruby, in you bin folder after you install rake gem. It told us all this is done by rubygems, or to be more specific, by gem install.

So let’s try to find out if there is a file called something like install in the rubygems package. Lucky enough, there is truly a installer.rb there!

1: def install

2:# If we're forcing the install then disable security unless the security

I think the author is kind enough to show all the gem installation logic in this single method. Here we only need to focus on line36, oh yes, it is rubygems responsibility to generate executable and put it in the bindir. Of cause, there are much more magic happens behind this simple line of "generate_bin", I bet you will be pleased to learn it by your self:)

Although it is a pretty long method, we only need to focus the first line for our goal:

1: rakefile, location = find_rakefile_location

And let’s see what find_rakefile_location could explain:

1: def find_rakefile_location

2: here = Dir.pwd

3:while ! (fn = have_rakefile)

4: Dir.chdir("..")

5:if Dir.pwd == here || options.nosearch

6:return nil

7:end

8: here = Dir.pwd

9:end

10: [fn, here]

11: ensure

12: Dir.chdir(Rake.original_dir)

13: end

find_rakefile_location find rake definition files from the current directory to all it parent directories. Not so hard to understand, right?

have_rakefile is the method for detecting if the rake definition file is exist in the current path from a candidate list @rakefiles:

1: def have_rakefile

2: @rakefiles.eachdo |fn|

3:if File.exist?(fn) || fn == ''

4:return fn

5:end

6:end

7:return nil

8: end

Finally, we found our rake definition files: @rakefiles, which is an array:

1: [rakefile, Rakefile, rakefile.rb, Rakefile.rb]

Haha , we got our answer from the source code:) It turns out we are not limited with only Rakefile, but also other three forms. It also explains why the Rakefile always located in the root path of a project, because rake will eventually reach definition files all the way up from all directories under the project path in which we execute the rake method.