Module: Dommy::Rack::FileUpload

Defined in:
lib/dommy/rack/file_upload.rb

Overview

Encodes collected form params (ordered [name, value] pairs) as a multipart/form-data body. File/Blob values become file parts; other values become text parts. Owns the multipart serialization so the HTTP layer (not Dommy::FormData) is responsible for it.

Constant Summary collapse

MIME_TYPES =
{
  ".txt" => "text/plain",
  ".html" => "text/html",
  ".htm" => "text/html",
  ".json" => "application/json",
  ".csv" => "text/csv",
  ".xml" => "application/xml",
  ".png" => "image/png",
  ".jpg" => "image/jpeg",
  ".jpeg" => "image/jpeg",
  ".gif" => "image/gif",
  ".pdf" => "application/pdf"
}.freeze

Class Method Summary collapse

Class Method Details

.encode(pairs, boundary = nil) ⇒ Object

Returns [body (ASCII-8BIT String), content_type with boundary].



41
42
43
44
45
46
47
# File 'lib/dommy/rack/file_upload.rb', line 41

def encode(pairs, boundary = nil)
  boundary ||= "----DommyRackBoundary#{SecureRandom.hex(16)}"
  body = +"".b
  pairs.each { |name, value| body << part(boundary, name, value) }
  body << "--#{boundary}--\r\n"
  [body, "multipart/form-data; boundary=#{boundary}"]
end

.escape(str) ⇒ Object



68
69
70
# File 'lib/dommy/rack/file_upload.rb', line 68

def escape(str)
  str.to_s.gsub('"', "%22").gsub(/[\r\n]/, "")
end

.file_content_type(value) ⇒ Object



63
64
65
66
# File 'lib/dommy/rack/file_upload.rb', line 63

def file_content_type(value)
  type = value.respond_to?(:type) ? value.type.to_s : ""
  type.empty? ? "application/octet-stream" : type
end

.mime_type_for(path) ⇒ Object

Guess a MIME type from a file path’s extension.



29
30
31
# File 'lib/dommy/rack/file_upload.rb', line 29

def mime_type_for(path)
  MIME_TYPES.fetch(::File.extname(path).downcase, "application/octet-stream")
end

.multipart?(pairs) ⇒ Boolean

True when any pair value is a File/Blob.

Returns:

  • (Boolean)


34
35
36
37
38
# File 'lib/dommy/rack/file_upload.rb', line 34

def multipart?(pairs)
  return false unless pairs

  pairs.any? { |(_name, value)| value.respond_to?(:__dommy_bytes__) }
end

.part(boundary, name, value) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/dommy/rack/file_upload.rb', line 49

def part(boundary, name, value)
  head = +"--#{boundary}\r\n"
  if value.respond_to?(:__dommy_bytes__) # File / Blob
    filename = value.respond_to?(:name) ? value.name.to_s : ""
    content_type = file_content_type(value)
    head << %(Content-Disposition: form-data; name="#{escape(name)}"; filename="#{escape(filename)}"\r\n)
    head << "Content-Type: #{content_type}\r\n\r\n"
    head.b << value.__dommy_bytes__ << "\r\n".b
  else
    head << %(Content-Disposition: form-data; name="#{escape(name)}"\r\n\r\n)
    head.b << value.to_s.dup.force_encoding(Encoding::ASCII_8BIT) << "\r\n".b
  end
end