parameters example - Ruby:¿Cómo convertir un hash en parámetros HTTP?




que significa (12)

Eso es bastante fácil con un hash simple como

{:a => "a", :b => "b"} 

que se traduciría en

"a=a&b=b"

Pero, ¿qué haces con algo más complejo como

{:a => "a", :b => ["c", "d", "e"]} 

lo que debería traducirse en

"a=a&b[0]=c&b[1]=d&b[2]=e" 

O incluso peor, (qué hacer) con algo como:

{:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]

Gracias por la ayuda tan apreciada con eso!


Answers

{:a=>"a", :b=>"b", :c=>"c"}.map{ |x,v| "#{x}=#{v}" }.reduce{|x,v| "#{x}&#{v}" }

"a=a&b=b&c=c"

Aquí hay otra manera. Para consultas sencillas.


Actualización: Esta funcionalidad fue eliminada de la gema.

Julien, tu auto-respuesta es buena, y me la tomé prestada descaradamente, pero no escapa a los personajes reservados, y hay algunos otros casos en los que se descompone.

require "addressable/uri"
uri = Addressable::URI.new
uri.query_values = {:a => "a", :b => ["c", "d", "e"]}
uri.query
# => "a=a&b[0]=c&b[1]=d&b[2]=e"
uri.query_values = {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]}
uri.query
# => "a=a&b[0][c]=c&b[0][d]=d&b[1][e]=e&b[1][f]=f"
uri.query_values = {:a => "a", :b => {:c => "c", :d => "d"}}
uri.query
# => "a=a&b[c]=c&b[d]=d"
uri.query_values = {:a => "a", :b => {:c => "c", :d => true}}
uri.query
# => "a=a&b[c]=c&b[d]"
uri.query_values = {:a => "a", :b => {:c => "c", :d => true}, :e => []}
uri.query
# => "a=a&b[c]=c&b[d]"

La gema es ' addressable '

gem install addressable

Para hashes básicos, no anidados, Rails / ActiveSupport tiene Object#to_query .

>> {:a => "a", :b => ["c", "d", "e"]}.to_query
=> "a=a&b%5B%5D=c&b%5B%5D=d&b%5B%5D=e"
>> CGI.unescape({:a => "a", :b => ["c", "d", "e"]}.to_query)
=> "a=a&b[]=c&b[]=d&b[]=e"

http://api.rubyonrails.org/classes/Object.html#method-i-to_query



Si está utilizando Ruby 1.9.2 o posterior, puede usar URI.encode_www_form si no necesita matrices.

Por ejemplo (de los documentos de Ruby en 1.9.3):

URI.encode_www_form([["q", "ruby"], ["lang", "en"]])
#=> "q=ruby&lang=en"
URI.encode_www_form("q" => "ruby", "lang" => "en")
#=> "q=ruby&lang=en"
URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en")
#=> "q=ruby&q=perl&lang=en"
URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]])
#=> "q=ruby&q=perl&lang=en"

Notará que los valores de matriz no se configuran con los nombres de clave que contienen [] como todos nos hemos acostumbrado a las cadenas de consulta. La especificación que utiliza encode_www_form está de acuerdo con la definición de HTML5 de datos de application/x-www-form-urlencoded .


Aquí tiene un breve resumen, si solo necesita admitir cadenas de consulta de clave / valor ASCII simples:

hash = {"foo" => "bar", "fooz" => 123}
# => {"foo"=>"bar", "fooz"=>123}
query_string = hash.to_a.map { |x| "#{x[0]}=#{x[1]}" }.join("&")
# => "foo=bar&fooz=123"

Robar a Merb:

# File merb/core_ext/hash.rb, line 87
def to_params
  params = ''
  stack = []

  each do |k, v|
    if v.is_a?(Hash)
      stack << [k,v]
    else
      params << "#{k}=#{v}&"
    end
  end

  stack.each do |parent, hash|
    hash.each do |k, v|
      if v.is_a?(Hash)
        stack << ["#{parent}[#{k}]", v]
      else
        params << "#{parent}[#{k}]=#{v}&"
      end
    end
  end

  params.chop! # trailing &
  params
end

Consulte http://noobkit.com/show/ruby/gems/development/merb/hash/to_params.html


Me gusta usar esta gema:

https://rubygems.org/gems/php_http_build_query

Uso de la muestra:

puts PHP.http_build_query({"a"=>"b","c"=>"d","e"=>[{"hello"=>"world","bah"=>"black"},{"hello"=>"world","bah"=>"black"}]})

# a=b&c=d&e%5B0%5D%5Bbah%5D=black&e%5B0%5D%5Bhello%5D=world&e%5B1%5D%5Bbah%5D=black&e%5B1%5D%5Bhello%5D=world

