module Rack module Test module Utils # :nodoc: include Rack::Utils def build_nested_query(value, prefix = nil) case value when Array value.map do |v| unless unescape(prefix) =~ /\[\]$/ prefix = "#{prefix}[]" end build_nested_query(v, "#{prefix}") end.join("&") when Hash value.map do |k, v| build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k)) end.join("&") when NilClass prefix.to_s else "#{prefix}=#{escape(value)}" end end module_function :build_nested_query def build_multipart(params, first = true) if first unless params.is_a?(Hash) raise ArgumentError, "value must be a Hash" end multipart = false query = lambda { |value| case value when Array value.each(&query) when Hash value.values.each(&query) when UploadedFile multipart = true end } params.values.each(&query) return nil unless multipart end flattened_params = Hash.new params.each do |key, value| k = first ? key.to_s : "[#{key}]" case value when Array value.map do |v| if (v.is_a?(Hash)) nested_params = {} build_multipart(v, false).each { |subkey, subvalue| nested_params[subkey] = subvalue } flattened_params["#{k}[]"] ||= [] flattened_params["#{k}[]"] << nested_params else flattened_params["#{k}[]"] = value end end when Hash build_multipart(value, false).each { |subkey, subvalue| flattened_params[k + subkey] = subvalue } else flattened_params[k] = value end end if first build_parts(flattened_params) else flattened_params end end module_function :build_multipart private def build_parts(parameters) get_parts(parameters).join + "--#{MULTIPART_BOUNDARY}--\r" end def get_parts(parameters) parameters.map { |name, value| if name =~ /\[\]\Z/ && value.is_a?(Array) && value.all? {|v| v.is_a?(Hash)} value.map { |hash| new_value = {} hash.each { |k, v| new_value[name+k] = v } get_parts(new_value).join }.join else if value.respond_to?(:original_filename) build_file_part(name, value) elsif value.is_a?(Array) and value.all? { |v| v.respond_to?(:original_filename) } value.map do |v| build_file_part(name, v) end.join else primitive_part = build_primitive_part(name, value) Rack::Test.encoding_aware_strings? ? primitive_part.force_encoding('BINARY') : primitive_part end end } end def build_primitive_part(parameter_name, value) unless value.is_a? Array value = [value] end value.map do |v| <<-EOF --#{MULTIPART_BOUNDARY}\r Content-Disposition: form-data; name="#{parameter_name}"\r \r #{v}\r EOF end.join end def build_file_part(parameter_name, uploaded_file) ::File.open(uploaded_file.path, "rb") do |physical_file| physical_file.set_encoding(Encoding::BINARY) if physical_file.respond_to?(:set_encoding) <<-EOF --#{MULTIPART_BOUNDARY}\r Content-Disposition: form-data; name="#{parameter_name}"; filename="#{escape(uploaded_file.original_filename)}"\r Content-Type: #{uploaded_file.content_type}\r Content-Length: #{::File.stat(uploaded_file.path).size}\r \r #{physical_file.read}\r EOF end end end end end