From b51987e2b976835ecfc6c0882ec794f7325121f6 Mon Sep 17 00:00:00 2001 From: Brandon Weaver Date: Sat, 8 Nov 2025 18:22:39 -0800 Subject: [PATCH 1/3] Add pattern matching support to Net::HTTPGenericRequest Implements deconstruct_keys to enable pattern matching on HTTP requests based on method, path, and other request attributes. Example: case request in method: 'POST', path: %r{^/api/} handle_api_request(request) in method: 'GET', path: '/' handle_root end --- lib/net/http/generic_request.rb | 20 +++++++++++++++++ test/net/http/test_http_request.rb | 35 ++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/lib/net/http/generic_request.rb b/lib/net/http/generic_request.rb index c92004e5..080ad9c6 100644 --- a/lib/net/http/generic_request.rb +++ b/lib/net/http/generic_request.rb @@ -9,6 +9,8 @@ # :include: doc/net-http/examples.rdoc # class Net::HTTPGenericRequest + # Valid keys for pattern matching via #deconstruct_keys. + PATTERN_MATCHING_KEYS = %i[method path uri body content_type].freeze include Net::HTTPHeader @@ -208,6 +210,24 @@ def body_stream=(input) input end + # Returns a hash of request attributes for pattern matching. + # + # Valid keys are: +:method+, +:path+, +:uri+, +:body+, +:content_type+ + # + # Example: + # + # case request + # in method: 'POST', path: %r{^/api/} + # handle_api_request(request) + # in method: 'GET', path: '/' + # handle_root + # end + # + def deconstruct_keys(keys) + valid_keys = keys ? PATTERN_MATCHING_KEYS & keys : PATTERN_MATCHING_KEYS + valid_keys.to_h { |key| [key, public_send(key)] } + end + def set_body_internal(str) #:nodoc: internal use only raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream) self.body = str if str diff --git a/test/net/http/test_http_request.rb b/test/net/http/test_http_request.rb index 7fd82b03..b0358b83 100644 --- a/test/net/http/test_http_request.rb +++ b/test/net/http/test_http_request.rb @@ -89,5 +89,40 @@ def test_header_set 'Bug #7831 - do not decode content if the user overrides' end if Net::HTTP::HAVE_ZLIB + def test_deconstruct_keys + uri = URI('https://example.com/api/users') + req = Net::HTTP::Post.new(uri) + req.body = 'test data' + req['content-type'] = 'application/json' + + keys = req.deconstruct_keys(nil) + assert_equal 'POST', keys[:method] + assert_equal '/api/users', keys[:path] + assert_equal uri, keys[:uri] + assert_equal 'test data', keys[:body] + assert_equal 'application/json', keys[:content_type] + end + + def test_deconstruct_keys_with_specific_keys + req = Net::HTTP::Get.new('/test') + + keys = req.deconstruct_keys([:method, :path]) + assert_equal({method: 'GET', path: '/test'}, keys) + end + + def test_pattern_matching + uri = URI('https://example.com/api/users') + req = Net::HTTP::Post.new(uri) + + matched = case req + in method: 'POST', path: %r{^/api/} + true + else + false + end + + assert_equal true, matched + end + end From c0fccb39f72c4a899b6cbddc283a1e51927cb704 Mon Sep 17 00:00:00 2001 From: Brandon Weaver Date: Sat, 8 Nov 2025 18:31:36 -0800 Subject: [PATCH 2/3] Add Ruby 2.6 compatibility for pattern matching tests --- test/net/http/test_http_request.rb | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test/net/http/test_http_request.rb b/test/net/http/test_http_request.rb index b0358b83..4573b034 100644 --- a/test/net/http/test_http_request.rb +++ b/test/net/http/test_http_request.rb @@ -114,14 +114,19 @@ def test_pattern_matching uri = URI('https://example.com/api/users') req = Net::HTTP::Post.new(uri) - matched = case req - in method: 'POST', path: %r{^/api/} - true - else - false + begin + matched = instance_eval <<~RUBY, __FILE__, __LINE__ + 1 + case req + in method: 'POST', path: %r{^/api/} + true + else + false + end + RUBY + assert_equal true, matched + rescue SyntaxError + skip "Pattern matching requires Ruby 2.7+" end - - assert_equal true, matched end end From 53aed15371e0c8341366212d710ca95f6f85e03a Mon Sep 17 00:00:00 2001 From: Brandon Weaver Date: Sat, 8 Nov 2025 18:37:45 -0800 Subject: [PATCH 3/3] Fix skip to omit for Test::Unit compatibility --- test/net/http/test_http_request.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/net/http/test_http_request.rb b/test/net/http/test_http_request.rb index 4573b034..e846703a 100644 --- a/test/net/http/test_http_request.rb +++ b/test/net/http/test_http_request.rb @@ -125,7 +125,7 @@ def test_pattern_matching RUBY assert_equal true, matched rescue SyntaxError - skip "Pattern matching requires Ruby 2.7+" + omit "Pattern matching requires Ruby 2.7+" end end