progress-reporters 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +14 -0
- data/History.txt +3 -0
- data/Rakefile +18 -0
- data/lib/progress-reporters.rb +9 -0
- data/lib/progress-reporters/metered_progress_reporter.rb +117 -0
- data/lib/progress-reporters/nil_progress_reporter.rb +27 -0
- data/lib/progress-reporters/nil_staged_progress_reporter.rb +21 -0
- data/lib/progress-reporters/progress_reporter.rb +76 -0
- data/lib/progress-reporters/staged_progress_reporter.rb +70 -0
- data/lib/progress-reporters/version.rb +5 -0
- data/spec/metered_progress_reporter_spec.rb +53 -0
- data/spec/progress_reporter_spec.rb +67 -0
- data/spec/spec_helper.rb +127 -0
- data/spec/staged_progress_reporter_spec.rb +63 -0
- metadata +57 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 66a446c2c9ee4116e599389eef90ab5f608c4cf1
|
4
|
+
data.tar.gz: 9a7249c0fff340985ad787cbbcec102235b3035b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5a83ed6314162a9005a517ad286d2d5101ddddaa5da23010beabdebef1567eb55b902d8edc4c65c7961395c41fe6ae249f0ec082a7cffd7862d7773fca383741
|
7
|
+
data.tar.gz: 1d8ac329f8646b2e94f08000d7bb831088f8acf93b0f789ba5fa14d25542c216d352fe3eb30bd9bbca893fc50810b102399a2e8e5ee7ff59596f4916b10d2da8
|
data/Gemfile
ADDED
data/History.txt
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
4
|
+
|
5
|
+
require 'bundler'
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
require 'rubygems/package_task'
|
8
|
+
|
9
|
+
require './lib/progress-reporters'
|
10
|
+
|
11
|
+
Bundler::GemHelper.install_tasks
|
12
|
+
|
13
|
+
task :default => :spec
|
14
|
+
|
15
|
+
desc 'Run specs'
|
16
|
+
RSpec::Core::RakeTask.new do |t|
|
17
|
+
t.pattern = './spec/**/*_spec.rb'
|
18
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module ProgressReporters
|
4
|
+
autoload :ProgressReporter, 'progress-reporters/progress_reporter'
|
5
|
+
autoload :NilProgressReporter, 'progress-reporters/nil_progress_reporter'
|
6
|
+
autoload :StagedProgressReporter, 'progress-reporters/staged_progress_reporter'
|
7
|
+
autoload :NilStagedProgressReporter, 'progress-reporters/nil_staged_progress_reporter'
|
8
|
+
autoload :MeteredProgressReporter, 'progress-reporters/metered_progress_reporter'
|
9
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module ProgressReporters
|
4
|
+
|
5
|
+
class MeteredProgressReporter < ProgressReporter
|
6
|
+
CALC_TYPES = [:avg, :moving_avg]
|
7
|
+
DEFAULT_MOVING_AVG_WINDOW_SIZE = 5
|
8
|
+
DEFAULT_CALC_TYPE = :moving_avg
|
9
|
+
DEFAULT_PRECISION = 2
|
10
|
+
|
11
|
+
attr_reader :calc_type, :window_size, :precision
|
12
|
+
|
13
|
+
def set_calc_type(calc_type)
|
14
|
+
if CALC_TYPES.include?(calc_type)
|
15
|
+
@calc_type = calc_type
|
16
|
+
self
|
17
|
+
else
|
18
|
+
raise ArgumentError, "#{calc_type} is not a supported calculation type."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_window_size(window_size)
|
23
|
+
@window_size = window_size
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_precision(precision)
|
28
|
+
@precision = precision
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset
|
33
|
+
super
|
34
|
+
@avg_sum = 0
|
35
|
+
@avg_count = 0
|
36
|
+
@avg_time_sum = 0
|
37
|
+
@avg_window = []
|
38
|
+
@avg_time_window = []
|
39
|
+
@last_timestamp = nil
|
40
|
+
@last_count = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def window_size
|
44
|
+
(@window_size || DEFAULT_MOVING_AVG_WINDOW_SIZE).to_f
|
45
|
+
end
|
46
|
+
|
47
|
+
def calc_type
|
48
|
+
@calc_type || DEFAULT_CALC_TYPE
|
49
|
+
end
|
50
|
+
|
51
|
+
def precision
|
52
|
+
@precision || DEFAULT_PRECISION
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
# this won't get called if on_progress_proc is nil
|
58
|
+
def notify_of_progress(count, total)
|
59
|
+
on_progress_proc.call(
|
60
|
+
count, total, percentage(count, total), rate(count, total)
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
def rate(count, total)
|
65
|
+
cur_time = Time.now
|
66
|
+
|
67
|
+
rate = if @last_timestamp
|
68
|
+
time_delta = cur_time - @last_timestamp
|
69
|
+
count_delta = count - last_count
|
70
|
+
apply_calc_type(time_delta, count_delta)
|
71
|
+
else
|
72
|
+
0
|
73
|
+
end
|
74
|
+
|
75
|
+
@last_timestamp = cur_time
|
76
|
+
@last_count = count
|
77
|
+
rate
|
78
|
+
end
|
79
|
+
|
80
|
+
def apply_calc_type(time_delta, count_delta)
|
81
|
+
case calc_type
|
82
|
+
when :avg
|
83
|
+
apply_avg(time_delta, count_delta)
|
84
|
+
when :moving_avg
|
85
|
+
apply_moving_avg(time_delta, count_delta)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def apply_avg(time_delta, count_delta)
|
90
|
+
@avg_sum += count_delta
|
91
|
+
@avg_time_sum += time_delta
|
92
|
+
@avg_count += 1
|
93
|
+
|
94
|
+
# (avg items) / (avg time) = avg items per second
|
95
|
+
answer = (@avg_sum / @avg_count) / (@avg_time_sum / @avg_count)
|
96
|
+
answer.round(precision)
|
97
|
+
end
|
98
|
+
|
99
|
+
def apply_moving_avg(time_delta, count_delta)
|
100
|
+
add_window_element(time_delta, count_delta)
|
101
|
+
avg_count = @avg_window.inject(&:+) / window_size
|
102
|
+
avg_time = @avg_time_window.inject(&:+) / window_size
|
103
|
+
(avg_count / avg_time).round(precision)
|
104
|
+
end
|
105
|
+
|
106
|
+
def add_window_element(time_delta, count_delta)
|
107
|
+
if @avg_window.size >= window_size
|
108
|
+
@avg_window.shift
|
109
|
+
@avg_time_window.shift
|
110
|
+
end
|
111
|
+
|
112
|
+
@avg_window << count_delta
|
113
|
+
@avg_time_window << time_delta
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module ProgressReporters
|
4
|
+
|
5
|
+
class NilProgressReporter
|
6
|
+
def self.instance
|
7
|
+
@instance ||= new
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_progress(&block)
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_complete(&block)
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_step(step)
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def report_progress(count, total); end
|
23
|
+
def report_complete; end
|
24
|
+
def reset; end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module ProgressReporters
|
4
|
+
|
5
|
+
class NilStagedProgressReporter < NilProgressReporter
|
6
|
+
def on_stage_changed(&block)
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_stage_finished(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_stage(stage)
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def change_stage(new_stage); end
|
18
|
+
def report_stage_finished(count, total); end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module ProgressReporters
|
4
|
+
|
5
|
+
class ProgressReporter
|
6
|
+
DEFAULT_STEP = 1
|
7
|
+
|
8
|
+
attr_reader :step, :last_count, :on_progress_proc, :on_complete_proc
|
9
|
+
|
10
|
+
protected :on_progress_proc
|
11
|
+
protected :on_complete_proc
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@step = DEFAULT_STEP
|
15
|
+
reset
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_progress(&block)
|
19
|
+
@on_progress_proc = block
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_complete(&block)
|
24
|
+
@on_complete_proc = block
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_step(step)
|
29
|
+
@step = step
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def report_progress(count, total)
|
34
|
+
if on_progress_proc
|
35
|
+
notify_of_progress(count, total) if count % step == 0
|
36
|
+
end
|
37
|
+
|
38
|
+
@last_count = count
|
39
|
+
end
|
40
|
+
|
41
|
+
def report_complete
|
42
|
+
if on_complete_proc
|
43
|
+
notify_of_completion
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def reset
|
48
|
+
@last_count = 0
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
# this won't get called if on_progress_proc is nil
|
54
|
+
def notify_of_progress(count, total)
|
55
|
+
on_progress_proc.call(
|
56
|
+
count, total, percentage(count, total)
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
# this won't get called if on_complete_proc is nil
|
61
|
+
def notify_of_completion
|
62
|
+
on_complete_proc.call
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def percentage(count, total, precision = 0)
|
68
|
+
if total > 0
|
69
|
+
((count.to_f / total.to_f) * 100).round(precision)
|
70
|
+
else
|
71
|
+
0
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module ProgressReporters
|
4
|
+
|
5
|
+
class StagedProgressReporter < ProgressReporter
|
6
|
+
DEFAULT_STAGE = :start
|
7
|
+
|
8
|
+
attr_reader :stage, :on_stage_changed_proc, :on_stage_finished_proc
|
9
|
+
protected :on_stage_changed_proc
|
10
|
+
protected :on_stage_finished_proc
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
super
|
14
|
+
@stage = DEFAULT_STAGE
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_stage(stage)
|
18
|
+
@stage = stage
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def change_stage(new_stage)
|
23
|
+
if on_stage_changed_proc
|
24
|
+
notify_of_stage_change(new_stage, stage)
|
25
|
+
end
|
26
|
+
|
27
|
+
@stage = new_stage
|
28
|
+
end
|
29
|
+
|
30
|
+
def report_stage_finished(count, total)
|
31
|
+
if on_stage_finished_proc
|
32
|
+
notify_of_stage_finish(count, total)
|
33
|
+
end
|
34
|
+
|
35
|
+
@last_count = count
|
36
|
+
end
|
37
|
+
|
38
|
+
def on_stage_changed(&block)
|
39
|
+
@on_stage_changed_proc = block
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_stage_finished(&block)
|
44
|
+
@on_stage_finished_proc = block
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
# this won't get called if on_progress_proc is nil
|
51
|
+
def notify_of_progress(count, total)
|
52
|
+
on_progress_proc.call(
|
53
|
+
count, total, percentage(count, total), stage
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
# this won't get called if on_finished_proc is nil
|
58
|
+
def notify_of_stage_finish(count, total)
|
59
|
+
on_stage_finished_proc.call(
|
60
|
+
count, total, percentage(count, total), stage
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
# this won't get called if on_stage_changed_proc is nil
|
65
|
+
def notify_of_stage_change(new_stage, old_stage)
|
66
|
+
on_stage_changed_proc.call(new_stage, old_stage)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
include ProgressReporters
|
6
|
+
|
7
|
+
describe ProgressReporters::MeteredProgressReporter do
|
8
|
+
before(:each) do
|
9
|
+
collector # call to instantiate collector
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'with a metered reporter' do
|
13
|
+
let(:reporter) { MeteredProgressReporter.new }
|
14
|
+
let(:collector) { MeteredCollector.new(reporter) }
|
15
|
+
|
16
|
+
context 'with a staged task' do
|
17
|
+
let(:task) { MeteredTask.new(reporter) }
|
18
|
+
|
19
|
+
it 'reports progress and calculates the average rate' do
|
20
|
+
reporter.set_calc_type(:avg)
|
21
|
+
task.execute(10, 0.5, 0)
|
22
|
+
|
23
|
+
collector.progress_notifications.each_with_index do |notification, idx|
|
24
|
+
expect(notification.quantity).to eq(idx)
|
25
|
+
expect(notification.total).to eq(10)
|
26
|
+
expect(notification.percentage).to eq(idx * 10)
|
27
|
+
expect(notification.rate).to eq(idx == 0 ? 0 : 2.0)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'reports progress and calculates the moving average rate' do
|
32
|
+
reporter.set_calc_type(:moving_avg)
|
33
|
+
reporter.set_window_size(2)
|
34
|
+
task.execute(10, 1, 1)
|
35
|
+
|
36
|
+
collector.progress_notifications.each_with_index do |notification, idx|
|
37
|
+
expect(notification.quantity).to eq(idx)
|
38
|
+
expect(notification.total).to eq(10)
|
39
|
+
expect(notification.percentage).to eq(idx * 10)
|
40
|
+
|
41
|
+
rate = case idx
|
42
|
+
when 0 then 0.0
|
43
|
+
when 1 then 1.0
|
44
|
+
else
|
45
|
+
(1 / (0.5 + (idx - 1))).round(2)
|
46
|
+
end
|
47
|
+
|
48
|
+
expect(notification.rate).to eq(rate)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
include ProgressReporters
|
6
|
+
|
7
|
+
describe ProgressReporters::ProgressReporter do
|
8
|
+
before(:each) do
|
9
|
+
collector # call to instantiate collector
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'with a linear reporter' do
|
13
|
+
let(:reporter) { ProgressReporter.new }
|
14
|
+
let(:collector) { LinearCollector.new(reporter) }
|
15
|
+
|
16
|
+
context 'with a linear task' do
|
17
|
+
let(:task) { LinearTask.new(reporter) }
|
18
|
+
|
19
|
+
it 'reports progress' do
|
20
|
+
task.execute(10)
|
21
|
+
|
22
|
+
collector.progress_notifications.each_with_index do |notification, idx|
|
23
|
+
expect(notification.quantity).to eq(idx)
|
24
|
+
expect(notification.total).to eq(10)
|
25
|
+
expect(notification.percentage).to eq(10 * idx)
|
26
|
+
end
|
27
|
+
|
28
|
+
expect(collector.complete_notification).to_not be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should only report progress every other time when step is set to 2' do
|
32
|
+
reporter.set_step(2)
|
33
|
+
task.execute(10)
|
34
|
+
|
35
|
+
expect(collector.progress_notifications.size).to eq(5)
|
36
|
+
|
37
|
+
collector.progress_notifications.each_with_index do |notification, idx|
|
38
|
+
expect(notification.quantity).to eq(idx * 2)
|
39
|
+
expect(notification.total).to eq(10)
|
40
|
+
expect(notification.percentage).to eq(10 * (idx * 2))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should be able to reset itself' do
|
45
|
+
task.execute(10)
|
46
|
+
expect(reporter.last_count).to eq(9)
|
47
|
+
reporter.reset
|
48
|
+
expect(reporter.last_count).to eq(0)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'with a nil reporter' do
|
53
|
+
let(:reporter) { NilProgressReporter.new }
|
54
|
+
let(:collector) { LinearCollector.new(reporter) }
|
55
|
+
|
56
|
+
context 'with a linear task' do
|
57
|
+
let(:task) { LinearTask.new(reporter) }
|
58
|
+
|
59
|
+
it 'should not actually report any progress' do
|
60
|
+
task.execute(10)
|
61
|
+
expect(collector.progress_notifications).to be_empty
|
62
|
+
expect(collector.complete_notification).to be_nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'progress-reporters'
|
5
|
+
require 'timecop'
|
6
|
+
require 'pry-nav'
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.mock_with :rr
|
10
|
+
end
|
11
|
+
|
12
|
+
LinearNotification = Struct.new(:quantity, :total, :percentage)
|
13
|
+
StagedNotification = Struct.new(:quantity, :total, :percentage, :stage)
|
14
|
+
StageChangedNotification = Struct.new(:new_stage, :old_stage)
|
15
|
+
MeteredNotification = Struct.new(:quantity, :total, :percentage, :rate)
|
16
|
+
|
17
|
+
class LinearCollector
|
18
|
+
attr_reader :progress_reporter
|
19
|
+
attr_reader :progress_notifications, :complete_notification
|
20
|
+
|
21
|
+
def initialize(progress_reporter)
|
22
|
+
@progress_notifications = []
|
23
|
+
|
24
|
+
@progress_reporter = progress_reporter
|
25
|
+
.on_progress { |*args| on_progress(*args) }
|
26
|
+
.on_complete { |*args| on_complete(*args) }
|
27
|
+
|
28
|
+
after_initialize
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def after_initialize
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_progress(*args)
|
37
|
+
@progress_notifications << make_notification(*args)
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_complete(*args)
|
41
|
+
if @complete_notification
|
42
|
+
raise StandardError, 'task already complete'
|
43
|
+
else
|
44
|
+
@complete_notification = make_notification(*args)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def make_notification(*args)
|
49
|
+
LinearNotification.new(*args)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class StagedCollector < LinearCollector
|
54
|
+
attr_reader :stage_changed_notifications
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def after_initialize
|
59
|
+
@stage_changed_notifications = []
|
60
|
+
progress_reporter.on_stage_changed { |*args| on_stage_changed(*args) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_stage_changed(*args)
|
64
|
+
@stage_changed_notifications << StageChangedNotification.new(*args)
|
65
|
+
end
|
66
|
+
|
67
|
+
def make_notification(*args)
|
68
|
+
StagedNotification.new(*args)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class MeteredCollector < LinearCollector
|
73
|
+
def make_notification(*args)
|
74
|
+
MeteredNotification.new(*args)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Task
|
79
|
+
attr_reader :progress_reporter
|
80
|
+
|
81
|
+
def initialize(progress_reporter)
|
82
|
+
@progress_reporter = progress_reporter
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class LinearTask < Task
|
87
|
+
def execute(quantity = 10)
|
88
|
+
quantity.times do |i|
|
89
|
+
progress_reporter.report_progress(i, quantity)
|
90
|
+
end
|
91
|
+
|
92
|
+
progress_reporter.report_complete
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class StagedTask < Task
|
97
|
+
def execute(stages = 2, quantity_per_stage = 10)
|
98
|
+
progress_reporter.set_stage(:stage_1)
|
99
|
+
|
100
|
+
stages.times do |stage|
|
101
|
+
quantity_per_stage.times do |i|
|
102
|
+
progress_reporter.report_progress(i, quantity_per_stage)
|
103
|
+
end
|
104
|
+
|
105
|
+
progress_reporter.report_stage_finished(
|
106
|
+
quantity_per_stage, quantity_per_stage
|
107
|
+
)
|
108
|
+
|
109
|
+
progress_reporter.change_stage(:"stage_#{stage + 2}")
|
110
|
+
end
|
111
|
+
|
112
|
+
progress_reporter.report_complete
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class MeteredTask < Task
|
117
|
+
def execute(quantity = 10, delay_seconds = 1, rate_of_delay = 1)
|
118
|
+
Timecop.freeze do
|
119
|
+
quantity.times do |i|
|
120
|
+
progress_reporter.report_progress(i, quantity)
|
121
|
+
Timecop.travel(Time.now + delay_seconds + (rate_of_delay * i))
|
122
|
+
end
|
123
|
+
|
124
|
+
progress_reporter.report_complete
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
include ProgressReporters
|
6
|
+
|
7
|
+
describe ProgressReporters::StagedProgressReporter do
|
8
|
+
before(:each) do
|
9
|
+
collector # call to instantiate collector
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'with a staged reporter' do
|
13
|
+
let(:reporter) { StagedProgressReporter.new }
|
14
|
+
let(:collector) { StagedCollector.new(reporter) }
|
15
|
+
|
16
|
+
context 'with a staged task' do
|
17
|
+
let(:task) { StagedTask.new(reporter) }
|
18
|
+
|
19
|
+
it 'reports progress in stages' do
|
20
|
+
task.execute(2, 10)
|
21
|
+
|
22
|
+
expect(collector.progress_notifications.size).to eq(20)
|
23
|
+
|
24
|
+
collector.progress_notifications.each_with_index do |notification, idx|
|
25
|
+
expect(notification.quantity).to eq(idx % 10)
|
26
|
+
expect(notification.total).to eq(10)
|
27
|
+
expect(notification.percentage).to eq((idx % 10) * 10)
|
28
|
+
expect(notification.stage).to eq(:"stage_#{(idx / 10) + 1}")
|
29
|
+
end
|
30
|
+
|
31
|
+
expect(collector.stage_changed_notifications.size).to eq(2)
|
32
|
+
|
33
|
+
collector.stage_changed_notifications.tap do |notifications|
|
34
|
+
notifications[0].first do |notification|
|
35
|
+
expect(notification.old_stage).to_eq(:stage_1)
|
36
|
+
expect(notification.new_stage).to eq(:stage_2)
|
37
|
+
end
|
38
|
+
|
39
|
+
notifications[1].first do |notification|
|
40
|
+
expect(notification.old_stage).to_eq(:stage_2)
|
41
|
+
expect(notification.new_stage).to eq(:stage_3)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with a nil reporter' do
|
48
|
+
let(:reporter) { NilStagedProgressReporter.new }
|
49
|
+
let(:collector) { StagedCollector.new(reporter) }
|
50
|
+
|
51
|
+
context 'with a linear task' do
|
52
|
+
let(:task) { StagedTask.new(reporter) }
|
53
|
+
|
54
|
+
it 'should not actually report any progress' do
|
55
|
+
task.execute(10)
|
56
|
+
expect(collector.progress_notifications).to be_empty
|
57
|
+
expect(collector.stage_changed_notifications).to be_empty
|
58
|
+
expect(collector.complete_notification).to be_nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: progress-reporters
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cameron Dutro
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-23 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Callback-oriented way to report the progress of a task.
|
14
|
+
email:
|
15
|
+
- camertron@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- Gemfile
|
21
|
+
- History.txt
|
22
|
+
- Rakefile
|
23
|
+
- lib/progress-reporters.rb
|
24
|
+
- lib/progress-reporters/metered_progress_reporter.rb
|
25
|
+
- lib/progress-reporters/nil_progress_reporter.rb
|
26
|
+
- lib/progress-reporters/nil_staged_progress_reporter.rb
|
27
|
+
- lib/progress-reporters/progress_reporter.rb
|
28
|
+
- lib/progress-reporters/staged_progress_reporter.rb
|
29
|
+
- lib/progress-reporters/version.rb
|
30
|
+
- spec/metered_progress_reporter_spec.rb
|
31
|
+
- spec/progress_reporter_spec.rb
|
32
|
+
- spec/spec_helper.rb
|
33
|
+
- spec/staged_progress_reporter_spec.rb
|
34
|
+
homepage: http://github.com/camertron
|
35
|
+
licenses: []
|
36
|
+
metadata: {}
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
requirements: []
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 2.2.2
|
54
|
+
signing_key:
|
55
|
+
specification_version: 4
|
56
|
+
summary: Callback-oriented way to report the progress of a task.
|
57
|
+
test_files: []
|