環境
- ruby 2.1.1p76
- Rails 4.1.1
rails console
した時のように、アプリ内のrailsコマンドを実行した時の流れを追ってみました。
exec_app_rails
rails new
と同じ処理の流れで、exec_app_railsを呼び出します。
lib/rails/cli.rb
require 'rails/app_rails_loader'
Rails::AppRailsLoader.exec_app_rails
rails/app_rails_loader.rb
module Rails
module AppRailsLoader
RUBY = Gem.ruby
EXECUTABLES = ['bin/rails', 'script/rails']
...
def self.exec_app_rails
original_cwd = Dir.pwd
loop do
if exe = find_executable
contents = File.read(exe)
if contents =~ /(APP|ENGINE)_PATH/
exec RUBY, exe, *ARGV
break # non reachable, hack to be able to stub exec in the test suite
railsコマンドを実行する際にカレントディレクトリをアプリ直下にしていますので、bin/railsファイルが見つかります。ここでは見つけたファイルを読み込んで、Rubyのスクリプトとして実行しています。
bin/rails
bin/rails
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
require 'rails/commands'
bin/railsでは、アプリとGemfileの設定を準備してから、フレームワークのcommands.rbをロードしています。
railties-4.1.1/lib
commands.rbを確認してみます。
引数が無ければヘルプ表示、各コマンドの省略形の対応を行った後でコマンドを実行するようです。
rails/commands.rb
ARGV << '--help' if ARGV.empty?
aliases = {
"g" => "generate",
"d" => "destroy",
"c" => "console",
"s" => "server",
"db" => "dbconsole",
"r" => "runner"
}
command = ARGV.shift
command = aliases[command] || command
require 'rails/commands/commands_tasks'
Rails::CommandsTasks.new(ARGV).run_command!(command)
rails/commands/commands_tasks.rb
module Rails
...
class CommandsTasks # :nodoc:
...
def run_command!(command)
command = parse_command(command)
if COMMAND_WHITELIST.include?(command)
send(command)
else
write_error_message(command)
end
end
consoleメソッドではrequire_commandを用いて、コマンドと同名のファイルを読み込んで実行しています。
rails/commands/commands_tasks.rb
...
def console
require_command!("console")
options = Rails::Console.parse_arguments(argv)
# RAILS_ENV needs to be set before config/application is required
ENV['RAILS_ENV'] = options[:environment] if options[:environment]
# shift ARGV so IRB doesn't freak
shift_argv!
require_application_and_environment!
Rails::Console.start(Rails.application, options)
end
...
private
...
def require_command!(command)
require "rails/commands/#{command}"
end
commands
提供されるコマンドは次のようになります(commands_tasks.rbを除く):
$ tree rails/commands
rails/commands
├── application.rb
├── commands_tasks.rb
├── console.rb
├── dbconsole.rb
├── destroy.rb
├── generate.rb
├── plugin.rb
├── runner.rb
├── server.rb
└── update.rb
0 directories, 10 files
console
rails/commands/console.rb
module Rails
class Console
class << self
def start(*args)
new(*args).start
end
...
def initialize(app, options={})
@app = app
@options = options
app.sandbox = sandbox?
app.load_console
@console = app.config.console || IRB
end
...
def start
...
if defined?(console::ExtendCommandBundle)
console::ExtendCommandBundle.send :include, Rails::ConsoleMethods
end
console.start
end
ConsoleMethodsで検索すると・・・app.rbとhelpers.rbで定義されているようです。
$ ack ConsoleMethods
rails/console/app.rb
5: module ConsoleMethods
rails/console/helpers.rb
2: module ConsoleMethods
app.rbは次のようになります:
rails/console/app.rb
require 'active_support/all'
require 'action_controller'
module Rails
module ConsoleMethods
# reference the global "app" instance, created on demand. To recreate the
# instance, pass a non-false value as the parameter.
def app(create=false)
@app_integration_instance = nil if create
@app_integration_instance ||= new_session do |sess|
sess.host! "www.example.com"
end
end
# create a new session. If a block is given, the new session will be yielded
# to the block before being returned.
def new_session
app = Rails.application
session = ActionDispatch::Integration::Session.new(app)
yield session if block_given?
session
end
# reloads the environment
def reload!(print=true)
puts "Reloading..." if print
ActionDispatch::Reloader.cleanup!
ActionDispatch::Reloader.prepare!
true
end
end
end
シンプルなコードですね。(reload!
はここにいたのか!)
IRB
$ rails c
Loading development environment (Rails 4.1.1)
001 > app.root_url
=> "http://www.example.com/"