Skip to content
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
37 changes: 37 additions & 0 deletions lib/uri/generic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,43 @@ def registry # :nodoc:
#
attr_reader :fragment

#
# Deconstructs the URI into a hash for pattern matching.
#
# Returns a hash with keys: +:scheme+, +:userinfo+, +:host+, +:port+,
# +:path+, +:query+, +:fragment+, and +:params+.
#
# The +:params+ key contains the parsed query string as a hash with
# symbol keys, and is only computed when explicitly requested to avoid
# unnecessary parsing.
#
# uri = URI("http://example.com/path?foo=bar&baz=qux")
# uri.deconstruct_keys(nil)
# # => {:scheme=>"http", :userinfo=>nil, :host=>"example.com", :port=>80,
# # :path=>"/path", :query=>"foo=bar&baz=qux", :fragment=>nil,
# # :params=>{:foo=>"bar", :baz=>"qux"}}
#
# case uri
# in scheme: "http", host: "example.com", params: { foo: "bar" }
# # matches
# end
#
def deconstruct_keys(keys)
h = {
scheme: @scheme,
userinfo: userinfo,
host: @host,
port: @port,
path: @path,
query: @query,
fragment: @fragment
}
if @query && (keys.nil? || keys.include?(:params))
h[:params] = URI.decode_www_form(@query).to_h { |k, v| [k.to_sym, v] }
end
h
end

# Returns the parser to be used.
#
# Unless the +parser+ is defined, DEFAULT_PARSER is used.
Expand Down
55 changes: 55 additions & 0 deletions test/uri/test_generic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1116,4 +1116,59 @@ def with_proxy_env_case_sensitive(h, &b)
yield h
end

def test_deconstruct_keys
uri = URI("http://example.com:8080/path?foo=bar&baz=qux#fragment")
keys = uri.deconstruct_keys(nil)
assert_equal "http", keys[:scheme]
assert_equal "example.com", keys[:host]
assert_equal 8080, keys[:port]
assert_equal "/path", keys[:path]
assert_equal "foo=bar&baz=qux", keys[:query]
assert_equal "fragment", keys[:fragment]
assert_equal({ foo: "bar", baz: "qux" }, keys[:params])
end

def test_deconstruct_keys_no_params
uri = URI("http://example.com/path")
keys = uri.deconstruct_keys([:scheme, :host, :path])
assert_equal "http", keys[:scheme]
assert_equal "example.com", keys[:host]
assert_equal "/path", keys[:path]
refute keys.key?(:params)
end

def test_pattern_matching_scheme_and_host
begin
uri = URI("https://example.com/path")
result = instance_eval <<~RUBY, __FILE__, __LINE__ + 1
case uri
in scheme: "https", host: "example.com"
"matched"
else
"no match"
end
RUBY
assert_equal "matched", result
rescue SyntaxError
omit "Pattern matching not supported in Ruby < 2.7"
end
end

def test_pattern_matching_with_params
begin
uri = URI("http://example.com/search?q=ruby&lang=en")
result = instance_eval <<~RUBY, __FILE__, __LINE__ + 1
case uri
in scheme: "http", path: "/search", params: { q: "ruby" }
"search query"
else
"no match"
end
RUBY
assert_equal "search query", result
rescue SyntaxError
omit "Pattern matching not supported in Ruby < 2.7"
end
end

end