This extension is part of the Sinatra::Contrib project. Run gem install sinatra-contrib to have it available.
Sinatra::Streaming
Sinatra 1.3 introduced the stream helper. This addon improves the streaming API by making the stream object immitate 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
endProper Deferrable
Handy when using EventMachine.
list = []
get '/' do
stream(:keep_open) do |out|
list << out
out.callback { list.delete out }
out.errback do
logger.warn "lost connection"
list.delete out
end
end
endBetter 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
endEven 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]
endNote 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
Sinatra