Rails 7.1 Adds path_params Option For url_for Helper Method

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
end

However, 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.id

After

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
end

url_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=2

Lets discuss the above example in detailed manner:

  • posts_path generates the path /current_user.id/posts, using the default user_id provided by path_params.

  • posts_path(user_id: 2) generates the path /2/posts, explicitly specifying user_id as 2. It overrides the default user_id provided by path_params.

  • comments_path generates the path /comments without any additional parameters.

  • comments_path(user_id: 2) generates the path /comments?user_id=2, adding user_id as a query parameter since comments_path doesn’t have a named segment for user_id.

  • signout_path generates the path /signout without any additional parameters.

  • signout_path(user_id: 2) generates the path /signout?user_id=2, adding user_id as a query parameter, similar to comments_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.

Need help on your Ruby on Rails or React project?

Join Our Newsletter