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

  • 前言

在《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

Ruby代码 复制代码
  1. ActionController::Routing::Routes.drawdo|map|
  2. map.purchase'products/:id/purchase',:controller=>'catalog',:action=>'purchase'
  3. ...
  4. end
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方法处理的。具体代码如下:

Ruby代码 复制代码
  1. defmethod_missing(route_name,*args,&proc)#:nodoc:
  2. superunlessargs.length>=1&&proc.nil?
  3. @set.add_named_route(route_name,*args)
  4. end
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方法。

Ruby代码 复制代码
  1. defadd_named_route(name,path,options={})
  2. #TODO-isoptionsEVERused?
  3. name=options[:name_prefix]+name.to_sifoptions[:name_prefix]
  4. named_routes[name.to_sym]=add_route(path,options)
  5. end
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中有如下定义:

Ruby代码 复制代码
  1. defadd(name,route)
  2. routes[name.to_sym]=route
  3. define_named_route_methods(name,route)
  4. end
  5. defget(name)
  6. routes[name.to_sym]
  7. end
  8. alias[]=add
  9. alias[]get
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方法。

Ruby代码 复制代码
  1. defdefine_named_route_methods(name,route)
  2. {:url=>{:only_path=>false},:path=>{:only_path=>true}}.eachdo|kind,opts|
  3. hash=route.defaults.merge(:use_route=>name).merge(opts)
  4. define_hash_accessroute,name,kind,hash
  5. define_url_helperroute,name,kind,hash
  6. end
  7. end
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强大的动态特性。具体实现分别如下:

Ruby代码 复制代码
  1. defdefine_hash_access(route,name,kind,options)
  2. selector=hash_access_name(name,kind)
  3. @module.module_eval<<-end_eval#Weusemodule_evaltoavoidleaks
  4. def#{selector}(options=nil)
  5. options?#{options.inspect}.merge(options):#{options.inspect}
  6. end
  7. protected:#{selector}
  8. end_eval
  9. helpers<<selector
  10. end
  11. defdefine_url_helper(route,name,kind,options)
  12. selector=url_helper_name(name,kind)
  13. #Thesegmentkeysusedforpositionalparamters
  14. hash_access_method=hash_access_name(name,kind)
  15. @module.module_eval<<-end_eval#Weusemodule_evaltoavoidleaks
  16. def#{selector}(*args)
  17. #{generate_optimisation_block(route,kind)}
  18. opts=ifargs.empty?||Hash===args.first
  19. args.first||{}
  20. else
  21. options=args.last.is_a?(Hash)?args.pop:{}
  22. args=args.zip(#{route.segment_keys.inspect}).inject({})do|h,(v,k)|
  23. h[k]=v
  24. h
  25. end
  26. options.merge(args)
  27. end
  28. url_for(#{hash_access_method}(opts))
  29. end
  30. protected:#{selector}
  31. end_eval
  32. helpers<<selector
  33. end
 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的加载就全部完成了。十分简单,不是吗?

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值