Skip to content

Conversation

bensheldon
Copy link
Contributor

Motivation / Background

The current_page? helper has historically only matched on GET and HEAD requests. This creates challenges when rendering content around validation errors on POST/PUT/PATCH actions.

This PR adds an optional method: option that allows passing an explicit http method (or methods) to the current_page? helper to match specific http verbs.

This makes it easier to render simple navigations like this:

<ul class="nav nav-tabs">
  <li class="nav-item"><%= link_to "All posts", posts_path, class: ["nav-link", { active: current_page?(posts_path) }] %></li>
  <li class="nav-item"><%= link_to "Create post", new_post_path, class: ["nav-link", { active: current_page?(new_post_path) || current_page?(posts_path, method: :post) }] %></li>
</ul>

This is necessary because both the PostsController#index and PostsController#create share the same URL path but are differentiated by their HTTP method.

Checklist

Before submitting the PR make sure the following are checked:

  • This Pull Request is related to one change. Unrelated changes should be opened in separate PRs.
  • Commit message has a detailed description of what changed and why. If this PR fixes a related issue include it in the commit message. Ex: [Fix #issue-number]
  • Tests are added or updated if you fix a bug or add a feature.
  • CHANGELOG files are updated for the changed libraries if there is a behavior change or additional feature. Minor bug fixes and documentation changes should not be included.

@rails-bot rails-bot bot added the actionview label Jul 1, 2025
@byroot
Copy link
Member

byroot commented Jul 11, 2025

Maybe I'm dense, and this change is probably fine, but I don't understand in which case you'd need pagination helpers on a POST.

@p8
Copy link
Member

p8 commented Jul 11, 2025

@byroot current_page? is unrelated to pagination. It returns true if "the current request URI was generated by the given options".
https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-current_page-3F

@bensheldon
Copy link
Contributor Author

@byroot The current_page? helper is almost exclusively used for adding an "active" state to HTML links in navigation elements within shared-partials and layouts.

That could be pagination, but my targeted need is when the navigation includes "New Post" or "Edit Post" links that render a form that could be submitted and have validation errors. In those cases, there can still be a requirement for an "active" state on the navigation when rendering validation errors. That's not possible without a change like this PR.

@byroot
Copy link
Member

byroot commented Jul 12, 2025

Yeah, sorry I had a bit of a brain fart.

I skimmed over the historical issues, seems like that behavior was almost immediately regretted and people wanted it reverted but nothing happened.

I need to find some clear headed time to see if there is an elegant solution to this.

@byroot
Copy link
Member

byroot commented Aug 22, 2025

Ok, backtracking a bit here. I haven't done frontend work in a very long time, and even then I don't remember using current_page?, so I need to read in tea leaves somewhat.

Here's what the doc says:

True if the current request URI was generated by the given options.

With examples such as:

current_page?(action: 'process')
# => false

current_page?(action: 'checkout')
# => true

current_page?(controller: 'library', action: 'checkout')
# => false

My read on this is that this helper kinda made sense back in the url_for days, but modern rails is more about path helpers.

Which begs the question of whether this need of your wouldn't be better solved by a newer method (or just a newer argument pattern) that is more "path helpers" native.

e.g. your example:

active: current_page?(new_post_path) || current_page?(posts_path, method: :post)

What initially rubbed me the wrong way here is that posts_path, method: :post was weird, because we're referencing the helper for a route with a method that don't match it. But at the same time, posts#create doesn't have a route helper, so... I guess that's the best we can do here?

In the end that method is still very much url_for alike, so supporting method: is probably fine.

I'll try to review the code more in detail though.

@byroot byroot force-pushed the current_page-methods branch from 17c2254 to dbf147e Compare August 22, 2025 13:38
check_parameters ||= options.is_a?(Hash) && options.delete(:check_parameters)
if options.is_a?(Hash)
check_parameters ||= options.delete(:check_parameters)
method ||= options.delete(:method)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you can do ||= here. it works for check_parameters because it defaults to false, bit method defaults to a truthy value.

method_matches = if method == :get
request.get? || request.head?
else
request_method = request.method.downcase.to_sym
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use Request#method_symbol here.

@byroot byroot force-pushed the current_page-methods branch from dbf147e to 274e40f Compare August 22, 2025 13:44
@@ -539,24 +539,46 @@ def mail_to(email_address, name = nil, html_options = {}, &block)
# current_page?('http://www.example.com/shop/checkout?order=desc&page=1')
# # => true
#
# Let's say we're in the <tt>http://www.example.com/products</tt> action with method POST in case of invalid product.
# Different actions may share the same URL path but have a different HTTP method. Let's say we
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the indentation is wrong here.

@byroot byroot force-pushed the current_page-methods branch from 274e40f to 63b056f Compare August 22, 2025 13:46
@byroot byroot force-pushed the current_page-methods branch from 63b056f to 0e19842 Compare August 22, 2025 13:48
Comment on lines +566 to +571
if options.is_a?(Hash)
check_parameters = options.delete(:check_parameters) { check_parameters }
method = options.delete(:method) { method }
else
options ||= options_as_kwargs
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tweaked the options parsing code a bit.

@byroot byroot merged commit 1f9ca6c into rails:main Aug 22, 2025
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants