Progress bar in Rails

Stjepan Hadjić
  —  4 min read

Ever needed a progress bar for some long-running task in your Rails application? You searched Google and couldn't find anything that easily integrates with Rails? Well, we created a progress_job gem that helps with that problem.

Progress_job is a gem that builds upon delayed_job to give you a simple progress bar you can use in your views. Create a class with your long-running task inside, update the job in each iteration and have ajax calls that will update the progress bar.



Progress_job depends on delayed_job, so I will write this tutorial using the delayed_job_active_record gem.

After adding the gem in your Gemfile

  gem 'delayed_job_active_record'
  gem 'progress_job'

and running bundle install,

  $ bundle install

run the progress_job generator to add migrations necessary for progress_job to work.

  $ rails generate delayed_job:active_record
  $ rails generate progress_job:install
  $ rake db:migrate

This will add three columns to the delayed_jobs table:

  • progress_stage : string => customizable description for the current progress stage of the task
  • progress_current : integer => number representing the current progress value of the task
  • progress_max : integer => number representing the maximum progress value of the task

Then you can create a custom class extending ProgressJob::Base which will give you access to some handy methods for manipulating the job.

  update_progress(step: 10)  # increase the progress_current for step
  update_stage('name of stage')  # change the progress_stage
  update_stage_progress('name of stage', step: 11)  # change progress_stage and increase progress_current for step
  update_progress_max(progress_max) # change progress_max

Progress_job also gives you a route from which you can get all the info on a progress_job, and it is located at:

  GET /progress-jobs/:job_id

Now all you need is an ajax call which will check the route every few seconds and update the progress bar visible on the screen.

Demo app

Progress job

I've created a demo app. You can take a look at the demo or view its source.

# app/jobs/export.rb
class ExportJob < ProgressJob::Base
  def initialize(users, progress_max)
    super progress_max: progress_max
    @users = users

  def perform
    update_stage('Exporting users')
    csv_string = CSV.generate do |csv|
      @users.each do |user|
        csv << user.to_csv
    end'path/to/export.csv', 'w') { |f| f.write(csv_string) }

# app/controllers/exports_controller.rb
class ExportsController < ApplicationController
  def index

  def export_users
    users = User.first(100)
    @job = Delayed::Job.enqueue, users.count)

# app/views/exports/index.html.haml
  = link_to 'Export Users', export_users_path, remote: true, class: 'btn btn-primary btn-lg'
  .well{style: 'display:none'}
  = link_to 'View csv', '/system/export.csv', class: 'btn btn-success export-link', style: 'display:none'

# config/routes.rb
Rails.application.routes.draw do
  root to: 'exports#index'
  get 'export_users' => 'exports#export_users', as: :export_users
// app/views/exports/export_users.js.haml
  var interval;
  $('.export .well').show();
  interval = setInterval(function(){
      url: '/progress-job/' + #{},
      success: function(job){
        var stage, progress;

        // If there are errors
        if (job.last_error != null) {

        progress = job.progress_current / job.progress_max * 100;
        // In job stage
        if (progress.toString() !== 'NaN'){
          $('.progress-status').text(job.progress_current + '/' + job.progress_max);
          $('.progress-bar').css('width', progress + '%').text(progress + '%');
      error: function(){
        // Job is no loger in database which means it finished successfuly
        $('.progress-bar').css('width', '100%').text('100%');
        $('.progress-status').text('Successfully exported!');
Greetings from our lovely team!
Achievement unlocked
Resize Master