标签:
本篇博文会从使用rails server命令到应用启动完成的代码调用顺序,介绍rails server的启动过程,是对rails guide的一个简略翻译和一些博主的认识,翻译的不好还请各位见谅。看的时候最好找一份rails4的源码一起对照的来看。
原文地址: http://guides.ruby-china.org/initialization.html
当我们使用rails c(onsole)或者rails s(erver)时会启动一个rails的应用,rails s其实相当于在当前路径下执行了一段ruby脚本:
version = ">=0" load Gem.bin_path(‘railties‘, ‘rails‘, version)
如果在Rails console里输入上面的命令,你会看到railties/bin/rails被执行了。
在railties/bin/rails源码的最后一行可以看到
require “rails/cli”
require了railties/lib/rails/cli,继续跟进,在railties/lib/rails/cli中:
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
继续进入railties/lib/rails/app_rails_loader.rb
RUBY = Gem.ruby
EXECUTABLES = [‘bin/rails‘, ‘script/rails‘]
......
class << self
......
def 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
elsif exe.end_with?(‘bin/rails‘) && contents.include?(‘This file was generated by Bundler‘)
$stderr.puts(BUNDLER_WARNING)
Object.const_set(:APP_PATH, File.expand_path(‘config/application‘, Dir.pwd))
require File.expand_path(‘../boot‘, APP_PATH)
require ‘rails/commands‘
break
end
end
# If we exhaust the search there is no executable, this could be a
# call to generate a new application, so restore the original cwd.
Dir.chdir(original_cwd) and return if Pathname.new(Dir.pwd).root?
# Otherwise keep moving upwards in search of an executable.
Dir.chdir(‘..‘)
end
end
def find_executable
EXECUTABLES.find { |exe| File.file?(exe) }
end
end
可以通过上面的代码看出Rails::AppRailsLoader.exec_app_rails会在当前目录下找bin/rails或script/rails,找到了就会执行:
exec Gem.ruby, bin/rails, *ARGV
即相当于
exec ruby bin/rails server
如果当前目录下没有bin/rails或script/rails就会一直递归向上直到找到bin(script)/rails,所以在rails项目的根目录以及子目录下的任何地方都可以使用rails server或rails console命令
bin/rails:
#!/usr/bin/env ruby APP_PATH = File.expand_path(‘../../config/application‘, __FILE__) require_relative ‘../config/boot‘ require ‘rails/commands‘
APP_PATH常量将会在之后的rails/commands里使用,require_relative ‘../config/boot‘是require了config/boot.rb文件,boot.rb负责加载启动Bundler
config/boot.rb
# Set up gems listed in the Gemfile. ENV[‘BUNDLE_GEMFILE‘] ||= File.expand_path(‘../../Gemfile‘, __FILE__) require ‘bundler/setup‘ if File.exist?(ENV[‘BUNDLE_GEMFILE‘])
在标准的的Rails应用中,Gemfile文件申明了所有应用依赖关系,ENV[‘BUNDLE_GEMFILE‘]存放Gemfile的文件地址,当Gemfile文件存在,回去require bundle/setup,用于Bundler配置加载Gemfile依赖关系的路径。
一个标准的Rails应用需要依赖几个gem包,如:
actionmailer
actionpack
actionview
activemodel
activerecord
activesupport
arel
builder
bundler
erubis
i18n
mime-types
polyglot
rack
rack-cache
rack-mount
rack-test
rails
railties
rake
sqlite3
thor
treetop
tzinfo
require了config/boot.rb后回到bin/rails,最后还会require rails/commands
rails/commands.rb
主要用于扩展rails命令参数的别名:
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 --help等效,rails s等效于rails server
rails/commands/command_tasks.rb
当输入了一个错误的rails命令,run_command方法负责抛出一个错误信息。如果命令是有效的,就会调用相同名称的方法
COMMAND_WHITELIST = %(plugin generate destroy console server dbconsole application runner new version help)
def run_command!(command)
command = parse_command(command)
if COMMAND_WHITELIST.include?(command)
send(command)
else
write_error_message(command)
end
end
def write_error_message(command)
puts "Error: Command ‘#{command}‘ not recognized"
if %x{rake #{command} --dry-run 2>&1 } && $?.success?
puts "Did you mean: `$ rake #{command}` ?\n\n"
end
write_help_message
exit(1)
end
def parse_command(command)
case command
when ‘--version‘, ‘-v‘
‘version‘
when ‘--help‘, ‘-h‘
‘help‘
else
command
end
end
等下继续
标签:
原文地址:http://my.oschina.net/u/1413049/blog/410036