2011-12-23 71 views
0

我想使用Sinatra在1.3中引入的Streaming功能以及一些stdout重定向。它基本上是长时间运行作业的直播输出。我在README中查看了this question和Sinatra streaming样本。Sinatra 1.3 Streaming with Ruby stdout重定向

OSX上运行1.8.7:

require 'stringio' 
require 'sinatra' 

$stdout.sync = true 

module Kernel 
    def capture_stdout 
    out = StringIO.new 
    $stdout = out 
    yield out 
    ensure 
    $stdout = STDOUT 
    end 
end 

get '/' do 
    stream do |out| 
    out << "Part one of a three part series... <br>\n" 
    sleep 1 
    out << "...part two... <br>\n" 
    sleep 1 
    out << "...and now the conclusion...\n" 

    Kernel.capture_stdout do |stream| 
     Thread.new do 
      until (line = stream.gets).nil? do 
       out << line 
      end 
     end 
     method_that_prints_text 
    end 
    end 
end 

def method_that_prints_text 
    puts "starting long running job..." 
    sleep 3 
    puts "almost there..." 
    sleep 3 
    puts "work complete!" 
end 

所以这段代码打印出前三串正常,并阻止而method_that_prints_text执行,并不会打印出任何东西到浏览器。我的感觉是stdout在第一次调用时是空的,它从不输出到out缓冲区。我不太确定正确的顺序会是什么,并会感谢任何建议。

我尝试了上述问题中提到的一些EventMachine实现,但无法让它们工作。

UPDATE

我试过的东西,我必须在一个新线程的run方法略有不同,如描述here重载stdout该线程...

代替上述Kernel.capture_stdout .. 。

s = StringIO.new 

Thread.start do 
    Thread.current[:stdout] = s 
    method_that_prints_text 
end.join 

while line = s.gets do 
    out << line 
end 

out << s.string 

有了上面的链接中列出的ThreadOut模块,这似乎工作好一点。但它不流。唯一一次打印到浏览器上的是最后一行out << s.stringStringIO没有流式传输功能吗?

回答

1

我最终通过发现s.string随着时间的推移而定期更新来解决这个问题,所以我只是在一个单独的线程中捕获输出并抓住了差异并将它们流出。看起来好像字符串重定向不像普通的IO对象。

s = StringIO.new 
t = Thread.start do 
    Thread.current[:stdout] = s 
    method_that_prints_text 
    sleep 2 
end 

displayed_text = '' 
while t.alive? do 
    current_text = s.string 
    unless (current_text.eql?(displayed_text)) 
    new_text = current_text[displayed_text.length..current_text.length] 
    out << new_text 
    displayed_text = current_text * 1 
    end 
    sleep 2 
end