Class: Philiprehberger::Multipart::Builder
- Inherits:
-
Object
- Object
- Philiprehberger::Multipart::Builder
- Defined in:
- lib/philiprehberger/multipart/builder.rb
Overview
DSL builder for constructing multipart/form-data bodies
Instance Attribute Summary collapse
-
#boundary ⇒ String
readonly
The multipart boundary string.
-
#parts ⇒ Array<Part>
readonly
The parts added to the builder.
Instance Method Summary collapse
-
#content_length ⇒ Integer
Calculate the byte size of the multipart body.
-
#content_type ⇒ String
Return the Content-Type header value with boundary.
-
#field(name, value) ⇒ self
Add a text field.
-
#field_names ⇒ Array<String>
List part names in the order they were added.
-
#file(name, path_or_io, filename: nil, content_type: nil) ⇒ self
Add a file field.
-
#headers ⇒ Hash
Return the headers hash for the request.
-
#initialize(boundary: nil) ⇒ Builder
constructor
A new instance of Builder.
-
#merge(other) ⇒ self
Append parts from another builder onto this one, preserving their existing ‘Part` objects so file contents are not re-encoded.
-
#part(name) ⇒ Part?
Look up the first part previously added with the given name.
-
#to_s ⇒ String
Render the complete multipart body as a string.
-
#write_to(io) ⇒ #write
Stream the multipart body to an IO object.
Constructor Details
#initialize(boundary: nil) ⇒ Builder
Returns a new instance of Builder.
17 18 19 20 |
# File 'lib/philiprehberger/multipart/builder.rb', line 17 def initialize(boundary: nil) @boundary = boundary || generate_boundary @parts = [] end |
Instance Attribute Details
#boundary ⇒ String (readonly)
Returns the multipart boundary string.
11 12 13 |
# File 'lib/philiprehberger/multipart/builder.rb', line 11 def boundary @boundary end |
#parts ⇒ Array<Part> (readonly)
Returns the parts added to the builder.
14 15 16 |
# File 'lib/philiprehberger/multipart/builder.rb', line 14 def parts @parts end |
Instance Method Details
#content_length ⇒ Integer
Calculate the byte size of the multipart body
121 122 123 124 |
# File 'lib/philiprehberger/multipart/builder.rb', line 121 def content_length size = @parts.sum { |part| part.to_s(@boundary).bytesize } size + "--#{@boundary}--\r\n".bytesize end |
#content_type ⇒ String
Return the Content-Type header value with boundary
101 102 103 |
# File 'lib/philiprehberger/multipart/builder.rb', line 101 def content_type "multipart/form-data; boundary=#{@boundary}" end |
#field(name, value) ⇒ self
Add a text field
27 28 29 30 |
# File 'lib/philiprehberger/multipart/builder.rb', line 27 def field(name, value) @parts << Part.new(name, value.to_s) self end |
#field_names ⇒ Array<String>
List part names in the order they were added.
Duplicates are preserved (this is a list, not a set). The returned array is a fresh copy — mutating it does not affect the builder.
72 73 74 |
# File 'lib/philiprehberger/multipart/builder.rb', line 72 def field_names @parts.map { |p| p.name.to_s } end |
#file(name, path_or_io, filename: nil, content_type: nil) ⇒ self
Add a file field
Accepts either a file path (String) or an IO object (responds to :read). When passing an IO object, the ‘filename:` keyword is required. Content type is auto-detected from the filename when not provided.
44 45 46 47 48 49 50 51 |
# File 'lib/philiprehberger/multipart/builder.rb', line 44 def file(name, path_or_io, filename: nil, content_type: nil) if path_or_io.respond_to?(:read) add_io_file(name, path_or_io, filename, content_type) else add_path_file(name, path_or_io, filename, content_type) end self end |
#headers ⇒ Hash
Return the headers hash for the request
129 130 131 132 133 134 |
# File 'lib/philiprehberger/multipart/builder.rb', line 129 def headers { 'Content-Type' => content_type, 'Content-Length' => content_length.to_s } end |
#merge(other) ⇒ self
Append parts from another builder onto this one, preserving their existing ‘Part` objects so file contents are not re-encoded. The receiver’s boundary is kept; only ‘other`’s parts are copied.
83 84 85 86 87 88 |
# File 'lib/philiprehberger/multipart/builder.rb', line 83 def merge(other) raise Error, 'merge requires a Builder' unless other.is_a?(Builder) @parts.concat(other.parts) self end |
#part(name) ⇒ Part?
Look up the first part previously added with the given name.
Allows post-construction tweaks, e.g. ‘builder.part(’avatar’).content_type = ‘image/webp’‘. String and Symbol lookups are equivalent.
61 62 63 64 |
# File 'lib/philiprehberger/multipart/builder.rb', line 61 def part(name) needle = name.to_s @parts.find { |p| p.name.to_s == needle } end |
#to_s ⇒ String
Render the complete multipart body as a string
93 94 95 96 |
# File 'lib/philiprehberger/multipart/builder.rb', line 93 def to_s body = @parts.map { |part| part.to_s(@boundary) }.join "#{body}--#{@boundary}--\r\n" end |
#write_to(io) ⇒ #write
Stream the multipart body to an IO object
Writes each part directly to the IO without building the full body string in memory. Useful for large file uploads.
112 113 114 115 116 |
# File 'lib/philiprehberger/multipart/builder.rb', line 112 def write_to(io) @parts.each { |part| io.write(part.to_s(@boundary)) } io.write("--#{@boundary}--\r\n") io end |