1 说明
这个类用于根据path来创建Route对象
2 代码分析
核心:
- def build(path, options)
-
- path = "/#{path}" unless path[0] == ?/
- path = "#{path}/" unless path[-1] == ?/
- path = "/#{options[:path_prefix].to_s.gsub(/^\//,'')}#{path}" if options[:path_prefix]
- 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 options.key?(:requirements) || route.requirements.keys.to_set != Routing::ALLOWED_REQUIREMENTS_FOR_OPTIMISATION
- route.optimise = false
- end
- if !route.significant_keys.include?(:controller)
- raise ArgumentError, "Illegal route: the :controller must be specified!"
- end
- route
- end
- def segments_for_route_path(path)
- rest, segments = path, []
- until rest.empty? # 循环将path分割为segment
- segment, rest = segment_for rest
- segments << segment
- end
- segments
- end
-
-
-
- def divide_route_options(segments, options)
- options = options.dup
- if options[:namespace]
- options[:controller] = "#{options[:path_prefix]}/#{options[:controller]}"
- options.delete(:path_prefix)
- options.delete(:name_prefix)
- options.delete(:namespace)
- end
- requirements = (options.delete(:requirements) || {}).dup
- defaults = (options.delete(:defaults) || {}).dup
- conditions = (options.delete(:conditions) || {}).dup
- path_keys = segments.collect { |segment| segment.key if segment.respond_to?(:key) }.compact
- options.each do |key, value|
- hash = (path_keys.include?(key) && ! value.is_a?(Regexp)) ? defaults : requirements
- hash[key] = value
- end
- [defaults, requirements, conditions]
- end
-
-
-
- def assign_route_options(segments, defaults, requirements)
- route_requirements = {}
- segment_named = Proc.new do |key|
- segments.detect { |segment| segment.key == key if segment.respond_to?(:key) }
- end
- requirements.each do |key, requirement|
- segment = segment_named[key]
- if segment
- raise TypeError, "#{key}: requirements on a path segment must be regular expressions" unless requirement.is_a?(Regexp)
- if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
- raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
- end
- if multiline_regexp?(requirement)
- raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
- end
- segment.regexp = requirement
- else
- route_requirements[key] = requirement
- end
- end
- defaults.each do |key, default|
- segment = segment_named[key]
- raise ArgumentError, "#{key}: No matching segment exists; cannot assign default" unless segment
- segment.is_optional = true
- segment.default = default.to_param if default
- end
- assign_default_route_options(segments)
- ensure_required_segments(segments)
- route_requirements
- end
-
-
- def segment_for(string)
- segment = case string
- when /\A:(\w+)/ # match :controller
- key = $1.to_sym
- case key
- when :controller then ControllerSegment.new(key)
- else DynamicSegment.new key
- end
- when /\A\*(\w+)/ then PathSegment.new($1.to_sym, :optional => true) # match path
- when /\A\?(.*?)\?/ # match static
- returning segment = StaticSegment.new($1) do
- segment.is_optional = true
- end
- when /\A(
- when Regexp.new(separator_pattern) then # match seperator
- returning segment = DividerSegment.new($&) do
- segment.is_optional = (optional_separators.include? $&)
- end
- end
- [segment, $~.post_match]
- end
That returning statement may look odd; it’s a method defined in ActiveSupport that makes it really easy to return a value, but only after performing some operations on it. You’ll find it used all over in Rails, so it’s worth getting familiar with it.)
If you are trying to extend Routes, this is where yourRouteBuilder subclass would extend segment_for to add it’s own custom string processing. As you can see, routing currently supports five different segment types:
DynamicSegment. This represents parts of the route that begin with a colon, like :action, :permalink or :id.ControllerSegment. This is actually a subclass ofDynamicSegment. It represents to special string :controller, because it does some special recognition on those strings. (We’ll cover that more in the next article).PathSegment. This is for segments that start with an asterisk, and which represent the remainder of the path. Routes like "/file/*path" use a PathSegment.StaticSegment. This is any static text in your route that must be matched (or generated) verbatim. If you have a path like "/one/two", the strings "one" and "two" are both static segments.DividerSegment. This is any segment that is used to delimit the other segments. Generally, this will be the forward slash character, but also includes commas, periods, semicolons, and question marks.
发表于 @
2008年12月22日 12:52:00 | | 编辑|
举报| 收藏