[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