How to force flushing ERB output?

How can I force flushing an ERB output?

I have a report that yields a very big HTML table that can take hours to finish processing depending on input params.

I would like to flush the already generated output to the browser even if the result is not complete. The idea is send the table lines once they are generated. Waiting for the entire output to be generated also could consume a lot of RAM, although it is not a major problem now, but flushing the output from time to time would allow less memory usage.

Just in case some of you have some experience with JRuby or could enlight me understanding another problem, let me ask you another question. The calculations I'm performing for generating this table are quite heavy and I'm using some Java libraries to help me performing them. I'm not using ActiveRecord to talk to the database, but Sequel instead in the Ruby part. I'm also using warbler to generate a war file to be deployed in a Java servlet container, such as Tomcat.

I would like to understand better why isn't Rails thread-based. I mean, why there is a need for multiple instances of Rails for dealing with concurrent connections? If this limitation is specific for ActiveRecord or MRI Ruby implementation, it shouldn't be a problem for my application, since it uses JRuby (with real threads) and does not depend on ActiveRecord. Is there anything I could do to allow multiple concurrent connections in a single Rails instance?

Thanks in advance,

Rodrigo.

I've talked to Yehuda today (actually yesterday, at least in my time
zone) and we come to some solutions.

First, he pointed me out the config.threadsafe! method, regarding the
ability to handle multiple connections concurrently in a single Rails
instance:

http://api.rubyonrails.org/classes/Rails/Configuration.html#M002542

Then, he remembered me why it is complicated to support forcing
flushing from inside an ERB. In Rails, we can set variables in a view
that will then be available when the layout is applied. This requires
Rails to process all the view before sending any output to browsers.

Then I tried to handle the ERB manually from inside the controller
using the "render :text => proc" approach. See the Streaming section
in:

http://api.rubyonrails.org/classes/ActionController/Base.html#M000658

First, some comments before the solution so that I can remember myself
to update the documentation. Here is the example from API:

# Streams about 180 MB of generated data to the browser.
  render :text => proc { |response, output|
    10_000_000.times do |i|
      output.write("This is line #{i}\n")
      output.flush
    end
  }

Apparently, output.flush is deprecated since flushing is handled
automatically, thus, this line should be removed from documentation.

One more thing: the above code won't work with Rails 3 beta, at least.
Actually I had other problems with Rails 3 beta. For instance:

- Usually, if one has multiple Rails installations and want to use
some specific version to create an application, he can do "rails
_2.3.4_ new_app". That won't work after installing Rails 3 beta.
Worse, it is not possible either to run the Rails binary from rubygems
installation because of some strange problem I couldn't understand
what the cause was. So, I had to uninstall Rails 3 beta before I could
achieve the solution I'll present next.

This was the solution I've found for my needs with the help of Yehuda:

Created a Rails 2 application and created a controller like this:

class TestController < ApplicationController
  def index
   render :text => proc { |response, output|
     response.content_type = 'text/plain'
     v = ActionView::Base.new([],{},self)
     v.instance_variable_set('@output', output)
     output.write ERB.new(IO.read((Rails.root+'app/views/test/
index.html.erb').to_s)).result(v.send :binding)
   }
  end
end

And the view index.html.erb:
<% 100.times do |i| %>

Of course, with this solution one should write the entire HTML output
since it will not use any layout as it is. But that is not a problem
for me.

Hope this helps others. If not, at least it will be useful for me not
to forget the solution :wink:

I'm planning updating Layout and Rendering guide to cover this
situation.

But I have another pending doubt I've just realized...

How can I trigger an action when the user cancel the request? My
report can take hours to complete and I would like Rails to stop
processing the request when the user interrupts the connection (by
canceling loading in the browser, for instance). Is that possible? I
know I'm pushing Rails limits here, but hey! What would be the fun
part if we don't do that! :wink:

Thank you once more, Yehuda.

Best regards,

Rodrigo.