powerbar 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .yardoc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in powerbar.gemspec
4
+ gemspec
@@ -0,0 +1,83 @@
1
+ = PowerBar
2
+
3
+ This is PowerBar - The last progressbar-library you'll ever need.
4
+
5
+ == Features
6
+
7
+ * Detects when stdout is not a terminal and automatically falls back to logging
8
+ * Does not clutter your log-files with ansi-codes!
9
+ * If your CLI-app can run interactively and non-interactively (e.g. cronjob)
10
+ you will automatically get reasonable progress-output in both modes.
11
+ * By default prints to stderr but can call any output-method
12
+ of your choice (e.g. your favorite Logger).
13
+
14
+ * Fully customizable; all output is template-driven.
15
+
16
+ * All output is optional. You may use PowerBar to silently collect progress
17
+ information (percentage-done, throughput, ETA, etc.) and then use the
18
+ computed values elsewhere in your app.
19
+
20
+ * All state can be updated at any time. If you're monitoring a
21
+ multi-part operation you can change the status-message of a running
22
+ PowerBar at any time.
23
+
24
+ == Screenshot
25
+
26
+ {screenshot}[http://github.com/busyloop/powerbar/raw/master/ass/screenshot.png]
27
+
28
+
29
+ == Installation
30
+
31
+ gem install powerbar
32
+
33
+ == Getting Started
34
+
35
+ Watch the demo that was installed along with the gem:
36
+
37
+ powerbar-demo
38
+
39
+ Then look at the {source-code}[https://github.com/busyloop/powerbar/blob/master/bin/powerbar-demo] of the demo. Pretty much all use-cases are covered in there, including templates and how to hook in your own logger.
40
+
41
+
42
+ == Example (for the impatient)
43
+
44
+ #!/usr/bin/env ruby
45
+
46
+ require 'powerbar'
47
+
48
+ total = 100000
49
+ step = 1000
50
+
51
+ p = PowerBar.new
52
+ (0..total).step(step).each do |i|
53
+ p.show({:msg => 'DEMO 1 - Ten seconds of progress', :done => i, :total => total})
54
+ sleep 0.1
55
+ end
56
+ p.close
57
+
58
+ == Documentation?
59
+
60
+ Use the {source}[https://github.com/busyloop/powerbar/blob/master/lib/powerbar.rb], Luke!
61
+
62
+ == License (MIT)
63
+
64
+ Copyright (C) 2011 by moe@busyloop.net
65
+
66
+ Permission is hereby granted, free of charge, to any person obtaining a copy
67
+ of this software and associated documentation files (the "Software"), to deal
68
+ in the Software without restriction, including without limitation the rights
69
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
70
+ copies of the Software, and to permit persons to whom the Software is
71
+ furnished to do so, subject to the following conditions:
72
+
73
+ The above copyright notice and this permission notice shall be included in
74
+ all copies or substantial portions of the Software.
75
+
76
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
77
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
78
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
79
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
80
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
81
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
82
+ THE SOFTWARE.
83
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
Binary file
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'powerbar'
4
+
5
+ # DEMO 1
6
+ def demo_1
7
+ total = 100000
8
+ step = 1000
9
+
10
+ p = PowerBar.new
11
+ (0..total).step(step).each do |i|
12
+ p.show({:msg => 'DEMO 1 - Ten seconds of progress', :done => i, :total => total})
13
+ sleep 0.1
14
+ end
15
+ p.close
16
+ end
17
+
18
+ # DEMO 2
19
+ def demo_2
20
+ total = 100000
21
+ step = 1000
22
+ text = " "*30 + "Need to change text in mid-progress? No Problemo!" +
23
+ " "*5 + "As seen in: DEMO 2 - Scrolling madness"
24
+ p = PowerBar.new
25
+ j = 0
26
+ (0..total).step(step).each do |i|
27
+ woah = sprintf('%-26s', text[j%text.length..j%text.length+25])
28
+ p.show({:msg => woah, :done => i, :total => total})
29
+ j += 1
30
+ sleep 0.1
31
+ end
32
+ p.close
33
+ end
34
+
35
+ # DEMO 3
36
+ def demo_3
37
+ total = 100000
38
+ step = 1000
39
+ text = "DEMO 3 - Don't sweat your message width too much, we can squeeze it some"
40
+ p = PowerBar.new
41
+ j = 0
42
+ (0..total).step(step).each do |i|
43
+ woah = text[0..j]
44
+ p.show({:msg => woah, :done => i, :total => total})
45
+ j += 1
46
+ sleep 0.1
47
+ end
48
+ p.close
49
+ end
50
+
51
+ # DEMO 4
52
+ def demo_4
53
+ total = 100000
54
+ step = 1000
55
+ text = "\e[0mDEMO 4 - Colors!"
56
+ p = PowerBar.new
57
+ p.settings.tty.finite.template.main = \
58
+ "${<msg>} ${<bar> }\e[0m${<rate>/s} \e[33;1m${<percent>%} " +
59
+ "\e[36;1m${<elapsed>}\e[31;1m${ ETA: <eta>}"
60
+ p.settings.tty.finite.template.padchar = "\e[30;1m\u2589"
61
+ p.settings.tty.finite.template.barchar = "\e[34;1m\u2589"
62
+ p.settings.tty.finite.template.exit = "\e[?25h\e[0m" # clean up after us
63
+ p.settings.tty.finite.template.close = "\e[?25h\e[0m\n" # clean up after us
64
+ p.settings.tty.finite.output = Proc.new{ |s|
65
+ # The default output function truncates our
66
+ # string to to enable the "squeezing" as seen in the
67
+ # previous demo. This doesn't mix so well with ANSI-colors,
68
+ # so if you want to use colors you'll have to make the output
69
+ # a little more naive. Like this:
70
+ $stderr.print s
71
+ }
72
+ j = 0
73
+ (0..total).step(step).each do |i|
74
+ p.show({:msg => text, :done => i, :total => total})
75
+ j += 1
76
+ sleep 0.1
77
+ end
78
+ p.close
79
+ end
80
+
81
+ # DEMO 5
82
+ def demo_5
83
+ total = 100000
84
+ step = 1000
85
+ text = "DEMO 5 - When total is :unknown then we only display what we know"
86
+ p = PowerBar.new
87
+ j = 0
88
+ (0..total).step(step).each do |i|
89
+ p.show({:msg => text, :done => i, :total => :unknown})
90
+ j += 1
91
+ sleep 0.1
92
+ end
93
+ p.close
94
+ end
95
+
96
+ # DEMO 6
97
+ def demo_6
98
+ total = 100000
99
+ step = 1000
100
+ text = "DEMO 6 - Still :unknown, now with a different template"
101
+ p = PowerBar.new
102
+ p.settings.tty.infinite.template.main = \
103
+ '${<msg>} > ${Rate: <rate>/s, }elapsed: ${<elapsed>}'
104
+ j = 0
105
+ (0..total).step(step).each do |i|
106
+ p.show({:msg => text, :done => i, :total => :unknown})
107
+ j += 1
108
+ sleep 0.1
109
+ end
110
+ p.close
111
+ end
112
+
113
+
114
+ # DEMO 7
115
+ def demo_7
116
+ total = 100000
117
+ step = 1000
118
+ text = "DEMO 7 - Forcing notty mode"
119
+ p = PowerBar.new
120
+ p.settings.force_mode = :notty
121
+ j = 0
122
+ (0..total).step(step).each do |i|
123
+ p.show({:msg => text, :done => i, :total => total})
124
+ j += 1
125
+ sleep 0.1
126
+ end
127
+ p.close
128
+ end
129
+
130
+ # DEMO 8
131
+ def demo_8
132
+ total = 100000
133
+ step = 1000
134
+ log = Logger.new(STDOUT)
135
+ text = "DEMO 8 - Forcing notty mode and using a logger"
136
+ p = PowerBar.new
137
+ p.settings.force_mode = :notty
138
+ p.settings.notty.finite.output = Proc.new { |s|
139
+ log.debug(s) unless s == ''
140
+ }
141
+ j = 0
142
+ (0..total).step(step).each do |i|
143
+ p.show({:msg => text, :done => i, :total => total})
144
+ j += 1
145
+ sleep 0.1
146
+ end
147
+ p.close
148
+ end
149
+
150
+ # DEMO 9
151
+ def demo_9
152
+ total = 100000
153
+ step = 1000
154
+
155
+ puts "DEMO 9 - No output by PowerBar."
156
+ puts "We only use it to collect stats and then roll our own output...\n---"
157
+ p = PowerBar.new
158
+ j = 0
159
+ (0..total).step(step).each do |i|
160
+ # update() does not output anything, it only updates (who would've guessed!)
161
+ p.update({:done => i, :total => total})
162
+ j += 1
163
+ if 0 == j % 50
164
+ puts "elapsed is: #{p.elapsed}, humanized elapsed is: #{p.h_elapsed}"
165
+ puts "percent is: #{p.percent}, humanized percent is: #{p.h_percent}"
166
+ puts "eta is: #{p.eta}, humanized eta is: #{p.h_eta}"
167
+ puts "done is: #{p.done}, humanized done is: #{p.h_done}"
168
+ puts "total is: #{p.total}, humanized done is: #{p.h_total}"
169
+ puts "total is: #{p.total}, humanized done is: #{p.h_total}"
170
+ puts "bar is: #{p.bar}"
171
+ puts "---"
172
+ end
173
+ sleep 0.1
174
+ end
175
+ #p.close # no need to close this one
176
+ end
177
+
178
+ # DEMO 10
179
+ def demo_10
180
+ total = 100000
181
+ step = 1000
182
+ text = " " * 30 + "DEMO 10 - OMGWTFBBQ!"
183
+ p = PowerBar.new
184
+ p.settings.tty.finite.template.padchar = "\e[30;1m\u2589"
185
+ p.settings.tty.finite.template.barchar = "\e[34;1m\u2589"
186
+ p.settings.tty.finite.output = Proc.new{ |s| $stderr.print s }
187
+ j = 0
188
+ total = 300000000
189
+ step = 1000000
190
+
191
+ spin = ')|(|'
192
+ spun = ',.oO^`^Oo'
193
+
194
+ (0..total).step(step).each do |i|
195
+ p.send(:state).scope = nil # omghax, don't try this at home!
196
+ wtf = sprintf('%-26s', text[j%text.length..j%text.length+25])
197
+ .gsub(/./) { |char| char.send(rand(2).zero? ? :downcase : :upcase) }
198
+ p.show(
199
+ {
200
+ :msg => wtf,
201
+ :done => i,
202
+ :total => total,
203
+ :settings => {
204
+ :tty => {
205
+ :finite => {
206
+ :template => {
207
+ :barchar => "\e[36;1m" + spin[j%spin.length],
208
+ :padchar => "\e[0;34m" + spun[j%spun.length],
209
+ :main => "\e[#{rand(1)};3#{rand(7)}m${<msg>}\e[0m "+
210
+ "\e[0m${<bar> }\e[0m${<rate>/s} "+
211
+ "\e[33;1m${<percent>%} " +
212
+ "\e[36;1m${<elapsed>}\e[31;1m${ ETA: <eta>}",
213
+ :exit => "\e[?25h\e[0m",
214
+ :close => "\e[?25h\e[0m\n"
215
+ }
216
+ }
217
+ }
218
+ } # chrr..
219
+ }
220
+ )
221
+ j += 1
222
+ sleep 0.1
223
+ end
224
+ p.wipe
225
+ p.close
226
+ end
227
+
228
+ begin
229
+ method("demo_#{ARGV[0]}".to_sym).call
230
+ rescue
231
+ (1..10).each do |i|
232
+ method("demo_#{i}".to_sym).call
233
+ puts "---"
234
+ sleep 1
235
+ end
236
+ end
@@ -0,0 +1,379 @@
1
+ #
2
+ # Copyright (C) 2011 by moe@busyloop.net
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+
23
+ require 'powerbar/version'
24
+ require 'ansi'
25
+ require 'hashie/mash'
26
+
27
+ class PowerBar
28
+ #
29
+ # This is PowerBar - The last progressbar-library you'll ever need.
30
+ #
31
+
32
+ STRIP_ANSI = Regexp.compile '\e\[(\d+)(;\d+)?(;\d+)?[m|K]', nil
33
+
34
+ def initialize(opts={})
35
+ @@exit_hooked = false
36
+ @state = Hashie::Mash.new( {
37
+ :time_last_show => Time.at(0), # <- don't mess with us
38
+ :time_last_update => Time.at(0), # <- unless you know
39
+ :time_start => nil, # <- what you're doing!
40
+ :time_now => nil, # <-
41
+ :msg => 'PowerBar!', # <--- this one is safe to mess with ;)
42
+ :settings => {
43
+ :rate_sample_max_interval => 10, # See PowerBar::Rate
44
+ :rate_sample_window => 6, # See PowerBar::Rate
45
+ :force_mode => nil, # set to :tty or :notty to force either mode
46
+ :tty => { # <== Settings when stdout is a tty
47
+ :finite => { # <== Settings for a finite progress bar (when total != :unknown)
48
+ # The :output Proc is called to draw on the screen --------------------.
49
+ :output => Proc.new{ |s| $stderr.print s[0..terminal_width()-1] }, # <-'
50
+ :interval => 0.1, # Minimum interval between screen refreshes (in seconds)
51
+ :template => { # <== template for a finite progress bar on a tty
52
+ :pre => "\e[1000D\e[?25l", # printed before the progress-bar
53
+ #
54
+ # :main is the progressbar template
55
+ #
56
+ # The following tokens are available:
57
+ # msg, bar, rate, percent, elapsed, eta, done, total
58
+ #
59
+ # Tokens may be used like so:
60
+ # ${<foo>}
61
+ # OR:
62
+ # ${surrounding <foo> text}
63
+ #
64
+ # The surrounding text is only rendered when <foo>
65
+ # evaluates to something other than nil.
66
+ :main => '${<msg>}: ${[<bar>] }${<rate>/s }${<percent>% }${<elapsed>}${, ETA: <eta>}',
67
+ :post => '', # printed after the progressbar
68
+ :wipe => "\e[1000D\e[K", # printed when 'wipe' is called
69
+ :close => "\e[?25h\n", # printed when 'close' is called
70
+ :exit => "\e[?25h", # printed if the process exits unexpectedly
71
+ :barchar => "\u2588", # fill-char for the progress-bar
72
+ :padchar => "\u2022" # padding-char for the progress-bar
73
+ },
74
+ },
75
+ :infinite => { # <== Settings for an infinite progress "bar" (when total is :unknown)
76
+ :output => Proc.new{ |s| $stderr.print s[0..terminal_width()-1] },
77
+ :interval => 0.1,
78
+ :template => {
79
+ :pre => "\e[1000D\e[?25l",
80
+ :main => "${<msg>}: ${<done> }${<rate>/s }${<elapsed>}",
81
+ :post => "\e[K",
82
+ :wipe => "\e[1000D\e[K",
83
+ :close => "\e[?25h\n",
84
+ :exit => "\e[?25h",
85
+ :barchar => "\u2588",
86
+ :padchar => "\u2022"
87
+ },
88
+ }
89
+ },
90
+ :notty => { # <== Settings when stdout is not a tty
91
+ :finite => {
92
+ # You may want to hook in your favorite Logger-Library here. ---.
93
+ :output => Proc.new{ |s| $stderr.print s }, # <----------------'
94
+ :interval => 1,
95
+ :line_width => 78, # Maximum output line width
96
+ :template => {
97
+ :pre => '',
98
+ :main => "${<msg>}: ${<done>}/${<total>}, ${<percent>%}${, <rate>/s}${, elapsed: <elapsed>}${, ETA: <eta>}\n",
99
+ :post => '',
100
+ :wipe => '',
101
+ :close => nil,
102
+ :exit => nil,
103
+ :barchar => "#",
104
+ :padchar => "."
105
+ },
106
+ },
107
+ :infinite => {
108
+ :output => Proc.new{ |s| $stderr.print s },
109
+ :interval => 1,
110
+ :line_width => 78,
111
+ :template => {
112
+ :pre => "",
113
+ :main => "${<msg>}: ${<done> }${<rate>/s }${<elapsed>}\n",
114
+ :post => "",
115
+ :wipe => "",
116
+ :close => nil,
117
+ :exit => nil,
118
+ :barchar => "#",
119
+ :padchar => "."
120
+ },
121
+ }
122
+ }
123
+ }
124
+ }.merge(opts) )
125
+ end
126
+
127
+ # Access the settings-hash
128
+ def settings
129
+ @state.settings
130
+ end
131
+
132
+ # Access settings under current scope (e.g. tty.infinite)
133
+ def scope
134
+ scope_hash = [settings.force_mode,state.total].hash
135
+ return @state.scope unless @state.scope.nil? or scope_hash != @state.scope_hash
136
+ state.scope_at = [
137
+ settings.force_mode || ($stdout.isatty ? :tty : :notty),
138
+ :unknown == state.total ? :infinite : :finite
139
+ ]
140
+ state.scope = state.settings
141
+ state.scope_at.each do |s|
142
+ begin
143
+ state.scope = state.scope[s]
144
+ rescue NoMethodError
145
+ raise StandardError, "Invalid configuration: #{state.scope_at.join('.')} "+
146
+ "(Can't resolve: #{state.scope_at[state.scope_at.index(s)-1]})"
147
+ end
148
+ end
149
+ state.scope_hash = scope_hash
150
+ state.scope
151
+ end
152
+
153
+ # Hook at_exit to ensure cleanup when we get interrupted
154
+ def hook_exit
155
+ return if @@exit_hooked
156
+ if scope.template.exit
157
+ at_exit do
158
+ exit!
159
+ end
160
+ end
161
+ @@exit_hooked = true
162
+ end
163
+
164
+ # This prints the close-template which normally prints a newline.
165
+ # Be a good citizen, always close your PowerBars!
166
+ def close
167
+ scope.output.call(scope.template.close) unless scope.template.close.nil?
168
+ state.closed = true
169
+ end
170
+
171
+ # Update state (and settings) without printing anything.
172
+ def update(opts={})
173
+ state.merge!(opts)
174
+ state.time_start ||= Time.now
175
+ state.time_now = Time.now
176
+
177
+ @rate ||= PowerBar::Rate.new(state.time_now,
178
+ state.settings.rate_sample_window,
179
+ state.settings.rate_sample_max_interval)
180
+ @rate.append(state.time_now, state.done)
181
+ end
182
+
183
+ # Display the PowerBar.
184
+ def show(opts={})
185
+ if scope.interval <= Time.now - state.time_last_show
186
+ update(opts)
187
+ hook_exit
188
+
189
+ state.time_last_show = Time.now
190
+ state.closed = false
191
+ scope.output.call(scope.template.pre)
192
+ scope.output.call(render)
193
+ scope.output.call(scope.template.post)
194
+ end
195
+ end
196
+
197
+ # Render the PowerBar and return as a string.
198
+ def render(opts={})
199
+ update(opts)
200
+ render_template
201
+ end
202
+
203
+ # Remove the PowerBar from the screen.
204
+ def wipe
205
+ scope.output.call(scope.template.wipe)
206
+ end
207
+
208
+ # Render the actual bar-portion of the PowerBar.
209
+ # The length of the bar is determined from the template.
210
+ # Returns nil if the bar-length would be == 0.
211
+ def bar
212
+ return nil if state.total.is_a? Symbol
213
+ blank = render_template(:main, skip=[:bar])
214
+ twid = state.scope_at[0] == :tty ? terminal_width() : scope.line_width
215
+ barlen = [twid - blank.gsub(STRIP_ANSI, '').length, 0].max
216
+ done = state.done
217
+ total = state.total
218
+ barchar = scope.template.barchar
219
+ padchar = scope.template.padchar
220
+ fill = [0,[(done.to_f/total*barlen).to_i,barlen].min].max
221
+ thebar = barchar * fill + padchar * [barlen - fill,0].max
222
+ thebar.length == 0 ? nil : thebar
223
+ end
224
+
225
+ def h_bar
226
+ bar
227
+ end
228
+
229
+ def msg
230
+ state.msg
231
+ end
232
+
233
+ def h_msg
234
+ msg
235
+ end
236
+
237
+ def eta
238
+ (state.total - state.done) / rate
239
+ end
240
+
241
+ # returns nil when eta is < 1 second
242
+ def h_eta
243
+ 1 < eta ? humanize_interval(eta) : nil
244
+ end
245
+
246
+ def elapsed
247
+ e = (state.time_now - state.time_start).to_f
248
+ end
249
+
250
+ def h_elapsed
251
+ humanize_interval(elapsed)
252
+ end
253
+
254
+ def percent
255
+ return 0.0 if state.total.is_a? Symbol
256
+ state.done.to_f/state.total*100
257
+ end
258
+
259
+ def h_percent
260
+ sprintf "%d", percent
261
+ end
262
+
263
+ def rate
264
+ @rate.avg
265
+ end
266
+
267
+ def h_rate
268
+ humanize_quantity(rate.round(1))
269
+ end
270
+
271
+ def total
272
+ state.total
273
+ end
274
+
275
+ def h_total
276
+ humanize_quantity(state.total)
277
+ end
278
+
279
+ def done
280
+ state.done
281
+ end
282
+
283
+ def h_done
284
+ humanize_quantity(state.done)
285
+ end
286
+
287
+ def terminal_width
288
+ ANSI::Terminal.terminal_width
289
+ end
290
+
291
+ private
292
+ def state
293
+ @state
294
+ end
295
+
296
+ # Cap'n Hook
297
+ def exit!
298
+ return if state.closed
299
+ scope.output.call(scope.template.exit) unless scope.template.exit.nil?
300
+ end
301
+
302
+ def render_template(tplid=:main, skip=[])
303
+ tpl = scope.template[tplid]
304
+ skip.each do |s|
305
+ tpl = tpl.gsub(/\$\{([^<]*)<#{s}>([^}]*)\}/, '\1\2')
306
+ end
307
+ tpl.gsub(/\${[^}]+}/) do |var|
308
+ sub = nil
309
+ r = var.gsub(/<[^>]+>/) do |t|
310
+ t = t[1..-2]
311
+ begin
312
+ sub = self.send(('h_'+t).to_sym)
313
+ rescue NoMethodError => e
314
+ raise NameError, "Invalid token '#{t}' in template '#{tplid}'"
315
+ end
316
+ end[2..-2]
317
+ sub.nil? ? '' : r
318
+ end
319
+ end
320
+
321
+ HQ_UNITS = %w(b k M G T).freeze
322
+ def humanize_quantity(number, format='%n%u', base=1024)
323
+ return nil if number.nil?
324
+ return nil if number.is_a? Float and (number.nan? or number.infinite?)
325
+ return number if number.to_i < base
326
+
327
+ max_exp = HQ_UNITS.size - 1
328
+ number = Float(number)
329
+ exponent = (Math.log(number) / Math.log(base)).to_i
330
+ exponent = max_exp if exponent > max_exp
331
+ number /= base ** exponent
332
+
333
+ unit = HQ_UNITS[exponent]
334
+ return format.gsub(/%n/, number.round(1).to_s).gsub(/%u/, unit)
335
+ end
336
+
337
+ def humanize_interval(s)
338
+ return nil if s.nil? or s.infinite?
339
+ sprintf("%02d:%02d:%02d", s / 3600, s / 60 % 60, s % 60)
340
+ end
341
+
342
+ class Rate < Array
343
+ attr_reader :last_sample_at
344
+ def initialize(at, len, max_interval=10, interval_step=0.1)
345
+ super([])
346
+ @last_sample_at = at
347
+ @sample_interval = 0
348
+ @sample_interval_step = interval_step
349
+ @sample_interval_max = max_interval
350
+ @counter = 0
351
+ @len = len
352
+ end
353
+
354
+ def append(at, v)
355
+ return if @sample_interval > at - @last_sample_at
356
+ @sample_interval += @sample_interval_step if @sample_interval < @sample_interval_max
357
+
358
+ rate = (v - @counter) / (at - @last_sample_at).to_f
359
+ return if rate.nan?
360
+
361
+ @last_sample_at = at
362
+ @counter = v
363
+
364
+ self << rate
365
+ if length > @len
366
+ shift
367
+ end
368
+ end
369
+
370
+ def sum
371
+ inject(:+).to_f
372
+ end
373
+
374
+ def avg
375
+ sum / size
376
+ end
377
+ end
378
+ end
379
+
@@ -0,0 +1,3 @@
1
+ module Powerbar
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "powerbar/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "powerbar"
7
+ s.version = Powerbar::VERSION
8
+ s.authors = ["Moe"]
9
+ s.email = ["moe@busyloop.net"]
10
+ s.homepage = "https://github.com/busyloop/powerbar"
11
+ s.summary = %q{The last progressbar-library you'll ever need}
12
+ s.description = %q{The last progressbar-library you'll ever need}
13
+
14
+ s.add_dependency "ansi", "~> 1.4.0"
15
+ s.add_dependency "hashie", "~> 1.1.0"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: powerbar
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Moe
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-07 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ansi
16
+ requirement: &17384200 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.4.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *17384200
25
+ - !ruby/object:Gem::Dependency
26
+ name: hashie
27
+ requirement: &17383420 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 1.1.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *17383420
36
+ description: The last progressbar-library you'll ever need
37
+ email:
38
+ - moe@busyloop.net
39
+ executables:
40
+ - powerbar-demo
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - README.rdoc
47
+ - Rakefile
48
+ - ass/screenshot.png
49
+ - bin/powerbar-demo
50
+ - lib/powerbar.rb
51
+ - lib/powerbar/version.rb
52
+ - powerbar.gemspec
53
+ homepage: https://github.com/busyloop/powerbar
54
+ licenses: []
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 1.8.10
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: The last progressbar-library you'll ever need
77
+ test_files: []
78
+ has_rdoc: