読者です 読者をやめる 読者になる 読者になる

PerlerのRuby日記

Rubyとか

Sinatraとsinatra-contribのStreamingの違い

Sinatra標準のStreamingと、公式拡張sinatra-contribのStreamingの違いについてメモ。

Sinatra標準Streaming

使い方

# coding: utf-8
require "sinatra"

get '/' do
  stream do |out|
    out << "やあ<br>"
    sleep 1
    out << "こんにちは、<br>"
    sleep 1

    error_message = "お前はここで終わりだがな!"
    out.callback { p error_message }
    #=> closeされたときに実行される

    out << "世界"
  end
end

WEBrickだと対応してないので、thinで。

$ ruby classic_streaming.rb -s thin

まあデフォルトだと「<<」と「callback」しかできることはない。


他にはstreamに渡すときに第1引数をtrueにすると、そのコネクションが開きっぱなしになるらしい。

以下、超簡単なpub/sub実装。

require "sinatra"
connections = []

# subscribe側
get '/' do
  # trueならなんでもいい、そう、シンボルでもね。
  stream(:keep_open) do |out|
    connections << out
  end
end

# publish側
get '/send' do
  connections.each do |out|
    out << params[:message]+"<br>"
  end
  "send message: #{params[:message]}"
end

いいね!

おわり。


sinatra-contribのStreaming

これをもう少し拡張したのがsinatra-contribのStreamingで別途インストールが必要になる。

# Gemfile
gem "sinatra-contrib"

とはいえ、唯一の特徴がIOインスタンスっぽく扱える、というだけのようだ。


classicスタイルでの使い方

require "sinatra"
require "sinatra/streaming"

get "/" do
  stream do |out|
    out.write "妖怪が鍛えた<br>"
    sleep 1
    out.syswrite "この楼観剣に<br>"
    sleep 1
    out.write_nonblock "斬れぬものなど<br>"
    sleep 1
    out.puts "あんまり無い"
    out.putc(33)
    out.putc(33)
    out.printf("<%s>", "br")
    sleep 1
    out.print "#{out.pos} bytes"
  end
end

なんか公式の例で

out.flush

とできるようになっているけど、中身の実装はなかった。。。(華麗にスルー)


まあ特別IOっぽくしたいっていうことでも無い限り、標準の方で事足りると思う。


ちなみ

また、uu59のメモ | Sinatra 1.3で追加されたstreamのHTTP解剖学 の通り

ちょいちょい送信することを示す「Transfer-Encoding: chunked」がない(わざと実装していない?)みたいなので

あんまりきっちりしたことには使えないとも思う。

$ curl -i localhost:4567/
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Connection: close
Server: thin 1.5.1 codename Straight Razor

公式ドキュメント

Sinatra: README

Sinatra::Streaming (part of Sinatra::Contrib)

参考

uu59のメモ | Sinatra 1.3で追加されたstreamのHTTP解剖学

ありがとうございます。