Rails 7.1 adds path_params option to url_for for flexible URL generation
The url_for method generates complete URLs (including protocol, host, port, and path) within Rails applications.
The url_for
syntax is as follows.
You can pass controller
,
action
,
and
id
as options
for this method.
url_for(options = {}, path_params = {}, format: nil)
Before Rails 7.1
Rails scoped routes allow you to group related routes under a common path prefix, controller namespace, or route helper prefix. This helps organize your routes, making them cleaner and easier to understand.
Prior to Rails 7.1,
generating links for scoped routes—those nested under a parent resource like
account_id
required repeatedly specifying the parent ID throughout your views.
This often led to tedious code
and
potential maintenance headaches.
# routes.rb
Rails.application.routes.draw do
scope ":account_id" do
get "dashboard" => "dashboard#index"
get "orders" => "orders#index"
end
get "/merchants" => "merchants#index"
end
In the view file,
you must explicitly pass the account_id
to the URL helper methods.
{/* app/views/home/index.html.erb */}
<%= link_to "Dashboard", dashboard_path(account_id: @current_account.id) %>
<%= link_to "Orders", orders_path(account_id: @current_account.id) %>
To resolve the above issue,
Rails added the
default_url_options
method.
The default_url_options
provides a way to set default options
for generating URLs with URL helpers like url_for
and
link_to
.
This is particularly useful for ensuring consistent URLs across different environments
(development,
testing,
production)
and
handling scenarios like email links where you need to specify a different host
or protocol.
You need to add the default_url_options
method in the ApplicationController
and
set the account_id
as shown below:
# application_controller.rb
class ApplicationController < ActionController::Base
def default_url_options
{ account_id: current_account&.id }
end
end
While the initial approach aimed to associate routes with a specific account
through the account_id
scope,
an unintended consequence emerged: the query param
?account_id=<account_id>
was inadvertently appended to all routes,
even those outside the intended scope. This led to URLs like:
orders_path
=> /<account_id>/orders
merchants_path
=> /merchants?account_id=<account_id>
This approach results in aesthetically unappealing URLs, and can lead to caching complications with CDN when employed in real-world scenarios.
In Rails 7.1
Rails 7.1 adds a path_params option to the url_for
method.
The path_params
option ensures precise control over URL path segments.
It only incorporates parameters that directly align with named segments in the route,
discarding those that don't match,
preventing them from becoming query parameters.
You need to change the default_url_options
methods as follows:
class ApplicationController < ActionController::Base
def default_url_options
{ path_params: { account_id: current_account&.id } }
end
end
The output of the url_for
method considers account_id
only when it
is required.
dashboard_path
=> /<account_id>/dashboard
dashboard_path(account_id: "account-id")
=> /account-id/dashboard
merchants_path
=> /merchants
merchants_path(account_id: "test-id")
=> /merchants
The benefits of this feature are:
-
Cleaner URLs with fewer query parameters
-
More control over URL structure
-
Potential SEO benefits
To know more about this feature, please refer to this PR.