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.