rails new としたとき何がおこるのか調べようとしてあきらめた
まず rails コマンドがどこにあるのか調べよう
$ which rails
/home/plonk/.rvm/gems/ruby-2.1.2/bin/rails
ただの Ruby スクリプトだ。
$ file `which rails`
/home/plonk/.rvm/gems/ruby-2.1.2/bin/rails: Ruby script, ASCII text executable
rails:
#!/usr/bin/env ruby_executable_hooks
#
# This file was generated by RubyGems.
#
# The application 'railties' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0"
if ARGV.first
str = ARGV.first
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then
version = $1
ARGV.shift
end
end
gem 'railties', version
load Gem.bin_path('railties', 'rails', version)
最初の行はスクリプトを実行するインタプリタを指定するもので
/usr/bin/env
を使うことでインタプリタのパスを直接指定せずに環境変数によっ
て切り替えるようになっている。
コメントによれば、RubyGems によって生成されたファイルで railties ってい うアプリが関係しているらしい。
第一引数で _VERSION_
の形で railties のバージョンが指定してあれば、それを “>= 0” の代わりに指定して gem 'railties', version
を呼び出すというコード。
gem はなにをしているかというと、指定のバージョンの railties が読み込ま
れるように $LOAD_PATH
を設定するらしい。
http://docs.ruby-lang.org/ja/2.0.0/method/Kernel/i/gem.html
Gem.bin_path
の部分は rails コマンドの実行パスを railties ジェムに求め
ている様子。
http://apidock.com/ruby/Gem/bin_path/class
実際に実行するとこういうパスが出てくる。
> Gem.bin_path('railties', 'rails')
=> "/home/plonk/.rvm/gems/ruby-2.1.2/gems/railties-4.1.7/bin/rails"
railties っていうのは何かと言うと…
$ gem list railties --details
*** LOCAL GEMS ***
railties (4.1.7)
Author: David Heinemeier Hansson
Homepage: http://www.rubyonrails.org
License: MIT
Installed at: /home/plonk/.rvm/gems/ruby-2.1.2
Tools for creating, working with, and running Rails applications.
なるほど、Rails のコマンド類は railties っていうサブパッケージに含まれ ているということかな? (railties は Rails と何のかばん語だろう?)
…ともかく rails コマンドの実際の(?)パスが出てきた。見てみよう。
#!/usr/bin/env ruby
git_path = File.expand_path('../../../.git', __FILE__)
if File.exist?(git_path)
railties_path = File.expand_path('../../lib', __FILE__)
$:.unshift(railties_path)
end
require "rails/cli"
スクリプトのある場所を基準にして3つ上のディレクトリにある .git ディレク トリを求めている。
plonk@xubuntu-14:~/.rvm/gems/ruby-2.1.2/gems/railties-4.1.7/bin$ ls ../../../.git
ls: ../../../.git にアクセスできません: そのようなファイルやディレクトリはありません
Whoops、存在しない。ともかく、存在すれば ../../lib
を $:
の先頭に追加するということのようだ。
$:
と $LOAD_PATH
と同じ。覚えられないから何度でも確認する。
> $LOAD_PATH == $:
=> true
> $LOAD_PATH.object_id == $:.object_id
=> true
ここまでのところ、自分の環境には関係のないことをしているようだ。そして rails/cli が require されている。cli っていうのは Command Line Interface だろう(どや顔)。
ここにあった。
$ locate rails/cli
/home/plonk/.rvm/gems/ruby-2.1.2/gems/railties-4.1.7/lib/rails/cli.rb
cli.rb:
require 'rails/app_rails_loader'
# If we are inside a Rails application this method performs an exec and thus
# the rest of this script is not run.
Rails::AppRailsLoader.exec_app_rails
require 'rails/ruby_version_check'
Signal.trap("INT") { puts; exit(1) }
if ARGV.first == 'plugin'
ARGV.shift
require 'rails/commands/plugin'
else
require 'rails/commands/application'
end
AppRailsLoader
クラスを一行目でロードしているようだ。コメントによれば
Rails アプリケーション内から呼び出されたのならば exec するのでこれ以降
は実行されないとある。
exec というのは現在のプロセスを置き換えるシステムコールだから、意味が通 る。http://api.rubyonrails.org/classes/Rails/AppRailsLoader.html
よくわからないが、./bin/rails
か ./script/rails
があればそれを実行する
ということなのだろう。…はっ、ということは rails と打つとき、そのプロジェ
クトの bin ディレクトリにある rails コマンドが実行されていたわけか!
…でも、rails new するときは、これらのファイルはまだ存在しないから
exec_app_rails
はフォールスルーするはずだ。
rails/ruby_version_check
のロードは本質的でなさそうなので、無視する。
次の行は、Ctrl+C が押されて SIGINT が発生したら改行を表示して(きっとエ
ラーメッセージとプロンプトがくっつかないようにするためだろう)終了するよ
うに設定している。
コマンドライン引数の最初の項目(サブコマンド)が plugin だったら読み捨て て rails/commands/plugin をロードし、それ以外だったら rails/commands/application をロードする。rails new の場合を考えているか ら実行するのは else 節の方のパスのはずだ。
というわけで application.rb に行く。
$ locate rails/commands/application
/home/plonk/.rvm/gems/ruby-2.1.2/gems/railties-4.1.7/lib/rails/commands/application.rb
内容。
require 'rails/generators'
require 'rails/generators/rails/app/app_generator'
module Rails
module Generators
class AppGenerator # :nodoc:
# We want to exit on failure to be kind to other libraries
# This is only when accessing via CLI
def self.exit_on_failure?
true
end
end
end
end
args = Rails::Generators::ARGVScrubber.new(ARGV).prepare!
Rails::Generators::AppGenerator.start args
基本的には AppGenerator クラスをロードして start クラスメソッドにコマン ドライン引数を渡しているようだ。こいつが主な仕事をしているんだろう。
というわけで、 app_generator.rb
と AppGenerator
のスーパークラスが
ある app_base.rb
を見にいった。だがさっぱりわからないぜ、というわけで
あきらめた。