0
votes

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
I just wanted to print some messages from sidekiq into my views and I had to use faye gem to do that. So I think it's not something so simple. - alexsmn
Did you get this sorted? I want to the do the same thing. - stephenmurdoch

1 Answers

1
votes

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.