Skip to content

Allow current_page? to match against specific HTTP method(s) with a method: option #55286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions actionview/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
* Allow `current_page?` to match against specific HTTP method(s) with a `method:` option.

*Ben Sheldon*

* Add `dom_target` helper to create `dom_id`-like strings from an unlimited
number of objects.

Expand Down
35 changes: 30 additions & 5 deletions actionview/lib/action_view/helpers/url_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -538,24 +538,49 @@ 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
# sent a POST to <tt>http://www.example.com/products</tt> and rendered a validation error.
#
# current_page?(controller: 'product', action: 'index')
# # => false
#
# current_page?(controller: 'product', action: 'create')
# # => false
#
# current_page?(controller: 'product', action: 'create', method: :post)
# # => true
#
# current_page?(controller: 'product', action: 'index', method: [:get, :post])
# # => true
#
# We can also pass in the symbol arguments instead of strings.
#
def current_page?(options = nil, check_parameters: false, **options_as_kwargs)
def current_page?(options = nil, check_parameters: false, method: :get, **options_as_kwargs)
unless request
raise "You cannot use helpers that need to determine the current " \
"page unless your view context provides a Request object " \
"in a #request method"
end

return false unless request.get? || request.head?

options ||= options_as_kwargs
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)
end

method_matches = if method == :get
request.get? || request.head?
else
request_method = request.method.downcase.to_sym
if method.is_a?(Array)
method << :head if method.include?(:get)
method.include?(request_method)
else
method == request_method
end
end
Comment on lines +571 to +581
Copy link
Member

Choose a reason for hiding this comment

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

The extra nesting of this if statement could probably be removed?

Suggested change
method_matches = if method == :get
request.get? || request.head?
else
request_method = request.method.downcase.to_sym
if method.is_a?(Array)
method << :head if method.include?(:get)
method.include?(request_method)
else
method == request_method
end
end
method_matches = if method == :get
request.get? || request.head?
elsif method.is_a?(Array)
request_method = request.method.downcase.to_sym
method << :head if method.include?(:get)
method.include?(request_method)
else
method == request_method
end

Or maybe even:

Suggested change
method_matches = if method == :get
request.get? || request.head?
else
request_method = request.method.downcase.to_sym
if method.is_a?(Array)
method << :head if method.include?(:get)
method.include?(request_method)
else
method == request_method
end
end
method_matches = case method
when :get
request.get? || request.head?
when Array
request_method = request.method.downcase.to_sym
method << :head if method.include?(:get)
method.include?(request_method)
else
method == request_method
end

Copy link
Member

Choose a reason for hiding this comment

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

Argh, nevermind. request_method is used in the last case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, I couldn't find the prettiest logic myself 😞 I was wanting to preserve the existing/default as a fast-path and the else block is "new behavior".

Copy link
Contributor Author

@bensheldon bensheldon Jul 10, 2025

Choose a reason for hiding this comment

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

... and I'm trying to avoid creating an array unnecessarily as I could coerce everything to Array(method) but I think I've been warned off that before 😁

return false unless method_matches

url_string = URI::RFC2396_PARSER.unescape(url_for(options)).force_encoding(Encoding::BINARY)

# We ignore any extra parameters in the request_uri if the
Expand Down
21 changes: 21 additions & 0 deletions actionview/test/template/url_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,27 @@ def test_current_page_with_not_get_verb
@request = request_for_url("/events", method: :post)

assert_not current_page?("/events")
assert current_page?("/events", method: :post)
end

def test_current_page_with_array_of_methods_including_request_method
@request = request_for_url("/events", method: :post)

assert current_page?("/events", method: [:post, :put, :delete])
assert_not current_page?("/events", method: [:put, :delete])
end

def test_current_page_with_array_of_methods_including_get_method_includes_head
@request = request_for_url("/events", method: :head)

assert current_page?("/events", method: [:post, :get])
end

def test_current_page_preserves_method_param_in_url
@request = request_for_url("/events?method=post", method: :put)

assert current_page?("/events?method=post", method: :put)
assert_not current_page?("/events?method=post", method: :post)
end

def test_link_unless_current
Expand Down