Sé que esta es una pregunta antigua, pero solo quería publicar este fragmento de código ya que no pude encontrar una gema simple para hacer esta tarea por mí.

module QueryParams

  def self.encode(value, key = nil)
    case value
    when Hash  then value.map { |k,v| encode(v, append_key(key,k)) }.join('&')
    when Array then value.map { |v| encode(v, "#{key}[]") }.join('&')
    when nil   then ''
    else            
      "#{key}=#{CGI.escape(value.to_s)}" 
    end
  end

  private

  def self.append_key(root_key, key)
    root_key.nil? ? key : "#{root_key}[#{key.to_s}]"
  end
end

Se enrolla como una gema aquí: https://github.com/simen/queryparams


require 'uri'

class Hash
  def to_query_hash(key)
    reduce({}) do |h, (k, v)|
      new_key = key.nil? ? k : "#{key}[#{k}]"
      v = Hash[v.each_with_index.to_a.map(&:reverse)] if v.is_a?(Array)
      if v.is_a?(Hash)
        h.merge!(v.to_query_hash(new_key))
      else
        h[new_key] = v
      end
      h
    end
  end

  def to_query(key = nil)
    URI.encode_www_form(to_query_hash(key))
  end
end

2.4.2 :019 > {:a => "a", :b => "b"}.to_query_hash(nil)
 => {:a=>"a", :b=>"b"}

2.4.2 :020 > {:a => "a", :b => "b"}.to_query
 => "a=a&b=b"

2.4.2 :021 > {:a => "a", :b => ["c", "d", "e"]}.to_query_hash(nil)
 => {:a=>"a", "b[0]"=>"c", "b[1]"=>"d", "b[2]"=>"e"}

2.4.2 :022 > {:a => "a", :b => ["c", "d", "e"]}.to_query
 => "a=a&b%5B0%5D=c&b%5B1%5D=d&b%5B2%5D=e"

No es necesario cargar el ActiveSupport hinchado o enrollar el suyo, puede usar Rack::Utils.build_query y Rack::Utils.build_nested_query . Aquí hay una entrada de blog que da un buen ejemplo:

require 'rack'

Rack::Utils.build_query(
  authorization_token: "foo",
  access_level: "moderator",
  previous: "index"
)

# => "authorization_token=foo&access_level=moderator&previous=index"

Incluso maneja matrices:

Rack::Utils.build_query( {:a => "a", :b => ["c", "d", "e"]} )
# => "a=a&b=c&b=d&b=e"
Rack::Utils.parse_query _
# => {"a"=>"a", "b"=>["c", "d", "e"]}

O las cosas más difíciles anidadas:

Rack::Utils.build_nested_query( {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}] } )
# => "a=a&b[][c]=c&b[][d]=d&b[][e]=e&b[][f]=f"
Rack::Utils.parse_nested_query _
# => {"a"=>"a", "b"=>[{"c"=>"c", "d"=>"d", "e"=>"e", "f"=>"f"}]}

Hay un in? Método en ActiveSupport (parte de Rails) desde v3.1, como lo señaló @campaterson. Entonces, dentro de Rails, o si require 'active_support' , puede escribir:

'Unicorn'.in?(['Cat', 'Dog', 'Bird']) # => false

OTOH, no hay in operador o #in? Método en el propio Ruby, a pesar de que se ha propuesto anteriormente, en particular por Yusuke Endoh, un miembro de primera categoría de ruby-core.

Como han señalado otros, el método inverso include? existe, para todos los Enumerable s incluyendo Array , Hash , Set , Range :

['Cat', 'Dog', 'Bird'].include?('Unicorn') # => false

Tenga en cuenta que si tiene muchos valores en su matriz, todos se verán uno tras otro (es decir, O(n) ), mientras que la búsqueda de un hash será constante (es decir, O(1) ). Por lo tanto, si la matriz es constante, por ejemplo, es una buena idea usar un Set lugar. P.ej:

require 'set'
ALLOWED_METHODS = Set[:to_s, :to_i, :upcase, :downcase
                       # etc
                     ]

def foo(what)
  raise "Not allowed" unless ALLOWED_METHODS.include?(what.to_sym)
  bar.send(what)
end

Una prueba rápida revela que las llamadas include? en un conjunto de 10 elementos es aproximadamente 3.5x más rápido que llamarlo en la Array equivalente (si no se encuentra el elemento).

Una nota final de cierre: ser cauteloso al usar include? en un Range , hay sutilezas, así que consulte el documento y compare con la cover? ...







ruby http parameters hashmap