标签:
本篇博文会从使用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