This project is archived and is in readonly mode.

#5874 ✓invalid
Nader Akhnoukh

url_for <model> generates a <model>_url route instead of <model>_path

Reported by Nader Akhnoukh | October 28th, 2010 @ 12:19 AM

Rails 3.0.0

for example link_to @user will generate the equivalent of user_url. shouldn't the default behavior be user_path?

This is because url_for line 134 is calling polymorphic_url instead of polymorphic_path

Comments and changes to this ticket

  • Denis Odorcic

    Denis Odorcic October 28th, 2010 @ 04:17 AM

    This was fixed with commit 6c95e0f8 in master. I guess it just wasn't pushed to 3-0-stable?

  • Andrew White

    Andrew White October 28th, 2010 @ 08:09 AM

    • State changed from “new” to “needs-more-info”
    • Assigned user changed from “Ryan Bigg” to “Andrew White”
    • Importance changed from “” to “Low”

    Nader, can you confirm in what context you are using url_for? In a view the :only_path option is passed to url_for so the fact that url_for is calling polymorphic_url shouldn't make a difference. In fact polymorphic_path calls polymorphic_url itself.

    Denis, I can't see anything in that commit that would change this behaviour - how are you comparing things?

  • Denis Odorcic

    Denis Odorcic October 28th, 2010 @ 03:13 PM

    You're right, I misread something. Woops ;)

  • Nader Akhnoukh

    Nader Akhnoukh October 28th, 2010 @ 04:30 PM

    I'm using link_to @user from a view.

    The code path goes like this:
    -routing/url_for.rb:url_for (options is just the user record) # i think this is the bug, shouldn't the call to polymorphic_url be polymorphic_path (so that the default is _path) -routing/polymorphic_routes.rb:polymorphic_url which calls.. -routing/polymorphic_routes.rb:build_named_route_call which calls.. -routing/polymorphic_routes.rb:routing_type which spits out user_url because there is no routing_type in options (routing_type gets set in polymorphic_path)

  • Andrew White

    Andrew White October 28th, 2010 @ 04:55 PM

    It should be first going through url_helper.rb:

    http://github.com/rails/rails/blob/master/actionpack/lib/action_vie...

    Can you provide a backtrace?

  • Nader Akhnoukh

    Nader Akhnoukh October 28th, 2010 @ 05:39 PM

    OK, I think I see what's happening. Here's the backtrace from where the routing type is set to url:

    --> #0 ActionDispatch::Routing::PolymorphicRoutes.routing_type(options#Hash) 
           at line /Library/Ruby/Gems/1.8/gems/actionpack-3.0.0/lib/action_dispatch/routing/polymorphic_routes.rb:147
        #1 ActionDispatch::Routing::PolymorphicRoutes.build_named_route_call(records#User,...) 
           at line /Library/Ruby/Gems/1.8/gems/actionpack-3.0.0/lib/action_dispatch/routing/polymorphic_routes.rb:173
        #2 ActionDispatch::Routing::PolymorphicRoutes.polymorphic_url(record_or_hash_or_array#User,...) 
           at line /Library/Ruby/Gems/1.8/gems/actionpack-3.0.0/lib/action_dispatch/routing/polymorphic_routes.rb:108
        #3 ActionDispatch::Routing::UrlFor.url_for(options#User) 
           at line /Library/Ruby/Gems/1.8/gems/actionpack-3.0.0/lib/action_dispatch/routing/url_for.rb:135
        #4 ApplicationController.url_for(options#User) 
           at line /Users/nader/repo/napa/app/controllers/application_controller.rb:122
        #5 Kernel.send(args#Array, blk#NilClass) 
           at line /Library/Ruby/Gems/1.8/gems/actionpack-3.0.0/lib/abstract_controller/helpers.rb:55
        #6 #<Module:0x1071fcb08>.url_for 
           at line /Library/Ruby/Gems/1.8/gems/actionpack-3.0.0/lib/abstract_controller/helpers.rb:55
        #7 ActionView::Helpers::UrlHelper.link_to 
           at line /Library/Ruby/Gems/1.8/gems/actionpack-3.0.0/lib/action_view/helpers/url_helper.rb:236
        #8 ActionView::Helpers::UrlHelper.link_to 
           at line /Library/Ruby/Gems/1.8/gems/actionpack-3.0.0/lib/action_view/helpers/url_helper.rb:229
        #9 ActionView::CompiledTemplates._app_views_menus__login_items_html_haml___947485747_2208807400_3573310(local_assigns#Hash,...) 
           at line /Users/nader/repo/napa/app/views/menus/_login_items.html.haml:5
    

    As you can see in Step 4 I have a url_for defined in my application_controller. It's not doing anything besides calling super though (it was, but i commented it out to test):

    def url_for(options = nil)
      super
    end
    

    It looks like my call to super is going to the url_for in routing/url_for.rb instead of the url_for in helpers/url_helper right?

  • Nader Akhnoukh

    Nader Akhnoukh October 28th, 2010 @ 08:36 PM

    Also, the reason this is a big issue for me is because my clients use CNAMEs to get to their site. I have a piece of rack middleware that looks up the CNAME and sets the request headers to the proper subdomain so the app knows what to do. With the above issue my urls get written out using the subdomain instead of the CNAME and the users are asked to reauthenticate as the session cookie isn't shared across different domains

  • Andrew White

    Andrew White October 29th, 2010 @ 06:26 AM

    • State changed from “needs-more-info” to “invalid”

    Actually it shouldn't be hitting the url_for in ApplicationController - it doesn't in a test app. In fact the only way I can get it to go to the url_for in ApplicationController is to use helper_method :url_for in ApplicationController, e.g:

    class ApplicationController < ActionController::Base
      helper_method :url_for
    
      def url_for(options = nil)
        super
      end
    end
    

    Doing this reproduces your error. Is this what you're doing? If so all I can say is don't do this. If you want to override the :host option you want to be looking at default_url_options and/or url_options. A simple before_filter in ApplicationController that sets :host in default_url_options should be all that you need.

  • Nader Akhnoukh

    Nader Akhnoukh October 29th, 2010 @ 07:27 AM

    Wow, that was some impressive sleuthing. That's exactly what I was doing. Thanks for the huge help.

  • Nader Akhnoukh

    Nader Akhnoukh October 29th, 2010 @ 09:30 AM

    Andrew, the only thing I can't figure out is if I'm setting this in a before_filter I don't have access to the options hash. I am passing in a subdomain field with the options and based on that want to set the host. Thus overriding url_for. What's the better way to do that?

  • Andrew White

    Andrew White October 29th, 2010 @ 09:46 AM

    This should work:

    class ApplicationController < ActionController::Base
      before_filter :set_default_host
    
      protected
        def set_default_host
          default_url_options[:host] = subdomain_to_host(request.subdomain)
        end
    end
    

    The subdomain_to_host method is however you map the subdomain to the CNAME.

  • Nader Akhnoukh

    Nader Akhnoukh October 29th, 2010 @ 03:47 PM

    Hi Andrew, sorry I wasn't clear.

    I want to set the host based on some application logic. So this was my old method:

    def url_for(options = nil)
      if options.kind_of?(Hash) && options.has_key?(:subdomain)
        options[:host] = with_subdomain(options.delete(:subdomain))
      end
    
      super
    end
    

    and in my view:

    root_url(:subdomain => "xyz")
    

    How would you recommend doing that? I suppose I could get rid of the convenience "subdomain" key and just set the host directly?

  • Andrew White

    Andrew White October 29th, 2010 @ 03:55 PM

    Firstly, why do you need to use root_url and not root_path - that way you don't have to worry about the host name. If you do need the full url then if you set the host in the controller before filter you don't need to pass in the subdomain key.

  • Nader Akhnoukh

    Nader Akhnoukh October 29th, 2010 @ 04:26 PM

    Let say that for example I'm at my root app domain mydomain.com. And each of my users has their own subdomain. So in the view they each have links like root_url(:subdomain => user.subdomain) which should link to user1.mydomain.com and I also use root_url(:subdomain => false) to take people back to the main domain

    So in this case don't I need root_url vs root_path as the host matters?

    In this case how would I set the host in the before_filter? How would the before_filter have access to which subdomain to set as the host? Pass it as a request param or something?

  • Andrew White

    Andrew White October 29th, 2010 @ 05:26 PM

    So why not set the :host directly?

      root_url :host => user.host
    
  • Nader Akhnoukh

    Nader Akhnoukh October 29th, 2010 @ 05:33 PM

    yep, i think that might be the best solution. seems like messing with url_for just leads to trouble. thanks for all your help. just out of curiosity, do you know why there are two different url_fors?

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Pages