`
woody_420420
  • 浏览: 42090 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

阅读更多
  • 前言

  在《Routing的载入》中,我大致介绍了一下Rails中最简单的route是如何加载的。这篇文章,我将来讲一讲Rails系统中更为复杂的named route和与RESTful相关的resource是如何被加载的。为了不重复太多的笔墨,这篇文章将在前文的基础上进行,如果发现单独看此文时,有少许云里雾里,建议先看一看我的前篇文章:Ruby On Rails-2.0.2源代码分析(2)-Routing的载入

  • 进化的routing-named route

  首先,named route的载入全部发生在routing.rb中。其实named route一点也不比普通的route高深些什么,Rails内部最终也是将named route解析为一个普通的route保存在RouseSet类的routes数组中(还记得这家伙么?最好牢牢记住他,因为,他还会在后续文章中继续登台发挥重要作用),之所以我称他进化,是因为named route既然提供了name,在Rails内部,将会生成一系列的helper方法,当我们在controller或者view中使用link_to,redirect_to等方法时,不需要指定相应的controller和action,从而简化我们的代码,不用多了,先来看一看我们所熟悉的routes.rb

ActionController::Routing::Routes.draw do |map|
  map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
  ...
end


  这里,我定义了一个purchase的named route(当然,你完全可以使用connect方法定义普通的route)。前一篇文章提到过,block中的map对象是Mapper类的实例,其实你可以想象到,其实Mapper类并没有定义purchase方法(天知道你要给你的named route起啥名?翠花?旺财?)。所有的一切,都是通过Mapper类的method_missing方法处理的。具体代码如下:

def method_missing(route_name, *args, &proc) #:nodoc:
  super unless args.length >= 1 && proc.nil?
  @set.add_named_route(route_name, *args)
end


  如果你记性还好,应该还记得@set对象是一个RouteSet类的实例,所以这里Mapper类将这个route的名称,还有所有的参数都传递到了RouteSet类的add_named_route方法。

def add_named_route(name, path, options = {})
  # TODO - is options EVER used?
  name = options[:name_prefix] + name.to_s if options[:name_prefix]
  named_routes[name.to_sym] = add_route(path, options)
end


  这里,看到我们前面已经熟悉过了的add_route方法了吧?对此方法不用再过多解释,Rails将生成一个普通的route,保存在RouteSet的routes数组中,并将这个route返回,赋给named_routes对象,此对象是NamedRouteCollection类的一个实例。在NamedRouteCollection中有如下定义:

 

def add(name, route)
  routes[name.to_sym] = route
  define_named_route_methods(name, route)
end

def get(name)
  routes[name.to_sym]
end

alias []=   add
alias []    get


  所以,接下来似乎应该关心下add方法了。这里,首先将此普通的route保存在NamedRouteCollection类的routes哈希中(注意和RouteSet的routes数组区分开来)。然后,named route开始其“进化”了----通过define_named_route_methods方法生成自己的一系列helper方法。

def define_named_route_methods(name, route)
  {:url => {:only_path => false}, :path => {:only_path => true}}.each do |kind, opts|
    hash = route.defaults.merge(:use_route => name).merge(opts)
    define_hash_access route, name, kind, hash
    define_url_helper route, name, kind, hash
  end
end


  或者你已经知道了,named route有name_url,和name_path两类helper方法,从上面这段代码中,我们能看到真正的实现。Rails这里为url和path分别生成hash access和url helper方法,其实现都利用了ruby强大的动态特性。具体实现分别如下:

 

 def define_hash_access(route, name, kind, options)
      selector = hash_access_name(name, kind)
      @module.module_eval <<-end_eval # We use module_eval to avoid leaks
        def #{selector}(options = nil)
          options ? #{options.inspect}.merge(options) : #{options.inspect}
        end
        protected :#{selector}
      end_eval
      helpers << selector
    end

    def define_url_helper(route, name, kind, options)
      selector = url_helper_name(name, kind)
      # The segment keys used for positional paramters

      hash_access_method = hash_access_name(name, kind)
      @module.module_eval <<-end_eval # We use module_eval to avoid leaks
        def #{selector}(*args)
          #{generate_optimisation_block(route, kind)}

          opts = if args.empty? || Hash === args.first
            args.first || {}
          else
            options = args.last.is_a?(Hash) ? args.pop : {}
            args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)|
              h[k] = v
              h
            end
            options.merge(args)
          end

          url_for(#{hash_access_method}(opts))
        end
        protected :#{selector}
      end_eval
      helpers << selector
    end


  其中@module是NamedRouteCollection类中的一个匿名module,他通过module_eval方法动态的增加了这一系列的helper方法,并且将方法名保存在helpers数组当中,以供其后controller或者view的使用(link_to, redirect_to)。至此,named route的加载就全部完成了。十分简单,不是吗?

  • 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转换的。
 
  整个流程比较的直观,Rails通过resource按部就班的完成各种route的生成,接下来我们看一看核心代码是如何完成这些功能的。首先,还是在routes.rb中,可能会定义如下的resource:

ActionController::Routing::Routes.draw do |map|

  map.resources :products
  ...
end


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

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方法的真面目:

 

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)拿出来作为示意,感兴趣的同学们可以自己查看相关的源代码。

 

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。完成了这些基本的route生成外,Rails还会处理嵌套,关联关系(存在block的处理和map_associations)。当然,基本逻辑都和上面的逻辑类似。
  如此云云。。。针对Resource的一张庞大的route表就生成完成了。

分享到:
评论
1 楼 lzjyrg 2008-06-28  
精深!RAILS还真是精深阿

相关推荐

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

    Ruby on Rails 安装指南是指安装 Ruby 1.8.6 和 Rails 2.0.2 的详细步骤。首先,需要下载 Ruby One-Click Installer 版本,并安装 Ruby。然后,下载 Rails 2.0.2 版本,并安装。接下来,需要安装 Mongrel 服务器。...

    ruby on rails源代码分析

    在本文中,我们将深入分析 Ruby on Rails 2.0.2 的源代码,特别是关注其启动过程。 首先,Rails 的启动始于 `config/boot.rb` 文件。这个文件是 Rails 应用程序的入口点,负责检查 Rails 是否已启动。如果没有,它...

    Ruby On Rails-Cheatsheet

    ### Ruby on Rails Cheat Sheet 本篇文章将从给定的文件中提炼出关于Ruby on Rails的重要知识点,主要包括命令、URL映射、命名规范、ERB标签、链接创建、数据库配置及查询、模型之间的关系等方面。 #### Ruby on ...

    行为驱动设计: Professional Ruby on Rails - Chapter 7

    3. **创建步骤定义(Step Definitions)**:在Ruby代码中为Gherkin步骤编写对应的实现,将自然语言转化为可执行的代码。 4. **运行测试**:执行特性文件,验证系统是否按预期工作。BDD工具如Cucumber会自动匹配并...

    Ruby on Rails入门经典代码

    通过学习和实践压缩包中的"Ruby on Rails入门经典代码",新手不仅可以了解Rails的基本概念,还能掌握实际项目中的应用技巧,逐步成长为一名熟练的Rails开发者。记得不断探索、实践和学习新的Rails知识,以适应不断...

    Ruby on Rails Guides v2 - Ruby on Rails 4.2.5

    ### Ruby on Rails Guides v2 - Ruby on Rails 4.2.5 #### 一、重要概念及基础假设 - **重要概念**:本指南旨在帮助读者深入理解Ruby on Rails(以下简称Rails)4.2.5版本的核心功能与最佳实践。 - **基础假设**:...

    Ruby on Rails安装包全集(Linux)

    这里的是FastCGI的源代码包,它允许Ruby on Rails应用通过FastCGI协议与Lighttpd交互。 5. **ruby-zlib-0.6.0.tar.gz**: Zlib是用于数据压缩的库,Ruby的内置标准库中包含了对Zlib的支持。此包可能用于Ruby与Gzip等...

    ruby1.8.6 + rails2.0.2 安装配置 详细说明

    在本文中,我们将深入探讨如何在您的计算机上安装和配置Ruby 1.8.6、Rails 2.0.2、RadRails 0.7.2 IDE以及MySQL数据库。这是一个适用于初学者和有一定经验的开发者的技术指南,旨在帮助您创建一个稳定的开发环境,...

    Ruby On Rails开发实例-源代码

    Ruby on Rails(简称RoR或Rails)是一种基于Ruby语言的开源Web应用框架,它遵循Model-View-Controller(MVC)架构模式,旨在使Web开发更简洁、高效。本实例将帮助你深入理解和实践Rails的开发流程。 首先,让我们从...

    Ruby-on-Rails-3.rar_site:www.pudn.com

    《Ruby-on-Rails-3.rar》是一个在Pudn网站上分享的压缩文件,主要针对的是希望通过Ruby语言进行Web开发的初级程序员。该资源的核心内容是《Web开发敏捷之道 - 应用Rails进行敏捷Web开发 - 第三版》这本书的PDF版本。...

    rails-dev-box, 面向 Ruby on Rails 核心开发的虚拟机.zip

    rails-dev-box, 面向 Ruby on Rails 核心开发的虚拟机 用于 Ruby on Rails 核心开发的虚拟机简介注意:这个虚拟机不是为 Rails 应用程序开发而设计的,只是为。 这个项目自动设置开发环境,以便在 Ruby on Rails ...

    Ruby-on-Rails-rails.zip

    Ruby_on_Rails_rails.zip Ruby_on_Rails_rails.zip Ruby_on_Rails_rails.zip Ruby_on_Rails_rails.zipRuby_on_Rails_rails.zip Ruby_on_Rails_rails.zip Ruby_on_Rails_rails.zip Ruby_on_Rails_rails.zipRuby_on_...

    Ruby on Rails 教程 - 201406

    本教程“Ruby on Rails 教程 - 201406”可能是针对2014年6月时的Rails版本,那时候Rails正处于3.x或4.x系列,虽然现在Rails已经发展到6.x版本,但基础概念和核心原则依然适用。 在Rails中,Model负责处理数据和业务...

    ruby on rails 101

    ### Ruby on Rails 101:深入理解与实践 #### 引言 《Ruby on Rails 101》是一本介绍Ruby on Rails(简称RoR或ROR)的基础书籍,旨在为初学者提供一个全面而深入的学习框架。本书由Peter Marklund编写,包含了五天...

    Ruby on Rails Tutorial

    在这个全球互联的世界中,计算机编程和 Web 应用程序开发都在迅猛发展,我很期待能为中国的开发者提供 Ruby on Rails 培训。学习英语这门世界语言是很重要的,但先通过母语学习往往会更有效果。正因为这样,当看到 ...

    Beginning Ruby on rails 源代码

    本资料包包含了《Beginning Ruby on Rails》一书的源代码,供读者学习和实践使用。 在深入探讨源代码之前,我们先来了解一下Ruby on Rails的核心特点和基础知识: 1. **DRY(Don't Repeat Yourself)原则**:Rails...

    ruby on rails最新版

    Ruby on Rails,简称Rails,是基于Ruby编程语言的一个开源Web应用程序框架,它遵循MVC(模型-视图-控制器)架构模式,旨在提高开发效率和代码的可读性。Rails以其“约定优于配置”(Convention over Configuration)...

Global site tag (gtag.js) - Google Analytics