2

I have seen this question asked before, but I am curious as to accomplish this using sinatra. Recognizing that sinatra already has a streaming method, I assume the solution is already 80% complete.

These are some similiar questions for reference:

Generic redirect stdout: Streaming stdout to a web page

Using sinatra to stream stdout (not working for me): Sinatra 1.3 Streaming w/ Ruby stdout redirection

Here's an example of what I am trying to accomplish(simplified for clarity):

require 'sinatra'
require 'thin'

get '/' do
  stream do |out|
    out << method_that_puts
  end
end

def method_that_puts
  puts 'I would like...'
  sleep 1.0
  puts 'to display this...'
  sleep 1.0
  puts 'on a web page!'
end

EDIT: The perfect example of this is travis-ci which is also built with sinatra. They are redirecting stdout to the build page... How is this possible?

Travis-CI build page

UPDATE:

Thanks to the help so far, I have a halfway solution working. I am currently saving $stdout to a new instance of StringIO and displaying it afterwards. BUT, this isn't the answer. For long running scripts (ie. travis-ci build) it would be ridiculous to wait for entire script to complete then display the $stdout. Need to figure out how to stream it as it comes...

Here's what I have so far:

foo = StringIO.new
$stdout = foo

get '/' do
  stream do |out|
    method_that_puts
    out.puts $stdout.string
  end
end

def method_that_puts
  puts 'I would like...'
  sleep 1.0
  puts 'to display this...'
  sleep 1.0
  puts 'on a web page!'
end
Community
  • 1
  • 1
binarymason
  • 1,351
  • 1
  • 14
  • 31
  • Ruby Sinatra is server side, meaning you can't just update the page like that without refreshing. Either you refresh, or you can utilize client-side JavaScript or jQuery, which is easier than you'd think. Also, to direct output to a webpage, you have to embed the value into an *.erb file and run the WEBrick server or use a host. – Charles Oct 22 '15 at 12:43
  • You sure it's not possible? My impression was a stream could be handled for a specific route and continues to 'load' while there is still data in the stream. There's no refresh -- it's the initial loading of the page. – binarymason Oct 22 '15 at 13:33
  • Capture the output and provide it as a view value. – Dave Newton Oct 22 '15 at 14:58
  • Is it possible to do that in real time? I thought perhaps saving `$stdout` to a variable and passing to a view, but it doesn't show `method_that_pus` concurrently. – binarymason Oct 22 '15 at 17:15
  • There is, to my knowledge, no such way to accomplish what you want to accomplish, due to Ruby Sinatra being server-side. If you want things to update, use client-side Javascript. – Charles Oct 23 '15 at 13:27

1 Answers1

1

You can use Sinatra::Streaming (from the sinatra-contrib gem), which provides an output object you can write to.

Here's the example from the docs:

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
Kristján
  • 18,165
  • 5
  • 50
  • 62
  • Nice. This is definitely in the right direction. I still can't figure out how to puts out the output of `method_that_puts` though. `out.method_that_puts` just puts out what was returned -- `nil`. – binarymason Oct 22 '15 at 17:19
  • The bad idea would be `$stdout = out`, which would redirect all `puts` output to your response. My real suggestion is to simply pass `out` to `method_that_puts`, and use it there. – Kristján Oct 22 '15 at 18:39
  • That makes sense. Here's my confusion -- wouldn't that require me to make my own method to pass in the args? I'm using gems I don't own. For example, I'm trying to embed Sinatra server log to page. – binarymason Oct 23 '15 at 13:32
  • If you're piping the log, maybe stealing `$stderr` isn't totally crazy. I would try [`file-tail`](https://github.com/flori/file-tail) to tail your file through to the streaming response, or you could [set up the logger](http://recipes.sinatrarb.com/p/middleware/rack_commonlogger) with a shared I/O object that the action can consume. – Kristján Oct 23 '15 at 16:05