├── Rakefile ├── lib ├── progress_job │ ├── version.rb │ ├── engine.rb │ └── base.rb ├── progress_job.rb └── generators │ └── progress_job │ ├── templates │ └── migration.rb │ ├── USAGE │ └── install_generator.rb ├── Gemfile ├── .gitignore ├── app └── controllers │ └── progress_job │ └── progress_controller.rb ├── progress_job.gemspec ├── LICENSE.txt └── README.md /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | -------------------------------------------------------------------------------- /lib/progress_job/version.rb: -------------------------------------------------------------------------------- 1 | module ProgressJob 2 | VERSION = "0.0.4" 3 | end 4 | -------------------------------------------------------------------------------- /lib/progress_job/engine.rb: -------------------------------------------------------------------------------- 1 | module ProgressJob 2 | class Engine < Rails::Engine; end 3 | end -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in progress_job.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /lib/progress_job.rb: -------------------------------------------------------------------------------- 1 | require "progress_job/base" 2 | require "progress_job/engine" 3 | require "progress_job/version" 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /lib/generators/progress_job/templates/migration.rb: -------------------------------------------------------------------------------- 1 | class AddProgressToDelayedJobs < ActiveRecord::Migration<%= migration_version %> 2 | def change 3 | change_table :delayed_jobs do |t| 4 | t.string :progress_stage, required: true 5 | t.integer :progress_current, required: true, default: 0 6 | t.integer :progress_max, required: true, default: 0 7 | end 8 | 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/generators/progress_job/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Generates migration for acts_as_simple_translatable. 3 | 4 | Example: 5 | rails generate acts_as_simple_translatable en category name description 6 | 7 | This will create a migration to translate the name and description fields in the categories table. 8 | It will insert the existing category and description values in the record_translations table using the 'en' (English) locale. 9 | Note: You cannot roll back this migration. -------------------------------------------------------------------------------- /app/controllers/progress_job/progress_controller.rb: -------------------------------------------------------------------------------- 1 | module ProgressJob 2 | class ProgressController < ActionController::Base 3 | 4 | def show 5 | @delayed_job = Delayed::Job.find(params[:job_id]) 6 | return unless @delayed_job.present? 7 | percentage = !@delayed_job.progress_max.zero? ? @delayed_job.progress_current / @delayed_job.progress_max.to_f * 100 : 0 8 | render json: @delayed_job.attributes.merge!(percentage: percentage).to_json 9 | end 10 | 11 | end 12 | end -------------------------------------------------------------------------------- /lib/generators/progress_job/install_generator.rb: -------------------------------------------------------------------------------- 1 | require "rails/generators" 2 | 3 | module ProgressJob 4 | 5 | module Generators 6 | class InstallGenerator < Rails::Generators::Base 7 | include Rails::Generators::Migration 8 | 9 | source_root File.expand_path('../templates', __FILE__) 10 | 11 | def install 12 | migration_template "migration.rb", "db/migrate/add_progress_to_delayed_jobs.rb" 13 | route "get 'progress-job/:job_id' => 'progress_job/progress#show'" 14 | end 15 | 16 | def self.next_migration_number(path) 17 | @migration_number = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i.to_s 18 | end 19 | 20 | def migration_version 21 | if rails5? 22 | "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/progress_job/base.rb: -------------------------------------------------------------------------------- 1 | module ProgressJob 2 | class Base 3 | def initialize(progress_max: 100) 4 | @progress_max = progress_max 5 | end 6 | 7 | def before(job) 8 | @job = job 9 | job.update_column(:progress_max, @progress_max) 10 | job.update_column(:progress_current, 0) 11 | end 12 | 13 | def update_progress(step: 1) 14 | @job.update_column(:progress_current, @job.progress_current + step) 15 | end 16 | 17 | def update_stage(stage) 18 | @job.update_column(:progress_stage, stage) 19 | end 20 | 21 | def update_stage_progress(stage, step: 1) 22 | update_stage(stage) 23 | update_progress(step: step) 24 | end 25 | 26 | def update_progress_max(progress_max) 27 | @job.update_column(:progress_max, progress_max) 28 | end 29 | 30 | def error(job, exception) 31 | job.update_column(:progress_stage, exception.message) 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /progress_job.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'progress_job/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "progress_job" 8 | spec.version = ProgressJob::VERSION 9 | spec.authors = ["Stjepan Hadjic"] 10 | spec.email = ["d4be4st@gmail.com"] 11 | spec.summary = %q{Delayed jobs with progress.} 12 | spec.description = %q{Add progress feature to delayed jobs} 13 | spec.homepage = "https://github.com/d4be4st/progress_job" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files -z`.split("\x0") 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_development_dependency "bundler", "~> 1.5" 22 | spec.add_development_dependency "rake" 23 | spec.add_dependency 'delayed_job' 24 | end 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Stjepan Hadjic 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProgressJob 2 | 3 | This gem adds a couple of columns to delayed job table, and gives you a basic class for working with progress 4 | 5 | ![progress_job](https://s3-eu-west-1.amazonaws.com/uploads-eu.hipchat.com/36110/270039/V6ljuGiWx70t2AF/progress%20%281%29.gif) 6 | 7 | ## Requirements 8 | 9 | Ruby 2.0.0 (as the methods are using keyword arguments.) 10 | 11 | ## Installation 12 | 13 | You need to have https://github.com/collectiveidea/delayed_job in you gem file 14 | 15 | Add this line to your application's Gemfile: 16 | 17 | gem 'progress_job' 18 | 19 | And then execute: 20 | 21 | $ bundle 22 | 23 | Run generator (run delayed job generators first!) 24 | 25 | $ rails generate progress_job:install 26 | 27 | ## Usage 28 | 29 | Create a new class that extends ProgressJob::Base 30 | 31 | class NewJob < ProgressJob::Base 32 | 33 | def perform 34 | # some actions 35 | end 36 | 37 | end 38 | 39 | Inside perform method you can use: 40 | 41 | update_progress(step: 10) # default step is 1 42 | update_stage('name of stage') 43 | update_stage_progress('name of stage', step: 11) 44 | update_progress_max(progress_max) 45 | 46 | methods to update the job progress. 47 | 48 | 49 | To create a new job use Delayed job enqueue method, and pass the progress_max value 50 | 51 | job = Delayed::Job.enqueue NewJob.new(progress_max: 100) # default progress_max is 100 52 | 53 | There is also a controller which returns the delayed job with calculated percentage 54 | 55 | GET 'progress-jobs/:job_id/' 56 | 57 | ## Examples 58 | 59 | ### Demo 60 | 61 | [demo](http://progress-job-demo.hadjic.com) 62 | [source](https://github.com/d4be4st/progress_job_demo) 63 | 64 | ### Progress job class 65 | 66 | ``` ruby 67 | class NewJob < ProgressJob::Base 68 | 69 | def perform 70 | handler = Handler.new 71 | 72 | update_stage('Handling ports') 73 | handler.handle_ports 74 | 75 | update_stage_progress('Handling cruise', step: 10) 76 | handler.handle_cruise 77 | 78 | update_stage_progress('Handling days', step: 10) 79 | handler.handle_days 80 | 81 | update_stage_progress('Handling pick up times', step: 10) 82 | handler.handle_pick_up_times 83 | 84 | update_stage_progress('Handling users', step: 10) 85 | handler.handle_users 86 | 87 | update_stage_progress('Handling item categories', step: 10) 88 | handler.handle_item_categories 89 | 90 | update_stage_progress('Handling items', step: 10) 91 | handler.handle_items 92 | handler.handle_other_items 93 | 94 | update_stage_progress('Handling event types', step: 10) 95 | handler.handle_event_types 96 | 97 | update_stage_progress('Handling events', step: 10) 98 | handler.handle_events 99 | end 100 | 101 | end 102 | ``` 103 | 104 | ### HAML 105 | 106 | ``` ruby 107 | = simple_form_for :import, url: [:import], remote: true do |f| 108 | .row 109 | .col-xs-10 110 | = f.input :file, as: :file 111 | .col-xs-2 112 | = f.button :submit, "Import", class: "btn btn-success" 113 | 114 | %br 115 | .well{style: "display:none"} 116 | .row 117 | .col-xs-12 118 | .progress-status.text-primary 119 | .row 120 | .col-xs-12 121 | .progress.progress-striped.active 122 | .progress-bar 123 | .text-primary 124 | 0% 125 | ``` 126 | 127 | ### Ajax usage 128 | 129 | Example of ajax call (this is a .html.haml remote: true response): 130 | 131 | ``` javascript 132 | var interval; 133 | $('.hermes-import .well').show(); 134 | interval = setInterval(function(){ 135 | $.ajax({ 136 | url: '/progress-job/' + #{@job.id}, 137 | success: function(job){ 138 | var stage, progress; 139 | 140 | // If there are errors 141 | if (job.last_error != null) { 142 | $('.progress-status').addClass('text-danger').text(job.progress_stage); 143 | $('.progress-bar').addClass('progress-bar-danger'); 144 | $('.progress').removeClass('active'); 145 | clearInterval(interval); 146 | } 147 | 148 | // Upload stage 149 | if (job.progress_stage != null){ 150 | stage = job.progress_stage; 151 | progress = job.progress_current / job.progress_max * 100; 152 | } else { 153 | progress = 0; 154 | stage = 'Uploading file'; 155 | } 156 | 157 | // In job stage 158 | if (progress !== 0){ 159 | $('.progress-bar').css('width', progress + '%').text(progress + '%'); 160 | } 161 | 162 | $('.progress-status').text(stage); 163 | }, 164 | error: function(){ 165 | // Job is no longer in database which means it finished successfully 166 | $('.progress').removeClass('active'); 167 | $('.progress-bar').css('width', '100%').text('100%'); 168 | $('.progress-status').text('Successfully imported!'); 169 | clearInterval(interval); 170 | } 171 | }) 172 | },100); 173 | ``` 174 | 175 | ## Contributing 176 | 177 | 1. Fork it ( http://github.com//progress_job/fork ) 178 | 2. Create your feature branch (`git checkout -b my-new-feature`) 179 | 3. Commit your changes (`git commit -am 'Add some feature'`) 180 | 4. Push to the branch (`git push origin my-new-feature`) 181 | 5. Create new Pull Request 182 | --------------------------------------------------------------------------------