progress-monitor 1.9.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,11 @@
1
1
  = progress-monitor
2
2
 
3
- Description goes here.
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
 
@@ -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
- alias :orig_times :times
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
- alias :orig_each :each
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
- alias :orig_collect :collect
184
- def collect (&block)
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
- alias :orig_each :each
201
- def each (&block)
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
- end
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
- alias :orig_each :each
220
- alias :orig_collect :collect
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 collect (&block)
233
- if Progress.active? then
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
 
@@ -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
+ }
@@ -16,7 +16,7 @@ def self.loop2
16
16
 
17
17
  end
18
18
 
19
- Progress.monitor("Message", 100, 1)
19
+ Progress.monitor("Message", :stack_depth => 1)
20
20
  puts "not monitored"
21
21
  100.times {
22
22
  sleep 0.01
@@ -3,7 +3,7 @@ require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'lib',
3
3
  # Skip first loop
4
4
  puts ""
5
5
 
6
- Progress.monitor("Main Loop",100, 1)
6
+ Progress.monitor("Main Loop", :skip => 1)
7
7
 
8
8
  puts "This loop must not be monitored"
9
9
  puts
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: 1.9.0
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-01 00:00:00 +01:00
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