vendor/rails/actionpack/lib/action_controller/routing/builder.rb in radiant-0.7.2 vs vendor/rails/actionpack/lib/action_controller/routing/builder.rb in radiant-0.8.0

- old
+ new

@@ -1,83 +1,74 @@ module ActionController module Routing class RouteBuilder #:nodoc: - attr_accessor :separators, :optional_separators + attr_reader :separators, :optional_separators + attr_reader :separator_regexp, :nonseparator_regexp, :interval_regexp def initialize - self.separators = Routing::SEPARATORS - self.optional_separators = %w( / ) - end + @separators = Routing::SEPARATORS + @optional_separators = %w( / ) - def separator_pattern(inverted = false) - "[#{'^' if inverted}#{Regexp.escape(separators.join)}]" + @separator_regexp = /[#{Regexp.escape(separators.join)}]/ + @nonseparator_regexp = /\A([^#{Regexp.escape(separators.join)}]+)/ + @interval_regexp = /(.*?)(#{separator_regexp}|$)/ end - def interval_regexp - Regexp.new "(.*?)(#{separators.source}|$)" - end - - def multiline_regexp?(expression) - expression.options & Regexp::MULTILINE == Regexp::MULTILINE - end - # Accepts a "route path" (a string defining a route), and returns the array # of segments that corresponds to it. Note that the segment array is only # partially initialized--the defaults and requirements, for instance, need # to be set separately, via the +assign_route_options+ method, and the # <tt>optional?</tt> method for each segment will not be reliable until after # +assign_route_options+ is called, as well. def segments_for_route_path(path) rest, segments = path, [] until rest.empty? - segment, rest = segment_for rest + segment, rest = segment_for(rest) segments << segment end segments end # A factory method that returns a new segment instance appropriate for the # format of the given string. def segment_for(string) - segment = case string - when /\A:(\w+)/ - 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) - when /\A\?(.*?)\?/ - returning segment = StaticSegment.new($1) do - segment.is_optional = true - end - when /\A(#{separator_pattern(:inverted)}+)/ then StaticSegment.new($1) - when Regexp.new(separator_pattern) then - returning segment = DividerSegment.new($&) do - segment.is_optional = (optional_separators.include? $&) - end - end + segment = + case string + when /\A\.(:format)?\// + OptionalFormatSegment.new + when /\A:(\w+)/ + key = $1.to_sym + key == :controller ? ControllerSegment.new(key) : DynamicSegment.new(key) + when /\A\*(\w+)/ + PathSegment.new($1.to_sym, :optional => true) + when /\A\?(.*?)\?/ + StaticSegment.new($1, :optional => true) + when nonseparator_regexp + StaticSegment.new($1) + when separator_regexp + DividerSegment.new($&, :optional => optional_separators.include?($&)) + end [segment, $~.post_match] end # Split the given hash of options into requirement and default hashes. The # segments are passed alongside in order to distinguish between default values # and requirements. def divide_route_options(segments, options) - options = options.dup + options = options.except(:path_prefix, :name_prefix) if options[:namespace] options[:controller] = "#{options.delete(:namespace).sub(/\/$/, '')}/#{options[:controller]}" - options.delete(:path_prefix) - options.delete(:name_prefix) end requirements = (options.delete(:requirements) || {}).dup defaults = (options.delete(:defaults) || {}).dup conditions = (options.delete(:conditions) || {}).dup + validate_route_conditions(conditions) + 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 @@ -100,11 +91,11 @@ 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) + if requirement.multiline? raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}" end segment.regexp = requirement else route_requirements[key] = requirement @@ -166,38 +157,41 @@ def build(path, options) # Wrap the path with slashes path = "/#{path}" unless path[0] == ?/ path = "#{path}/" unless path[-1] == ?/ - path = "/#{options[:path_prefix].to_s.gsub(/^\//,'')}#{path}" if options[:path_prefix] + prefix = options[:path_prefix].to_s.gsub(/^\//,'') + path = "/#{prefix}#{path}" unless prefix.blank? segments = segments_for_route_path(path) defaults, requirements, conditions = divide_route_options(segments, options) requirements = assign_route_options(segments, defaults, requirements) - route = Route.new + # TODO: Segments should be frozen on initialize + segments.each { |segment| segment.freeze } - route.segments = segments - route.requirements = requirements - route.conditions = conditions + route = Route.new(segments, requirements, conditions) - if !route.significant_keys.include?(:action) && !route.requirements[:action] - route.requirements[:action] = "index" - route.significant_keys << :action - end - - # Routes cannot use the current string interpolation method - # if there are user-supplied <tt>:requirements</tt> as the interpolation - # code won't raise RoutingErrors when generating - 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 + route.freeze end + + private + def validate_route_conditions(conditions) + if method = conditions[:method] + [method].flatten.each do |m| + if m == :head + raise ArgumentError, "HTTP method HEAD is invalid in route conditions. Rails processes HEAD requests the same as GETs, returning just the response headers" + end + + unless HTTP_METHODS.include?(m.to_sym) + raise ArgumentError, "Invalid HTTP method specified in route conditions: #{conditions.inspect}" + end + end + end + end end end end