0

Good morning!

I have a form that takes in an IP, start date, and stop date that calls a SideKiq worker that executes a script on an external server through ssh, then sFTPs the resulting file to the local server.

Controller method:

def create
  @script = Script.new(params[:script])
  if @script.valid?
    @job_id = LogsWorker.perform_async(@script.ip, @script.start_date, @script.stop_date)
  else
    render :action => 'new'
  end
end

SideKiq worker:

def perform(ip, start_date, stop_date)
  Net::SSH.start('server', 'username') do |ssh|
    result = ssh.exec!("<script here> #{ip} #{start_date} #{stop_date}")
    ssh.sftp.connect do |sftp|
      local_result = "#{Rails.root}/public/uploads/" + File.basename(result)
      sftp.download!(result, local_result)
    end
  end
    .. to be continued ..

I also have the SideKiq worker parse the resulting file into a hash:

  .. continuing ..
  @file_hash = {}
  i = 0
  File.open(local_result, "r") do |fp|
    fp.each_line do |line|
      ip, date, method, url_full = line.split("\t")
      @file_hash[i] = {ip: ip, date: date, method: method, url_full: url_full}
      i += 1
    end
  end
end

And the partial _results.html.erb that parses it:

<% if @file_hash %>
  <% @file_hash.each do |key, value| %>
    <tr>
      <td><%= value[:ip] %></td>
      <td><%= value[:date] %></td>
      <td><%= value[:method] %></td>
      <td><%= value[:url_full] %></td>
    </tr>
  <% end %>
<% end %>

I tried rendering the results after the job has completed with <%= render partial: 'results', locals: { file_hash: @file_hash } %> in the create.js.coffee file, but I do not think it has any idea about the instance variable.

How can I pass a result from a completed SideKiq variable to a view? I think I should note I have another controller method that queries for the completion percentage of the SideKiq job, so I should be able to tell when it is finished.

Edit:

percentage_done in controller

def percentage_done
  job_id = params[:job_id] # grabbing the job_id from params

  container = SidekiqStatus::Container.load(job_id)

  @pct_complete = container.pct_complete

  respond_to do |format|
    format.json {
      render :json => {
        :percentage_done => @pct_complete, # responding with json with the percent complete data
      }
    }
  end
end

current ajax

queryForPercentage = () ->
  job_id = $('#job_id').text() # grabbing the job_id
  $.ajax({
    url: "/percentage_done" # sending an ajax request to /percentage_done
    data: job_id: job_id # using the job_id from the DOM
    success: (data) -> # executed after a successful call to the percentage_done controller
      percentage = 'width: ' + data['percentage_done'] + '%;'
      # writing the percentage done to the progress bar
      $('#job-progress').attr('style', percentage).text(data['percentage_done'] + '%')
      $('#job-progress').attr('aria-valuenow', data['percentage_done'])

      if $('#job-progress').text() != '100%'
        setTimeout((-> queryForPercentage()), 1500)
      else
        # replace the html of the div script_lists with the updated new one
        $("#scripts_list").html("<%= escape_javascript(render partial: 'results', locals: { file_hash: @file_hash } ) %>");
  })

queryForPercentage()

1 Answers1

1

Sidekiq is used to process background jobs. IMO sending a result from a Sidekiq job back to the view defeats the purpose of using Sidekiq; why not just perform the job in the same request.

With that said, you could accomplish this with an AJAX call on the view that polls the controller that queries Sidekiq for completion. That controller can also return the contents of the file. If the Sidekiq job doesn't take too long to complete it might not be a bad user experience.

kcdragon
  • 1,724
  • 1
  • 14
  • 23
  • It is a long running process.. usually anywhere from 5 seconds up to 10 minutes+, though generally probably in the 30 second or 3-5 minute range. I am using a percentage_done method (action? sorry, new to rails) that tracks it's completion. Is there a way I can pass the resulting file name to another action and update my view doing that? – aplewandowski Jun 27 '14 at 17:13
  • How about specifying the name of the file and then passing that name into the Sidekiq worker? – kcdragon Jun 27 '14 at 17:16
  • I added my current controller method and the ajax I am using to the question. – aplewandowski Jun 27 '14 at 17:19
  • I cannot think of a way to get the name of a file from a Sidekiq job in the same request. I would pass the name of the file into the Sidekiq job and then you will know where the file is located. – kcdragon Jun 27 '14 at 17:22
  • The name of the file is actually determined within the Sidekiq job. `local_result = "#{Rails.root}/public/uploads/" + File.basename(result)`. Is there an easy way to perhaps use ajax to render the new action and display the partial with the proper instance variable? Actually it doesn't even need to be an instance variable.. – aplewandowski Jun 27 '14 at 17:29
  • I have a feeling I am going to end up answering my own question, but thank you for entertaining it. I think I am mostly confused on how to do it using ajax rather than the usual way. – aplewandowski Jun 27 '14 at 17:32
  • You could store the name of the file in a database and then query on it based on the Job ID. The file name can be returned in the same AJAX call for progress. – kcdragon Jun 27 '14 at 17:33