I would like to take out a parameter from a URL by its name without knowing which parameter it is, and reassemble the URL again.
I guess it is not that hard to write something on my own using CGI or URI, but I imagine such functionality exists already. Any suggestions?
In:
http://example.com/path?param1=one¶m2=2¶m3=something3
Out:
http://example.com/path?param2=2¶m3=something3
The addressable gem will do this nicely; please see the superior answer by The Tin Man. But if you want to roll your own, here's how. The only claim this code has to elegance is that it hides the ugly in a method:
#!/usr/bin/ruby1.8
def reject_param(url, param_to_reject)
# Regex from RFC3986
url_regex = %r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$"
raise "Not a url: #{url}" unless url =~ url_regex
scheme_plus_punctuation = $1
authority_with_punctuation = $3
path = $5
query = $7
fragment = $9
query = query.split('&').reject do |param|
param_name = param.split(/[=;]/).first
param_name == param_to_reject
end.join('&')
[scheme_plus_punctuation, authority_with_punctuation, path, '?', query, fragment].join
end
url = "http://example.com/path?param1=one¶m2=2¶m3=something3"
p url
p reject_param(url, 'param2')
# => "http://example.com/path?param1=one¶m2=2¶m3=something3"
# => "http://example.com/path?param1=one¶m3=something3"
I prefer to use:
require 'addressable/uri'
uri = Addressable::URI.parse('http://example.com/path?param1=one¶m2=2¶m3=something3')
params = uri.query_values #=> {"param1"=>"one", "param2"=>"2", "param3"=>"something3"}
params.delete('param1') #=> "one"
uri.query_values = params #=> {"param2"=>"2", "param3"=>"something3"}
uri.to_s #=> "http://example.com/path?param2=2¶m3=something3"
Maybe a little off-topic, but for anyone who's attempting to do this in the context of a rails app you can simply do:
url_for(params.except(:name_of_param_to_delete))
N.B. Tested in rails v2.3.9.
If you don't want to include an extra Gem and if you don't want nasty Regex, here's my prefered way:
require 'cgi'
require 'uri'
url = "http://example.com/path?param1=one¶m2=2¶m3=something3"
uri = URI(url) #=> #<URI::HTTP:0x007fbe25141a78 URL:http://example.com/path?param1=one¶m2=2¶m3=something3>
params = Rack::Utils.parse_nested_query(uri.query || "") #=> {"param1"=>["one"], "param2"=>["2"], "param3"=>["something3"]}
params.delete('param1') #=> ["one"]
uri.query = URI.encode_www_form(params) #=> "param2=2¶m3=something3"
uri.to_s #=> "http://example.com/path?param2=2¶m3=something3"
Note, choose wisely:
CGI.parse('a=b&a=c') #=> {"a"=>["b", "c"]}
Rack::Utils.parse_nested_query('a=b&a=c') #=> {"a"=>"c"}
And:
URI.encode_www_form({ a: :b, c: { d: 2 }}) #=> "a=b&c=%7B%3Ad%3D%3E2%7D"
{ a: :b, c: { d: 2 }}.to_query #=> "a=b&c%5Bd%5D=2"
I came up with something like this
def uri_remove_param(uri, params = nil)
return uri unless params
params = Array(params)
uri_parsed = URI.parse(uri)
return uri unless uri_parsed.query
escaped = uri_parsed.query.grep(/&/).size > 0
new_params = uri_parsed.query.gsub(/&/, '&').split('&').reject { |q| params.include?(q.split('=').first) }
uri = uri.split('?').first
amp = escaped ? '&' : '&'
"#{uri}?#{new_params.join(amp)}"
end
One line should be enough:
url.sub(/\?param_to_remove=[^&]*/, '?').sub(/\¶m_to_remove=[^&]*/, '').sub(/\?$/,'')