原创作者: hideto
阅读:1655次
评论:1条
更新时间:2011-05-26
满城尽带黄金甲,源码尽在routing.rb:
该文件就是大名鼎鼎的RAILS_ROOT/config/routes.rb文件里的程序逻辑的源代码出处
让我们看看ActionController::Routing::Routes.draw do |map|到底是怎样工作的:
1) Routes = RouteSet.new,draw方法当然也是调用的RouteSet对象的draw方法
2) draw方法则yield Mapper.new(self),即RouteSet将方法调用继续传递给它的内部类Mapper
3) Mapper类定义了两个方法: connect和method_missing
connect就是我们在传说中的routes.rb文件中用来定义URL routing的map.connect
method_missing则调用了@set.add_named_route,即RouteSet类的add_named_route方法
4)RouteSet定义了routes和named_routes这两个实例变量,分别用来存放connect route和named_route
5)add_named_route方法中调用了add_route方法中的RouteBuilder.build方法来构造一个route
而NamedRouteCollection定义了add方法,其中调用了define_named_route_methods -> define_url_helper
其中定义了#{route_name}_url和#{route_name}_path方法,而且使用了UrlWriter模块的url_for方法
我们还要注意几点:
1,routes有priority,routes.rb文件里从上到下routing规则的priority为从高到低
2,map.root方法在Rails 2.0中用来作为map.connect ''的shortcut,实际上是@set.add_named_route("root", *args),我们可以使用root_url和root_path方法
3,将*path这种route rule放在routes.rb文件的最后,用来做Route globbing,即前面的routes规则都没匹配时,使用该规则来匹配,如:
module ActionController module Routing class Route attr_accessor :segments, :requirements, :conditions end class RouteBuilder def build(path, options) path = "/#{path}" unless path[0] == ?/ path = "#{path}/" unless path[-1] == ?/ segments = segments_for_route_path(path) defaults, requirements, conditions = divide_route_options(segments, options) requirements = assign_route_options(segments, defaults, requirements) route = Route.new route.segments = segments route.requirements = requirements route.conditions = conditions if !route.significant_keys.include?(:action) && !route.requirements[:action] route.requirements[:action] = "index" route.significant_keys << :action end if !route.significant_keys.include?(:controller) raise ArgumentError, "Illegal route: the :controller must be specified!" end route end end class RouteSet class Mapper def initialize(set) @set = set end def connect(path, options = {}) @set.add_route(path, options) end def root(*args, &proc) super unless args.length >= 1 && proc.nil? @set.add_named_route("root", *args) end def method_missing(route_name, *args, &proc) super unless args.length >= 1 && proc.nil? @set.add_named_route(route_name, *args) end end class NamedRouteCollection include Enumerable attr_reader :routes, :helpers def initialize clear! end def clear! @routes = {} @helpers = [] @module ||= Module.new @module.instance_methods.each do |selector| @module.send :remove_method, selector end end def add(name, route) routes[name.to_sym] = route define_named_route_methods(name, route) end def install(destinations = [ActionController::Base, ActionView::Base]) Array(destinations).each { |dest| dest.send :include, @module } end private 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 def define_url_helper(route, name, kind, options) selector = url_helper_name(name, kind) segment_keys = route.segments.collect do |segment| segment.key if segment.respond_to? :key end.compact hash_access_method = hash_access_name(name, kind) @module.send :module_eval, <<-end_eval def #{selector}(*args) opts = if args.empty? || Hash === args.first args.first || {} else args.zip(#{segment_keys.inspect}).inject({}) do |h, (v, k)| h[k] = v h end end url_for(#{hash_access_method}(opts)) end end_eval @module.send(:protected, selector) helpers << selector end def url_helper_name(name, kind = :url) :"#{name}_#{kind}" end end attr_accessor :routes, :named_routes def initialize self.routes = [] self.named_routes = NamedRouteCollection.new end def builder @builder ||= RouteBuilder.new end def draw clear! yield Mapper.new(self) named_routes.install end def clear! routes.clear named_routes.clear @combined_regexp = nil @routes_by_controller = nil end def add_route(path, options = {}) route = builder.build(path, options) routes << route route end def add_named_route(name, path, options = {}) named_routes[name] = add_route(path, options) end end Routes = RouteSet.new end end
该文件就是大名鼎鼎的RAILS_ROOT/config/routes.rb文件里的程序逻辑的源代码出处
让我们看看ActionController::Routing::Routes.draw do |map|到底是怎样工作的:
1) Routes = RouteSet.new,draw方法当然也是调用的RouteSet对象的draw方法
2) draw方法则yield Mapper.new(self),即RouteSet将方法调用继续传递给它的内部类Mapper
3) Mapper类定义了两个方法: connect和method_missing
connect就是我们在传说中的routes.rb文件中用来定义URL routing的map.connect
method_missing则调用了@set.add_named_route,即RouteSet类的add_named_route方法
4)RouteSet定义了routes和named_routes这两个实例变量,分别用来存放connect route和named_route
5)add_named_route方法中调用了add_route方法中的RouteBuilder.build方法来构造一个route
而NamedRouteCollection定义了add方法,其中调用了define_named_route_methods -> define_url_helper
其中定义了#{route_name}_url和#{route_name}_path方法,而且使用了UrlWriter模块的url_for方法
我们还要注意几点:
1,routes有priority,routes.rb文件里从上到下routing规则的priority为从高到低
2,map.root方法在Rails 2.0中用来作为map.connect ''的shortcut,实际上是@set.add_named_route("root", *args),我们可以使用root_url和root_path方法
3,将*path这种route rule放在routes.rb文件的最后,用来做Route globbing,即前面的routes规则都没匹配时,使用该规则来匹配,如:
map.wildcard "*wildcard", :controller => "website", :action => "not_found"
1 楼 linjie_830914 2009-08-19 13:40