Class: ActionController::Routing::RouteBuilder
- Defined in:
- lib/action_controller/routing/builder.rb
Overview
:nodoc:
Instance Attribute Summary collapse
-
#optional_separators ⇒ Object
Returns the value of attribute optional_separators.
-
#separators ⇒ Object
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.
- #interval_regexp ⇒ Object
- #multiline_regexp?(expression) ⇒ Boolean
-
#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.
- #separator_pattern(inverted = false) ⇒ Object
Constructor Details
#initialize ⇒ RouteBuilder
Returns a new instance of RouteBuilder.
6 7 8 9 |
# File 'lib/action_controller/routing/builder.rb', line 6 def initialize self.separators = Routing::SEPARATORS self.optional_separators = %w( / ) end |
Instance Attribute Details
#optional_separators ⇒ Object
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 |
#separators ⇒ Object
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.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/action_controller/routing/builder.rb', line 130 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.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/action_controller/routing/builder.rb', line 92 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 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 (segments) ensure_required_segments(segments) route_requirements end |
#build(path, options) ⇒ Object
Construct and return a route with the given path and options.
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/action_controller/routing/builder.rb', line 167 def build(path, ) # Wrap the path with slashes path = "/#{path}" unless path[0] == ?/ path = "#{path}/" unless path[-1] == ?/ path = "/#{[:path_prefix].to_s.gsub(/^\//,'')}#{path}" if [:path_prefix] segments = segments_for_route_path(path) defaults, requirements, conditions = (segments, ) requirements = (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 <tt>:requirements</tt> as the interpolation # code won't raise RoutingErrors when generating if .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.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/action_controller/routing/builder.rb', line 66 def (segments, ) = .dup if [:namespace] [:controller] = "#{[:path_prefix]}/#{[:controller]}" .delete(:path_prefix) .delete(:name_prefix) .delete(:namespace) end requirements = (.delete(:requirements) || {}).dup defaults = (.delete(:defaults) || {}).dup conditions = (.delete(:conditions) || {}).dup 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.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/action_controller/routing/builder.rb', line 150 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_regexp ⇒ Object
15 16 17 |
# File 'lib/action_controller/routing/builder.rb', line 15 def interval_regexp Regexp.new "(.*?)(#{separators.source}|$)" end |
#multiline_regexp?(expression) ⇒ Boolean
19 20 21 |
# File 'lib/action_controller/routing/builder.rb', line 19 def multiline_regexp?(expression) expression. & Regexp::MULTILINE == Regexp::MULTILINE end |
#segment_for(string) ⇒ Object
A factory method that returns a new segment instance appropriate for the format of the given string.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/action_controller/routing/builder.rb', line 41 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.
29 30 31 32 33 34 35 36 37 |
# File 'lib/action_controller/routing/builder.rb', line 29 def segments_for_route_path(path) rest, segments = path, [] until rest.empty? segment, rest = segment_for rest segments << segment end segments end |