Protocol::RackSourceProtocolRackBodyEnumerable

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.

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