rails 原码分析,理顺则好用

一直专业写rails程序,很多东西也没有理清,不通则不快。趁过年前有时间,剖析一下!系统化程度越高,构建才会快,质量高。开始:

动机: 一直不太明白ActiveController和ActiveView的关系,即两者的上下文关系。

对老系统的Rails 2.1.2 剖析,3.0.0改动太大了,下次再仔细研究。

1. 从CGI 的dispatch 一路dispatch过来,到AC(ActiveController)

def handle_request
@controller = Routing::Routes.recognize(@request)
@controller.process(@request, @response).out(@output)
end

从url中把路由取出来,很自然,找到对应的controller,如HomeController,但还不是实例,类型是Class。

2. controller来process

class << self
# Factory for the standard create, process loop where the controller is discarded after processing.
def process(request, response) #:nodoc:
new.process(request, response)
end
...
end

这里的new很有意思,把自己new出来然后出处理。下面几行代码是ruby的模块与类的魔法

module ActionController #:nodoc:
module SessionManagement #:nodoc:
def self.included(base)
base.class_eval do
extend ClassMethods
alias_method_chain :process, :session_management_support
alias_method_chain :process_cleanup, :session_management_support
end
end

include(base)。 再向base注入Module方法:seesion_mangement_support

上面把session的管理option放入request

3. 进入Filter Module的before_filter

def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc:
@before_filter_chain_aborted = false
process_without_filters(request, response, method, *arguments)
end

基本上rails的一个特性就有一个Module对应,这里就是filter这个module

4. 关键:进入AR::Base的process

def process(request, response, method = :perform_action, *arguments) #:nodoc:
initialize_template_class(response)
assign_shortcuts(request, response)
initialize_current_url
assign_names
forget_variables_added_to_assigns

log_processing
send(method, *arguments)

assign_default_content_type_and_charset

response.request = request
response.prepare! unless component_request?
response
ensure
process_cleanup
end

initialize_template_class(response) 非常关键把 AV(ActiveView)new出来了,开始了AC与AV的关系!这个view名字为template

assign_shortcuts(request, response)
initialize_current_url
assign_names
forget_variables_added_to_assigns
都是内部变量记录,无实质性内容!

5. send(method, *arguments) 进入perform_ation

send(action_name)就进到了客户的HomeController里,动态语言的好处就是不断的send,不用做各种工厂!


def perform_action
if self.class.action_methods.include?(action_name)
send(action_name)
default_render unless performed?
elsif respond_to? :method_missing
method_missing action_name
default_render unless performed?
elsif template_exists? && template_public?
default_render
else
raise UnknownAction, "No action responded to #{action_name}", caller
end
end

6. 在自己的action里,调用 respond_to

def respond_to(*types, &block)
raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block
block ||= lambda { |responder| types.each { |type| responder.send(type) } }
responder = Responder.new(self)
block.call(responder)
responder.respond
end

之前一直觉得respond_to方法名字和后的block不是很雅,也不知那哪里跑出个这么的妖怪

都是用以下的方法mixin类方法进来的

module MimeResponds #:nodoc:
def self.included(base)
base.module_eval do
include ActionController::MimeResponds::InstanceMethods
end
end

module InstanceMethods

7. 走自己controller的repond_to后面的块

respond_to do |format|
format.html {render :template=>"home/template"}
end

有时这个块会写两层,其它很不方便的

respond_to do |format|
format.js{ render :update do |page|
page.replace_html "{$1 div_id}", :partial=>"", :object=>@
end}
end


8. 进入AC的render

else
if file = options[:file]
render_for_file(file, options[:status], options[:use_full_path], options[:locals] || {})

elsif template = options[:template]
render_for_file(template, options[:status], true, options[:locals] || {})


9. 关键:进入AV的rend_file
@template.render_file最后new出一个template

在AV里有一个Template
Template.new(self, template_path, use_full_path, local_assigns).render_template

10. 关键:进入Template的render_template
module ActionView #:nodoc:
class Template #:nodoc:


template里有一个handler指是是什么类型的模板,如:ActionView::TemplateHandlers::ERB

def render
prepare!
@handler.render(self)
end

prepare!是一些准备工作,hander里有个compile_template,不是很明白是做什么的。ms是在template做一些求值。接着再转回AR::base

@hander是要Render的类型,是ebd,还是haml。

11. 关键:进入TemplateHandlers的render

@view.send :execute, template

又回到 AV

def execute(template)
send(template.method, template.locals) do |*names|
instance_variable_get "@content_for_#{names.first || 'layout'}"
end
end

就进去我们自己的erb文件了


此时 method 为_run_erb_47app47views47home47template46html46erb

显然,47是/折算过来的,这个方法也不知道是什么时候产生的。

个人觉得很牛,很神,把一个erb文件当ruby文件来跑。

总之呢,动态的把我们的erb文件变成了一个方法,send一下就去到erb文件里做计算<%= %> 输出了。

=========================

总结:

AR与AV的关系好像是管家与工人,前者是一个请求的生开始到最后完成,完成的全过程。

AV则是与render一起,其中两个可定制的地方,一个是不同类型的请求,如js, xml, json可以不同的resonder。AV里一个template,可以不同的hander,如对付erb的,和其它的。

AV生命周期更短一些,是管家的render工人,做完了更早被boss fire掉!

AR和AV都有自己的render,很容易搞混!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值