Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

assert_generates expects unexpected generated_path

#5689

The following difficulty concerning assert_generates (defined in actionpack-3.0.0/lib/action_dispatch/testing/assertions/routing.rb:94) occured while using RSpec2 and was discussed as "ruby-related issue" in November '09 (https://rspec.lighthouseapp.com/projects/5645/tickets/907-the-gener....

Perhaps this is not a malfunction, but a misunderstanding of the usage of this assertion as illustrated in http://apidock.com/rails/v3.0.0/ActionDispatch/Assertions/RoutingAs... .

Given I have set up the following routes in Rails 3.0.0 (Ruby 1.9.2):

MyApp::Application.routes.draw do
  match 'logout1' => 'user_sessions#destroy', :as => 'logout1'
  match 'logout2' => 'user_sessions#destroy', :as => 'logout2'
  match 'logout3' => 'user_sessions#destroy'
  match 'login' => 'user_sessions#new', :as => 'login'

  resources :user_sessions
end

The following assertions (excerpt from routing spec) ...

assert_generates "/logout1", :controller => "user_sessions", :action => "new"
assert_generates "/logout2", :controller => "user_sessions", :action => "new"
assert_generates "/logout3", :controller => "user_sessions", :action => "new"
assert_generates "/login", :controller => "user_sessions", :action => "new"

... used e.g. in RSpec produce the following results:

Failures:
  1) General routing recognizes /logout2
     Failure/Error: { :get => "/logout2" }.should route_to(:controller => "user_sessions", :action => "destroy")
     The generated path <"/logout1"> did not match <"/logout2">.
 
  2) General routing recognizes /logout3
     Failure/Error: { :get => "/logout3" }.should route_to(:controller => "user_sessions", :action => "destroy")
     The generated path <"/logout1"> did not match <"/logout3">.
 
  3) General routing recognizes /login
     Failure/Error: { :get => "/login" }.should route_to(:controller => "user_sessions", :action => "new")
     The generated path <"/user_sessions/new"> did not match <"/login">.

The first named route is accepted as generated_path and expected_path both have the value "/logout1". The following two are not recognized, but work fine as does the "login"-route.

In [...]action_dispatch/testing/assertions/routing.rb:86 @routes.generate_extras seems to return the "unexpected" route.

      def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
        if expected_path =~ %r{://}
          begin
            uri = URI.parse(expected_path)
            expected_path = uri.path.to_s.empty? ? "/" : uri.path
          rescue URI::InvalidURIError => e
            raise ActionController::RoutingError, e.message
          end
        else
          expected_path = "/#{expected_path}" unless expected_path.first == '/'
        end
        # Load routes.rb if it hasn't been loaded.

        generated_path, extra_keys = @routes.generate_extras(options, defaults)
        found_extras = options.reject {|k, v| ! extra_keys.include? k}

        msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
        assert_block(msg) { found_extras == extras }
        msg = build_message(message, "The generated path <?> did not match <?>", generated_path,
            expected_path)
        assert_block(msg) { expected_path == generated_path }
      end

Though they are all four existing as in "rake routes":

logout1        /logout1(.:format)                                  {:action=>"destroy", :controller=>"user_sessions"}
logout2        /logout2(.:format)                                  {:action=>"destroy", :controller=>"user_sessions"}
logout3        /logout3(.:format)                                  {:action=>"destroy", :controller=>"user_sessions"}
login          /login(.:format)                                    {:action=>"new", :controller=>"user_sessions"}
[...]
user_sessions     GET    /user_sessions(.:format)                  {:action=>"index", :controller=>"user_sessions"}
user_sessions     POST   /user_sessions(.:format)                  {:action=>"create", :controller=>"user_sessions"}
new_user_session  GET    /user_sessions/new(.:format)              {:action=>"new", :controller=>"user_sessions"}
edit_user_session GET    /user_sessions/:id/edit(.:format)         {:action=>"edit", :controller=>"user_sessions"}
user_session      GET    /user_sessions/:id(.:format)              {:action=>"show", :controller=>"user_sessions"}
user_session      PUT    /user_sessions/:id(.:format)              {:action=>"update", :controller=>"user_sessions"}
user_session      DELETE /user_sessions/:id(.:format)              {:action=>"destroy", :controller=>"user_sessions"}

I haven't found a ticket concerning assert_generates and saw that Josh worked on this bit of code, so I figured to create a ticket to clarify this issue.

Reported by Frederik Vollert · September 23rd, 2010 @ 12:17 PM

State: invalid
Milestone: none
Assigned to: nobody
Importance: Low

Activity

  1. Frederik Vollert
    Frederik Vollert
    • Tag changed from routing assert_generates, generate_extras to routing assert_generates, generate_extras, rails3.0.0

    September 23rd, 2010 @ 12:18 PM

  2. Andrew White
    Andrew White
    • State changed from new to invalid
    • Assigned user cleared.
    • Importance changed from to Low

    The assert_generate code tries to generate a url from a set of options - in your case these options are the same so it always generates the first logout route. In the login case the get method constraint of the /user_sessions/new path provides a more specific match so overrides the the /login route.

    September 23rd, 2010 @ 02:16 PM