November 7, 2023
This blog is part of our Rails 7 series.
With Rails 7.1, the url_for helper now supports a new option called
path_params.
Prior to Rails 7.1, if you had your routes configured to be scoped under say,
user_id as shown below, you would have to explicitly specify the user_id in
every single place where you want to generate a link for the scoped routes, such
as when writing view files.
#routes.rb
Rails.application.routes.draw do
root "articles#index"
scope ":user_id" do
get "/articles", to: "articles#index"
get "/articles/:id", to: "articles#show"
end
get "/categories", to: "categories#index"
end
<!-- app/views/articles/index.html.erb -->
<a href="<%= articles_path(user_id: @current_user.id) %>"> Articles </a>
This could be solved by updating ApplicationController and overwriting the
default_url_options method:
# application_controller.rb
class ApplicationController < ActionController::Base
def default_url_options
{ user_id: "unique-id" }
end
end
The default_url_options method is used to overwrite and set default options
for all the methods based on url_for. However, this meant that all routes,
even those that did not belong to the user_id scope, would have the
?user_id=unique-id query param added to the end of them resulting in the
following output:
articles_path # => /unique-id/articles
categories_path # => /categories?user_id=unique-id
Rails 7.1 fixes this issue with the addition of the path_params option. If you
pass a hash of parameters to this key, those parameters will only be used for
the named segments of the route. If they aren't used, they are discarded instead
of being appended to the end of the route as query params.
With this change, we can implement the default_url_options method as follows:
class ApplicationController < ActionController::Base
def default_url_options
{ path_params: { user_id: "unique-id" } }
end
end
The url_for helper method will now give you the following output:
articles_path # => /unique-id/articles
articles_path(user_id: "test-id") # => /test-id/articles
categories_path # => /categories
categories_path(user_id: "test-id") # => /categories
The view file can now be written as:
<a href="<%= articles_path %>"> Articles </a>
This is very useful in situations where you only want to add a required param that is part of the route's URL without introducing unnecessary query params for other routes.
Please check out this pull request for more details.
If this blog was helpful, check out our full blog archive.