In Rails, the url_for helper method is used to generate URLs with given set of parameters for different actions within our application.
# Generating a URL for a specific controller action
url_for(controller: 'posts', action: 'show', id: 1)
# Output: "/posts/1"# Generating a URL with named routes
url_for(controller: 'posts', action: 'index')
# Output: "/posts"# Generating a URL with additional parameters
url_for(controller: 'posts', action: 'index', page: 2)
# Output: "/posts?page=2"# routes.rb
Rails.application.routes.draw do
get 'posts/:id', to: 'posts#show', as: 'post'
end# Generating a URL using a named route
url_for(post_path(1))
# Output: "/posts/1"# Generating a URL for a specific object
post = Post.find(1)
url_for(post)
# Output: "/posts/1"Before
Before Rails 7.1, when routes were scoped (e.g., under user_id), generating links for scoped routes in view files require explicit specification of user_id in every instance, creating repetitive code for link generation.
Rails.application.routes.draw do
scope "user_id" do
get "/posts", to: "posts#index", as: :posts
get "/posts/:id", to: "posts#show", as: :post
end
get "/comments", to: "comments#index", as: :comments
delete "/signout", to: "sessions#destroy", as: :signout
end<!-- app/views/posts/index.html.erb -->
<a href="<%= posts_path(user_id: current_user.id) %>"> Post </a>Instead of passing user_id to every scoped URL, we can set a default value for user_id using default_url_options method inside ApplicationController.
default_url_options method allows us to set default URL options that will be used by URL-generating methods, such as url_for, across the entire application.
class ApplicationController < ActionController::Base
def default_url_options
{ user_id: current_user.id }
end
endHowever, with the default_url_options configuration, all routes, those lived outside the user_id scope (like authentication routes or comment routes) will have ?user_id=current_user.id query parameter at their end.
posts_path # => /current_user.id/posts
comments_path # => /comments?user_id=current_user.id
signout_path # => /signout?user_id=current_user.idAfter
The above behavior causes URLs for non-scoped routes to include the scoped parameters set by default_url_options, leading to potential confusion, incorrect URLs,
and aesthetics issues in scenarios where the parameters are not expected or needed.
The introduction of path_params option in Rails 7.1 for url_for method addresses the above issue by allowing specified parameters to be used exclusively for named segments of the route, avoiding their addition to non-segment parts of URLs.
class ApplicationController < ActionController::Base
def default_url_options
{ path_params: { user_id: current_user.id } }
end
endurl_for helpers will now behave as follows:
posts_path # => /current_user.id/posts
posts_path(user_id: 2) # => /2/posts
comments_path # => /comments
comments_path(user_id: 2) # => /comments?user_id=2
signout_path # => /signout
signout_path(user_id: 2) # => /signout?user_id=2Lets discuss the above example in detailed manner:
-
posts_pathgenerates the path/current_user.id/posts, using the defaultuser_idprovided bypath_params. -
posts_path(user_id: 2)generates the path/2/posts, explicitly specifyinguser_idas 2. It overrides the defaultuser_idprovided bypath_params. -
comments_pathgenerates the path/commentswithout any additional parameters. -
comments_path(user_id: 2)generates the path/comments?user_id=2, addinguser_idas a query parameter sincecomments_pathdoesn’t have a named segment foruser_id. -
signout_pathgenerates the path/signoutwithout any additional parameters. -
signout_path(user_id: 2)generates the path/signout?user_id=2, addinguser_idas a query parameter, similar tocomments_path.
Summary
Scoping routes under any value (e.g. user_id) leads to redundancy, requiring explicit value (user_id) inclusion in URL helper methods.
As default_url_options affecting non-scoped routes like authentication ones by adding the scoped value (user_id) as query params resulting in unexpected outputs.
Where as path_params enables specified parameters to be exclusively used for named segments in scoped routing, preventing unnecessary additions to every URL.
