Skip to content

Commit a8f86fc

Browse files
Merge pull request rails#53158 from seanpdoyle/browser-block-with-method-name
AllowBrowser: support method names for `:block`
2 parents de71ea8 + 59e66f7 commit a8f86fc

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

actionpack/CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
* Update `ActionController::AllowBrowser` to support passing method names to `:block`
2+
3+
```ruby
4+
class ApplicationController < ActionController::Base
5+
allow_browser versions: :modern, block: :handle_outdated_browser
6+
7+
private
8+
def handle_outdated_browser
9+
render file: Rails.root.join("public/custom-error.html"), status: :not_acceptable
10+
end
11+
end
12+
```
13+
14+
*Sean Doyle*
15+
116
* Raise an `ArgumentError` when invalid `:only` or `:except` options are passed into `#resource` and `#resources`.
217

318
*Joshua Young*

actionpack/lib/action_controller/metal/allow_browser.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ module ClassMethods
3636
# end
3737
#
3838
# class ApplicationController < ActionController::Base
39+
# # Allow only browsers natively supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has
40+
# allow_browser versions: :modern, block: :handle_outdated_browser
41+
#
42+
# private
43+
# def handle_outdated_browser
44+
# render file: Rails.root.join("public/custom-error.html"), status: :not_acceptable
45+
# end
46+
# end
47+
#
48+
# class ApplicationController < ActionController::Base
3949
# # All versions of Chrome and Opera will be allowed, but no versions of "internet explorer" (ie). Safari needs to be 16.4+ and Firefox 121+.
4050
# allow_browser versions: { safari: 16.4, firefox: 121, ie: false }
4151
# end
@@ -55,7 +65,7 @@ def allow_browser(versions:, block:)
5565

5666
if BrowserBlocker.new(request, versions: versions).blocked?
5767
ActiveSupport::Notifications.instrument("browser_block.action_controller", request: request, versions: versions) do
58-
instance_exec(&block)
68+
block.is_a?(Symbol) ? send(block) : instance_exec(&block)
5969
end
6070
end
6171
end

actionpack/test/controller/allow_browser_test.rb

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,20 @@ def hello
88
head :ok
99
end
1010

11+
allow_browser versions: { safari: "16.4", chrome: "119", firefox: "123", opera: "106", ie: false }, block: :head_upgrade_required, only: :hello_method_name
12+
def hello_method_name
13+
head :ok
14+
end
15+
1116
allow_browser versions: :modern, block: -> { head :upgrade_required }, only: :modern
1217
def modern
1318
head :ok
1419
end
20+
21+
private
22+
def head_upgrade_required
23+
head :upgrade_required
24+
end
1525
end
1626

1727
class AllowBrowserTest < ActionController::TestCase
@@ -25,11 +35,16 @@ class AllowBrowserTest < ActionController::TestCase
2535
OPERA_106 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 OPR/106.0.0.0"
2636
GOOGLE_BOT = "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/W.X.Y.Z Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
2737

28-
test "blocked browser below version limit" do
38+
test "blocked browser below version limit with callable" do
2939
get_with_agent :hello, FIREFOX_114
3040
assert_response :upgrade_required
3141
end
3242

43+
test "blocked browser below version limit with method name" do
44+
get_with_agent :hello_method_name, FIREFOX_114
45+
assert_response :upgrade_required
46+
end
47+
3348
test "blocked browser by name" do
3449
get_with_agent :hello, IE_11
3550
assert_response :upgrade_required
@@ -68,9 +83,25 @@ class AllowBrowserTest < ActionController::TestCase
6883
assert_response :ok
6984
end
7085

86+
test "a blocked request instruments a browser_block.action_controller event" do
87+
event, *rest = capture_instrumentation_events "browser_block.action_controller" do
88+
get_with_agent :modern, CHROME_118
89+
end
90+
91+
assert_equal request, event.payload[:request]
92+
assert_not_empty event.payload[:versions]
93+
assert_empty rest
94+
end
95+
7196
private
7297
def get_with_agent(action, agent)
7398
@request.headers["User-Agent"] = agent
7499
get action
75100
end
101+
102+
def capture_instrumentation_events(pattern, &block)
103+
events = []
104+
ActiveSupport::Notifications.subscribed(->(e) { events << e }, pattern, &block)
105+
events
106+
end
76107
end

0 commit comments

Comments
 (0)