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.

:requirements not passed to named_route generated by :member

#2054

Example

routes.rb

map.resources :users, :requirements => { :id => %r([^/;,?]+) }, :member => { :baz => :get }

user.rb

def to_param self.name end

The error

Assume @user.name == 'foo'

user_path(@user) # => /users/foo baz_user_path(@user) # => /users/foo/baz

Both work, and would without the :requirements option on the named resource.

Now, with @user.name == 'foo.bar'

user_path(@user) # => /users/foo.bar baz_user_path(@user) # => /users/foo.bar/baz

While the generated route seems legitimate, Rails will fail to respond properly to the baz path. While the first path (/users/foo.bar) works, thanks to :requirements specifying the legal formats of params[:id], :requirements doesn't seem to be passed down to the member function.

Workarounds

One could do the follow above the users resource in routes.rb:

map.baz_user "/users/:id/baz", :requirements => { :id => %r([^/;,?]+) }

.. and then remove the member function. While the requirement RE can be moved into a constant, this is obviously an imperfect solution.

Conclusion

I hope this is a legitimate bug as I have spent some time searching both the Web and Lighthouse for similar bugs but haven't found anything.

Reported by Marc Bowes · February 23rd, 2009 @ 10:46 PM

State: resolved
Milestone: none
Assigned to: josh josh
Importance: none

Activity

  1. Marc Bowes
    Marc Bowes

    Oh dddear. The blockquotes didn't work so well for the example code, it seems to have been squished onto one line. Sorry :)

    February 23rd, 2009 @ 10:48 PM

  2. DHH
    DHH
    • Assigned user set to josh
    • Milestone cleared.

    February 27th, 2009 @ 01:58 PM

  3. CancelProfileIsBroken
    CancelProfileIsBroken

    The problem is in resources.rb, in action_options_for. This method comes up with the options for a route, including which requirements to use, by looking at the action name. The baz route falls into the else:

    default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements)

    That's correct for an added collection route (no ID required in the route), but for an added member route it ought to be:

    default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements(require_id))

    One possible fix would be to change the else to:

    resource.member_methods.include?(method) ? default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements(require_id)) : default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements)

    March 4th, 2009 @ 12:05 PM

  4. josh
    josh
    • State changed from new to wontfix
    • Milestone set to 2.x

    @Mike that seems to cause a bunch of other problems with routes with name prefixes.

    Please attach some failing unit tests otherwise punting till 2.x

    March 4th, 2009 @ 07:15 PM

  5. CancelProfileIsBroken
    CancelProfileIsBroken

    Here's a failing test that passes with the fix I made (though as you point out that fix has other issues). I'll spend a bit more time looking at a possible patch.

    March 4th, 2009 @ 11:00 PM

  6. CancelProfileIsBroken
    CancelProfileIsBroken
    • State changed from wontfix to open

    And...here's a patch that passes the test, and doesn't fail any other tests. (this patch includes the tests as well)

    March 4th, 2009 @ 11:59 PM

  7. Repository
    Repository
    • State changed from open to resolved

    (from [5b7527ca44521edf9782b3d7f449bf09a29267f2]) Failing test for routes with member & requirement [#2054 :requirements not passed to named_route generated by :member state:resolved]

    Signed-off-by: Joshua Peek josh@joshpeek.com http://github.com/rails/rails/co...

    March 6th, 2009 @ 12:49 AM

  8. Luke Melia
    Luke Melia

    This change breaks collection routes when a resource has a collection action and member action named the same. Attached is a failing test demonstrating as much. In the context of action_options_for, there is not enough information to determine whether to add the ID resource requirement or not.

    Given we're close to 2.3, I'd recommend rolling back this commit to the previous broken behavior, rather than moving the problem, but perhaps someone can come up with a solution quickly.

    March 13th, 2009 @ 09:37 PM

  9. josh
    josh
    • Milestone cleared.
    • State changed from resolved to open

    March 13th, 2009 @ 10:07 PM

  10. Repository
    Repository
    • State changed from open to wontfix

    (from [5b025a1d119eaf09f5376209da212b76725747f8]) Revert 5b7527ca "Failing test for routes with member & requirement" [#2054 :requirements not passed to named_route generated by :member state:wontfix] http://github.com/rails/rails/co...

    March 13th, 2009 @ 11:14 PM

  11. CancelProfileIsBroken
    CancelProfileIsBroken
    • State changed from wontfix to open

    Attached patch handles both cases (the original and the breakage that Luke spotted) by passing additional information into action_options_for. I'd like a more elegant solution, but I believe this one is safe.

    March 14th, 2009 @ 01:55 AM

  12. Luke Melia
    Luke Melia

    I can confirm that Mike's new patch not only passes the test but also works properly in the real-world app that surfaced this issue.

    March 14th, 2009 @ 04:04 AM

  13. Repository
    Repository
    • State changed from open to resolved

    (from [07710fd3e0c5c84521b7929738ba33cea99bc108]) Fix requirements for additional member/collection routes [#2054 :requirements not passed to named_route generated by :member state:resolved]

    Signed-off-by: Joshua Peek josh@joshpeek.com http://github.com/rails/rails/co...

    March 14th, 2009 @ 03:06 PM

  14. David Krmpotic
    David Krmpotic

    Before the change this (minimal case):

    class Spot def to_param

    id
    
    

    end end

    worked. After the change, I got:

    app.spot_path(Spot.first) TypeError: can't convert Fixnum into String

    from generated code (/Users/david/Projects/oc/vendor/rails/actionpack/lib/action_controller/routing/route.rb:160):6:in `generate_raw'
    from /Users/david/Projects/oc/vendor/rails/actionpack/lib/action_controller/routing/route.rb:131:in `generate'
    from /Users/david/Projects/oc/vendor/rails/actionpack/lib/action_controller/routing/route_set.rb:384:in `generate'
    from /Users/david/Projects/oc/vendor/rails/actionpack/lib/action_controller/url_rewriter.rb:205:in `rewrite_path'
    from /Users/david/Projects/oc/vendor/rails/actionpack/lib/action_controller/url_rewriter.rb:184:in `rewrite_url'
    from /Users/david/Projects/oc/vendor/rails/actionpack/lib/action_controller/url_rewriter.rb:162:in `rewrite'
    from /Users/david/Projects/oc/vendor/rails/actionpack/lib/action_controller/integration.rb:243:in `url_for'
    from (eval):16:in `spot_path'
    from (irb):3
    
    

    I realize that I should have returned id.to_s in the first place, but I'm not sure if is/was expected to work with integers too... All in all now it doesn't anymore... Don't know enough about this subject to contribute a patch, but would love to learn more - especially if it's ok as it is or it does need to be patched.

    Thank you david

    April 5th, 2009 @ 04:31 PM