In routing loading, I roughly introduced how to load the simplest route in rails. In this article, I will introduce how more complex named route and restful-related resources in the rails system are loaded. In order not to repeat too much pen and ink, this article will be carried out on the basis of the previous article. If you find that when reading this article separately, you are a little confused. I suggest you take a look at my previous article first: ruby on Rails-2.0.2 source code analysis (2)-loading Routing
- Evolutionary routing-named route
First, all loading of the named route occurs in routing. RB. In fact, named route is no more advanced than normal route, rails eventually resolves the named route into a normal route and saves it in the routes array of the rouseset class (remember this guy? It is best to keep him in mind, because he will continue to play an important role in subsequent articles). The reason why I called him evolution is that since named route provides name, inside rails, A series of helper methods will be generated. When we use link_to, redirect_to, and other methods in the Controller or view, we do not need to specify the corresponding controller and action to simplify our code, no more. Let's take a look at the routes we are familiar. RB
Ruby code
- Actioncontroller: routing: routes. DrawDo| Map |
- Map. Purchase 'products/: ID/purchase ',: controller => 'catalog',: Action => 'purchase'
- ...
- End
ActionController::Routing::Routes.draw do |map| map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase' ...end
Here, I define a named route for purchase (of course, you can use the connect method to define a normal route ). As mentioned in the previous article, the map object in the block is an mapper class instance. As you can imagine, in fact, the Mapper class does not define the purchase method (which name do you want to give your named route? Cuihua? Wangcai ?). Everything is handled through the method_missing method of the Mapper class. The Code is as follows:
Ruby code
- DefMethod_missing (route_name, * ARGs, & proc) #: nodoc:
- Super UnlessArgs. length> = 1 & Proc.Nil?
- @ Set. Add_named_route (route_name, * ARGs)
- End
def method_missing(route_name, *args, &proc) #:nodoc: super unless args.length >= 1 && proc.nil? @set.add_named_route(route_name, *args)end
If you have a good memory, remember that the @ set object is an instance of the routeset class, so here the Mapper class will name this route, all other parameters are passed to the add_named_route method of the routeset class.
Ruby code
- DefAdd_named_route (name, path, Options = {})
- # Todo-Is options ever used?
- Name = options [: name_prefix] + name. to_sIfOptions [: name_prefix]
- Named_routes [name. to_sym] = add_route (path, options)
- 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
Here, we can see the add_route method we have already been familiar? This method does not need to be explained too much. rails will generate a normal route, store it in the routes array of routeset, return this route, and assign it to the named_routes object, this object is an instance of the namedroutecollection class. The following definitions are available in namedroutecollection:
Ruby code
- DefAdd (name, route)
- Routes [name. to_sym] = route
- Define_named_route_methods (name, route)
- End
- DefGet (name)
- Routes [name. to_sym]
- End
- Alias[] = Add
- Alias[] Get
def add(name, route) routes[name.to_sym] = route define_named_route_methods(name, route)enddef get(name) routes[name.to_sym]endalias []= addalias [] get
Therefore, the next step seems to be concerned about the add method. Here, the normal route is saved in the routes hash of the namedroutecollection class (note that it is distinguished from the routes array of the routeset ). Then, the named route begins to "evolve" ---- generate a series of helper methods by using the define_named_route_methods method.
Ruby code
- DefDefine_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_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 endend
Or you already know that named route has two types of helper Methods: name_url and name_path. From the above code, we can see the real implementation. Here, rails generates hash access and URL helper methods for URLs and paths respectively, and their implementation uses Ruby's powerful dynamic features. The specific implementation is as follows:
Ruby code
- DefDefine_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
- DefDefine_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 =IfArgs. 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
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 is an anonymous module in the namedroutecollection class. It dynamically adds this series of helper methods through the module_eval method and saves the method name in the helpers array, for later use by the Controller or view (link_to, redirect_to ). So far, the loading of the named route is complete. Very simple, isn't it?
This article from: http://woody-420420.javaeye.com/blog/174352