Class: ActionController::Routing::RouteBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/action_controller/routing.rb

Overview

:nodoc:

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeRouteBuilder

Returns a new instance of RouteBuilder.



860
861
862
863
# File 'lib/action_controller/routing.rb', line 860

def initialize
  self.separators = Routing::SEPARATORS
  self.optional_separators = %w( / )
end

Instance Attribute Details

#optional_separatorsObject

Returns the value of attribute optional_separators.



858
859
860
# File 'lib/action_controller/routing.rb', line 858

def optional_separators
  @optional_separators
end

#separatorsObject

Returns the value of attribute separators.



858
859
860
# File 'lib/action_controller/routing.rb', line 858

def separators
  @separators
end

Instance Method Details

#assign_default_route_options(segments) ⇒ Object

Assign default options, such as ‘index’ as a default for :action. This method must be run after user supplied requirements and defaults have been applied to the segments.



977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
# File 'lib/action_controller/routing.rb', line 977

def assign_default_route_options(segments)
  segments.each do |segment|
    next unless segment.is_a? DynamicSegment
    case segment.key
      when :action
        if segment.regexp.nil? || segment.regexp.match('index').to_s == 'index'
          segment.default ||= 'index'
          segment.is_optional = true
        end
      when :id
        if segment.default.nil? && segment.regexp.nil? || segment.regexp =~ ''
          segment.is_optional = true
        end
    end
  end
end

#assign_route_options(segments, defaults, requirements) ⇒ Object

Takes a hash of defaults and a hash of requirements, and assigns them to the segments. Any unused requirements (which do not correspond to a segment) are returned as a hash.



942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
# File 'lib/action_controller/routing.rb', line 942

def assign_route_options(segments, defaults, requirements)
  route_requirements = {} # Requirements that do not belong to a segment

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

#build(path, options) ⇒ Object

Construct and return a route with the given path and options.



1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
# File 'lib/action_controller/routing.rb', line 1014

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]

  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

  # Routes cannot use the current string interpolation method
  # if there are user-supplied :requirements 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
end

#divide_route_options(segments, options) ⇒ Object

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.



916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
# File 'lib/action_controller/routing.rb', line 916

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

#ensure_required_segments(segments) ⇒ Object

Makes sure that there are no optional segments that precede a required segment. If any are found that precede a required segment, they are made required.



997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
# File 'lib/action_controller/routing.rb', line 997

def ensure_required_segments(segments)
  allow_optional = true
  segments.reverse_each do |segment|
    allow_optional &&= segment.optional?
    if !allow_optional && segment.optional?
      unless segment.optionality_implied?
        warn "Route segment \"#{segment.to_s}\" cannot be optional because it precedes a required segment. This segment will be required."
      end
      segment.is_optional = false
    elsif allow_optional && segment.respond_to?(:default) && segment.default
      # if a segment has a default, then it is optional
      segment.is_optional = true
    end
  end
end

#interval_regexpObject



869
870
871
# File 'lib/action_controller/routing.rb', line 869

def interval_regexp
  Regexp.new "(.*?)(#{separators.source}|$)"
end

#segment_for(string) ⇒ Object

A factory method that returns a new segment instance appropriate for the format of the given string.



891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
# File 'lib/action_controller/routing.rb', line 891

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, $~.post_match]
end

#segments_for_route_path(path) ⇒ Object

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 #optional? method for each segment will not be reliable until after #assign_route_options is called, as well.



879
880
881
882
883
884
885
886
887
# File 'lib/action_controller/routing.rb', line 879

def segments_for_route_path(path)
  rest, segments = path, []

  until rest.empty?
    segment, rest = segment_for rest
    segments << segment
  end
  segments
end

#separator_pattern(inverted = false) ⇒ Object



865
866
867
# File 'lib/action_controller/routing.rb', line 865

def separator_pattern(inverted = false)
  "[#{'^' if inverted}#{Regexp.escape(separators.join)}]"
end