infobar 0.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,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,15 @@
1
+ class Infobar::Duration
2
+ def initialize(value, format: nil)
3
+ duration = Tins::Duration.new(value)
4
+ @string =
5
+ if format
6
+ duration.format(format)
7
+ else
8
+ duration
9
+ end.to_s
10
+ end
11
+
12
+ def to_s
13
+ @string
14
+ end
15
+ 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,10 @@
1
+ class Infobar::Number
2
+ def initialize(value, format: nil)
3
+ duration = Tins::Duration.new(value)
4
+ @string = format ? (format % value) : value.to_s
5
+ end
6
+
7
+ def to_s
8
+ @string
9
+ end
10
+ 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