Permalink
Browse files

apply [6113] to stable

git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/1-2-stable@6114 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent 86519a6 commit 21fafdd290eb176fffc0d643db0be5fdd218b934 @technoweenie technoweenie committed Feb 4, 2007
@@ -1,5 +1,11 @@
*SVN*
+* improve error message for Routing for named routes. [Rob Sanheim]
+
+* Added enhanced docs to routing assertions. [Rob Sanheim]
+
+* fix form_for example in ActionController::Resources documentation. [gnarg]
+
* Add singleton resources from trunk [Rick Olson]
* TestSession supports indifferent access so session['foo'] == session[:foo] in your tests. #7372 [julik, jean.helou]
@@ -3,13 +3,23 @@ module Assertions
module RoutingAssertions
# Asserts that the routing of the given path was handled correctly and that the parsed options match.
#
- # assert_recognizes({:controller => 'items', :action => 'index'}, 'items')
+ # assert_recognizes({:controller => 'items', :action => 'index'}, 'items') # check the default action
+ # assert_recognizes({:controller => 'items', :action => 'list'}, 'items/list') # check a specific action
+ # assert_recognizes({:controller => 'items', :action => 'list', :id => '1'}, 'items/list/1') # check an action with a parameter
#
# Pass a hash in the second argument to specify the request method. This is useful for routes
- # requiring a specific method.
+ # requiring a specific HTTP method. The hash should contain a :path with the incoming request path
+ # and a :method containing the required HTTP verb.
#
+ # # assert that POSTing to /items will call the create action on ItemsController
# assert_recognizes({:controller => 'items', :action => 'create'}, {:path => 'items', :method => :post})
#
+ # You can also pass in "extras" with a hash containing URL parameters that would normally be in the query string. This can be used
+ # to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the
+ # extras argument, appending the query string on the path directly will not work. For example:
+ #
+ # # assert that a path of '/items/list/1?view=print' returns the correct options
+ # assert_recognizes({:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" })
def assert_recognizes(expected_options, path, extras={}, message=nil)
if path.is_a? Hash
request_method = path[:method]
@@ -33,7 +43,12 @@ def assert_recognizes(expected_options, path, extras={}, message=nil)
end
end
- # Asserts that the provided options can be used to generate the provided path.
+ # Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes.
+ # For example:
+ #
+ # assert_generates("/items", :controller => "items", :action => "index")
+ # assert_generates("/items/list", :controller => "items", :action => "list")
+ # assert_generates("/items/list/1", { :controller => "items", :action => "list", :id => "1" })
def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
clean_backtrace do
expected_path = "/#{expected_path}" unless expected_path[0] == ?/
@@ -53,7 +68,8 @@ def assert_generates(expected_path, options, defaults={}, extras = {}, message=n
end
# Asserts that path and options match both ways; in other words, the URL generated from
- # options is the same as path, and also that the options recognized from path are the same as options
+ # options is the same as path, and also that the options recognized from path are the same as options. This
+ # essentially combines assert_recognizes and assert_generates into one step.
def assert_routing(path, options, defaults={}, extras={}, message=nil)
assert_recognizes(options, path, extras, message)
@@ -158,7 +158,7 @@ def initialize(entity, options)
#
# or
#
- # <% form_for :message, @message, message_path(@message), :html => {:method => :put} do |f| %>
+ # <% form_for :message, @message, :url => message_path(@message), :html => {:method => :put} do |f| %>
#
# The #resources method accepts various options, too, to customize the resulting
# routes:
@@ -1241,8 +1241,11 @@ def generate(options, recall = {}, method=:generate)
if named_route
path = named_route.generate(options, merged, expire_on)
- raise RoutingError, "#{named_route_name}_url failed to generate from #{options.inspect}, expected: #{named_route.requirements.inspect}, diff: #{named_route.requirements.diff(options).inspect}" if path.nil?
- return path
+ if path.nil?
+ raise_named_route_error(options, named_route, named_route_name)
+ else
+ return path
+ end
else
merged[:action] ||= 'index'
options[:action] ||= 'index'
@@ -1262,6 +1265,18 @@ def generate(options, recall = {}, method=:generate)
raise RoutingError, "No route matches #{options.inspect}"
end
+
+ # try to give a helpful error message when named route generation fails
+ def raise_named_route_error(options, named_route, named_route_name)
+ diff = named_route.requirements.diff(options)
+ unless diff.empty?
+ raise RoutingError, "#{named_route_name}_url failed to generate from #{options.inspect}, expected: #{named_route.requirements.inspect}, diff: #{named_route.requirements.diff(options).inspect}"
+ else
+ required_segments = named_route.segments.select {|seg| (!seg.optional?) && (!seg.is_a?(DividerSegment)) }
+ required_keys_or_values = required_segments.map { |seg| seg.key rescue seg.value } # we want either the key or the value from the segment
+ raise RoutingError, "#{named_route_name}_url failed to generate from #{options.inspect} - you may have ambiguous routes, or you may need to supply additional parameters for this route. content_url has the following required parameters: #{required_keys_or_values.inspect} - are they all satisifed?"
+ end
+ end
def recognize(request)
params = recognize_path(request.path, extract_request_environment(request))
@@ -236,7 +236,39 @@ def test_paths_do_not_accept_defaults
map.connect ':controller/:action/:id'
end
end
+
+ def test_should_list_options_diff_when_routing_requirements_dont_match
+ rs.draw do |map|
+ map.post 'post/:id', :controller=> 'post', :action=> 'show', :requirements => {:id => /\d+/}
+ end
+ exception = assert_raise(ActionController::RoutingError) { rs.generate(:controller => 'post', :action => 'show', :bad_param => "foo", :use_route => "post") }
+ assert_match /^post_url failed to generate/, exception.message
+ from_match = exception.message.match(/from \{[^\}]+\}/).to_s
+ assert_match /:bad_param=>"foo"/, from_match
+ assert_match /:action=>"show"/, from_match
+ assert_match /:controller=>"post"/, from_match
+
+ expected_match = exception.message.match(/expected: \{[^\}]+\}/).to_s
+ assert_no_match /:bad_param=>"foo"/, expected_match
+ assert_match /:action=>"show"/, expected_match
+ assert_match /:controller=>"post"/, expected_match
+ diff_match = exception.message.match(/diff: \{[^\}]+\}/).to_s
+ assert_match /:bad_param=>"foo"/, diff_match
+ assert_no_match /:action=>"show"/, diff_match
+ assert_no_match /:controller=>"post"/, diff_match
+ end
+
+ # this specifies the case where your formerly would get a very confusing error message with an empty diff
+ def test_should_have_better_error_message_when_options_diff_is_empty
+ rs.draw do |map|
+ map.content '/content/:query', :controller => 'content', :action => 'show'
+ end
+ exception = assert_raise(ActionController::RoutingError) { rs.generate(:controller => 'content', :action => 'show', :use_route => "content") }
+ expected_message = %[content_url failed to generate from {:action=>"show", :controller=>"content"} - you may have ambiguous routes, or you may need to supply additional parameters for this route. content_url has the following required parameters: ["content", :query] - are they all satisifed?]
+ assert_equal expected_message, exception.message
+ end
+
def test_dynamic_path_allowed
rs.draw do |map|
map.connect '*path', :controller => 'content', :action => 'show_file'

0 comments on commit 21fafdd

Please sign in to comment.