`

rails3 初始化和启动 Initialization Process

 
阅读更多
原文出处:

http://zires.info/2011/02/rails3-%E5%88%9D%E5%A7%8B%E5%8C%96%E5%92%8C%E5%90%AF%E5%8A%A8-initialization-process/

参考链接:
http://blog.csdn.net/tomwang1013/article/details/8657191
http://blog.csdn.net/tomwang1013/article/details/8691335

一直想搞清楚rails的启动和整个生命进程,好在有官方的guide用来参考,The Rails Initialization Process。
1)先来看看rails的组织结构

%w(
  actionmailer
  actionpack
  activemodel
  activerecord
  activeresource
  activesupport
  railties
)
2)rails是如何启动的?
按下‘rails s’ 命令发生了什么?
rails3中rails已经变成了一个全局的命令,s是它的参数,s是server的缩写。
rails命令在gem包的bin/rails脚本中

#!/usr/bin/env ruby

begin
  require "rails/cli"
rescue LoadError
  railties_path = File.expand_path('../../railties/lib', __FILE__)
  $:.unshift(railties_path)
  require "rails/cli"
end
如果找不到rails/cli就把railties_path加到当前环境变量中。railties作用是将每个rails模块串联起来,它负责rails的启动顺序,管理rails的命令行接口,并且提供Rails的generators。

” rails/cli ” 是负责什么的?

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!

require 'rails/ruby_version_check'
Signal.trap("INT") { puts; exit(1) }

if ARGV.first == 'plugin'
  ARGV.shift
  require 'rails/commands/plugin_new'
else
  require 'rails/commands/application'
end
rbconfig是对ruby标准库的配置和补充,在ruby编译的时候起效,和rails关系不大,不谈。
走进script_rails_loader.rb文件,里面定义了两个常量

RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"]
SCRIPT_RAILS = File.join('script', 'rails')
RUBY是ruby的bin文件位置,不管是Mac OS还是Windows还是其他,都会指向可执行文件。
SCRIPT_RAILS就是指向rails生成项目script目录下的rails脚本。大体内容如下:

#!/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'
回到cli文件中,下一句可以看到执行了一个方法

Rails::ScriptRailsLoader.exec_script_rails!
exec_script_rails!方法的内容:

def self.exec_script_rails!
      cwd = Dir.pwd
      return unless in_rails_application? || in_rails_application_subdirectory?
      exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application?
      Dir.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
exec RUBY, SCRIPT_RAILS, *ARGV 这边大致上可以看出端倪了,这行代码的实质是:

ruby script/rails [arguments]
# 这里的arguments 可以是server, generate, console等
OK,重点来看看script/rails

首先有一个APP_PATH的常量,可以看到指向的是config目录下的application.rb文件。接着require了boot文件和commands文件。这里可以看到boot文件是第一个被载入的。

boot文件的任务其实很简单,就是准备好Gemfile中的gems。

# rubygems第一个被加载
require 'rubygems'

# Set up gems listed in the Gemfile.
gemfile = File.expand_path('../../Gemfile', __FILE__)
begin
    ENV['BUNDLE_GEMFILE'] = gemfile
    require 'bundler'
    Bundler.setup
rescue Bundler::GemNotFound => e
    STDERR.puts e.message
    STDERR.puts "Try running `bundle install`."
    exit!
end if File.exist?(gemfile)
命令行的实质就全在rails/commands.rb中了,源码地址commands.rb

ARGV < < '--help' if ARGV.empty?

aliases = {
  "g" => "generate",
  "c" => "console",
  "s" => "server",
  "db" => "dbconsole"
}

command = ARGV.shift
command = aliases[command] || command
可以看到这里的ARGV就是上面提到的exec RUBY, SCRIPT_RAILS, *ARGV后面的参数,也就是我们传进去的”server,generate,console”等等。这里为空就是”–help”,并且还有4个aliases方便简写。

主要还是看server是如何启动的。

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
  }
上面代码有三点,第一是实例化了Rails::server,第二是require APP_PATH(也就是application.rb),第三调用了server#start方法

实例化了一个Rails::Server,这是个什么东东?参考源码commands/server.rb

require 'fileutils'
require 'optparse'
require 'action_dispatch'

module Rails
  class Server < ::Rack::Server
    ......
  end
end
原来Rails::Server继承了Rack::Server(终于和Rack挂钩了),Rack::Server用来给所有基于rack的应用提供一般的server接口。而且,这里第一次引入了action_dispatch。Rails::Server的initialize方法如下:

def initialize(*)
      super
      set_environment
end
super,Rack::Server源码那么Rack::Server的initialize方法如下:

def initialize(options = nil)
      @options = options
      @app = options[:app] if options && options[:app]
end
由于options为nil所以Rack::Server中的initialize无作为,回到Rails::Server中,super的下一句是set_environment,代码如下:

def set_environment
      ENV["RAILS_ENV"] ||= options[:environment]
end
一目了然,设定环境咯,production,development,还是test。这里options是父类Rack::Server中的:

def options
      @options ||= parse_options(ARGV)
end

def parse_options(args)
    options = default_options

    # Don't evaluate CGI ISINDEX parameters.
    # http://hoohoo.ncsa.uiuc.edu/cgi/cl.html
    args.clear if ENV.include?("REQUEST_METHOD")

    options.merge! opt_parser.parse! args
    options[:config] = ::File.expand_path(options[:config])
    ENV["RACK_ENV"] = options[:environment]
    options
end
default_options在Rails::Server中被覆写了:

def default_options
      super.merge({
        :port => 3000,
        :environment => (ENV['RAILS_ENV'] || "development").dup,
        :daemonize => false,
        :debugger => false,
        :pid => File.expand_path("tmp/pids/server.pid"),
        :config => File.expand_path("config.ru") # config.ru被指定,这也是commands.rb中server要chdir项目根目录的原因。
      })
end
第二是require APP_PATH(也就是application.rb),其实就是将项目的application给配置好,里面有这么一句:

require 'rails/all'
其实就是将rails所有的模块都引入进来,all.rb如下:

require "rails"

  %w(
    active_record
    action_controller
    action_mailer
    active_resource
    rails/test_unit
  ).each do |framework|
    begin
      require "#{framework}/railtie"
    rescue LoadError
    end
  end
第三是调用了server#start方法,在Rails::Server中:

def start
      puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"
      puts "=> Rails #{Rails.version} application starting in #{Rails.env} on http://#{options[:Host]}:#{options[:Port]}"
      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

      super
    ensure
      # 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
多熟悉啊,先一步步的屏显。然后是trap(:INT) { exit },是响应Ctrl-C中断的。再创建了tmp文件夹和四个子文件夹,最后回到Rack::Server的start方法中。

Rack::Server的start方法有这么一句:

wrapped_app
实际调用的是:

def wrapped_app
    @wrapped_app ||= build_app app
end
这里有两个方法的调用,一个是#build_app,另一个是#app,先来看#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
      end
end
了然了,原来通过Rack::Builder和config.ru构建了一个Rack application啊.Rack::Builder源码

再来看#build_app方法:

def build_app(app)
  middleware[options[:environment]].reverse_each do |middleware|
    middleware = middleware.call(self) if middleware.respond_to?(:call)
    next unless middleware
    klass = middleware.shift
    app = klass.new(app, *middleware)
  end
  app
end
通过middleware来新建一个app,像剥洋葱一样,一个一个middleware的往外套。

ok,由上面我们知道,通过config.ru,我们创建了一个Rack Application,下面来看看config.ru

# This file is used by Rack-based servers to start the application.

require ::File.expand_path('../config/environment',  __FILE__)
run example::Application
可以看到引入了environment.rb文件。如下:

# Load the rails application
require File.expand_path('../application', __FILE__)

# Initialize the rails application
example::Application.initialize!
初始化了example::Application,example::Application是继承的Rails::Application,那么Rails::Application是什么呢?

#Rails::Application is responsible for executing all railties, engines and plugin
# initializers. Besides, it also executed some bootstrap initializers (check
# Rails::Application::Bootstrap) and finishing initializers, after all the others
# are executed (check Rails::Application::Finisher).

通过代码,Rails::Application继承了Engine。

看看initialize!方法:

def initialize!
  raise "Application has been already initialized." if @initialized
  run_initializers(self)
  @initialized = true
  self
end
至此,Rails Application的初始化和Rails Server基本上都已经启动完成。
分享到:
评论

相关推荐

    Rails3 使用rake启动后台任务

    以下是一些关于如何在 Rails3 中使用 rake 启动后台任务的知识点: 1. **Rakefile**: 每个 Rails 项目都有一个 Rakefile 文件,这是 Rake 执行任务的入口点。在这个文件中,你可以定义自定义的任务。 2. **Task ...

    rails3教程

    **Rails3**是Ruby on Rails框架的一个重要版本,它对之前的版本进行了大量的改进和优化,使其更加强大和灵活。Rails3的核心设计理念围绕着几个关键的概念,这些概念不仅帮助开发者编写更加简洁高效的代码,还使得...

    启动Rails服务器指定端口号

    rails server命令启动web服务器的默认端口号为3000,当然我们也可以自定义指定端口号。

    Rails3常用命令行命令

    Rails3 是 Ruby on Rails 框架的一个版本,它提供了一系列强大的命令行工具,使得开发者可以快速地构建和管理Web应用。在本文中,我们将深入探讨Rails3中的常用命令,帮助你更高效地进行开发工作。 首先,新建一个...

    rails版本区别

    在Rails 2中,配置信息主要存储在`config/environment.rb`文件中,其中包含了初始化过程中的各种设置,如加载路径、gem依赖和插件配置等。然而,Rails 3对此进行了重大重构,将配置信息移到了`config/application.rb...

    ruby on rails源代码分析

    通过以上分析,我们可以看到 Rails 的启动流程是从 `config/boot.rb` 开始,经过预初始化,选择启动方式,然后通过 `initializer.rb` 完成核心配置和初始化。这个过程涉及到了 Rails 框架的核心组件,如数据库适配器...

    ruby on rails 3 tutorial.pdf

    《Ruby on Rails 3 Tutorial》是一本专门为初学者设计的指南,旨在帮助读者快速掌握Ruby on Rails这一强大的Web开发框架。Ruby on Rails(简称Rails)是基于Ruby语言的一个开源框架,它采用MVC(Model-View-...

    centOS Rails3环境搭建

    在开始部署Rails 3开发环境之前,我们需要确保系统上已经安装了一些基本的软件包和工具。这一步骤对于后续的Ruby和Rails安装至关重要。 ##### 1. 安装Node.js Node.js在某些Rails应用中是必需的,尤其是在使用某些...

    Rails 3 in Action

    《Rails 3 in Action》不仅覆盖了Rails 3.1的核心概念和技术,还涵盖了从开发到部署的全过程,是Rails开发者不可或缺的参考书籍。通过阅读这本书,开发者可以深入理解Rails的工作原理,提升开发技能,并学会构建高效...

    rails 项目起步示例

    Rails是Ruby语言的一个著名Web开发框架,全称为Ruby on Rails,它遵循MVC(Model-View-Controller)架构模式,旨在提高开发效率和代码可读性。本示例"rails项目起步示例"是一个购物系统,非常适合初学者入门学习。 ...

    The Rails3 Way, 2nd Edition

    Ruby on Rails strips complexity from the development process, enabling professional developers to focus on what matters most: delivering business value via clean and maintainable code. The Rails™ 3 ...

    Rails入门教程一(翻译)

    - **db**:数据库相关文件夹,包含数据库迁移文件和初始化脚本。 - **test**:测试文件夹,包含功能测试、集成测试和单元测试文件。 - **public**:静态资源文件夹,包含HTML、CSS、JavaScript等文件。 - **script**...

    ruby on rails 3

    Ruby on Rails 3 是一个基于Ruby编程语言的开源Web应用程序框架,它遵循MVC(Model-...提供的文档如"Ruby192和Rails3.0.3的新征程.doc"和"rails3入门教程.pdf"等,将有助于深入理解这一框架及其在实际项目中的应用。

    Ruby on Rails安装指南(Ruby 1.8.6+Rails 2.0.2)

    该命令会自动初始化一系列的目录结构和基础文件,这些文件是Rails应用程序的骨架。 **知识点5:启动Mongrel服务器** 新创建的Rails应用程序需要一个服务器来托管,这时就可以启动Mongrel服务器。在Rails项目的根...

    rails3.1安装与mysql配置 windows

    5. 初始化和迁移数据库 文件"libmySQL.dll"是MySQL客户端库的一部分,用于Rails应用连接到MySQL数据库。确保这个文件在系统路径中或者Rails应用的bin目录下,以便Rails能够正确地与MySQL通信。通过这些步骤,你就...

    rails2.3.2

    在压缩包的文件名称列表中,只有一个条目 "rails",这可能意味着压缩包内包含了 Rails 框架的核心文件,如 gemspec 文件、库文件、初始化脚本等。开发者可以通过解压这个文件,进一步了解 Rails 框架的结构和工作...

    Rails3 device and cancan

    标题《Rails3 device and cancan》与描述《ROR ruby on rails device plugin教程》指出本文是关于如何在Rails 3.2应用程序中整合Devise认证插件和Cancan授权插件的教程。Devise是一个流行的Ruby on Rails的认证解决...

    ruby on rails 搭建redmine

    6. **初始化数据库**:执行`rake db:migrate`来创建数据库表结构。 7. **设置Redmine**:根据需求修改`config/configuration.yml`,设置邮件服务器、URL等配置。 8. **创建管理员用户**:运行`rake redmine:admin:...

Global site tag (gtag.js) - Google Analytics