aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2005-02-19 01:16:09 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2005-02-19 01:16:09 +0000
commitcdd2e844a400ad2616cbaf24919d8af826c0b6c6 (patch)
tree14408346e2ba05b161cf8572f3ce7f26e248d4e4 /actionpack
parent578d0ce5e51aaa4d76c03cd1258785f8af617b77 (diff)
downloadrails-cdd2e844a400ad2616cbaf24919d8af826c0b6c6.tar.gz
rails-cdd2e844a400ad2616cbaf24919d8af826c0b6c6.tar.bz2
rails-cdd2e844a400ad2616cbaf24919d8af826c0b6c6.zip
Added defaults and regexp requirements to Routing #666
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@677 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/lib/action_controller/routing.rb60
1 files changed, 50 insertions, 10 deletions
diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb
index 1206fda5ec..acdb4635f0 100644
--- a/actionpack/lib/action_controller/routing.rb
+++ b/actionpack/lib/action_controller/routing.rb
@@ -7,13 +7,24 @@ module ActionController
def initialize(path, hash={})
raise ArgumentError, "Second argument must be a hash!" unless hash.kind_of?(Hash)
- @defaults = {}
- @requirements = {}
+ @defaults = hash[:defaults].kind_of?(Hash) ? hash.delete(:defaults) : {}
+ @requirements = hash[:requirements].kind_of?(Hash) ? hash.delete(:requirements) : {}
self.items = path
hash.each do |k, v|
- raise TypeError, "Hash may only contain symbols!" unless k.kind_of? Symbol
- (@items.include?(k) ? @defaults : @requirements)[k] = v
+ raise TypeError, "Hash keys must be symbols!" unless k.kind_of? Symbol
+ if v.kind_of? Regexp
+ raise ArgumentError, "Regexp requirement on #{k}, but #{k} is not in this route's path!" unless @items.include? k
+ @requirements[k] = v
+ else
+ (@items.include?(k) ? @defaults : @requirements)[k] = v
+ end
+ end
+
+ @defaults.each do |k, v|
+ raise ArgumentError, "A default has been specified for #{k}, but #{k} is not in the path!" unless @items.include? k
+ @defaults[k] = v.to_s unless v.kind_of?(String) || v.nil?
end
+ @requirements.each {|k, v| raise ArgumentError, "A Regexp requirement has been specified for #{k}, but #{k} is not in the path!" if v.kind_of?(Regexp) && ! @items.include?(k)}
# Add in defaults for :action and :id.
[[:action, 'index'], [:id, nil]].each do |name, default|
@@ -31,15 +42,16 @@ module ActionController
# then that component is removed. This is applied as many times as possible. So, your index controller's
# index action will generate []
def generate(options, defaults={})
- non_matching = @requirements.inject([]) {|a, (k, v)| ((options[k] || defaults[k]) == v) ? a : a << k}
- return nil, "Options mismatch requirements: #{non_matching.join ', '}" unless non_matching.empty?
+ non_matching = @requirements.keys.select {|name| ! passes_requirements?(name, options[name] || defaults[name])}
+ non_matching.collect! {|name| requirements_for(name)}
+ return nil, "Mismatching option#{'s' if non_matching.length > 1}:\n #{non_matching.join '\n '}" unless non_matching.empty?
- used_names = @requirements.inject({}) {|hash, (k, v)| hash[k] = true; hash}
+ used_names = @requirements.inject({}) {|hash, (k, v)| hash[k] = true; hash} # Mark requirements as used so they don't get put in the query params
components = @items.collect do |item|
if item.kind_of? Symbol
used_names[item] = true
value = options[item] || defaults[item] || @defaults[item]
- return nil, "#{item.inspect} was not given and has no default." if value.nil? && ! (@defaults.key?(item) && @defaults[item].nil?) # Don't leave if nil value.
+ return nil, requirements_for(item) unless passes_requirements?(item, value)
defaults = {} unless defaults == {} || value == defaults[item] # Stop using defaults if this component isn't the same as the default.
(value.nil? || item == :controller) ? value : CGI.escape(value.to_s)
else item
@@ -86,9 +98,10 @@ module ActionController
components = remaining_components
end
options[:controller] = controller_class.controller_path
+ return nil, requirements_for(:controller) unless passes_requirements?(:controller, options[:controller])
elsif item.kind_of? Symbol
value = components.shift || @defaults[item]
- return nil, "No value or default for parameter #{item.inspect}" if value.nil? && ! (@defaults.key?(item) && @defaults[item].nil?)
+ return nil, requirements_for(item) unless passes_requirements?(item, value)
options[item] = value.nil? ? value : CGI.unescape(value)
else
return nil, "No value available for component #{item.inspect}" if components.empty?
@@ -102,7 +115,7 @@ module ActionController
raise RoutingError, "Illegal controller path for route default: #{@requirements[:controller]}" unless controller_class && extras.empty?
options[:controller] = controller_class.controller_path
end
- options = @requirements.merge(options)
+ @requirements.each {|k,v| options[k] ||= v unless v.kind_of?(Regexp)}
return nil, "Route recognition didn't find a controller class!" unless controller_class
return nil, "Unused components were left: #{components.join '/'}" unless components.empty?
@@ -145,6 +158,33 @@ module ActionController
seen
end
end
+
+ # Verify that the given value passes this route's requirements
+ def passes_requirements?(name, value)
+ return @defaults.key?(name) && @defaults[name].nil? if value.nil? # Make sure it's there if it should be
+
+ case @requirements[name]
+ when nil then true
+ when Regexp then
+ value = value.to_s
+ match = @requirements[name].match(value)
+ match && match[0].length == value.length
+ else
+ @requirements[name] == value.to_s
+ end
+ end
+ def requirements_for(name)
+ presence = (@defaults.key?(name) && @defaults[name].nil?)
+ requirement = case @requirements[name]
+ when nil then nil
+ when Regexp then "match #{@requirements[name].inspect}"
+ else "be equal to #{@requirements[name].inspect}"
+ end
+ if presence && requirement then "#{name} must be present and #{requirement}"
+ elsif presence || requirement then "#{name} must #{requirement || 'be present'}"
+ else "#{name} has no requirements"
+ end
+ end
end
class RouteSet