set_form_data
set_form_data(params, sep = '&')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
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
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
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
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