Skip to content

Loading…

route url defaults take precedence over controller params/url_options #7144

Closed
the8472 opened this Issue · 18 comments
@the8472

Using 3.2.6

#routes.rb
match "/test(/:foo)" => "my_controller#index", :defaults => {:foo => "my_default"}, :as => :test_index
match ":id/test(/:foo)" => "my_controller#show", :defaults => {:foo => "my_default"}, :as => :test_show
# my_controller.rb

# this shouldn't even be necessary since url_for takes its context from the controller anyway
def url_options
   super.merge(:foo => params[:foo])
end

load /test/something-thats-not-default

-# index.html.haml

=link_to "link text", test_show_path(some_object)

Resulting link goes to 1/test/ instead of 1/test/something-thats-not-default

This works on the other hand, but seems verbose as i have to drag along parameters everywhere:

 =link_to "link text", test_show_path(some_object, :foo => params[:foo])
@kytrinyx

Resulting link goes to 1/test/ instead of 1/test/something-thats-not-default

I'm not sure how =link_to "link text", test_show_path(some_object) can be expected to link to 1/test/something-thats-not-default, since you're creating a link, and you're not giving it a value for foo.

Could you point me to the spot in the documentation that suggests that link_to would pass url_options into the named url method?

@the8472

http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-url_options

url_options work just fine if no defaults are specified in the routes.

@kytrinyx

I don't see anything there which suggests that url_for will call url_options for you.

url_options work just fine if no defaults are specified in the routes

You mean that if you change your routes.rb file to remove defaults, this works? I.e:

match "/test(/:foo)" => "my_controller#index", :as => :test_index
match ":id/test(/:foo)" => "my_controller#show", :as => :test_show
@the8472

I don't see anything there which suggests that url_for will call url_options for you.

It does.

Here, a puts caller straight from the url_options method in the controller:

/home/private/.rvm/gems/ruby-1.9.3-p194@project/gems/actionpack-3.2.6/lib/action_view/helpers/url_helper.rb:37:in `url_options'
/home/private/.rvm/gems/ruby-1.9.3-p194@project/gems/actionpack-3.2.6/lib/action_dispatch/routing/url_for.rb:148:in `url_for'
/home/private/.rvm/gems/ruby-1.9.3-p194@project/gems/actionpack-3.2.6/lib/action_view/helpers/url_helper.rb:107:in `url_for'
/home/private/.rvm/gems/ruby-1.9.3-p194@project/gems/actionpack-3.2.6/lib/action_dispatch/routing/route_set.rb:213:in `job_portal_publications_path'
/home/private/workspace/project/vendor/core-extensions/job_portal/app/views/project/job_portal/publications/index.html.haml:22:in `__home_private_workspace_project_vendor_core_extensions_job_portal_app_views_project_job_portal_publications_index_html_haml___1805346634538105884_90943260'

And other people use it too: http://stackoverflow.com/questions/6100954/default-url-options-and-rails-3

@kytrinyx

Ok, thanks for patiently walking me through this.

I've made a sample app so that I can see this happening. I think it is the reverse_merge! in this line that is causing this behavior:

https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/routing/url_for.rb#L148

The options that come into the method are the defaults (:foo => 'my_default'), and the url_options contains the override from the controller (:foo => 'something-thats-not-default'). Since this is a reverse merge rather than a merge, the defaults from options are overwriting any identical keys from url_options.

A lot of people have worked on this method, I'm not sure who knows what the intended behavior is. @wycats, is this familiar territory for you?

@timraymond

5 months, is this still an issue?

@pixeltrix pixeltrix was assigned
@Flauschbaellchen

It seems it is, I've just stumbled over it in my own app

@timraymond

Okay, thanks for confirming. Are you also using 3.2.6 or a different version?

@Flauschbaellchen

Sorry I didn't mention it.
I'm using rails 3.2.9/actionpack 3.2.9.

@bundacia

Just hit this issue myself (on rails 3.2.13). Is there a good workaround? Or a timeline for getting this fixed?

@Govinda-Fichtner

Is this still an issue? I think I also hit this problem...

@njakobsen

I'm having this issue in Rails 4.0.2.

# routes.rb
namespace :staff do
  resources :stores, :only => [:update] do
    get :edit_layout, :on => :member, :defaults => {:section => 'general'}
  end
end

# in my view
url_for(:section => 'accounts') # => http://localhost:3000/staff/stores/1/edit_layout

Note the section param is not present in the url.

@raphaelcosta

I'm having this issue too in Rails 4.1.0.rc1, you guys know any workaround for that?

@pixeltrix
Ruby on Rails member

Closing for the reasons outlined in this comment

@pixeltrix pixeltrix closed this
@the8472

The linked comment refers to the distinction of query vs. path parameters, but my example above is about path parameter defaults, not query ones.

@pixeltrix
Ruby on Rails member

@the8472 okay, I'll take another look - the PR was using query parameters as an example

@pixeltrix pixeltrix reopened this
@robin850 robin850 added this to the 4.0.6 milestone
@kuldeepaggarwal

@pixeltrix any updates? Is this still an issue?

@pixeltrix
Ruby on Rails member

@kuldeepaggarwal I'm going to close this as it appears to work as expected. The named url helpers explicitly use the route defaults unless overridden by arguments passed to it. I think what @the8472 was thinking is that they should use recall parameters (i.e. params from the current request) to override the defaults in the url helper but this would likely to cause problems as it would pick up other things as well like controller, action, id, etc. Using url_for with an options hash may work for what they want but it's unclear from the original example - typically you'd use it when switching from one action to another on an object, e.g:

def show
  redirect_to action: :edit
end

Here url_for would use the current request's params as input to the url (including :foo in @the8472's case) and then overwrite the :action parameter.

@pixeltrix pixeltrix closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.