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.