module Sinatra::Streaming

Sinatra::Streaming

Sinatra 1.3 introduced the stream helper. This addon improves the streaming API by making the stream object imitate an IO object, turning it into a real Deferrable and making the body play nicer with middleware unaware of streaming.

IO-like behavior

This is useful when passing the stream object to a library expecting an IO or StringIO object.

get '/' do
  stream do |out|
    out.puts "Hello World!", "How are you?"
    out.write "Written #{out.pos} bytes so far!\n"
    out.putc(65) unless out.closed?
    out.flush
  end
end

Better Middleware Handling

Blocks passed to map! or map will actually be applied when streaming takes place (as you might have suspected, map! applies modifications to the current body, while map creates a new one):

class StupidMiddleware
  def initialize(app) @app = app end

  def call(env)
    status, headers, body = @app.call(env)
    body.map! { |e| e.upcase }
    [status, headers, body]
  end
end

use StupidMiddleware

get '/' do
  stream do |out|
    out.puts "still"
    sleep 1
    out.puts "streaming"
  end
end

Even works if each is used to generate an Enumerator:

def call(env)
  status, headers, body = @app.call(env)
  body = body.each.map { |s| s.upcase }
  [status, headers, body]
end

Note that both examples violate the Rack specification.

Setup

In a classic application:

require "sinatra"
require "sinatra/streaming"

In a modular application:

require "sinatra/base"
require "sinatra/streaming"

class MyApp < Sinatra::Base
  helpers Sinatra::Streaming
end

Public Instance Methods

stream(*) click to toggle source
Calls superclass method
# File lib/sinatra/streaming.rb, line 79
def stream(*)
  stream = super
  stream.extend Stream
  stream.app = self
  env['async.close'].callback { stream.close } if env.key? 'async.close'
  stream
end