Rails源码学习(一)

本文深入探讨了使用Rails框架快速搭建Web应用的过程。从执行Rails命令开始,逐步解析RubyGems的作用、Rails版本的加载机制及如何通过Generator自动生成应用程序的基本结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Ruby是一门神奇的语言,里面有很灵活的语法和很多令人惊奇的特性。Rails这个时下异常流行的web开发框架将Ruby语言的特性与优势很好的发挥出来。最近用Rails开发了几个小程序,发现其开发效率真是很高,同时也应了Ruby语言的哲学:快乐编程。
要想很好的了解和使用Rails,最好的方法莫过于读它的源码,最近正好在学习Rails的源码,把这个过程中的一些心得以及遇到的一些问题记录下来。在这个学习过程中,我使用的ruby 版本是1.8.6,使用的Rails版本是2.3.2。
在使用Rails的时候,创建一个app是很简单的,一个命令’rails app_name’就能生成一个程序的基本框架。这是很神奇的事情,但是这是怎么做到的呢?先看Rails命令的执行过程,在Ruby安装目录的bin文件夹下在此文中使用{RUBY_HOME}表示Ruby的安装目录,可以看到rails命令,用文本编辑器打开:

#!c:/ruby/bin/ruby
#
# This file was generated by RubyGems.
#
# The application 'rails' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'
version = "> 0"
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
version = $1
ARGV.shift
end
gem 'rails', version
load 'rails'

先解释一下这这个文件中的内容,#开头的注释不多说:
1、require‘rubygems’,这句话说明需要加载rubygems.rb文件,那么这个文件在什么地方呢?这边需要了解require加载文件时候的查找路径都有哪些,ruby中也有类似java classpath的一个东西叫做$LOAD_PATH,存储在$:中,require加载后面的文件就是从这个变量中的路径开始。打开命令行窗口,输入ruby –e “puts $:”或者 ruby –e ”puts $LOAD_PATH”可以查看你系统中的ruby的loadpath。

C:\Documents and Settings\Administrator>ruby -e "puts $:"
c:/ruby/lib/ruby/site_ruby/1.8
c:/ruby/lib/ruby/site_ruby/1.8/i386-msvcrt
c:/ruby/lib/ruby/site_ruby
c:/ruby/lib/ruby/1.8
c:/ruby/lib/ruby/1.8/i386-mswin32
.

那么require ’rubygems’它就会去上面列出的几个路径中查找一个文件名为rubygems.rb,不难发现在{RUBY_HOME}/lib/ruby/site_ruby/1.8下面有这个文件。
2、version = "> 0",这句话说明加载gem的默认版本
3、这一段if可以接收用户输入gem的版本,如果不输入,则默认加载最新的版本。
4、gem ‘rails’,version调用的rubygems.rb中的方法,方法定义如下,这个方法的作用是激活满足版本条件的rails
{RUBY_HOME}/lib/ruby/site_ruby/1.8/rubygems.rb

def gem(gem_name, *version_requirements) # :doc:
skip_list = (ENV['GEM_SKIP'] || "").split(/:/)
raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name
Gem.activate(gem_name, *version_requirements)
end

调用的用来加载gem安装的gem_name相关的文件,这个方法的定义也在rubygems.rb中,这个方法有点长,前面是检查version,后面是动态插入gem_name相关的bin_path到$LOAD_PATH中,以便后面使用。
5、到最后一句load ‘rails’了,这句话其实跟reqiure的功能一样。只不过是每次都重新加载rails文件,这个‘rails’文件也是从$LOAD_PATH中查找,在上一点说过Gem.activate(gem_name, *version_requirements)这个方法最后动态插入了gem_name相关的bin_path到$LOAD_PATH中,这个bin_path就包含了我们要找的’rails’。我们可以编辑rails命令的文件为如下:

#!c:/ruby/bin/ruby
#
# This file was generated by RubyGems.
#
# The application 'rails' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'
version = "> 0"
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
version = $1
ARGV.shift
end
gem 'rails', version
puts Gem.bin_path('rails','rails',version) #打印出rails的 bin路径[color=red][/color]
load 'rails'

