Class: ActionController::Routing::RouteBuilder
- Defined in:
- lib/action_controller/routing/builder.rb
Overview
:nodoc:
Instance Attribute Summary collapse
-
#interval_regexp ⇒ Object
readonly
Returns the value of attribute interval_regexp.
-
#nonseparator_regexp ⇒ Object
readonly
Returns the value of attribute nonseparator_regexp.
-
#optional_separators ⇒ Object
readonly
Returns the value of attribute optional_separators.
-
#separator_regexp ⇒ Object
readonly
Returns the value of attribute separator_regexp.
-
#separators ⇒ Object
readonly
Returns the value of attribute separators.
Instance Method Summary collapse
-
#assign_default_route_options(segments) ⇒ Object
Assign default options, such as ‘index’ as a default for
:action
. -
#assign_route_options(segments, defaults, requirements) ⇒ Object
Takes a hash of defaults and a hash of requirements, and assigns them to the segments.
-
#build(path, options) ⇒ Object
Construct and return a route with the given path and options.
-
#divide_route_options(segments, options) ⇒ Object
Split the given hash of options into requirement and default hashes.
-
#ensure_required_segments(segments) ⇒ Object
Makes sure that there are no optional segments that precede a required segment.
-
#initialize ⇒ RouteBuilder
constructor
A new instance of RouteBuilder.
-
#segment_for(string) ⇒ Object
A factory method that returns a new segment instance appropriate for the format of the given string.
-
#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.
Constructor Details
#initialize ⇒ RouteBuilder
Returns a new instance of RouteBuilder.
7 8 9 10 11 12 13 14 |
# File 'lib/action_controller/routing/builder.rb', line 7 def initialize @separators = Routing::SEPARATORS @optional_separators = %w( / ) @separator_regexp = /[#{Regexp.escape(separators.join)}]/ @nonseparator_regexp = /\A([^#{Regexp.escape(separators.join)}]+)/ @interval_regexp = /(.*?)(#{separator_regexp}|$)/ end |
Instance Attribute Details
#interval_regexp ⇒ Object (readonly)
Returns the value of attribute interval_regexp.
5 6 7 |
# File 'lib/action_controller/routing/builder.rb', line 5 def interval_regexp @interval_regexp end |
#nonseparator_regexp ⇒ Object (readonly)
Returns the value of attribute nonseparator_regexp.
5 6 7 |
# File 'lib/action_controller/routing/builder.rb', line 5 def nonseparator_regexp @nonseparator_regexp end |
#optional_separators ⇒ Object (readonly)
Returns the value of attribute optional_separators.
4 5 6 |
# File 'lib/action_controller/routing/builder.rb', line 4 def optional_separators @optional_separators end |
#separator_regexp ⇒ Object (readonly)
Returns the value of attribute separator_regexp.
5 6 7 |
# File 'lib/action_controller/routing/builder.rb', line 5 def separator_regexp @separator_regexp end |
#separators ⇒ Object (readonly)
Returns the value of attribute separators.
4 5 6 |
# File 'lib/action_controller/routing/builder.rb', line 4 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.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/action_controller/routing/builder.rb', line 120 def (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.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/action_controller/routing/builder.rb', line 82 def (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 if requirement.multiline? 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 (segments) ensure_required_segments(segments) route_requirements end |
#build(path, options) ⇒ Object
Construct and return a route with the given path and options.
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/action_controller/routing/builder.rb', line 157 def build(path, ) # Wrap the path with slashes path = "/#{path}" unless path[0] == ?/ path = "#{path}/" unless path[-1] == ?/ prefix = [:path_prefix].to_s.gsub(/^\//,'') path = "/#{prefix}#{path}" unless prefix.blank? segments = segments_for_route_path(path) defaults, requirements, conditions = (segments, ) requirements = (segments, defaults, requirements) # TODO: Segments should be frozen on initialize segments.each { |segment| segment.freeze } route = Route.new(segments, requirements, conditions) if !route.significant_keys.include?(:controller) raise ArgumentError, "Illegal route: the :controller must be specified!" end route.freeze 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.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/action_controller/routing/builder.rb', line 57 def (segments, ) = .except(:path_prefix, :name_prefix) if [:namespace] [:controller] = "#{.delete(:namespace).sub(/\/$/, '')}/#{[:controller]}" end requirements = (.delete(:requirements) || {}).dup defaults = (.delete(:defaults) || {}).dup conditions = (.delete(:conditions) || {}).dup validate_route_conditions(conditions) path_keys = segments.collect { |segment| segment.key if segment.respond_to?(:key) }.compact .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.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/action_controller/routing/builder.rb', line 140 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 |
#segment_for(string) ⇒ Object
A factory method that returns a new segment instance appropriate for the format of the given string.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/action_controller/routing/builder.rb', line 34 def segment_for(string) 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 |
#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.
22 23 24 25 26 27 28 29 30 |
# File 'lib/action_controller/routing/builder.rb', line 22 def segments_for_route_path(path) rest, segments = path, [] until rest.empty? segment, rest = segment_for(rest) segments << segment end segments end |