From 9175358272fb609675818b35452035026f427b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20Gonc=CC=A7alves?= Date: Tue, 9 Jun 2026 10:58:32 +0100 Subject: [PATCH 1/2] Replace CGI.parse with internal parser for Ruby 4.0 compatibility Ruby 4.0 removed CGI.parse from the cgi stdlib, so parsing a referer's query string raised "undefined method 'parse' for class CGI". Add a small dependency-free parse_query helper (using the still-available CGI.unescape) that returns the same Hash-of-arrays shape CGI.parse produced. Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/referer-parser/parser.rb | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/referer-parser/parser.rb b/lib/referer-parser/parser.rb index 8bfb8aa..662c97e 100644 --- a/lib/referer-parser/parser.rb +++ b/lib/referer-parser/parser.rb @@ -103,7 +103,7 @@ def parse(obj) # Parse parameters if the referer uses them if url.query && referer_data[:parameters] - query_params = CGI.parse(url.query) + query_params = parse_query(url.query) referer_data[:parameters].each do |param| # If there is a matching parameter, get the first non-blank value unless (values = query_params[param]).empty? @@ -121,6 +121,21 @@ def parse(obj) protected + # Parse an x-www-form-urlencoded query string into a Hash of arrays, e.g. + # "q=a&q=b&hl=en" => {"q" => ["a", "b"], "hl" => ["en"]}. Missing keys return + # an empty array. This replaces CGI.parse, which was removed from Ruby's cgi + # stdlib in Ruby 4.0; CGI.unescape (used below) is still available. + def parse_query(query) + params = Hash.new { |hash, key| hash[key] = [] } + + query.to_s.split('&').each do |pair| + key, value = pair.split('=', 2).map { |component| CGI.unescape(component) } + params[key] << value if value + end + + params + end + # Determine the correct name_key for this host and path def domain_and_name_key_for(uri) # Create a proc that will return immediately From 0b11650a539c21a3a43dadd6a2ec80397775d901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20Gonc=CC=A7alves?= Date: Tue, 9 Jun 2026 11:11:04 +0100 Subject: [PATCH 2/2] parse_query: preserve keys that appear without '=' (match CGI.parse) A bare key like "a" in "a&b=1" was dropped because its value is nil. CGI.parse keeps it as an empty array ("a&b=1" => {"a"=>[], "b"=>["1"]}; "a=" => {"a"=>[""]}). Always materialize the key and only append when a value is present, and set the hash default to [] so missing keys behave like CGI.parse. Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/referer-parser/parser.rb | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/referer-parser/parser.rb b/lib/referer-parser/parser.rb index 662c97e..21490bd 100644 --- a/lib/referer-parser/parser.rb +++ b/lib/referer-parser/parser.rb @@ -121,18 +121,25 @@ def parse(obj) protected - # Parse an x-www-form-urlencoded query string into a Hash of arrays, e.g. - # "q=a&q=b&hl=en" => {"q" => ["a", "b"], "hl" => ["en"]}. Missing keys return - # an empty array. This replaces CGI.parse, which was removed from Ruby's cgi - # stdlib in Ruby 4.0; CGI.unescape (used below) is still available. + # Parse an x-www-form-urlencoded query string into a Hash of arrays, mirroring + # CGI.parse (removed from Ruby's cgi stdlib in Ruby 4.0; CGI.unescape, used + # below, is still available). Matching CGI.parse exactly: + # "q=a&q=b&hl=en" => {"q" => ["a", "b"], "hl" => ["en"]} + # "a&b=1" => {"a" => [], "b" => ["1"]} # bare key => empty array + # "a=" => {"a" => [""]} # key with empty value + # and missing keys return an empty array. def parse_query(query) - params = Hash.new { |hash, key| hash[key] = [] } + params = {} query.to_s.split('&').each do |pair| key, value = pair.split('=', 2).map { |component| CGI.unescape(component) } + next if key.nil? + + params[key] ||= [] params[key] << value if value end + params.default = [].freeze params end