这样从新在命令行下输入rails,可以看见会先打印出rails所在的bin路径。也就是load ‘rails’所找的位置。我电脑中显示出来的是:c:/ruby/lib/ruby/gems/1.8/gems/rails-2.3.2/bin/rails.
接下去我们转到c:/ruby/lib/ruby/gems/1.8/gems/rails-2.3.2/bin/rails路径下的rails文件,用文本编辑器打开:

require File.dirname(__FILE__) + '/../lib/ruby_version_check'
Signal.trap("INT") { puts; exit }

require File.dirname(__FILE__) + '/../lib/rails/version'
if %w(--version -v).include? ARGV.first
puts "Rails #{Rails::VERSION::STRING}"
exit(0)
end

freeze = ARGV.any? { |option| %w(--freeze -f).include?(option) }

app_path = ARGV.first

require File.dirname(__FILE__) + '/../lib/rails_generator'

require 'rails_generator/scripts/generate'
Rails::Generator::Base.use_application_sources!
Rails::Generator::Scripts::Generate.new.run(ARGV, :generator => 'app')

Dir.chdir(app_path) { `rake rails:freeze:gems`; puts "froze" } if freeze

1、进行ruby的版本检查
2、信号量相关,在后面的链接中有信号量的相关说明不过不知道在这个地方起了什么做用?([url]http://www.kuqin.com/rubycndocument/man/built-in-class/module_signal.html[/url])
3、载入rails的版本信息,接下去的if是如果用户输入的命令中带-v或—version的话就显示出rails的版本。
4、Freeze是冻结你的rails版本,可以防止你的rails自动升级而导致rails应用程序出现一些兼容性的问题。如果你输入了-f或者—freeze,在最后会执行命令:rake rails:freeze:gems,会把rails单独拷贝到你的 vender下,而rails启动的时候先看这个目录,如果有就加载,没有的话再加载系统的。
5、后面两句Rails::Generator开头的是调用generator的地方,第一句调用rails-2.3.2\lib\rails_generator\look_up.rb中的use_application_sources!方法,

# Use application generators (app, ?).
def use_application_sources!
reset_sources
sources << PathSource.new(:builtin,"#{File.dirname(__FILE__)}/generators/applications")
end

使用PathSource定位到rails-2.3.2\lib\rails_generator\generators\applications文件夹,第二句调用rails-2.3.2\lib\rails_generator\scripts.rb中的run方法。

# Run the generator script. Takes an array of unparsed arguments
# and a hash of parsed arguments, takes the generator as an option
# or first remaining argument, and invokes the requested command.
def run(args = [], runtime_options = {})
begin
parse!(args.dup, runtime_options)
rescue OptionParser::InvalidOption => e
# Don't cry, script. Generators want what you think is invalid.
end

# Generator name is the only required option.
unless options[:generator]
usage if args.empty?
options[:generator] ||= args.shift
end

# Look up generator instance and invoke command on it.
Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke!
rescue => e
puts e
puts " #{e.backtrace.join("\n ")}\n" if options[:backtrace]
raise SystemExit
end

这个run方法会先解析ARGV中的参数,然后查找对应的generator,生成对应generator的实例,不同的generator有对应的templates。在这个地方调用的是app_generator.rb,生成整个应用程序的框架。

最后对上面的整个过程做一下整理,调用rails命令,生成程序框架的大致过程为:
(1)执行ruby\bin\rails命令--->(2)ruby,RubyGems,rails的版本检查--->(3)加载gem目录下rails安装路径的bin目录下的rails文件--->(4)解析命令行参数,调用对应的generator生成应用程序框架

PS:关于freeze: Ruby on Rails允许你freeze你应用程序所使用的Rails和相关Ruby Gems版本。这样不管你server上安装的Rails版本是多少,你的应用程序始终使用你所指定的版本。 当你的应用程序是用于商业或者是对稳定性有很高要求的时候,很有必要freeze你的rails,如果不freeze rails的话,有可能在升级了新版本的rails之后,导致你的产品由于兼容性问题而无法正常运行。然而,当你freeze了你的rails,那么你将不能得到rails自动更新带来的方便,你要时刻自己跟踪rails的最新版本,并且在需要的时候自己升级。后面这个链接中有freeze的详细信息。([url]http://support.tigertech.net/freeze-rails[/url])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值