progress-monitor 1.9.0 → 2.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.
- data/README.rdoc +6 -1
- data/lib/progress-monitor.rb +264 -205
- data/samples/announce.rb +16 -0
- data/samples/announce_monitor.rb +16 -0
- data/samples/depth.rb +1 -1
- data/samples/skip.rb +1 -1
- metadata +4 -2
data/README.rdoc
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
= progress-monitor
|
2
2
|
|
3
|
-
|
3
|
+
Un-obtrusive progress monitor for the command line. Patch each, collect and
|
4
|
+
times methods from different classes to produce loop progress reports. You may
|
5
|
+
monitor loops that are close by, or set up monitoring for loops down the call
|
6
|
+
stack so you do not need to modify the target class.
|
7
|
+
|
8
|
+
See the samples directory for example scripts
|
4
9
|
|
5
10
|
== Note on Patches/Pull Requests
|
6
11
|
|
data/lib/progress-monitor.rb
CHANGED
@@ -10,238 +10,297 @@
|
|
10
10
|
# iterations it has to go through and how many have been executed
|
11
11
|
# already. Every now and then, it prints the progress report.
|
12
12
|
class Progress
|
13
|
-
|
14
|
-
@@progress_meters = Array.new
|
15
|
-
def self.progress_meters
|
16
|
-
@@progress_meters
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.caller_info(callers, depth = 0)
|
20
|
-
return [nil, nil] if callers.length <= depth
|
21
|
-
|
22
|
-
line = callers[depth]
|
23
|
-
if line.match(/(.*):\d+(?::in `(.*)')/)
|
24
|
-
return [$1, $2]
|
25
|
-
end
|
26
|
-
|
27
|
-
if line.match(/(.*):\d+/)
|
28
|
-
return [$1, nil ]
|
29
|
-
end
|
30
|
-
|
31
|
-
info
|
32
|
-
end
|
33
|
-
|
34
|
-
@@monitor = false
|
35
|
-
@@desc = ""
|
36
|
-
@@skip = 0
|
37
|
-
|
38
|
-
# This function will activate monitoring of the next _supported_ loop.
|
39
|
-
#
|
40
|
-
# If a description is given as a parameter it will show at the
|
41
|
-
# beginning of the progress report.
|
42
|
-
def self.monitor(desc = "", num_reports = 100, stack_depth = nil, skip = 0)
|
43
|
-
@@monitor = true
|
44
|
-
@@desc = desc
|
45
|
-
@@num_reports=num_reports
|
46
|
-
@@call_info = caller_info(caller)
|
47
|
-
@@stack_depth = stack_depth
|
48
|
-
@@skip = skip
|
49
|
-
end
|
50
|
-
|
51
|
-
# Returns true if next loop must be monitored.
|
52
|
-
#
|
53
|
-
def self.active?
|
54
|
-
|
55
|
-
return false unless @@monitor
|
56
|
-
|
57
|
-
if @@stack_depth != nil
|
58
|
-
call_info = caller_info(caller, @@stack_depth + 1)
|
59
|
-
return false if call_info != @@call_info
|
60
|
-
end
|
61
|
-
|
62
|
-
if @@skip > 0
|
63
|
-
@@skip -= 1
|
64
|
-
return false
|
65
|
-
else
|
66
|
-
return true
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
|
71
|
-
# Creates a new instance. Max is the total number of iterations of the
|
72
|
-
# loop. The depth represents how many other loops are above this one,
|
73
|
-
# this information is used to find the place to print the progress
|
74
|
-
# report.
|
75
|
-
#
|
76
|
-
def initialize(max,depth)
|
77
|
-
@max = max
|
78
|
-
@max = 1 if @max < 1
|
79
|
-
@current = 0
|
80
|
-
@time = Time.now
|
81
|
-
@last_report = 0
|
82
|
-
@num_reports = @@num_reports
|
83
|
-
@depth = depth
|
84
|
-
@desc = @@desc
|
85
|
-
@@monitor = false
|
86
|
-
report
|
87
|
-
end
|
88
|
-
|
89
|
-
# Used to register a new completed loop iteration.
|
90
|
-
#
|
91
|
-
def tick(steps = 1)
|
92
|
-
@current += steps
|
93
|
-
percent = @current.to_f/ @max.to_f
|
94
|
-
if percent - @last_report > 1.to_f/@num_reports.to_f then
|
95
|
-
report
|
96
|
-
@last_report=percent
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def set(step)
|
101
|
-
@current = step
|
102
|
-
percent = @current.to_f/ @max.to_f
|
103
|
-
if percent - @last_report > 1.to_f/@num_reports.to_f then
|
104
|
-
report
|
105
|
-
@last_report=percent
|
106
|
-
end
|
107
|
-
|
108
|
-
end
|
109
|
-
|
110
|
-
# Prints de progress report. It backs up as many lines as the meters
|
111
|
-
# depth. Prints the progress as a line of dots, a percentage, time
|
112
|
-
# spent, and time left. And then goes moves the cursor back to its
|
113
|
-
# original line. Everything is printed to stderr.
|
114
|
-
#
|
115
|
-
def report
|
116
|
-
|
117
|
-
percent = @current.to_f/ @max.to_f
|
118
|
-
percent = 0.001 if percent < 0.001
|
119
|
-
if @desc != ""
|
120
|
-
indicator = @desc + ": "
|
121
|
-
else
|
122
|
-
indicator = "Progress "
|
123
|
-
end
|
124
|
-
indicator += "["
|
125
|
-
10.times{|i|
|
126
|
-
if i < percent * 10 then
|
127
|
-
indicator += "."
|
128
|
-
else
|
129
|
-
indicator += " "
|
130
|
-
end
|
131
|
-
}
|
132
|
-
indicator += "] done #{(percent * 100).to_i}% "
|
133
|
-
|
134
|
-
eta = (Time.now - @time)/percent * (1-percent)
|
135
|
-
eta = eta.to_i
|
136
|
-
eta = [eta/3600, eta/60 % 60, eta % 60].map{|t| t.to_s.rjust(2, '0')}.join(':')
|
137
|
-
|
138
|
-
used = (Time.now - @time).to_i
|
139
|
-
used = [used/3600, used/60 % 60, used % 60].map{|t| t.to_s.rjust(2, '0')}.join(':')
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
indicator += " (Time left #{eta} seconds) (Started #{used} seconds ago)"
|
144
|
-
|
145
|
-
$stderr.print("\033[#{@depth + 1}F\033[2K" + indicator + "\033[#{@depth + 1}E" )
|
146
|
-
|
147
|
-
end
|
148
|
-
|
149
13
|
|
14
|
+
module Progress::MonitorableProgress
|
15
|
+
|
16
|
+
def monitorable1(method_name)
|
17
|
+
module_eval{
|
18
|
+
orig_name = ('orig_' + method_name.to_s).to_sym
|
19
|
+
|
20
|
+
eval "alias #{ orig_name } #{ method_name }"
|
21
|
+
|
22
|
+
define_method(method_name) do |&block|
|
23
|
+
if Progress.active?
|
24
|
+
progress_meter = Progress.add_progress_meter(self.monitor_size) if Progress.monitor?
|
25
|
+
announcement = Progress.get_announcement if Progress.announce?
|
26
|
+
|
27
|
+
monitor_step = nil unless defined? monitor_step
|
28
|
+
|
29
|
+
res = self.send(orig_name.to_sym) {|v|
|
30
|
+
progress_meter.tick(monitor_step) if progress_meter
|
31
|
+
puts announcement.call(v) if announcement
|
32
|
+
block.call(v)
|
33
|
+
}
|
34
|
+
|
35
|
+
Progress.remove_last_meter
|
36
|
+
res
|
37
|
+
else
|
38
|
+
self.send(orig_name.to_sym) {|v| block.call(v)}
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def monitorable2(method_name)
|
46
|
+
module_eval{
|
47
|
+
orig_name = ('orig_' + method_name.to_s).to_sym
|
48
|
+
|
49
|
+
eval "alias #{ orig_name } #{ method_name }"
|
50
|
+
|
51
|
+
define_method(method_name) do |&block|
|
52
|
+
if Progress.active?
|
53
|
+
progress_meter = Progress.add_progress_meter(self.monitor_size) if Progress.monitor?
|
54
|
+
announcement = Progress.get_announcement if Progress.announce?
|
55
|
+
|
56
|
+
monitor_step = nil unless defined? monitor_step
|
57
|
+
|
58
|
+
res = self.send(orig_name.to_sym) {|v1,v2|
|
59
|
+
progress_meter.tick(monitor_step) if progress_meter
|
60
|
+
puts announcement.call(v1,v2) if announcement
|
61
|
+
block.call(v1,v2);
|
62
|
+
}
|
63
|
+
|
64
|
+
Progress.remove_last_meter
|
65
|
+
res
|
66
|
+
else
|
67
|
+
self.send(orig_name.to_sym) {|v1,v2| block.call(v1,v2) }
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
@@progress_meters = Array.new
|
77
|
+
def self.progress_meters
|
78
|
+
@@progress_meters
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.caller_info(callers, depth = 0)
|
82
|
+
return [nil, nil] if callers.length <= depth
|
83
|
+
|
84
|
+
line = callers[depth]
|
85
|
+
if line.match(/(.*):\d+(?::in `(.*)')/)
|
86
|
+
return [$1, $2]
|
87
|
+
end
|
88
|
+
|
89
|
+
if line.match(/(.*):\d+/)
|
90
|
+
return [$1, nil ]
|
91
|
+
end
|
92
|
+
|
93
|
+
info
|
94
|
+
end
|
95
|
+
|
96
|
+
@@monitor = false
|
97
|
+
@@announce = false
|
98
|
+
|
99
|
+
def self.process_options(options)
|
100
|
+
@@stack_depth = options[:stack_depth]
|
101
|
+
@@skip = options[:skip] || 0
|
102
|
+
|
103
|
+
if options[:announcement]
|
104
|
+
@@announce = true
|
105
|
+
@@announcement = options[:announcement]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# This function will activate monitoring of the next _supported_ loop.
|
110
|
+
#
|
111
|
+
# If a description is given as a parameter it will show at the
|
112
|
+
# beginning of the progress report.
|
113
|
+
def self.monitor(desc = "", options = {})
|
114
|
+
@@monitor = true
|
115
|
+
@@desc = desc
|
116
|
+
@@num_reports = options[:num_reports] || 100
|
117
|
+
@@call_info = caller_info(caller)
|
118
|
+
process_options(options)
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.announce(announcement, options = {})
|
122
|
+
@@announce = true
|
123
|
+
@@call_info = caller_info(caller)
|
124
|
+
@@announcement = announcement
|
125
|
+
process_options(options)
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.get_announcement
|
129
|
+
@@announce = false
|
130
|
+
@@announcement
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.this_loop?
|
134
|
+
if @@stack_depth != nil
|
135
|
+
call_info = caller_info(caller, @@stack_depth + 2)
|
136
|
+
return false if call_info != @@call_info
|
137
|
+
end
|
138
|
+
|
139
|
+
if @@skip > 0
|
140
|
+
@@skip -= 1
|
141
|
+
return false
|
142
|
+
else
|
143
|
+
return true
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns true if next loop must be monitored.
|
148
|
+
#
|
149
|
+
def self.monitor?
|
150
|
+
return @@monitor
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.announce?
|
154
|
+
return @@announce
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.active?
|
158
|
+
return (monitor? || announce?) && this_loop?
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.add_progress_meter(max)
|
162
|
+
progress_meter = self.new(max, progress_meters.size)
|
163
|
+
|
164
|
+
progress_meters.push(progress_meter)
|
165
|
+
|
166
|
+
progress_meter
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.remove_last_meter
|
170
|
+
progress_meters.pop
|
171
|
+
end
|
172
|
+
|
173
|
+
# Creates a new instance. Max is the total number of iterations of the
|
174
|
+
# loop. The depth represents how many other loops are above this one,
|
175
|
+
# this information is used to find the place to print the progress
|
176
|
+
# report.
|
177
|
+
#
|
178
|
+
def initialize(max,depth)
|
179
|
+
@max = max
|
180
|
+
@max = 1 if @max < 1
|
181
|
+
@current = 0
|
182
|
+
@time = Time.now
|
183
|
+
@last_report = -1
|
184
|
+
@num_reports = @@num_reports
|
185
|
+
@depth = depth
|
186
|
+
@desc = @@desc
|
187
|
+
@@monitor = false
|
188
|
+
end
|
189
|
+
|
190
|
+
# Used to register a new completed loop iteration.
|
191
|
+
#
|
192
|
+
def tick(step = nil)
|
193
|
+
|
194
|
+
if step.nil?
|
195
|
+
@current += 1
|
196
|
+
else
|
197
|
+
@current = step
|
198
|
+
end
|
199
|
+
|
200
|
+
percent = @current.to_f/ @max.to_f
|
201
|
+
if percent - @last_report > 1.to_f/@num_reports.to_f
|
202
|
+
report
|
203
|
+
@last_report=percent
|
204
|
+
end
|
205
|
+
|
206
|
+
nil
|
207
|
+
end
|
208
|
+
|
209
|
+
# Prints de progress report. It backs up as many lines as the meters
|
210
|
+
# depth. Prints the progress as a line of dots, a percentage, time
|
211
|
+
# spent, and time left. And then goes moves the cursor back to its
|
212
|
+
# original line. Everything is printed to stderr.
|
213
|
+
#
|
214
|
+
def report
|
215
|
+
|
216
|
+
percent = @current.to_f/ @max.to_f
|
217
|
+
percent = 0.001 if percent < 0.001
|
218
|
+
if @desc != ""
|
219
|
+
indicator = @desc + ": "
|
220
|
+
else
|
221
|
+
indicator = "Progress "
|
222
|
+
end
|
223
|
+
indicator += "["
|
224
|
+
10.times{|i|
|
225
|
+
if i < percent * 10 then
|
226
|
+
indicator += "."
|
227
|
+
else
|
228
|
+
indicator += " "
|
229
|
+
end
|
230
|
+
}
|
231
|
+
indicator += "] done #{(percent * 100).to_i}% "
|
232
|
+
|
233
|
+
eta = (Time.now - @time)/percent * (1-percent)
|
234
|
+
eta = eta.to_i
|
235
|
+
eta = [eta/3600, eta/60 % 60, eta % 60].map{|t| t.to_s.rjust(2, '0')}.join(':')
|
236
|
+
|
237
|
+
used = (Time.now - @time).to_i
|
238
|
+
used = [used/3600, used/60 % 60, used % 60].map{|t| t.to_s.rjust(2, '0')}.join(':')
|
239
|
+
|
240
|
+
|
241
|
+
|
242
|
+
indicator += " (Time left #{eta} seconds) (Started #{used} seconds ago)"
|
243
|
+
|
244
|
+
$stderr.print("\033[#{@depth + 1}F\033[2K" + indicator + "\033[#{@depth + 1}E" )
|
245
|
+
|
246
|
+
end
|
150
247
|
end
|
151
248
|
|
152
249
|
class Integer
|
153
|
-
|
154
|
-
|
155
|
-
def times (&block)
|
156
|
-
if Progress.active? then
|
157
|
-
progress_meters = Progress::progress_meters
|
158
|
-
progress_meters.push(Progress.new(self, progress_meters.size ))
|
159
|
-
orig_times {|w|block.call(w);progress_meters.last.tick;}
|
160
|
-
progress_meters.pop
|
161
|
-
else
|
162
|
-
orig_times &block
|
163
|
-
end
|
250
|
+
class << self
|
251
|
+
include Progress::MonitorableProgress
|
164
252
|
end
|
165
|
-
end
|
166
253
|
|
254
|
+
def monitor_size
|
255
|
+
self
|
256
|
+
end
|
167
257
|
|
258
|
+
self.monitorable1(:times)
|
259
|
+
end
|
168
260
|
|
169
261
|
class Array
|
170
|
-
|
171
|
-
|
172
|
-
def each (&block)
|
173
|
-
if Progress.active? then
|
174
|
-
progress_meters = Progress::progress_meters
|
175
|
-
progress_meters.push(Progress.new(self.length, progress_meters.size ))
|
176
|
-
orig_each {|w|block.call(w);progress_meters.last.tick;}
|
177
|
-
progress_meters.pop
|
178
|
-
else
|
179
|
-
orig_each &block
|
180
|
-
end
|
262
|
+
class << self
|
263
|
+
include Progress::MonitorableProgress
|
181
264
|
end
|
182
265
|
|
183
|
-
|
184
|
-
|
185
|
-
if Progress.active? then
|
186
|
-
progress_meters = Progress::progress_meters
|
187
|
-
progress_meters.push(Progress.new(self.length, progress_meters.size ))
|
188
|
-
res = orig_collect {|w| r = block.call(w);progress_meters.last.tick; r}
|
189
|
-
progress_meters.pop
|
190
|
-
res
|
191
|
-
else
|
192
|
-
orig_collect &block
|
193
|
-
end
|
266
|
+
def monitor_size
|
267
|
+
self.length
|
194
268
|
end
|
269
|
+
|
270
|
+
self.monitorable1(:each)
|
271
|
+
self.monitorable1(:collect)
|
195
272
|
end
|
196
273
|
|
197
274
|
|
198
275
|
|
199
276
|
class Hash
|
200
|
-
|
201
|
-
|
202
|
-
if Progress.active? then
|
203
|
-
progress_meters = Progress::progress_meters
|
204
|
-
progress_meters.push(Progress.new(self.length, progress_meters.size ))
|
205
|
-
orig_each {|k,v|block.call(k,v);progress_meters.last.tick;}
|
206
|
-
progress_meters.pop
|
207
|
-
else
|
208
|
-
orig_each &block
|
209
|
-
end
|
277
|
+
class << self
|
278
|
+
include Progress::MonitorableProgress
|
210
279
|
end
|
211
280
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
281
|
+
def monitor_size
|
282
|
+
self.length
|
283
|
+
end
|
216
284
|
|
285
|
+
self.monitorable2(:each)
|
286
|
+
self.monitorable2(:collect)
|
287
|
+
end
|
217
288
|
|
218
289
|
class File
|
219
|
-
|
220
|
-
|
221
|
-
def each (&block)
|
222
|
-
if Progress.active? then
|
223
|
-
progress_meters = Progress::progress_meters
|
224
|
-
progress_meters.push(Progress.new(self.stat.size, progress_meters.size ))
|
225
|
-
orig_each {|l| block.call(l);progress_meters.last.set(self.pos);}
|
226
|
-
progress_meters.pop
|
227
|
-
else
|
228
|
-
orig_each &block
|
229
|
-
end
|
290
|
+
class << self
|
291
|
+
include Progress::MonitorableProgress
|
230
292
|
end
|
231
293
|
|
232
|
-
def
|
233
|
-
|
234
|
-
progress_meters = Progress::progress_meters
|
235
|
-
progress_meters.push(Progress.new(self.stat.size, progress_meters.size ))
|
236
|
-
res = orig_collect {|l| r = block.call(l);progress_meters.last.set(self.pos); r}
|
237
|
-
progress_meters.pop
|
238
|
-
res
|
239
|
-
else
|
240
|
-
orig_collect &block
|
241
|
-
end
|
294
|
+
def monitor_size
|
295
|
+
self.stat.size
|
242
296
|
end
|
243
297
|
|
298
|
+
def monitor_step
|
299
|
+
self.pos
|
300
|
+
end
|
244
301
|
|
302
|
+
self.monitorable1(:each)
|
303
|
+
self.monitorable1(:collect)
|
245
304
|
end
|
246
305
|
|
247
306
|
|
data/samples/announce.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# This is an example using a hash. It also shows what happens when you
|
2
|
+
# print stuff. In this case, we print new lines, and the progress meter
|
3
|
+
# is always moved to the line above the last one.
|
4
|
+
|
5
|
+
require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'lib', 'progress-monitor')
|
6
|
+
|
7
|
+
h = Hash.new
|
8
|
+
|
9
|
+
('a'..'z').to_a.each{|l|
|
10
|
+
h[l] = l.upcase
|
11
|
+
}
|
12
|
+
|
13
|
+
Progress.announce(Proc.new {|d,u| "Downcase #{d}, uppercase #{u}"} )
|
14
|
+
h.each{|d,u|
|
15
|
+
sleep(0.2)
|
16
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This is an example using a hash. It also shows what happens when you
|
2
|
+
# print stuff. In this case, we print new lines, and the progress meter
|
3
|
+
# is always moved to the line above the last one.
|
4
|
+
|
5
|
+
require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'lib', 'progress-monitor')
|
6
|
+
|
7
|
+
h = Hash.new
|
8
|
+
|
9
|
+
('a'..'z').to_a.each{|l|
|
10
|
+
h[l] = l.upcase
|
11
|
+
}
|
12
|
+
|
13
|
+
Progress.monitor("hash",:num_reports => 5, :announcement => Proc.new {|d,u| "Downcase #{d}, uppercase #{u}" } )
|
14
|
+
h.each{|d,u|
|
15
|
+
sleep(0.2)
|
16
|
+
}
|
data/samples/depth.rb
CHANGED
data/samples/skip.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: progress-monitor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miguel Vazquez
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02-
|
12
|
+
date: 2010-02-03 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -24,6 +24,8 @@ extra_rdoc_files:
|
|
24
24
|
- README.rdoc
|
25
25
|
files:
|
26
26
|
- lib/progress-monitor.rb
|
27
|
+
- samples/announce.rb
|
28
|
+
- samples/announce_monitor.rb
|
27
29
|
- samples/collect.rb
|
28
30
|
- samples/depth.rb
|
29
31
|
- samples/file.rb
|