Preface
Rails 3 has many new terms, such as railtie, engine, and application, which are closely related to the rails Startup Process and gem development, try to analyze the Startup Process of rails 3 and give yourself an explanation.
Enter:
Rails s
Start. First, let's see what rails is in this command:
$ which rails
/Home/tomwang/. rvm/gems/ruby-1.9.3-p194 @ rails323/bin/rails
The STARTUP script is as follows:
#!/usr/bin/env ruby## 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/ version = $1 ARGV.shift endendgem 'railties', versionload Gem.bin_path('railties', 'rails', version)
This file mainly involves two tasks:
- Import the railties gem, which is the core concept in rails 3 and will be analyzed in more detail later.
- Load railties another rails script under this gem:/home/tomwang/. rvm/gems/ruby-1.9.3-p194 @ rails323/gems/railties-3.2.3/bin/rails
Its content is as follows:
#!/usr/bin/env rubyif File.exists?(File.join(File.expand_path('../../..', __FILE__), '.git')) railties_path = File.expand_path('../../lib', __FILE__) $:.unshift(railties_path)endrequire "rails/cli"
This rails is mainly require.
"railties-3.2.3/lib/rails/cli.rb "
The content of this file is as follows:
require 'rbconfig'require 'rails/script_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::ScriptRailsLoader.exec_script_rails!
The function executed in the last row is as follows:
Module rails module scriptrailsloader Ruby = file. join (* rbconfig: config. values_at ("bindir", "ruby_install_name") + rbconfig: config ["exeext"] script_rails = file. join ('script', 'rails ') def self.exe c_script_rails! CWD = dir. pwd return unless in_rails_application? | In_rails_application_subdirectory? Exec Ruby, script_rails, * argv if in_rails_application? # Finally run script/rails dir in our project directory. chdir (".. ") Do # recurse in a chdir block: if the search fails we want to be sure # The application is generated in the original working directory. exec_script_rails! Unless CWD = dir. pwd end rescue systemcallerror # cocould not chdir, no problem just return end def self. in_rails_application? File. exists? (Script_rails) end def self. in_rails_application_subdirectory? (Path = pathname. New (dir. pwd) file. exists? (File. Join (path, script_rails) |! Path. Root? & In_rails_application_subdirectory? (Path. Parent) end
As shown in the preceding comments, script/rails in our own project directory is executed. Its content is as follows:
#!/usr/bin/env ruby # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. APP_PATH = File.expand_path('../../config/application', __FILE__) require File.expand_path('../../config/boot', __FILE__) require 'rails/commands'
It defines app_path, that is, our project file config/application. RB, and require config/boot. The boot file mainly initializes bundle. Then rails/commands (railties-3.2.3/lib/rails/commands. RB) the file is loaded in. Since the input command parameter is s, that is, server, we only display the corresponding code:
when 'server' # Change to the application's path if there is no config.ru file in current dir. # This allows us to run script/rails server from other directories, but still get # the main config.ru and properly set the tmp directory. Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru")) require 'rails/commands/server' Rails::Server.new.tap { |server| # We need to require application after the server sets environment, # otherwise the --environment option given to the server won't propagate. require APP_PATH Dir.chdir(Rails.application.root) server.start }
Server. Start function:
def start url = "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}" puts "=> Call with -d to detach" unless options[:daemonize] trap(:INT) { exit } puts "=> Ctrl-C to shutdown server" unless options[:daemonize] #Create required tmp directories if not found %w(cache pids sessions sockets).each do |dir_to_make| FileUtils.mkdir_p(Rails.root.join('tmp', dir_to_make)) end puts 'server start ---' superensure # The '-h' option calls exit before @options is set. # If we call 'options' with it unset, we get double help banners. puts 'Exiting' unless @options && options[:daemonize] end
The final super will call the Rack: Server # Start method:
def start &blk if options[:warn] $-w = true end if includes = options[:include] $LOAD_PATH.unshift(*includes) end if library = options[:require] require library end if options[:debug] $DEBUG = true require 'pp' p options[:server] pp wrapped_app pp app end # Touch the wrapped app, so that the config.ru is loaded before # daemonization (i.e. before chdir, etc). wrapped_app daemonize_app if options[:daemonize] write_pid if options[:pid] trap(:INT) do if server.respond_to?(:shutdown) server.shutdown else exit end end server.run wrapped_app, options, &blkend
Wrapped_app method finally calls Rack: Server # app:
def app @app ||= begin if !::File.exist? options[:config] abort "configuration #{options[:config]} not found" end app, options = Rack::Builder.parse_file(self.options[:config], opt_parser) self.options.merge! options app endend
Because we didn't specify the config command line parameter, the system will take the default parameter and the: config.ru in the project directory. This file is related to rack (which will be discussed in detail later. This line of code imports config.ru with the following content:
require ::File.expand_path('../config/environment', __FILE__)run Tao800Fire::Application
It will then import the config/environment. RB file, in which our program will complete initialization:
# Load the rails application require File.expand_path('../application', __FILE__) # Initialize the rails application Tao800Fire::Application.initialize!
After the initialization is complete, we return
The last line of rack: Server # Start:
server.run wrapped_app, options, &blk
Because we didn't specify the server command line parameter, rack selects the default server for us:
def server @_server ||= Rack::Handler.get(options[:server]) || Rack::Handler.default(options)end
def self.default(options = {}) # Guess. if ENV.include?("PHP_FCGI_CHILDREN") # We already speak FastCGI options.delete :File options.delete :Port Rack::Handler::FastCGI elsif ENV.include?("REQUEST_METHOD") Rack::Handler::CGI else begin Rack::Handler::Thin rescue LoadError Rack::Handler::WEBrick end end end
Generally, webrick is used unless you have installed other application servers. Then webrick starts and displays the following information:
[2013-03-11 17:30:11] INFO WEBrick 1.3.1[2013-03-11 17:30:11] INFO ruby 1.9.3 (2012-04-20) [x86_64-linux][2013-03-11 17:30:11] INFO WEBrick::HTTPServer#start: pid=28371 port=3000
Now, the entire startup process is complete. The next chapter will explain some key steps in detail and introduce some important concepts involved in the process.