Is it possible to override the standard Rails rendering infrastructure
for a single controller action, in such a way that I can generated
“progressive” HTTP output? That is, the browser can display text as it
is generated, rather than Rails buffering the whole page and sending
it once at completion?
Here’s a trivial example, written as a CGI:
#!/usr/bin/ruby -w
$stdout.sync = true
puts “Content-Type: text/html”
puts
puts “”, " " * 256
puts “”
(1…10).each do |i|
puts “
#{i}
”sleep 1
end
puts “”
If you stick this in /cgi-bin/ and point a browser at it then you can
see the numbers going down the screen one per second. (The real-world
application I have is for displaying progress during a long-running
operation, such as uploading firmware to a router). I can’t write the
above in Rails currently.
And here’s another example, which I found on the web, using x-mixed-
replace:
#!/bin/sh
#echo “HTTP/1.0 200”
echo “Content-type: multipart/x-mixed-replace;boundary=—
ThisRandomString—”
echo “”
echo “—ThisRandomString—”
while true
do
echo “Content-type: text/html”
echo “”
echo “Processes on this machine updated every 5 seconds
”
echo "time: "
date
echo “
”
echo “”
ps -el
echo “—ThisRandomString—”
sleep 5
done
Now, clearly I can run these as CGIs, but then I would lose Rails’
lovely routing and parameter handling, and its integration with
ActiveRecord with persistent database connections.
Basically I want something like this:
def myaction
…
browser.puts “string1”
…
browser.puts “string2”
…
render :none=>true
end
For me the key issue is: do the underlying web servers that Rails runs
under, such as webrick and mongrel, allow the response to be sent in
parts? Is there some IO-like object hanging around that I could call
‘puts’ or ‘write’ on to send data directly back to the browser?
There are other ways to achieve what I want, but they add a lot of
complexity and are less real-time. For example, the browser can be set
to poll for status every 5 seconds, but then one Rails process is
doing work and has to publish its progress to some object, which
another Rails process checks for data.
Other ideas are welcomed.
Regards,
Brian.