Какой самый элегантный способ в Ruby удалить параметр из URL?

Я хотел бы вынуть параметр из URL по его имени, не зная, какой параметр он есть, и снова собрать URL.

Я думаю, что не так сложно писать что-то самостоятельно, используя CGI или URI, но я предполагаю, что такая функциональность уже существует. Любые предложения?

В:

http://example.com/path?param1=one&param2=2&param3=something3

Из:

http://example.com/path?param2=2&param3=something3

Ответы

Ответ 1

Адресаемый драгоценный камень сделает это красиво; см. превосходный ответ The Tin Man. Но если вы хотите бросить свои собственные, вот как. Единственное утверждение, что этот код должен быть элегантным, заключается в том, что он скрывает уродливость в методе:

#!/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&param2=2&param3=something3"
p url
p reject_param(url, 'param2')

# => "http://example.com/path?param1=one&param2=2&param3=something3"
# => "http://example.com/path?param1=one&param3=something3"

Ответ 2

Я предпочитаю использовать:

require 'addressable/uri'

uri = Addressable::URI.parse('http://example.com/path?param1=one&param2=2&param3=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&param3=something3"

Ответ 3

Возможно, немного не по теме, но для тех, кто пытается это сделать в контексте приложения rails, вы можете просто сделать:

url_for(params.except(:name_of_param_to_delete))

N.B. Протестировано в рельсах v2.3.9.

Ответ 4

Если вы не хотите включать дополнительный драгоценный камень и если вы не хотите отвратительного регулярного выражения, вот мой предпочтительный способ:

require 'cgi'
require 'uri'

url = "http://example.com/path?param1=one&param2=2&param3=something3"

uri = URI(url) #=> #<URI::HTTP:0x007fbe25141a78 URL:http://example.com/path?param1=one&param2=2&param3=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&param3=something3"

uri.to_s #=> "http://example.com/path?param2=2&param3=something3"

Обратите внимание, выбирайте мудро:

CGI.parse('a=b&a=c') #=> {"a"=>["b", "c"]}
Rack::Utils.parse_nested_query('a=b&a=c') #=> {"a"=>"c"}

А:

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"

Ответ 5

Я придумал что-то вроде этого

 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(/&amp;/).size > 0
   new_params = uri_parsed.query.gsub(/&amp;/, '&').split('&').reject { |q| params.include?(q.split('=').first) }
   uri = uri.split('?').first
   amp = escaped ? '&amp;' : '&'
   "#{uri}?#{new_params.join(amp)}"
 end

Ответ 6

Одной строки должно быть достаточно:

url.sub(/\?param_to_remove=[^&]*/, '?').sub(/\&param_to_remove=[^&]*/, '').sub(/\?$/,'')