Class: Dinie::Internal::Multipart

Inherits:
Object
  • Object
show all
Defined in:
lib/dinie/runtime/multipart.rb,
sig/dinie/runtime/multipart.rbs

Overview

Transport wrapper for a multipart/form-data request body (architecture §10/§14, story 009).

The runtime serializes a normal request body to a JSON String (the JSON-only path in HttpClient#serialize_body). The KYC attachment upload — the project's only multipart endpoint (POST /customers/{id}/kyc-attachments) — needs multipart/form-data instead, so this is the seam Ruby adds that the V0.2 TS runtime deferred (the tracked RUNTIME GAP).

An instance holds the deterministic scalar field-map (fields) plus the optional binary file part. HttpClient recognizes it in serialize_body, hands #to_faraday_body to Faraday with a bare multipart/form-data content type, and Faraday's :multipart request middleware (mounted on the shared connection) appends the boundary and encodes every entry — scalars become plain form-data parts, the file becomes a binary part. Setting the content type explicitly forces multipart encoding even for the email variant, which carries no file part (only a scalar value).

── runtime ↔ generated boundary (architecture §4) ── Lives in runtime/ (CODEOWNER humans). It is transport-aware (Faraday multipart parts) so the generated KYC layer stays transport-agnostic: Dinie::Kyc.serialize_upload produces a plain Kyc::UploadForm (the conformance unit — fields + opaque file), and the customers resource wraps it here at the boundary.

Constant Summary collapse

CONTENT_TYPE =

Content type Faraday's multipart middleware matches on; it appends ; boundary=… itself.

Returns:

  • (String)
"multipart/form-data"
DEFAULT_FILE_CONTENT_TYPE =

Fallback MIME type for a wrapped file part when the caller passes a bare IO/String. The API validates the real format from the bytes; the SDK does not sniff it.

Returns:

  • (String)
"application/octet-stream"
DEFAULT_FILE_FILENAME =

Fallback filename for a wrapped file part with no #path.

Returns:

  • (String)
"upload"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(fields:, file: nil) ⇒ Multipart

Returns a new instance of Multipart.

Parameters:

  • fields (Hash{Symbol => String}) —

    scalar form fields

  • file (Object, nil) (defaults to: nil) —

    optional binary file: a Faraday::Multipart::FilePart/ParamPart (passed through), an IO (File.open(...)), or a String of raw bytes

  • fields: (Hash[Symbol, String])
  • file: (Object) (defaults to: nil)


45
46
47
48
# File 'lib/dinie/runtime/multipart.rb', line 45

def initialize(fields:, file: nil)
  @fields = fields
  @file = file
end

Instance Attribute Details

#fields ⇒ Hash{Symbol => String} (readonly)

Returns the scalar form fields (alphabetical — R-ORDER).

Returns:

  • (Hash{Symbol => String}) —

    the scalar form fields (alphabetical — R-ORDER)



38
39
40
# File 'lib/dinie/runtime/multipart.rb', line 38

def fields
  @fields
end

#file ⇒ Object? (readonly)

Returns the file part (a Faraday::Multipart::FilePart, an IO, or raw bytes).

Returns:

  • (Object, nil) —

    the file part (a Faraday::Multipart::FilePart, an IO, or raw bytes)



40
41
42
# File 'lib/dinie/runtime/multipart.rb', line 40

def file
  @file
end

Instance Method Details

#multipart_part?(file) ⇒ Boolean

Parameters:

  • file (Object)

Returns:

  • (Boolean)


72
73
74
# File 'lib/dinie/runtime/multipart.rb', line 72

def multipart_part?(file)
  file.is_a?(Faraday::Multipart::FilePart) || file.is_a?(Faraday::Multipart::ParamPart)
end

#to_faraday_body ⇒ Hash[Symbol, untyped]

The Faraday multipart body: scalar fields plus the wrapped file part when present.

Returns:

  • (Hash[Symbol, untyped])


54
55
56
57
58
# File 'lib/dinie/runtime/multipart.rb', line 54

def to_faraday_body
  body = @fields.dup
  body[:file] = wrap_file(@file) unless @file.nil?
  body
end

#wrap_file(file) ⇒ Object

Wrap a caller-supplied file into a Faraday multipart part. An already-wrapped part passes through; an IO is wrapped directly; a String is treated as raw bytes via a StringIO.

Parameters:

  • file (Object)

Returns:

  • (Object)


64
65
66
67
68
69
70
# File 'lib/dinie/runtime/multipart.rb', line 64

def wrap_file(file)
  return file if multipart_part?(file)

  io = file.respond_to?(:read) ? file : StringIO.new(file.to_s)
  filename = file.respond_to?(:path) && file.path ? File.basename(file.path) : DEFAULT_FILE_FILENAME
  Faraday::Multipart::FilePart.new(io, DEFAULT_FILE_CONTENT_TYPE, filename)
end