Ruby On Rails-2.0.2源代码分析(3)-resource

  • RESTful的化身----resource

  当然,光把RESTful和resource扯到一起似乎相当狭义,在Rails中,ActionController::Resources抽象了REST中的Resource,这里,我不谈REST的相关概念,网上资料一大坨。我们就来看看Rails中是如何通过Resource来轻松,简便的完成RESTful应用的吧。

  resources.rb

  源代码路径:/actionpack-2.0.2/lib/action_controller/resources.rb
  首先,我们也不需要将resource看得多么的高深,你可以把他理解为,当你在routes.rb中定义如下的resource的时候:

map.resources :products
  Rails会自动为我们生成众多的named route,这些route通过http verb和相应的controller中的action对应起来,当然了,众多的helper方法也随即产生。如下表所示:

Named Route Helpers
product
product_url, hash_for_product_url,
product_path, hash_for_product_path
new_product
new_product_url, hash_for_new_product_url,
new_product_path, hash_for_new_product_path
edit_product
edit_product_url, hash_for_edit_product_url,
edit_product_path, hash_for_edit_product_path
......

  从这个角度来想,你可以把resource想成是众多相关named route的一个马甲。
  
  整个流程比较的直观,Rails通过resource按部就班的完成各种route的生成,接下来我们看一看核心代码是如何完成这些功能的。首先,还是在routes.rb中,可能会定义如下的resource:

Ruby代码 复制代码
  1. ActionController::Routing::Routes.draw do |map|   
  2.   
  3.   map.resources :products  
  4.   ...   
  5. end  
ActionController::Routing::Routes.draw do |map|

  map.resources :products
  ...
end


  resources方法定义在ActionController::Resources这个module中,然后通过mixin进入到Mapper类的。那我们首先来看一看这个方法:

Ruby代码 复制代码
  1. def resources(*entities, &block)   
  2.   options = entities.extract_options!   
  3.   entities.each { |entity| map_resource(entity, options.dup, &block) }   
  4. end  
def resources(*entities, &block)
  options = entities.extract_options!
  entities.each { |entity| map_resource(entity, options.dup, &block) }
end


  很简单,将entities和options从参数中分离开来,然后针对每一个entity执行map_resource操作。我们继续进行,看看map_resource方法的真面目:

 

Ruby代码 复制代码
  1. def map_resource(entities, options = {}, &block)   
  2.   resource = Resource.new(entities, options)   
  3.   
  4.   with_options :controller => resource.controller do |map|   
  5.     map_collection_actions(map, resource)   
  6.     map_default_collection_actions(map, resource)   
  7.     map_new_actions(map, resource)   
  8.     map_member_actions(map, resource)   
  9.   
  10.     map_associations(resource, options)   
  11.   
  12.     if block_given?   
  13.       with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], &block)   
  14.     end  
  15.   end  
  16. end  
def map_resource(entities, options = {}, &block)
  resource = Resource.new(entities, options)

  with_options :controller => resource.controller do |map|
    map_collection_actions(map, resource)
    map_default_collection_actions(map, resource)
    map_new_actions(map, resource)
    map_member_actions(map, resource)

    map_associations(resource, options)

    if block_given?
      with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], &block)
    end
  end
end


  有了entity和options,还等什么呢?马上生成我们的Resource对象,Resource对象封装了和此resource相关的collection method,member method,new method,path prefix,name prefix,单/复数表示,还有option。生成这个Resource对象无非就是将此对象的相应属性从options中解析出来,保存起来,代码比较简单,这里就不再贴出。
  现在,Resource对象有了,从上面代码我们就可以看出来,接下来,就该处理和此resource相关named route了。具体的处理逻辑都类似,这里将map_member_actions(map, resource)拿出来作为示意,感兴趣的同学们可以自己查看相关的源代码。

 

Ruby代码 复制代码
  1. def map_member_actions(map, resource)   
  2.   resource.member_methods.each do |method, actions|   
  3.     actions.each do |action|   
  4.       action_options = action_options_for(action, resource, method)   
  5.       map.named_route("#{action}_#{resource.name_prefix}#{resource.singular}""#{resource.member_path}#{resource.action_separator}#{action}", action_options)   
  6.       map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.singular}""#{resource.member_path}#{resource.action_separator}#{action}.:format",action_options)   
  7.     end  
  8.   end  
  9.   
  10.   show_action_options = action_options_for("show", resource)   
  11.   map.named_route("#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options)   
  12.   map.named_route("formatted_#{resource.name_prefix}#{resource.singular}""#{resource.member_path}.:format", show_action_options)   
  13.   
  14.   update_action_options = action_options_for("update", resource)   
  15.   map.connect(resource.member_path, update_action_options)   
  16.   map.connect("#{resource.member_path}.:format", update_action_options)   
  17.   
  18.   destroy_action_options = action_options_for("destroy", resource)   
  19.   map.connect(resource.member_path, destroy_action_options)   
  20.   map.connect("#{resource.member_path}.:format", destroy_action_options)   
  21. end  
def map_member_actions(map, resource)
  resource.member_methods.each do |method, actions|
    actions.each do |action|
      action_options = action_options_for(action, resource, method)
      map.named_route("#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action}", action_options)
      map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action}.:format",action_options)
    end
  end

  show_action_options = action_options_for("show", resource)
  map.named_route("#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options)
  map.named_route("formatted_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", show_action_options)

  update_action_options = action_options_for("update", resource)
  map.connect(resource.member_path, update_action_options)
  map.connect("#{resource.member_path}.:format", update_action_options)

  destroy_action_options = action_options_for("destroy", resource)
  map.connect(resource.member_path, destroy_action_options)
  map.connect("#{resource.member_path}.:format", destroy_action_options)
end


  这里,我们可以很直观的看到,Rails为resource的member相关方法生成了众多的route,我们可以看到Controller中熟悉的show,update,destroy action。是的,在这里,Rails就为url到controller的action生成了相应的route。

 

本文转自:http://woody-420420.javaeye.com/blog/174352

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值