This is my first post here but I have gotten some great info from this site already. So I thought someone may be able to help.
In my view I have a form that once submitted, the controller passes the job off to navvy and it really works well. The problem is I would like to have another partial on the same page as the form update with the new info once navvy is done working. So in my controller I have:
Navvy::Job.enqueue( GetZip, :get_zip, @series, :job_options => {:priority => 8})
And then in my navvy block which is located in config/initializers/navvy.rb I have:
class GetZip
def self.get_zip(params)
fetch = Fetch.new
fetch.get_zip(params)
# What to put here to update partial #
end
end
Which works as expected. I am just not sure how I can have the partial开发者_StackOverflow中文版 in my view updated once the navvy job is completed. Any suggestions would be greatly appreciated.
The problem here is that once you've fired up a background process you're no longer tied into that user's request (which is obviously the point of the background process).
So your background job is not aware of what the user is doing in the meantime. For example they could navigate to another page, or even leave your website.
You could get your background job to create a record in a database that indicates it has started processing and update this status when it has finished processing. That way when the user next refreshes the page you can check the status of that record and the partial can be updated accordingly.
If you want the page to auto-update you could keep polling the status of that record with ajax requests. On the initial submission of the form you could start the polling rather than have it running all the time.
Here's what I'm using. This is where the Job is called:
Setting::progress = "Starting..."
Navvy::Job.enqueue(EmailWorker, :async_email, stuff)
Settings is super simple:
class Setting < ActiveRecord::Base
def Setting::progress=(value)
setn = Setting.find_or_initialize_by_name("email_status")
setn.value = value
setn.save
end
end
and the navvy job EmailWorker is:
class EmailWorker
def self.async_email(options)
# send one at a time
total = options[:list].length
errors = []
options[:list].each_with_index do |email_addr, n|
begin
Setting::progress = "#{n+1}/#{total}: #{email_addr}"
Postoffice.one_notice(email_addr, options).deliver
rescue Exception => e
Setting::progress = "#{email_addr} #{e.message}"
errors << "<span class='red'>#{email_addr}</span> #{e.message}"
end
# get stack level too deep errors at random when this is removed. I don't get it.
sleep 0.05
end
Setting::progress = "DONE sending #{total} with #{errors.length} errors<br/>#{errors.join("<br/>")}"
"Done" # just for display in Navvy console output
end
end
Then the triggering page has this:
<%-
# had to add this back as rails 3 took it away
def periodically_call_remote(options = {})
frequency = options[:frequency] || 10 # every ten seconds by default
code = "new PeriodicalExecuter(function() {#{remote_function(options)}}, #{frequency})"
javascript_tag(code)
end
-%>
<script type="text/javascript">
//<![CDATA[
check_var = true;
//]]>
</script>
<%= periodically_call_remote(
:condition => "check_var == true",
:url => { :controller => "application",
:action => "update" },
:frequency => '3') -%>
<div id="progress">
<p>Monitoring progress of emails</p>
</div>
And heres the method that's called repeatedly:
def update
raise "not for direct" if (!request.xhr?)
@progress = Setting::progress
@again = !(/^DONE/ =~ @progress)
render :action => "update"
end
Which trigers an in-place update via update.rjs
page.assign('check_var', @again)
page.replace_html "progress", :partial => "info", :locals => { :info => @progress }
page.visual_effect :highlight, 'progress', :startcolor => "#3333ff", :restorecolor => '#ffffff', :duration => 0.5
One word of warning about Navvy - since it runs as a background task until killed, it will stay executing your old app when you cap deploy. You must kill navvy in old "current" and move to new "current".
精彩评论