Pasek postępu i batch

Chcę zrobić coś na kształt przetwarzania batchowego z paskiem postępu.
Z formularza w akcji import jest wczytywany plik, który ma utworzyć tabelę.
Na razie działa dodawanie po jednej linii sterowane z przeglądarki przez periodically_call_remote.
Ale to dodaje po 1 sekundzie ekstra na każdą linię. Dodatkowo plik jest przechowywany w session.

Czy jest możliwe wywołanie “asynchronicznie” metody Line.addall(params[:file]) i w kontrolerze odpytywanie
tylko metody Line.progress. Napisałem jak widać te metody ale nie wiem jak je użyć.

Czy ktoś ma może jakiś inny pomysł na pasek postępu.

To jest tylko szkielet aplikacji (ale działający).

[code]# line.rb - model zawiera tylko jedno pole verse - przechowujące kolejne wiersze pliku wejściowego
class Line < ActiveRecord::Base

def self.add1 v
r = Line.new
r.verse = v
r.save
sleep 1 # symulacja baaardzo skomplikowanej operacji na bazie
end

def self.addall vs
vs = vs.read.split($/)
l = vs.length
@@progress = 0
vs.each_index do |i|
@@progress = (i.to_f/l.to_f*100).to_i
Line.add1 v[i]
end
end

def self.progress
@@progress
end

end[/code]

# import.html.erb <% form_tag({:action => 'do'}, {:multipart => true}) do %> <%= file_field_tag 'file' %><br /> <%= submit_tag %> <% end %>

[code]# do.html.erb
<%= periodically_call_remote(:url => { :action => ‘progress’ }, :frequency => ‘1’, :update => ‘bar’) %>

[/code] [code]# line_controller.rb class LineController < ApplicationController

def do
if params[:file].blank?
redirect_to :action=>‘import’
return
end

zamiast poniższych linii - przydałoby się wywołać ‘asynchronicznie’ addall(params[:file])

session[:file] = params[:file].read.split($/)
session[:ifile] = 0

end

def progress
i = session[:ifile]
l = session[:file].length
if i>=l
session[:file] = nil
render :inline=>"<%= javascript_tag “location=’#{url_for :action=>‘import’}’” %>", :layout=>false
else

a zamiast dwóch następnych linii pr = Line.progress

  Line.add1 session[:file][i] # tworzy jeden rekord
  pr = (i.to_f/l.to_f*100).to_i.to_s
  render :inline=>"<span style='padding-left:#{pr}mm;background:red'>#{pr}%</span>", :layout=>false
  session[:ifile] += 1
end

end

end[/code]

Zainteresuj się jakimś pakietem do obsługi procesów działających w tle - np. backgroundrb. Dramatycznie upraszczając, twój kontroler uruchamia proces w tle, i otrzymuje token, który jest jakimś tam stringiem. Znająć token, możesz wymieniać informacje z procesem, który ciągle pracuje. W tym przypadku więc możesz za pomocą okresowych zapytań AJAX-em sprawdzać postęp realizacji zadania. BackgroundRb jest tylko przykładem, ale jego przestudiowanie pozwala zrozumieć mechanizm działania takich aplikacji.

Udało się to zrobić przy pomocy plugina spawn.
Pasek postępu się wyświetla w przeglądarce co 1 sekundę, a baza się aktualizuje asynchronicznie.
W sesji trzymany tylko procent postępu (session[:progress]).
Ale chodzi tylko w środowisku produkcyjnym.
W development mam wyjątek:
#<ArgumentError: A copy of Line has been removed from the module tree but is still active!>
Pewnie model Line nie powinien być przeładowywany, ale jak to zrobić?
Ciężko się uruchamia aplikacje w środowisku produkcyjnym. Czy mogę liczyć na jakąś podpowiedź?

[code]# line.rb

model zawiera tylko jedno pole verse - przechowujące kolejne wiersze pliku wej˜ciowego

class Line < ActiveRecord::Base

def self.add1 v
r = Line.new
r.verse = v
r.save
sleep 1 # symulacja baaardzo skomplikowane operacji na bazie
end

def self.addall vs, session
l = vs.length
session[:progress] = 0
vs.each_index do |i|
session[:progress] = (i.to_f/l.to_f*100).to_i
Line.add1 vs[i]
end
session[:progress] = 100
end

def self.progress session
session[:progress]
end

end[/code]

[code]# line_controller.rb
class LineController < ApplicationController

def do
if params[:file].blank?
redirect_to :action=>‘import’
return
end
spawn do
Line.addall params[:file].read.split($/), session
end
end

def progress
pr = Line.progress session
if pr>=100
render :inline=>"<%= javascript_tag “location=’#{url_for :action=>‘import’}’” %>", :layout=>false
else
render :inline=>"#{pr}%", :layout=>false
end
end

end[/code]