method

set_form_data

v1_9_2_180 - Show latest stable - Class: Net::HTTPHeader
set_form_data(params, sep = '&')
public

Set header fields and a body from HTML form data. params should be a Hash containing HTML form data. Optional argument sep means data record separator.

This method also set Content-Type: header field to application/x-www-form-urlencoded.

Example:

http.form_data = {"q" => "ruby", "lang" => "en"}
http.form_data = {"q" => ["ruby", "perl"], "lang" => "en"}
http.set_form_data({"q" => "ruby", "lang" => "en"}, ';')

4Notes

Doesn't handle nested hashes

Oleg · Jun 23, 20102 thanks

If you pass something like this: http.set_form_data({:a => {:b => :c}}) it will completely mangle the value. So don't use it.

Handling nested hashes and arrays

CraigBuchek · Mar 23, 20111 thank

You can use this code to handle nested hashes and arrays. I'm not sure if it handles every case, and it could probably be refactored better, but it's working quite well for us.

require 'active_support/core_ext/hash'

def normalize_params(params, key=nil)
params = params.flatten_keys if params.is_a?(Hash)
result = {}
params.each do |k,v|
  case v
    when Hash
      result[k.to_s] = normalize_params(v)
    when Array
      v.each_with_index do |val,i|
        result["#{k.to_s}[#{i}]"] = val.to_s
      end
    else
      result[k.to_s] = v.to_s
  end
end
result
end

# Adapted from http://snippets.dzone.com/posts/show/6776
class Hash
def flatten_keys(newhash={}, keys=nil)
  self.each do |k, v|
    k = k.to_s
    keys2 = keys ? keys+"[#{k}]" : k
    if v.is_a?(Hash)
      v.flatten_keys(newhash, keys2)
    else
      newhash[keys2] = v
    end
  end
  newhash
end
end

Backport from 1.9

eric_programmer · Apr 28, 20111 thank

Below is a backport of the Ruby 1.9 implementation (minus some encoding stuff). The main thing this provides you is the ability to say :foo => ['bar', 'baz'] and have that turn into foo=bar&foo=baz (i.e. multiple values for the same key).

Just require into your project and use it like you are on 1.9.

module Net
module HTTPHeader

  def set_form_data(params, sep = '&')
    query = URI.encode_www_form(params)
    query.gsub!(/&/, sep) if sep != '&'
    self.body = query
    self.content_type = 'application/x-www-form-urlencoded'
  end
  alias form_data= set_form_data

end
end

module URI

def self.encode_www_form(enum)
  enum.map do |k,v|
    if v.nil?
      k
    elsif v.respond_to?(:to_ary)
      v.to_ary.map do |w|
        str = k.dup
        unless w.nil?
          str << '='
          str << w
        end
      end.join('&')
    else
      str = k.dup
      str << '='
      str << v
    end
  end.join('&')
end

end

Agree with Oleg

richdrich · Mar 3, 2011

Yes, the only way round this seems to be to code e.g:

postArgs = { 'table[field]' => value, 'table[f2]' => v2 }

after the fashion of the browsers form definition.

This lets you do nested attributes as well, e.g: postargs['table[children_attributes[0][field]'] = value