class Enumerable
Wraps a Rack response body that responds to each
.
The body must only yield String
values and may optionally respond to close
.
This class provides both streaming and buffered access to the response body.
Definitions
CONTENT_LENGTH = "content-length".freeze
The content-length header key.
def self.wrap(body, length = nil)
Wraps a Rack response body into an class Protocol::Rack::Body::Enumerable
instance.
If the body is an Array, its total size is calculated automatically.
Signature
-
parameter
body
Object
The Rack response body that responds to
each
.-
parameter
length
Integer
Optional content length of the response body.
-
returns
Enumerable
A new enumerable body instance.
Implementation
def self.wrap(body, length = nil)
if body.is_a?(Array)
length ||= body.sum(&:bytesize)
return self.new(body, length)
else
return self.new(body, length)
end
end
def initialize(body, length)
Initialize the enumerable body wrapper.
Signature
-
parameter
body
Object
The Rack response body that responds to
each
.-
parameter
length
Integer
The content length of the response body.
Implementation
def initialize(body, length)
@length = length
@body = body
@chunks = nil
end
attr :body
Signature
-
attribute
Object
The wrapped Rack response body.
attr :length
Signature
-
attribute
Integer
The total size of the response body in bytes.
def empty?
Check if the response body is empty.
A body is considered empty if its length is 0 or if it responds to empty?
and is empty.
Signature
-
returns
Boolean
True if the body is empty.
Implementation
def empty?
@length == 0 or (@body.respond_to?(:empty?) and @body.empty?)
end
def ready?
Check if the response body can be read immediately.
A body is ready if it's an Array or responds to to_ary
.
Signature
-
returns
Boolean
True if the body can be read immediately.
Implementation
def ready?
body.is_a?(Array) or body.respond_to?(:to_ary)
end
def close(error = nil)
Close the response body.
If the body responds to close
, it will be called.
Signature
-
parameter
error
Exception
Optional error that occurred during processing.
Implementation
def close(error = nil)
if @body and @body.respond_to?(:close)
@body.close
end
@body = nil
@chunks = nil
super
end
def each(&block)
Enumerate the response body. Each chunk yielded must be a String. The body is automatically closed after enumeration.
Signature
-
yields
{|chunk| ...}
-
parameter
chunk
String
A chunk of the response body.
-
parameter
Implementation
def each(&block)
@body.each(&block)
rescue => error
raise
ensure
self.close(error)
end
def stream?
Check if the body is a streaming response.
A body is streaming if it doesn't respond to each
.
Signature
-
returns
Boolean
True if the body is streaming.
Implementation
def stream?
!@body.respond_to?(:each)
end
def call(stream)
Stream the response body to the given stream. The body is automatically closed after streaming.
Signature
-
parameter
stream
Object
The stream to write the body to.
Implementation
def call(stream)
@body.call(stream)
rescue => error
raise
ensure
self.close(error)
end
def read
Read the next chunk from the response body. Returns nil when there are no more chunks.
Signature
-
returns
String | Nil
The next chunk or nil if there are no more chunks.
Implementation
def read
@chunks ||= @body.to_enum(:each)
return @chunks.next
rescue StopIteration
return nil
end
def inspect
Get a string representation of the body.
Signature
-
returns
String
A string describing the body's class and length.
Implementation
def inspect
"\#<#{self.class} length=#{@length.inspect} body=#{@body.class}>"
end