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.
@@ -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