- 浏览: 41082 次
- 性别:
- 来自: 北京
最新评论
-
lijingtx:
为什么我报错了。in `alias_method`:undef ...
Rails中如何更加优雅的处理文件上传 -
gigix:
woody_420420 写道rainchen 写道几时cuc ...
让测试并行起来吧 -
woody_420420:
rainchen 写道几时cucumber也能并发跑scena ...
让测试并行起来吧 -
rainchen:
有时为了保证测试环境和开发、生产的特性一致,减少非必要的环境差 ...
让测试并行起来吧 -
woody_420420:
是的。耗时主要在数据库访问上。
内存数据库,我倒真没想过用这个 ...
让测试并行起来吧
-
前言
本文主要是针对Ruby On Rails 2.0.2的源代码进行分析,学习与研究。所使用的工具是NetBean 6.1
Beta,WEBRick,SciTE,ruby-debug-base(0.10.0),ruby-debug-ide(0.1.10)。Ruby版本为1.8.6。
应该怎么分析总结,是开始最令人头痛的事,Ruby是面向对象的语言,从对象的层次记录吧,似乎一切都不那么直观,一个庞大的系统摆在眼前,整理一个类图,继承关系图。。。有点牛啃南瓜,无从下口的感觉。最后,决定打算从Ruby的本质-解释语言下手,从解释器的角度出发,跟着解释器的步伐,从细微入手,一步一步深入Rails,以达到从局部到整体,了解学习的目的。所以,最终,我决定从源代码执行顺序的角度去分析Rails。
为方便起见,我直接使用NetBean的调试环境,使用Ruby自带的WEBRick,从接触Rails最基本的ruby
script/server开始,首先来看Rails是怎么启动起来的。
-
Rails的启动
ruby
script/server,应该是搞rails的同学们耳熟能详的命令了。server脚本主要执行两个的过程:1.启动Rails;2.启动web服务器(当然,我这里是启动WEBRick了)。我们就从这里入手,看看Rails是怎么样被启动起来的。
前面我说过,我将从源代码执行顺序的角度去分析,所以,让我们先来看一看Rails启动时,核心源代码的执行顺序,具体见下图(为了使得分析简单明了,抓住关键本质所在,我只把个人认为与启动有关的源代码列出来,执行过程中,其他类似关于ActiveSupport中关于core的extension之类的代码就不列出):
boot.rb
源代码路径:RAILS_ROOT/config/boot.rb
这个代码文件是Rails的启动入口,完成的功能是:首先判断Rails是否启动,如果未启动则先执行一个“预初始化”(preinitialize)过程,然后选择一种启动方式(Vendor/Gem),执行相应类上的run方法。主方法boot!代码如下:
def boot! unless booted? preinitialize pick_boot.run end end
其中,与初始化过程是执行RAILS_ROOT/config目录下面的preinitializer.rb(如果存在的话)。这个过程的目的是在加载environment.rb文件执行执行一些初始化工作。参见:http://yudionrails.com/2008/1/7/what-s-new-in-edge-rails-pre-environment-load-hook 。此源代码中包含一个module Rails,此模块下面包括三个类:VendorBoot,GemBoot,他们都继承自Boot类,分别代表是通过Vendor还是Gem的方式启动Rails(如果RAILS_ROOT/vender/下面存在名为rails的目录,则以Vendor方式启动Rails,否则,从Gem启动Rails)。当使用Gem方式启动Rails的话,还有一个重要的功能就是判断加载哪个版本的Rails,当然,正如我们所知,environment.rb中的RAILS_GEM_VERSION起了作用。总得来说,boot代码逻辑较简单,没有什么费解的东西,下面给出这个文件的整个执行逻辑流程图:
initialize.rb
源代码路径:gems/rails-2.0.2/lib/initializer.rb
(Gem方式启动)
RAILS_ROOT/vendor/rails/railties/lib/initializer.rb(Vendor方式启动)
虽然两种不同启动方式执行的源代码不同,但是他们完成的功能都大同小异,都对Rails执行必要的配置以及初始化。我们先来看看上一步--执行boot.rb代码的最后一步(还记得pick_boot.run么?),具体代码如下:
class Boot def run load_initializer Rails::Initializer.run(:set_load_path) end end
当完成了选择一个boot方式后,会执行相应Boot对象的run方法,那么run方法首先载入初始化器,VendorRoot通过如下方式载入:
class VendorBoot < Boot def load_initializer require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer" end end
GemRoot通过如下方式载入:
class GemBoot < Boot def load_initializer self.class.load_rubygems load_rails_gem require 'initializer' end ... end
OK。初始化器载入完成,Boot的run方法立即执行初始化器对象的类方法run,注意这里的run方法参数是:set_load_path符号。好了,下面,我们可以深入到initialize.rb里面去看个究竟了。 initialize.rb中定义了一个module Rails,其中包括了此代码文件中最重要的两个类:Configuration和Initializer。从类名我们就可以很清晰的了解到,Initializer类完成Rails的初始化工作,当然这个过程需要各种各样的配置,参数,这些则由Configuration提供。那么首先来看看Configuration提供了Rails所需的哪些配置参数,详见下表:
配置名(accessor名) | 具体描述 |
frameworks | 会被载入的Rails框架组件列表,会包括action_controller,action_view等 |
load_paths | 附加的load路径列表,app/controller;app/models等Rails项目下的目录 |
load_once_paths | Rails只会load一次的目录,似乎目前版本的Rails未用到这个参数 |
log_path | 日志文件的路径,根据目前的环境(development,test,production)决定 |
log_level | Rails日志器的日志级别(info,debug) |
view_path | view的目录路径,默认路径是app/view了 |
controller_paths | controller的目录路径,默认路径是app/controller |
cache_classes | 是否对类进行缓存。目前未使用(一直是false) |
whiny_nils | true/false,当设置为true的话,当你在Rails中调用一个nil方法的时候,将会得到警告 |
plugins | 载入的插件列表,默认为空 |
plugin_paths | 插件路径,默认是RAILS_ROOT/vendor/plugins目录 |
plugin_locators | 插件的定位器,默认是Plugin::FileSystemLocator |
plugin_loader | 插件的载入器,默认是Plugin::Loader |
database_configuration_file | 数据库配置文件,默认位于RAILS_ROOT/config/database.yml |
绕了一圈,现在让我们回到Initializer类的run方法(由boot.rb调用:Rails::Initializer.run(:set_load_path)),十分简单:
def self.run(command = :process, configuration = Configuration.new) yield configuration if block_given? initializer = new configuration initializer.send(command) initializer end
现在我们姑且不管block(boot.rb调用他的时候确实也没有关联一个block),接下来的工作是生成一个新的Configuration对象,并赋给Initializer的构造函数(然后由Initializer对象保存该配置对象),然后执行initializer上的command方法,默认情况是执行process方法,这里通过boot.rb的调用,将执行set_load_path方法。在这里值得注意的是,新生成的Configuration对象的所有配置参数都是默认值,例如:frameworks参数通过如下方法得到默认值:
def default_frameworks [ :active_record, :action_controller, :action_view, :action_mailer, :active_resource ] end
controller_path参数通过如下方法得到默认值:
def default_controller_paths paths = [File.join(root_path, 'app', 'controllers')] paths.concat builtin_directories paths end
其实,所有的这些配置都不是定死的,我们可以在enviroment.rb中重新定义他们,象下面这样:
Rails::Initializer.run do |config| config.frameworks -= [ :active_record, :active_resource, :action_mailer ] config.plugins = [ :exception_notification, :ssl_requirement, :all ] ... end
到这里,initializer.rb的介绍暂时结束,只是简单的执行了set_load_path方法设置load路径。接下来,执行流程回到了script/server:
require File.dirname(__FILE__) + '/../config/boot' require 'commands/server'
该执行第二句了,下面轮到server.rb出场了。
server.rb
源代码路径:gems/rails-2.0.2/lib/commands/server.rb
server.rb主要完成两个功能:1.加载active_support;2.加载web服务器。
加载active_support十分简单,只是通过源代码开始的第一句:
require 'active_support'
加载web服务器相比复杂一些。首先,Rails会试图加载FastCGI,然后会试图加载mongrel,如下代码所示:
begin require_library_or_gem 'fcgi' rescue Exception # FCGI not available end begin require_library_or_gem 'mongrel' rescue Exception # Mongrel not available end
最终,会通过defined?(Mongrel)和defined?(FCGI)来决定使用哪种服务器。当然,本文前面提到了将使用WEBRick作为web服务器,这里最终加载的服务器当然是webrick。server.rb的最后一句代码:
require "commands/servers/#{server}"
在这里#{server}当然是webrick了,所以,接下来执行的将是webrick.rb
webrick.rb
源代码路径:gems/rails-2.0.2/lib/commands/servers/webrick.rb
webrick.rb完成如下几个主要功能:1.加载Ruby自带的webrick库;2.加载environment.rb;3.加载webrick_server.rb;4.执行DispatchServlet(在webrick_server.rb中定义)的类方法dispatch。整个过程都是顺序完成的,所以,示意的源代码可以如下所示:
require 'webrick' require RAILS_ROOT + "/config/environment" require 'webrick_server' DispatchServlet.dispatch(OPTIONS)
environment.rb
源代码路径:RAILS_ROOT/config/environment.rb
回到了我们熟悉的environment.rb中,在这里我们可以对Rails运行的环境进行配置,这里不做过多阐述,可以参与相关Rails文档。
webrick_server.rb
源代码路径:gems/rails-2.0.2/lib/webrick_server.rb
这是Rails启动所执行的最后一个源代码文件。我前面提到了一点,此源代码文件中定义了DispatchServlet,这是一个自定义的dispatch
servlet,用于将浏览器的请求dispatch到相应的controller,action上。因为这里我只打算介绍Rails的启动,所以,我们只用关注如下的代码即可:
class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet # Start the WEBrick server with the given options, mounting the # DispatchServlet at <tt>/</tt>. def self.dispatch(options = {}) Socket.do_not_reverse_lookup = true # patch for OS X params = { :Port => options[:port].to_i, :ServerType => options[:server_type], :BindAddress => options[:ip] } params[:MimeTypes] = options[:mime_types] if options[:mime_types] server = WEBrick::HTTPServer.new(params) server.mount('/', DispatchServlet, options) trap("INT") { server.shutdown } server.start end end
这是一个经典的启动WEBRick服务器的方式,不用过多阐述,可以参考webrick的相关文档。当然,webrick_server.rb文件中还有一个相当重要的类CGI,并且,DispatchServlet类中还有一些重要的方法,我们暂时可以将他们搁在一旁。以后进一步分析Rails的时候再讲解。 OK。至此,Rails就正式上马了,web服务器也启动起来了,接下来的事情,当然是等着web服务器将request转发给Rails进行处理了,按照国际惯例,这当然是下文分解的事了~
评论
看了这遍文章.让我回忆了一下. 收藏(自己也不用写了)
看了这遍文章.让我回忆了一下. 收藏(自己也不用写了)
我看了的 1 和 2 觉得很晕,特别是Router的代码分析,看上去更象代码复述。其中使用到的设计模式没有一个被提出。一些重要的可扩张方法,比如 Route::recognition_conditions RouteSet::extract_request_environment 竟被简化掉了。。。
不理解楼主的动机,是想要重构Rails还是hack Rails?
让我不禁想起很多年前看过的一本所谓《JavaScript宝典》的恐怖书....
我觉得为什么写出来要有一定的深度呢,在这里记录下来自己的学习过程也很好啊!
有点汗。。。恰如halfmile同学所说的相反,我的原意是:首先将事情简单化。Rails的代码庞大高深,正如你所讲的里面有很多设计模式和重要的扩展方法,但是我开始所想的是,首先不能太照顾每一个细节,比如你所说的,recognition_conditions等重要的方法,我承认他们确实重要,不过,试想,如果我将下面的代码罗列出来:
def recognition_conditions result = ["(match = #{Regexp.new(recognition_pattern).inspect}.match(path))"] result << "conditions[:method] === env[:method]" if conditions[:method] result end
我只怕没有深入研究过Rails的同学们看完会晕,但是如果我详细的讲解一个个内部的细节的话,那估计我写的东西就会变成老太婆的裹脚了。。。
至于我的动机,首先是学习。不过我不是想写Rails内部的API之类的。。。其次,我想先从一个比较高的角度,我认为比较好理解Rails的角度来讲解Rails。让没有读过Rails源代码的同学能从一个宏观的角度稍微了解Rails一些(当然,想要真的了解Rails,只能靠个人深入了)。
其实,我开始的想法就是,先从总体讲起,完了,再具体谈一些细节,比如你所提到的各种细节的模式,方法,应用,技巧等等。
最后,我写这些东西能让你这样读过源代码的同学都感觉到晕,想起当年的“恐怖”,真的有点惭愧了,看来我得好好思考一下自己的写作,总结方式了。感谢你的意见,你对任何东西有任何建议,欢迎来函来电。
我看了的 1 和 2 觉得很晕,特别是Router的代码分析,看上去更象代码复述。其中使用到的设计模式没有一个被提出。一些重要的可扩张方法,比如 Route::recognition_conditions RouteSet::extract_request_environment 竟被简化掉了。。。
不理解楼主的动机,是想要重构Rails还是hack Rails?
让我不禁想起很多年前看过的一本所谓《JavaScript宝典》的恐怖书....
我觉得为什么写出来要有一定的深度呢,在这里记录下来自己的学习过程也很好啊!
和朋友一起玩过思维导图的游戏和练习,个人觉得:
1.从工具的角度来讲,似乎我没发现什么特别方便的mind-map工具,用手划十分不错,但是不好搞上网。
2.思维导图比较适合发散性问题的思考,我觉得我这种情况倒不如一些简单的流程图,类图来得直接。
研究得不多,个人观点而已。
希望能看到LZ源源不断的系列文章。
我觉得为什么写出来要有一定的深度呢,在这里记录下来自己的学习过程也很好啊!
另外,写出来一个重要的目的就是如果各位同学们觉得哪里有问题,可以直接告诉我,这样也让我少走一点转路,还可以分享给大家,又有何不可呢?:)
发表评论
-
Rack Middleware Profile
2009-05-26 21:27 1534Rack是一个高效,简洁的框架(Webserver Int ... -
column_timestamp plugin
2008-10-16 00:00 1056有些时候,我们可能需要记录某些列的更新时间,类似于rail ... -
为Rails中的validation error增加error_code
2008-08-05 22:49 1951各位同学对model中一 ... -
Rails中如何更加优雅的处理文件上传
2008-07-19 22:23 2226通常,在rails中处理文件上传,我们会这么做,在view ... -
慎用typo(theme_support)的换肤机制
2008-07-17 23:29 1809前言 本文提到的typo版本是目前最新的5.0.3 ... -
Ruby中&&操作符的妙用(旁门左道)
2008-07-09 22:30 1799几乎所有的现代编程 ... -
Ruby生成斐波拉契数列
2008-07-09 13:52 1808不管你是用c,c++,c#,java。。。不管你是用循环, ... -
Ruby On Rails-2.0.2源代码分析(4)-寻找Controller
2008-03-24 20:25 2976前言 经过一番试 ... -
Ruby On Rails-2.0.2源代码分析(3)-named route和resource
2008-03-21 00:28 2680前言 在《Routing的载入》中,我大致介绍了一 ... -
netbean调试ActiveSupport::OptionMerger需注意的一个问题
2008-03-18 15:08 1662这两天,在调试Rails ... -
Ruby On Rails-2.0.2源代码分析(2)-Routing的载入
2008-03-16 22:58 3860前言 在前一篇 ...
相关推荐
Ruby on Rails Guides v2 - Ruby on Rails 4.2.5
rails-dev-box, 面向 Ruby on Rails 核心开发的虚拟机 用于 Ruby on Rails 核心开发的虚拟机简介注意:这个虚拟机不是为 Rails 应用程序开发而设计的,只是为。 这个项目自动设置开发环境,以便在 Ruby on Rails ...
A cheatsheet for Ruby on Rails
《Ruby on Rails Tutorial》中文版(原书第2版,涵盖 Rails 4) Ruby 是一门很美的计算机语言,其设计原则就是“让编程人员快乐”。David Heinemeier Hansson 就是看重了这一点,才在开发 Rails 框架时选择了 Ruby...
Ruby on Rails安装,有人说难,但其实也很方便。要基于ruby开发应用程序,我们必须安装ruby、gem、rails、mongrel。
请结合我上次上传的“Ruby中文文档”,学习了解之后,再来实际操作,理解“Ruby On Rails”框架开发web程序,这个是很不错的实例,能够在短期内实现Rails的web开发。
Ubuntu系统ruby on rails安装 Ubuntu系统ruby on rails安装 Ubuntu系统ruby on rails安装 Ubuntu系统ruby on rails安装 Ubuntu系统ruby on rails安装 Ubuntu系统ruby on rails安装 Ubuntu系统ruby on rails安装 ...
Ruby On Rails中文教材(PDF)
Ruby on Rails入门经典代码,非常适合新手学习使用
Ruby on Rails源代码
ruby on rails社区网站开发源码
Ruby on Rails与MongoDB 您可以在MongoDB的帮助下轻松... rails new ruby-on-rails-with-mongodb --skip-active-record从您的Gemfile中删除sqlite3(如果存在),将Mongoid添加到您的Gemfile中,然后运行“ bundle”。
Ruby on Rails入门经典-例子,有很多rails工程实例。
原文是Web版本,已经导出成PDF版本供大家查看。原版是英文版的《Ruby on Rails Tutorial》,特别适合有其他语言开发经验的Rails入门。
ruby_on_rails 开发者实战 源代码上 1-12章 来之不易啊。仅供学习.
Ruby on Rails Web开发学习实录 内容简介: 在目前的主流web开发技术中,基于ruby语言的rails框架是做网站开发速度最快的工具。它可以达到j2ee框架开发速度的5~10倍,并且代码量也非常少。另外由于代码量的大幅度...
本书算是最好的rails的与ruby的教程。。。全书共144M,此为part1,还有part2
ruby on rails对mongodb的操作ruby on rails对mongodb的操作ruby on rails对mongodb的操作ruby on rails对mongodb的操作
Beginning Ruby on rails 源代码