infobar 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.utilsrc +26 -0
- data/Gemfile +5 -0
- data/README.md +38 -0
- data/Rakefile +28 -0
- data/TODO.md +22 -0
- data/VERSION +1 -0
- data/infobar.gemspec +52 -0
- data/lib/infobar.rb +139 -0
- data/lib/infobar/counter.rb +101 -0
- data/lib/infobar/display.rb +131 -0
- data/lib/infobar/duration.rb +15 -0
- data/lib/infobar/fancy_interface.rb +26 -0
- data/lib/infobar/frequency.rb +33 -0
- data/lib/infobar/input_output.rb +25 -0
- data/lib/infobar/message.rb +154 -0
- data/lib/infobar/number.rb +10 -0
- data/lib/infobar/spinner.rb +51 -0
- data/lib/infobar/timer.rb +47 -0
- data/lib/infobar/version.rb +8 -0
- data/spec/config/infobar.yml +20 -0
- data/spec/infobar/config_spec.rb +27 -0
- data/spec/infobar/counter_spec.rb +135 -0
- data/spec/infobar/display_spec.rb +71 -0
- data/spec/infobar/duration_spec.rb +12 -0
- data/spec/infobar/frequency_spec.rb +44 -0
- data/spec/infobar/input_output_spec.rb +34 -0
- data/spec/infobar/message_spec.rb +70 -0
- data/spec/infobar/number_spec.rb +12 -0
- data/spec/infobar/spinner_spec.rb +37 -0
- data/spec/infobar/timer_spec.rb +31 -0
- data/spec/infobar_spec.rb +148 -0
- data/spec/spec_helper.rb +12 -0
- metadata +210 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'term/ansicolor'
|
2
|
+
require 'stringio'
|
3
|
+
require 'infobar/frequency'
|
4
|
+
|
5
|
+
class Infobar::Display
|
6
|
+
class << self
|
7
|
+
prepend Tins::Delegate
|
8
|
+
end
|
9
|
+
include Term::ANSIColor
|
10
|
+
|
11
|
+
def self.default_style
|
12
|
+
{
|
13
|
+
done_fill: ?░,
|
14
|
+
done_fg_color: 22,
|
15
|
+
done_bg_color: 40,
|
16
|
+
todo_fill: ' ',
|
17
|
+
todo_fg_color: 40,
|
18
|
+
todo_bg_color: 22,
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
self.output = $stdout
|
24
|
+
self.input = $stdin
|
25
|
+
self.frequency = 0.05
|
26
|
+
@show = true
|
27
|
+
self.style = self.class.default_style
|
28
|
+
end
|
29
|
+
|
30
|
+
def frequency=(duration)
|
31
|
+
@frequency = Infobar::Frequency.new(duration)
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :frequency
|
35
|
+
|
36
|
+
attr_accessor :show
|
37
|
+
|
38
|
+
def show?
|
39
|
+
@show
|
40
|
+
end
|
41
|
+
|
42
|
+
def output=(io)
|
43
|
+
@output = io
|
44
|
+
@output.ask_and_send(:respond_to?, :sync=, true)
|
45
|
+
end
|
46
|
+
|
47
|
+
def output
|
48
|
+
if show?
|
49
|
+
@output
|
50
|
+
else
|
51
|
+
NULL
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
attr_writer :input
|
56
|
+
|
57
|
+
def input
|
58
|
+
if show?
|
59
|
+
@input
|
60
|
+
else
|
61
|
+
NULL
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def style
|
66
|
+
self.class.default_style.each_key.each_with_object({}) do |attribute, h|
|
67
|
+
h[attribute] = instance_variable_get "@#{attribute}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def style=(new_style)
|
72
|
+
self.class.default_style.each_key do |attribute|
|
73
|
+
value = new_style[attribute]
|
74
|
+
value.nil? and next
|
75
|
+
instance_variable_set "@#{attribute}", value
|
76
|
+
end
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def update(message:, progressed:, force: false, **options)
|
81
|
+
force and @frequency.reset
|
82
|
+
@frequency.call do
|
83
|
+
message = Infobar.convert_to_message(message)
|
84
|
+
carriage_return
|
85
|
+
self.style = options
|
86
|
+
cols = columns
|
87
|
+
todo = message.to_str.center cols, replace_character
|
88
|
+
done = todo.slice!(0, (progressed * cols))
|
89
|
+
done.gsub!(replace_character, @done_fill[0])
|
90
|
+
todo.gsub!(replace_character, @todo_fill[0])
|
91
|
+
output << color(@done_fg_color, on_color(@done_bg_color, done)) +
|
92
|
+
color(@todo_fg_color, on_color(@todo_bg_color, todo))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
delegate :called, to: :frequency, as: :updates
|
97
|
+
|
98
|
+
def reset
|
99
|
+
clear
|
100
|
+
@frequency.reset
|
101
|
+
self.style = self.class.default_style
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def clear
|
106
|
+
carriage_return
|
107
|
+
output << ' ' * columns
|
108
|
+
carriage_return
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
def carriage_return
|
113
|
+
output << ?\r
|
114
|
+
self
|
115
|
+
end
|
116
|
+
|
117
|
+
def newline
|
118
|
+
output << $/
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def replace_character
|
125
|
+
"\uFFFC"
|
126
|
+
end
|
127
|
+
|
128
|
+
def columns
|
129
|
+
Tins::Terminal.columns
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Infobar::FancyInterface
|
2
|
+
def ~
|
3
|
+
reset
|
4
|
+
end
|
5
|
+
|
6
|
+
def +@
|
7
|
+
progress by: 1
|
8
|
+
end
|
9
|
+
|
10
|
+
def +(by)
|
11
|
+
progress by: by
|
12
|
+
end
|
13
|
+
|
14
|
+
def coerce(other)
|
15
|
+
return self, other
|
16
|
+
end
|
17
|
+
|
18
|
+
def <<(item)
|
19
|
+
progress by: 1
|
20
|
+
end
|
21
|
+
|
22
|
+
def add(*items)
|
23
|
+
progress by: items.size
|
24
|
+
end
|
25
|
+
alias push add
|
26
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Infobar::Frequency
|
2
|
+
def initialize(duration)
|
3
|
+
@duration = duration.to_f
|
4
|
+
@called = 0
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :duration
|
8
|
+
|
9
|
+
attr_reader :called
|
10
|
+
|
11
|
+
def update(now: Time.now)
|
12
|
+
@update = now
|
13
|
+
@called += 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(&block)
|
17
|
+
now = Time.now
|
18
|
+
if !@update || now - @update > @duration
|
19
|
+
update now: now
|
20
|
+
block.call
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def reset
|
25
|
+
@update = nil
|
26
|
+
@called = 0
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
@duration.to_s
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Infobar::InputOutput
|
2
|
+
def newline
|
3
|
+
@display.newline
|
4
|
+
self
|
5
|
+
end
|
6
|
+
|
7
|
+
def clear
|
8
|
+
@display.clear
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
%i[ print printf putc puts ].each do |method|
|
13
|
+
define_method(method) do |*a, &b|
|
14
|
+
@display.clear
|
15
|
+
@display.output.__send__(method, *a, &b)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
%i[ gets readline readlines ].each do |method|
|
20
|
+
define_method(method) do |*a, &b|
|
21
|
+
@display.clear
|
22
|
+
@display.input.__send__(method, *a, &b)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'infobar/spinner'
|
2
|
+
require 'infobar/duration'
|
3
|
+
require 'infobar/number'
|
4
|
+
|
5
|
+
class Infobar::Message
|
6
|
+
class << self
|
7
|
+
prepend Tins::Delegate
|
8
|
+
|
9
|
+
def register(directive, **opts, &block)
|
10
|
+
directives.key?(directive) and
|
11
|
+
warn "Overwriting old directive #{directive}."
|
12
|
+
directives[directive] = block
|
13
|
+
directive_default_options[directive] = opts
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def directives
|
18
|
+
@directives ||= {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def directive_default_options
|
22
|
+
@directive_default_options ||= {}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# current counter value of items
|
27
|
+
register '%c' do
|
28
|
+
Infobar.counter.current
|
29
|
+
end
|
30
|
+
|
31
|
+
# total counter value of items
|
32
|
+
register '%t' do
|
33
|
+
Infobar.counter.total
|
34
|
+
end
|
35
|
+
|
36
|
+
# number of items to go
|
37
|
+
register '%T' do
|
38
|
+
Infobar.counter.to_go
|
39
|
+
end
|
40
|
+
|
41
|
+
# label of progress bar
|
42
|
+
register '%l' do
|
43
|
+
Infobar.label
|
44
|
+
end
|
45
|
+
|
46
|
+
# progressed so far as a float in 0..1
|
47
|
+
register('%p', format: '%.3f') do |directive, opts|
|
48
|
+
Infobar::Number.new(Infobar.counter.progressed, **opts)
|
49
|
+
end
|
50
|
+
|
51
|
+
# not yet progressed as a float in 0..1
|
52
|
+
register('%q', format: '%1.3f') do |directive, opts|
|
53
|
+
Infobar::Number.new(1 - Infobar.counter.progressed, **opts)
|
54
|
+
end
|
55
|
+
|
56
|
+
# progressed as a percentage float in 0..100
|
57
|
+
register('%P', format: '%3.2f') do |directive, opts|
|
58
|
+
Infobar::Number.new(100 * Infobar.counter.progressed, **opts)
|
59
|
+
end
|
60
|
+
|
61
|
+
# not yet progressed as a percentage float in 0..100
|
62
|
+
register('%Q', format: '%2.2f') do |directive, opts|
|
63
|
+
Infobar::Number.new(100 - 100 * Infobar.counter.progressed, **opts)
|
64
|
+
end
|
65
|
+
|
66
|
+
# time elapsed as a duration
|
67
|
+
register('%te', format: '%h:%m:%s') do |directive, opts|
|
68
|
+
Infobar::Duration.new(Infobar.counter.time_elapsed, **opts)
|
69
|
+
end
|
70
|
+
|
71
|
+
# total time as a duration
|
72
|
+
register('%tt', format: '%h:%m:%s') do |directive, opts|
|
73
|
+
Infobar::Duration.new(Infobar.counter.total_time, **opts)
|
74
|
+
end
|
75
|
+
|
76
|
+
# ETA as a duration
|
77
|
+
register('%e', format: '%h:%m:%s') do |directive, opts|
|
78
|
+
Infobar::Duration.new(Infobar.counter.time_remaining, **opts)
|
79
|
+
end
|
80
|
+
|
81
|
+
# ETA as a datetime
|
82
|
+
register('%E', format: '%T') do |directive, opts|
|
83
|
+
if format = opts[:format]
|
84
|
+
Infobar.counter.eta.strftime(format)
|
85
|
+
else
|
86
|
+
Infobar.counter.eta
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# rate with or without units
|
91
|
+
register('%r', unit: nil, prefix: 1000, format: '%f %U') do |directive, opts|
|
92
|
+
if opts[:unit]
|
93
|
+
Tins::Unit.format(Infobar.counter.rate, **opts)
|
94
|
+
else
|
95
|
+
Infobar.counter.rate
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# average time as a duration
|
100
|
+
register('%a', format: '%m:%s.%f') do |directive, opts|
|
101
|
+
Infobar::Duration.new(Infobar.counter.average_time, **opts)
|
102
|
+
end
|
103
|
+
|
104
|
+
# spinner
|
105
|
+
register('%s', frames: :pipe, message: ?✓) do |directive, opts|
|
106
|
+
if Infobar.finished?
|
107
|
+
if message = opts[:message]
|
108
|
+
Infobar.convert_to_message(message).to_str
|
109
|
+
end
|
110
|
+
elsif opts[:random]
|
111
|
+
Infobar::Spinner.new(opts[:frames]).spin(:random)
|
112
|
+
else
|
113
|
+
Infobar::Spinner.new(opts[:frames]).spin(Infobar.display.updates)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# literal percentag character
|
118
|
+
register '%%' do
|
119
|
+
?%
|
120
|
+
end
|
121
|
+
|
122
|
+
def initialize(opts = {})
|
123
|
+
@format = opts.delete(:format) or
|
124
|
+
raise ArgumentError, 'format option required'
|
125
|
+
@opts = opts.each_with_object({}) { |(k, v), h| h[k.to_s] = v }
|
126
|
+
end
|
127
|
+
|
128
|
+
attr_reader :opts
|
129
|
+
|
130
|
+
delegate :directives, to: self
|
131
|
+
|
132
|
+
delegate :directive_default_options, to: self
|
133
|
+
|
134
|
+
def opts_for(directive)
|
135
|
+
@opts.fetch(directive, directive_default_options[directive])
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_str
|
139
|
+
keys = directives.keys.sort_by { |k| -k.size }
|
140
|
+
@format.gsub(/(?<!%)(#{keys * ?|})/) do
|
141
|
+
directives[$1].call($1, opts_for($1))
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
attr_reader :format
|
146
|
+
|
147
|
+
alias to_s format
|
148
|
+
|
149
|
+
def to_hash
|
150
|
+
{
|
151
|
+
format: format,
|
152
|
+
}.merge(@opts)
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class Infobar::Spinner
|
2
|
+
PREDEFINED = {
|
3
|
+
pipe: %w[ | / – \\ ],
|
4
|
+
arrow: %w[ ↑ ↗ → ↘ ↓ ↙ ← ↖ ],
|
5
|
+
bar1: %w[ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▇ ▆ ▅ ▄ ▃ ▂ ],
|
6
|
+
bar2: %w[ █ ▉ ▊ ▋ ▌ ▍ ▎ ▏ ▎ ▍ ▌ ▋ ▊ ▉ ],
|
7
|
+
braille7: %w[ ⣾ ⣽ ⣻ ⢿ ⡿ ⣟ ⣯ ⣷ ],
|
8
|
+
braille1: %w[ ⠁ ⠂ ⠄ ⡀ ⢀ ⠠ ⠐ ⠈ ],
|
9
|
+
square1: %w[ ▖ ▘ ▝ ▗ ],
|
10
|
+
square2: %w[ ◰ ◳ ◲ ◱ ],
|
11
|
+
tetris: %w[ ▌ ▀ ▐▄ ],
|
12
|
+
eyes: %w[ ◡◡ ⊙⊙ ◠◠ ],
|
13
|
+
corners: %w[ ┤ ┘ ┴ └ ├ ┌ ┬ ┐ ],
|
14
|
+
triangle: %w[ ◢ ◣ ◤ ◥ ],
|
15
|
+
circle1: %w[ ◴ ◷ ◶ ◵ ],
|
16
|
+
circle2: %w[ ◐ ◓ ◑ ◒ ],
|
17
|
+
circle3: %w[ ◜ ◝ ◞ ◟ ],
|
18
|
+
cross: %w[ + × ],
|
19
|
+
cylon: [ '● ', ' ● ', ' ●', ' ● ' ],
|
20
|
+
pacman: [ 'ᗧ∙∙∙∙●', ' O∙∙∙●', ' ᗧ∙∙●',' O∙●', ' ᗧ●', 'ᗣ O', ' ᗣ ᗤ', ' ᗣ O ', ' ᗣ ᗤ ', ' ᗣO ', ' ᗤ ', 'O ∞ ', 'ᗧ ∞ ', 'O ' ],
|
21
|
+
asteroids: [ 'ᐊ ◍', 'ᐃ ◍', 'ᐓ ◍', 'ᐅ· ◍', 'ᐅ ·◍', 'ᐅ ○', 'ᐅ ◌', 'ᐁ ' ],
|
22
|
+
}
|
23
|
+
|
24
|
+
def initialize(frames = nil)
|
25
|
+
@frames =
|
26
|
+
case frames
|
27
|
+
when Array
|
28
|
+
frames
|
29
|
+
when Symbol
|
30
|
+
PREDEFINED.fetch(frames) do
|
31
|
+
|k| raise KeyError, "frames #{k} not predefined"
|
32
|
+
end
|
33
|
+
when nil
|
34
|
+
PREDEFINED[:pipe]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def spin(count)
|
39
|
+
@string =
|
40
|
+
if count == :random
|
41
|
+
@frames[rand(@frames.size)]
|
42
|
+
else
|
43
|
+
@frames[count % @frames.size]
|
44
|
+
end
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
@string
|
50
|
+
end
|
51
|
+
end
|