Search code examples
cssruby-on-railsrubyruby-on-rails-4resque-status

Ruby on Rails 4 resque-status progress bar


I developed a RoR app that does some file processing in the background using resque-status.

I want to be able to display a CSS progress bar of the percentage status of the background job.

Is there a way to get the status of the job from the view (in real time) so I can change the value of a progress bar?


Solution

  • I have been considering this myself!


    Push

    You'll want to use something called push technology (the same as iPhone "Push Notifications"). This will give you the ability to send specific updates to your front-end.

    Let me explain the issue, and how to resolve it...

    The issue you have is that sending persistent updates is not within the remit of Rails, or HTTP for that matter. These are both built on the stateless protocol pattern, meaning that each request is on a clean slate.

    This means that unless you open a persistent connection (from which you can specify different data attributes), you will only be able to send one-time updates, through asynchronous requests


    Persistence

    In order to "track progress", you need to create a system which firstly pushes your data (so you need a way to receive that data), and then you need to be able to process any of the pushed updates you'll receive. This is how you'll be able to create the "look" of a progress bar.

    There are two ways to achieve this:

    1. SSE's
    2. Websockets

    Both of these have a very simple way of working (you can see here: What are Long-Polling, Websockets, Server-Sent Events (SSE) and Comet?)

    They essentially give you the ability to connect your front-end DOM (via JS) to a backend "push" system. This will then allow your JS to process any of the updates sent from your backend to your "listening" JS calls.

    I won't go into specifics about SSE / Websockets, apart from saying that websockets are the best solution - they are truly persistent. SSE's just use ajax long-polling to your controller endpoint, highly inefficient & less secure.

    --

    Pushing

    In order to achieve the "progress bar" type interface, you'll want to consider the following pattern:

    1. Your user's DOM connects to your endpoint using SSE's / Websockets
    2. Using the pub/sub pattern, you'll have a channel for each user
    3. Send push notifications to the respective channels

    I would recommend using a system called Pusher for this (no affiliation). It's a websocket interface, connecting your front-end to the backend controller methods of your system

    This will allow you to perform the following:

    #app/assets/stylesheets/application.css.sass
    .progress
       background: #ccc
       .bar
          background: #f0f
    
    #app/workers/your_job_worker.rb
    class YourJobWorker
      include Resque::Plugins::Status
    
      def perform
        # send update to pusher here
      end
    
    end
    
    #app/assets/javascripts/application.js
    var pusher = new Pusher('***');
    var channel = pusher.subscribe('*****');
    channel.bind('progress', function(data) {
        $(".bar .progress").css("width", data + "%");
    });
    

    Obviously a very rough outline for you - hopefully will explain the methodology of how you'd tackle this issue.