module URI

def self.decode_www_form(str)

See URI.decode_www_form_component, URI.encode_www_form

p Hash[ary] # => {"a"=>"2", "b"=>"3"}
p ary.rassoc('a').last #=> '2'
p ary.assoc('b').last #=> '3'
p ary.assoc('a').last #=> '1'
p ary #=> [['a', '1'], ['a', '2'], ['b', '3']]
ary = URI.decode_www_form("a=1&a=2&b=3")

This refers http://www.w3.org/TR/html5/forms.html#url-encoded-form-data

see also http://www.w3.org/TR/html5/syntax.html#character-encodings-0
to Ruby's encoding is not clear yet.
_charset_ hack is not supported now because the mapping from given charset

This internally uses URI.decode_www_form_component.
and returns array of key-value array.
This decodes application/x-www-form-urlencoded data

Decode URL-encoded form data from given +str+.
def self.decode_www_form(str)
  return [] if str.empty?
  unless /\A#{WFKV_}=#{WFKV_}(?:[;&]#{WFKV_}=#{WFKV_})*\z/o =~ str
    fail(ArgumentError, "invalid data of application/x-www-form-urlencoded (#{str})")
  end
  ary = []
  $&.scan(/([^=;&]+)=([^;&]*)/) do
    ary << [decode_www_form_component(Regexp.last_match[1]), decode_www_form_component(Regexp.last_match[2])]
  end
  ary
end

def self.decode_www_form_component(str)

See URI.encode_www_form_component, URI.decode_www_form

This decods + to SP.

Decode given +str+ of URL-encoded form data.
def self.decode_www_form_component(str)
  fail(ArgumentError, "invalid %-encoding (#{str})") unless /\A[^%]*(?:%\h\h[^%]*)*\z/ =~ str
  str.gsub(/\+|%\h\h/) { |chr| TBLDECWWWCOMP_[chr] }
end

def self.encode_www_form(enum)

See URI.encode_www_form_component, URI.decode_www_form

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

http://www.w3.org/TR/html5/forms.html#url-encoded-form-data
This is an implementation of

multipart/form-data.
This method doesn't handle files. When you send a file, use

ASCII incompatible encoding are converted to UTF-8.)
encoding or mixed encoding data. (Strings which are encoded in an HTML5
before call this method if you want to send data as other than original
This method doesn't convert the encoding of given items, so convert them

This internally uses URI.encode_www_form_component(str).

from given an Enumerable object.
This generates application/x-www-form-urlencoded data defined in HTML5

Generate URL-encoded form data from given +enum+.
def self.encode_www_form(enum)
  enum.map do |k, v|
    if v.nil?
      encode_www_form_component(k)
    elsif v.respond_to?(:to_ary)
      v.to_ary.map do |w|
        next unless w
        str = encode_www_form_component(k)
        str << '='
        str << encode_www_form_component(w)
      end.join('&')
    else
      str = encode_www_form_component(k)
      str << '='
      str << encode_www_form_component(v)
    end
  end.join('&')
end

def self.encode_www_form_component(str)

See URI.decode_www_form_component, URI.encode_www_form

http://www.w3.org/TR/html5/association-of-controls-and-forms.html#url-encoded-form-data
This is an implementation of

(ASCII space) to + and converts others to %XX.
This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP

Encode given +str+ to URL-encoded form data.
def self.encode_www_form_component(str)
  str.to_s.gsub(/[^*\-.0-9A-Z_a-z]/) { |chr| TBLENCWWWCOMP_[chr] }
end