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