Class: Hatetepe::Body

Inherits:
Object
  • Object
show all
Includes:
EM::Deferrable
Defined in:
lib/hatetepe/body.rb

Overview

Thin wrapper around StringIO for asynchronous body processing.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data = "") ⇒ Body

Create a new Body instance.

Parameters:

  • data (String) (defaults to: "")

    Initial content of the StringIO object.



20
21
22
23
# File 'lib/hatetepe/body.rb', line 20

def initialize(data = "")
  @receivers = []
  @io = StringIO.new(data)
end

Instance Attribute Details

#ioObject (readonly)

The wrapped StringIO.



11
12
13
# File 'lib/hatetepe/body.rb', line 11

def io
  @io
end

#sourceObject

The origin Client or Server connection.



14
15
16
# File 'lib/hatetepe/body.rb', line 14

def source
  @source
end

Instance Method Details

#close_writeundefined

Forwards to StringIO#close_write.

Write-closes the body and succeeds, thus releasing all blocking method calls like #length, #each, #read and #get.

Returns:

  • (undefined)


92
93
94
95
# File 'lib/hatetepe/body.rb', line 92

def close_write
  io.close_write
  succeed
end

#closed_write?Boolean

Forwards to StringIO#closed_write?.

Returns true if the body is write-closed, false otherwise.

Returns:

  • (Boolean)

    True if the body is write-closed, false otherwise.



103
104
105
# File 'lib/hatetepe/body.rb', line 103

def closed_write?
  io.closed_write?
end

#each {|String| ... } ⇒ Enumerator, self

Yields incoming body data.

Immediately yields all data that has already arrived. Blocks until the Body is write-closed and yields for each call to #write until then.

Yields:

  • (String)

    Block to execute for each incoming data chunk.

Returns:

  • (Enumerator, self)


115
116
117
118
119
120
121
122
123
# File 'lib/hatetepe/body.rb', line 115

def each(&block)
  return to_enum(__method__) unless block

  @receivers << block
  block.call io.string.dup unless io.string.empty?
  sync

  self
end

#empty?Boolean

Returns true if the underlying StringIO is empty, false otherwise.

Returns:

  • (Boolean)

    True if empty, false otherwise.



52
53
54
# File 'lib/hatetepe/body.rb', line 52

def empty?
  length == 0
end

#getsString?

Forwards to StringIO#gets.

Reads one line from the IO. Returns the line or nil if EOF has been reached.

Returns:

  • (String)

    One line.

  • (nil)

    If has been reached.



159
160
161
162
# File 'lib/hatetepe/body.rb', line 159

def gets
  sync
  io.gets
end

#lengthFixnum

Forwards to StringIO#length.

Blocks until the Body is write-closed. Returns the current length of the underlying StringIO’s content.

Returns:

  • (Fixnum)

    The StringIO’s length.



43
44
45
46
# File 'lib/hatetepe/body.rb', line 43

def length
  sync
  io.length
end

#posFixnum

Forwards to StringIO#pos.

Returns the underlying StringIO’s current pointer position.

Returns:

  • (Fixnum)

    The current pointer position.



62
63
64
# File 'lib/hatetepe/body.rb', line 62

def pos
  io.pos
end

#read(*args) ⇒ nil, String

Forwards to StringIO#read.

From the Rack Spec: If given, length must be a non-negative Integer (>= 0) or nil, and buffer must be a String and may not be nil. If length is given and not nil, then this method reads at most length bytes from the input stream. If length is not given or nil, then this method reads all data until EOF. When EOF is reached, this method returns nil if length is given and not nil, or “” if length is not given or is nil. If buffer is given, then the read data will be placed into buffer instead of a newly created String object.

Parameters:

  • length (Fixnum)

    (optional) How many bytes to read.

  • buffer (String)

    (optional) Buffer for read data.

Returns:

  • (nil)

    nil if EOF has been reached.

  • (String)

    All data or at most length bytes of data if length is given.



145
146
147
148
# File 'lib/hatetepe/body.rb', line 145

def read(*args)
  sync
  io.read *args
end

#rewindundefined

Forwards to StringIO#rewind.

Moves the underlying StringIO’s pointer back to the beginnung.

Returns:

  • (undefined)


71
72
73
74
# File 'lib/hatetepe/body.rb', line 71

def rewind
  sync
  rewind!
end

#rewind!Object

Rewinds underlying IO without blocking

TODO this is a hack. the whole blocking/rewinding stuff needs to be

more though out.


82
83
84
# File 'lib/hatetepe/body.rb', line 82

def rewind!
  io.rewind
end

#syncundefined

Blocks until the Body is write-closed.

Use this if you want to wait until all of the body has arrived before continuing. It will resume the originating connection if it’s paused.

Returns:

  • (undefined)


31
32
33
34
# File 'lib/hatetepe/body.rb', line 31

def sync
  source.resume if source && source.paused?
  EM::Synchrony.sync self
end

#write(data) ⇒ Fixnum

Forwards to StringIO#write.

Appends the given String to the underlying StringIO annd returns the number of bytes written.

Parameters:

  • data (String)

    The data to append.

Returns:

  • (Fixnum)

    The number of bytes written.



174
175
176
177
178
179
180
# File 'lib/hatetepe/body.rb', line 174

def write(data)
  ret = io.write data
  @receivers.each do |r|
    Fiber.new { r.call data }.resume
  end
  ret
end