[Backgroundrb-devel] "tailing" a running system command?
mattp at digimonkey.com
mattp at digimonkey.com
Wed Aug 16 19:15:34 EDT 2006
Nice. Based on your suggestion, I also found a simpler way (for me at least).
[controller]
def ping_response
if request.xhr?
msg = MiddleMan.get_worker(session[:job_key]).result.gets
render :update do |page|
if msg.nil?
MiddleMan.delete_worker(session[:job_key])
page.remove('spinner')
page.call('stop_xhr') # <=== LOOK HERE! THIS IS THE FANCY BIT!!
else
page.insert_html :bottom, 'ping_result', msg+'<br />'
end
end
else
redirect_to :action => 'index'
end
end
[view]
<script language="javascript">
var stop_polling = false;
function stop_xhr() {
stop_polling = true;
}
</script>
<%= periodically_call_remote(:url => {:action => :ping_response},
:frequency => 0.5, :condition => "stop_polling == false" ) -%>
Quoting Ezra Zygmuntowicz <ezmobius at gmail.com>:
> Very cool! I finally figured out how to stop a
> periodically_call_remote via rjs or js. Do it like this, in the view
> where you render your periodic call:
>
> <script>stop_polling = false;</script>
>
> <%= periodically_call_remote(:url => {:action => 'update_results'},
> :frequency => 5,
> :condition => "stop_polling == false") %>
>
> <a href='#' onclick="stop_polling=true">Stop Polling</a>
>
>
> #Or if you need to tell it to stop from the controller via rjs:
>
> def stop_polling
> render :update { |p| p.assign 'stop_polling', true }
> end
>
>
>
> And thats it, the periodic call will stop polling once the js variable
> stop_polling is set to true. Of course you could also just set it to
> true in the view but it depends on where you get the info that tells
> you to stop polling.
>
>
> Cheers-
> -Ezra
>
>
> On Aug 16, 2006, at 2:47 PM, mattp at digimonkey.com wrote:
>
>> Check it out... I got it working:
>>
>> [ worker ]
>> class PingWorker < BackgrounDRb::Rails
>> attr_accessor :result
>>
>> def do_work(args)
>> @result = IO.popen("ping -n 10 www.google.com")
>> end
>> end
>>
>> [ controller ]
>> def ping_task
>> session[:job_key] = MiddleMan.new_worker(:class => :ping_worker)
>> end
>>
>> def ping_response
>> if request.xhr?
>> msg = MiddleMan.get_worker(session[:job_key]).result.gets
>> render :update do |page|
>> if msg.nil?
>> MiddleMan.delete_worker(session[:job_key])
>> # ??? need some way to stop the PeriodicalExecuter.
>> else
>> page.insert_html :bottom, 'ping_result', msg+'<br />'
>> end
>> end
>> else
>> redirect_to :action => 'index'
>> end
>> end
>>
>> [ view ]
>> <div id="ping_result"></div>
>> <%= periodically_call_remote(:url => {:action => :ping_response},
>> :frequency => 1) -%>
>>
>>
>> Now if I can just figure out a way to stop the PeriodicalExecuter...
>>
>> [mp]
>>
>> Quoting Charles Brian Quinn <me at seebq.com>:
>>
>>> We're working on a super cool new tool to help automate capistrano and
>>> rake deployment task running on remote servers, and we do something
>>> similiar to this using backgrounDRb (thanks Ezra!).
>>>
>>> It is based off our RailsDay 2006 entry:
>>> http://heartbeat.highgroove.com/ -- enough with the bragging, here is
>>> how we do it:
>>>
>>> your worker now needs to have an @output class variable (instead of
>>> progress).
>>>
>>> in your worker, you can run the task one of two ways which might or
>>> might not work:
>>>
>>> # you can use Kernel.system to make the call which I don't
>>> know how you get output
>>> if not Kernel.system("ping google)
>>> errors.add_to_base(" your error here ") # this is
>>> technically in a model hence the errors obj
>>> raise
>>> end
>>>
>>> # or:
>>> cmd_to_run = "ping google"
>>>
>>> stdin, stdout, stderr = Open3.popen3(cmd_to_run)
>>>
>>> s_stderr = stderr.read.to_s
>>> s_stdout = stdout.read.to_s
>>>
>>> # if you'd like to see it
>>> logger.info "stdout: #{s_stdout}"
>>> logger.info "stderr: #{s_stderr}"
>>>
>>> if s_stderr.empty? && s_stdout.starts_with?("error stuff here")
>>> return true
>>> else
>>> errors.add_to_base("error msg here")
>>> return false
>>> end
>>>
>>> But, as for setting your @output, well, you'd need to run inside a
>>> loop or perhaps even in another thread within the bg worker thread.
>>> We don't need to do this because we're executing our task using ssh
>>> and we have an open channel and just do @output << in the loop as it
>>> runs. You may not be able to use the popen3 calls above, to be honest
>>> -- the read pretty much kills that idea. Our worker method that
>>> appends output looks like:
>>>
>>> Net::SSH.start( host,
>>> :username => username,
>>> :password => password,
>>> :port => port ) do |session|
>>> session.open_channel do |channel|
>>>
>>> channel.on_data do |ch, data|
>>> @output << "#{data}"
>>> end
>>>
>>> channel.on_extended_data do |ch, type, data|
>>> @output << "#{data}"
>>> end
>>>
>>> channel.exec( "ping google\n" )
>>> end
>>> session.loop
>>> end
>>>
>>> Maybe you can find a simliar asynchronous call for executing methods,
>>> that updates output as it runs.
>>>
>>> Now, in your controller:
>>>
>>> the method, let's say execute:
>>>
>>> def execute
>>> session[:job_key] = Middleman.... # call your worker
>>> end
>>>
>>> and for the view we used rjs to update a page (a lightbox actually):
>>>
>>> <div id="task_output">
>>> Connecting to Server...
>>> </div>
>>>
>>> execute.rjs:
>>>
>>> page << "showBG();"
>>> page << "showIbox('#{url_for(:action => 'output', :id =>
>>> @task)}','',parseQuery('width=760&height=400'))"
>>> page << "output = new
>>> Ajax.PeriodicalUpdater('task_output','#{url_for(:action =>
>>> 'update_output', :id=> @task.id)}',2);"
>>>
>>> back to your controller, it can be as simple as:
>>>
>>> def update_output
>>> @job = MiddleMan.get_worker(session[:job_key])
>>> render :inline => '<%= simple_format(@job.output) %>'
>>> end
>>>
>>> The hard part will be appending @output as it's running. I know I
>>> didn't answer the @output side, but hopefully helped with the
>>> backgroundrb worker side.
>>>
>>> cheers and good luck, let us know how it goes.
>>>
>>> On 8/16/06, mattp at digimonkey.com <mattp at digimonkey.com> wrote:
>>>> Dunno if anyone saw the short question at the end of my last thread,
>>>> so i'm reposting in a thread with the appropriate subject:
>>>>
>>>> Here's what I need to do --
>>>>
>>>> Run a system-level command like 'ping -t www.google.com' and have it
>>>> periodically update a <div> with the command's ouput. Is this possible?
>>>>
>>>> (the trick here is that I want *periodic* updates of the div... i want
>>>> to see incremental output as 'ping' is running, just like if I were
>>>> looking at console...)
>>>> _______________________________________________
>>>> Backgroundrb-devel mailing list
>>>> Backgroundrb-devel at rubyforge.org
>>>> http://rubyforge.org/mailman/listinfo/backgroundrb-devel
>>>>
>>>
>>>
>>> --
>>> Charles Brian Quinn
>>> self-promotion: www.seebq.com
>>> highgroove studios: www.highgroove.com
>>> slingshot hosting: www.slingshothosting.com
>>
>>
>> _______________________________________________
>> Backgroundrb-devel mailing list
>> Backgroundrb-devel at rubyforge.org
>> http://rubyforge.org/mailman/listinfo/backgroundrb-devel
>>
More information about the Backgroundrb-devel
mailing list