Class: Stripe::MultipartEncoder

Inherits:
Object
  • Object
show all
Defined in:
lib/stripe/multipart_encoder.rb

Overview

Encodes parameters into a ‘multipart/form-data` payload as described by RFC 2388:

https://tools.ietf.org/html/rfc2388

This is most useful for transferring file-like objects.

Parameters should be added with ‘#encode`. When ready, use `#body` to get the encoded result and `#content_type` to get the value that should be placed in the `Content-Type` header of a subsequent request (which includes a boundary value).

Constant Summary collapse

MULTIPART_FORM_DATA =
"multipart/form-data"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeMultipartEncoder

Initializes a new multipart encoder.



37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/stripe/multipart_encoder.rb', line 37

def initialize
  # Kind of weird, but required by Rubocop because the unary plus operator
  # is considered faster than `Stripe.new`.
  @body = +""

  # Chose the same number of random bytes that Go uses in its standard
  # library implementation. Easily enough entropy to ensure that it won't
  # be present in a file we're sending.
  @boundary = SecureRandom.hex(30)

  @closed = false
  @first_field = true
end

Instance Attribute Details

#boundaryObject (readonly)

Gets the object’s randomly generated boundary string.



34
35
36
# File 'lib/stripe/multipart_encoder.rb', line 34

def boundary
  @boundary
end

Class Method Details

.encode(params) ⇒ Object

A shortcut for encoding a single set of parameters and finalizing a result.

Returns an encoded body and the value that should be set in the content type header of a subsequent request.



26
27
28
29
30
31
# File 'lib/stripe/multipart_encoder.rb', line 26

def self.encode(params)
  encoder = MultipartEncoder.new
  encoder.encode(params)
  encoder.close
  [encoder.body, encoder.content_type]
end

Instance Method Details

#bodyObject

Gets the encoded body. ‘#close` must be called first.



52
53
54
55
56
# File 'lib/stripe/multipart_encoder.rb', line 52

def body
  raise "object must be closed before getting body" unless @closed

  @body
end

#closeObject

Finalizes the object by writing the final boundary.



59
60
61
62
63
64
65
66
67
68
# File 'lib/stripe/multipart_encoder.rb', line 59

def close
  raise "object already closed" if @closed

  @body << "\r\n"
  @body << "--#{@boundary}--"

  @closed = true

  nil
end

#content_typeObject

Gets the value including boundary that should be put into a multipart request’s ‘Content-Type`.



72
73
74
# File 'lib/stripe/multipart_encoder.rb', line 72

def content_type
  "#{MULTIPART_FORM_DATA}; boundary=#{@boundary}"
end

#encode(params) ⇒ Object

Encodes a set of parameters to the body.

Note that parameters are expected to be a hash, but a “flat” hash such that complex substructures like hashes and arrays have already been appropriately Stripe-encoded. Pass a complex structure through ‘Util.flatten_params` first before handing it off to this method.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/stripe/multipart_encoder.rb', line 82

def encode(params)
  raise "no more parameters can be written to closed object" if @closed

  params.each do |name, val|
    if val.is_a?(::File) || val.is_a?(::Tempfile)
      write_field(name, val.read, filename: ::File.basename(val.path))
    elsif val.respond_to?(:read)
      write_field(name, val.read, filename: "blob")
    else
      write_field(name, val, filename: nil)
    end
  end

  nil
end