Sinatraのレスポンスの設定とストリーミングヘルパー

通常はルーティングブロックの戻り値にセットした文字列がbodyにセットされます。
任意の評価フローでbodyをセットしたい場合はどうすればいいでしょうか。
Sinatraには任意の評価フローでbodyをセットできる、bodyヘルパーが用意されています。

1
2
3
4
5
6
7
get '/foo' do
body "bar"
end

after do
puts body
end

Status Codeを設定するstatusヘルパー、headerを設定するheadersヘルパーも用意されています。

1
2
3
4
5
6
7
get '/foo' do
status 418
headers \
"Allow" => "BREW, POST, GET, PROPFIND, WHEN",
"Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
body "I'm a tea pot!"
end

引数の伴わないbody, status, headersは現在の値を確認するために使えます。

Streamingヘルパー

streamヘルパーを使うとおもしろいことができます。
レスポンスボディの部分を生成している段階でデータを送信することができます。
この仕組みを使ってストリーミングAPIを実装することもできます。

1
2
3
4
5
6
7
8
9
get '/' do
stream do |out|
out << "It's gonna be legen -\n"
sleep 0.5
out << " (wait for it) \n"
sleep 1
out << "- dary!\n"
end
end

以下は、ストリーミングAPIのサンプルです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# app.rb
# long polling

set :server, :thin
connections = []

get '/subscribe' do
# register a client's interest in server events
stream(:keep_open) do |out|
connections << out
# purge dead connections
connections.reject!(&:closed?)
end
end

post '/message' do
connections.each do |out|
# notify client that a new message has arrived
out << params['message'] << "\n"

# indicate client to connect again
out.close
end

# acknowledge
"message received"
end

ストリーミングはThinやRainbowsなどのイベント型サーバーでないと動かないので注意が必要です。

サーバーを起動させてみます。

1
2
$ gem insatall thin
$ ruby app.rb

別のターミナルを開いて、以下を実行します。

1
$ curl http://localhost:4567/subscribe

何もレスポンスがありませんが、接続が維持された状態となります。

別のタブを開いて以下の実行してみましょう。

1
$ curl http://localhost:4567/message -X POST -d "message=hello"

すると、subscribeのほうにhelloと表示されて接続が切れます。
これは、/messageのout.closeを実行しているからです。
試しに、out.closeをコメントアウトしてサーバーを起動すると接続維持されたままになります。