tty-progressbar 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -3
  3. data/CHANGELOG.md +9 -0
  4. data/README.md +32 -6
  5. data/lib/tty-progressbar.rb +2 -0
  6. data/lib/tty/progressbar.rb +63 -76
  7. data/lib/tty/progressbar/configuration.rb +44 -0
  8. data/lib/tty/progressbar/converter.rb +34 -2
  9. data/lib/tty/progressbar/formatter.rb +29 -0
  10. data/lib/tty/progressbar/{bar_formatter.rb → formatter/bar.rb} +14 -1
  11. data/lib/tty/progressbar/formatter/byte.rb +32 -0
  12. data/lib/tty/progressbar/{current_formatter.rb → formatter/current.rb} +14 -1
  13. data/lib/tty/progressbar/formatter/elapsed.rb +33 -0
  14. data/lib/tty/progressbar/{estimated_formatter.rb → formatter/estimated.rb} +16 -3
  15. data/lib/tty/progressbar/{percent_formatter.rb → formatter/percent.rb} +14 -1
  16. data/lib/tty/progressbar/formatter/total.rb +28 -0
  17. data/lib/tty/progressbar/pipeline.rb +6 -1
  18. data/lib/tty/progressbar/version.rb +1 -1
  19. data/spec/unit/clear_spec.rb +21 -0
  20. data/spec/unit/converter/to_bytes_spec.rb +27 -0
  21. data/spec/unit/{converter_spec.rb → converter/to_time_spec.rb} +0 -0
  22. data/spec/unit/custom_formatter_spec.rb +4 -0
  23. data/spec/unit/{bar_formatter_spec.rb → formatter/bar_spec.rb} +0 -0
  24. data/spec/unit/formatter/byte_spec.rb +20 -0
  25. data/spec/unit/{current_formatter_spec.rb → formatter/current_spec.rb} +0 -0
  26. data/spec/unit/{elapsed_formatter_spec.rb → formatter/elapsed_spec.rb} +1 -1
  27. data/spec/unit/{estimated_formatter_spec.rb → formatter/estimated_spec.rb} +0 -0
  28. data/spec/unit/{percent_formatter_spec.rb → formatter/percent_spec.rb} +0 -0
  29. data/spec/unit/new_spec.rb +12 -0
  30. data/spec/unit/resize_spec.rb +39 -0
  31. data/spec/unit/width_spec.rb +26 -0
  32. data/tasks/console.rake +1 -1
  33. data/tty-progressbar.gemspec +3 -2
  34. metadata +40 -27
  35. data/lib/tty/progressbar/elapsed_formatter.rb +0 -20
  36. data/lib/tty/progressbar/total_formatter.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 434eb41ca96e22fc08eec8d921a1e58926bcdc53
4
- data.tar.gz: b7cc419624722fd02b25a6de8b2bb9ec781df647
3
+ metadata.gz: f04f4c768fd866bf975c789315865b20506ee916
4
+ data.tar.gz: c50d09a231f989552fb19baa5ea81bcb5cc49a5c
5
5
  SHA512:
6
- metadata.gz: ae0c2edda8fc6eac3577a1a4c74a55b3e5cd01759e13a5978786a5777c4ebb4292bb89daa62da7e2043932d352cdb1af28eef579f0f4eae8025a5c433e5bd16c
7
- data.tar.gz: 9b3bf8f4bae6156ff201ea8f1382c6097d2ba033cd8378832081018efe0c57d90913a77c6053b423460bc961b9f071a4f217990a8441a51e354553d01a36fecd
6
+ metadata.gz: 3aa6b1cbcb9d6310900ddadf151cc53147dafbba951d34cca60996c30319df9c3d73a1f9c1cac2547c5c64c921b45cc14137cb880dad69c8b473d55f46d0b957
7
+ data.tar.gz: bddfaf5c090ef9f0552c1e3162ddc150467496aeb4d93103aafa44b71376e633c96687749d6cce3a7bca4a0cafdf38a84af99fddd0b8f2dea673d48b7dba72c7
data/.travis.yml CHANGED
@@ -16,9 +16,6 @@ matrix:
16
16
  allow_failures:
17
17
  - rvm: ruby-head
18
18
  - rvm: jruby-head
19
- - rvm: jruby-19mode
20
- - rvm: jruby-20mode
21
- - rvm: jruby-21mode
22
19
  fast_finish: true
23
20
  branches:
24
21
  only: master
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ 0.3.0 (December 21, 2014)
2
+
3
+ * Catch INT signal and cleanly end progress
4
+ * Add tty-screen dependency for terminal size detection
5
+ * Add to_bytes converter
6
+ * Add formatter for managing formats pipeline
7
+ * Change to add matching condition to formatter
8
+ * Add block configuration
9
+
1
10
  0.2.0 (November 9, 2014)
2
11
 
3
12
  * Add estimated time formatter.
data/README.md CHANGED
@@ -9,14 +9,15 @@
9
9
  [codeclimate]: https://codeclimate.com/github/peter-murach/tty-progressbar
10
10
  [coverage]: https://coveralls.io/r/peter-murach/tty-progressbar
11
11
 
12
- A flexible progress bars drawing in terminal emulators.
12
+ > A flexible progress bars drawing in terminal emulators.
13
13
 
14
14
  **TTY::ProgressBar** provides independent progress bars component for [TTY](https://github.com/peter-murach/tty) toolkit.
15
15
 
16
16
  ## Features
17
17
 
18
- * Extremly flexible progress display formatting
19
- * Ability to define your custom format tokens
18
+ * Fully [configurable](#2-configuration)
19
+ * Extremly flexible progress display [formatting](#3-formatting)
20
+ * Ability to define your custom format [tokens](#31-tokens)
20
21
  * Works on all ECMA-48 compatible terminals
21
22
 
22
23
  ## Installation
@@ -41,6 +42,7 @@ Or install it yourself as:
41
42
  * [1.1 advance](#11-advance)
42
43
  * [1.2 finish](#12-finish)
43
44
  * [1.3 complete?](#13-complete)
45
+ * [1.4 resize](#14-resize)
44
46
  * [2. Configuration](#2-configuration)
45
47
  * [2.1 Frequency](#21-frequency)
46
48
  * [3. Formatting](#3-formatting)
@@ -69,7 +71,7 @@ downloading [======================= ]
69
71
 
70
72
  ### 1.1 advance
71
73
 
72
- Once you have **ProgressBar** instance, you can progress the display by calling `advance` method. By default it will increase by `1` but you can pass any number of steps, for instance, when used to advance number of bytes of downloaded file.
74
+ Once you have **TTY::ProgressBar** instance, you can progress the display by calling `advance` method. By default it will increase by `1` but you can pass any number of steps, for instance, when used to advance number of bytes of downloaded file.
73
75
 
74
76
  ```ruby
75
77
  bar.advance(1000)
@@ -99,6 +101,14 @@ During progresion you can check if bar is finished or not by calling `complete?`
99
101
  bar.complete? # => false
100
102
  ```
101
103
 
104
+ ### 1.4 resize
105
+
106
+ If you wish for a progress bar to change it's current width, you can use `resize` by passing in a new desired length:
107
+
108
+ ```ruby
109
+ bar.resize(50) # => will resize bar proportionately from this point onwards
110
+ ```
111
+
102
112
  ## 2. Configuration
103
113
 
104
114
  There are number of configuration options that can be provided:
@@ -110,6 +120,17 @@ There are number of configuration options that can be provided:
110
120
  * `output` the output stream defaulting to `stderr`
111
121
  * `frequency` used to throttle the output, by default `0` (see [Frequency](#21-frequency))
112
122
  * `hide_cursor` to hide display cursor defaulting to `false`
123
+ * `clear` to clear the finished bar defaulting to `false`
124
+
125
+ All the above options can be passed in as hash options or block parameters:
126
+
127
+ ```ruby
128
+ TTY::ProgressBar.new "[:bar]" do |config|
129
+ config.total = 30
130
+ config.frequency = 10
131
+ config.clear = true
132
+ end
133
+ ```
113
134
 
114
135
  ### 2.1 Frequency
115
136
 
@@ -134,6 +155,7 @@ Every **TTY::ProgressBar** instance requires a format string, which apart from r
134
155
  These are the tokens that are currently supported:
135
156
 
136
157
  * `:bar` the progress bar
158
+ * `:byte` the current progress in bytes
137
159
  * `:current` the current progress number
138
160
  * `:total` the total progress number
139
161
  * `:percent` the completion percentage
@@ -144,7 +166,7 @@ These are the tokens that are currently supported:
144
166
 
145
167
  If the provided tokens do not meet your needs, you can write your own formatter and instrument formatting pipeline to use a formatter you prefer.
146
168
 
147
- For example, begin by creating custom formatter called `TimeFormatter` that will dynamicly update `:time` token in format string as follows:
169
+ For example, begin by creating custom formatter called `TimeFormatter` that will dynamicly update `:time` token in format string. The methods that you need to specify are `initialize`, `matches?` and `format` like follows:
148
170
 
149
171
  ```ruby
150
172
  class TimeFormatter
@@ -152,7 +174,11 @@ class TimeFormatter
152
174
  @progress = progress
153
175
  end
154
176
 
155
- def format(value)
177
+ def matches?(value) # specify condition to match for
178
+ value.to_s =~ /:time/
179
+ end
180
+
181
+ def format(value) # specify how value is formatted
156
182
  transformed = transform(value)
157
183
  value.gsub(/:time/, transformed.to_s) # => :time token
158
184
  end
@@ -1 +1,3 @@
1
+ # coding: utf-8
2
+
1
3
  require 'tty/progressbar'
@@ -2,16 +2,21 @@
2
2
 
3
3
  require 'io/console'
4
4
  require 'forwardable'
5
+ require 'tty-screen'
5
6
 
7
+ require 'tty/progressbar/configuration'
6
8
  require 'tty/progressbar/converter'
7
9
  require 'tty/progressbar/version'
8
10
  require 'tty/progressbar/pipeline'
9
- require 'tty/progressbar/bar_formatter'
10
- require 'tty/progressbar/current_formatter'
11
- require 'tty/progressbar/elapsed_formatter'
12
- require 'tty/progressbar/estimated_formatter'
13
- require 'tty/progressbar/percent_formatter'
14
- require 'tty/progressbar/total_formatter'
11
+ require 'tty/progressbar/formatter'
12
+
13
+ require 'tty/progressbar/formatter/bar'
14
+ require 'tty/progressbar/formatter/byte'
15
+ require 'tty/progressbar/formatter/current'
16
+ require 'tty/progressbar/formatter/elapsed'
17
+ require 'tty/progressbar/formatter/estimated'
18
+ require 'tty/progressbar/formatter/percent'
19
+ require 'tty/progressbar/formatter/total'
15
20
 
16
21
  module TTY
17
22
  # Used for creating terminal progress bar
@@ -20,35 +25,26 @@ module TTY
20
25
  class ProgressBar
21
26
  extend Forwardable
22
27
 
23
- ECMA_ESC = "\x1b"
24
- ECMA_CSI = "\x1b["
25
- ECMA_CHA = 'G'
28
+ ECMA_ESC = "\e".freeze
29
+ ECMA_CSI = "\e[".freeze
30
+ ECMA_CHA = 'G'.freeze
31
+ ECMA_CLR = 'K'.freeze
26
32
 
27
- DEC_RST = 'l'
28
- DEC_SET = 'h'
29
- DEC_TCEM = '?25'
33
+ DEC_RST = 'l'.freeze
34
+ DEC_SET = 'h'.freeze
35
+ DEC_TCEM = '?25'.freeze
30
36
 
31
37
  attr_reader :format
32
38
 
33
- attr_reader :total
34
-
35
- attr_reader :width
36
-
37
- attr_reader :no_width
38
-
39
39
  attr_reader :current
40
40
 
41
41
  attr_reader :start_at
42
42
 
43
- attr_reader :complete
43
+ def_delegators :@configuration, :total, :width, :no_width,
44
+ :complete, :incomplete, :hide_cursor, :clear,
45
+ :output, :frequency
44
46
 
45
- attr_reader :incomplete
46
-
47
- attr_reader :hide_cursor
48
-
49
- attr_reader :output
50
-
51
- def_delegator :@pipeline, :use
47
+ def_delegator :@formatter, :use
52
48
 
53
49
  # Create progress bar
54
50
  #
@@ -74,28 +70,21 @@ module TTY
74
70
  #
75
71
  # @api public
76
72
  def initialize(format, options = {})
77
- @format = format
78
- @total = options.fetch(:total) { fail ArgumentError }
79
- @width = options.fetch(:width) { @total }
80
- @no_width = options.fetch(:no_width) { false }
81
- @clear = options.fetch(:clear) { false }
82
- @incomplete = options.fetch(:incomplete) { ' ' }
83
- @complete = options.fetch(:complete) { '=' }
84
- @hide_cursor = options.fetch(:hide_cursor) { false }
85
- @output = options.fetch(:output) { $stderr }
86
- @frequency = options.fetch(:frequency) { 0 } # 0Hz
87
-
88
- @width = 0 if @no_width
89
- @render_period = @frequency == 0 ? 0 : 1.0 / @frequency
73
+ @format = format
74
+ @configuration = Configuration.new(options)
75
+ yield @configuration if block_given?
76
+
77
+ @width = 0 if no_width
78
+ @render_period = frequency == 0 ? 0 : 1.0 / frequency
90
79
  @current = 0
91
80
  @readings = 0
92
81
  @last_render_time = Time.now
93
82
  @last_render_width = 0
94
83
  @done = false
95
84
  @start_at = Time.now
96
- @pipeline = TTY::ProgressBar::Pipeline.new
85
+ @formatter = TTY::ProgressBar::Formatter.new
97
86
 
98
- default_pipeline
87
+ @formatter.load
99
88
  register_callbacks
100
89
  end
101
90
 
@@ -126,27 +115,20 @@ module TTY
126
115
  #
127
116
  # @api public
128
117
  def ratio
129
- proportion = (@current.to_f / @total)
118
+ proportion = (@current.to_f / total)
130
119
  [[proportion, 0].max, 1].min
131
120
  end
132
121
 
133
- # Determine terminal width
134
- #
135
- # @api public
136
- def max_columns
137
- IO.console.winsize.last
138
- end
139
-
140
122
  # Render progress to the output
141
123
  #
142
124
  # @api private
143
125
  def render
144
126
  return if @done
145
- if @hide_cursor && @last_render_width == 0 && !(@current >= total)
127
+ if hide_cursor && @last_render_width == 0 && !(@current >= total)
146
128
  write(ECMA_CSI + DEC_TCEM + DEC_RST)
147
129
  end
148
130
 
149
- formatted = @pipeline.decorate(self, @format)
131
+ formatted = @formatter.decorate(self, @format)
150
132
  write(formatted, true)
151
133
 
152
134
  @last_render_time = Time.now
@@ -159,9 +141,9 @@ module TTY
159
141
  #
160
142
  # @api private
161
143
  def write(data, clear_first = false)
162
- @output.print(ECMA_CSI + '1' + ECMA_CHA) if clear_first
163
- @output.print(data)
164
- @output.flush
144
+ output.print(ECMA_CSI + '1' + ECMA_CHA) if clear_first
145
+ output.print(data)
146
+ output.flush
165
147
  end
166
148
 
167
149
  # Resize progress bar with new configuration
@@ -171,11 +153,11 @@ module TTY
171
153
  fail 'Cannot resize finished progress bar' if @done
172
154
 
173
155
  if new_width
174
- @no_width = false
175
- @width = new_width
156
+ @configuration.no_width = false
157
+ @configuration.width = new_width
176
158
  else
177
- @no_width = true
178
- @width = 0
159
+ @configuration.no_width = true
160
+ @configuration.width = 0
179
161
  end
180
162
 
181
163
  advance(0) # rerender with new configuration
@@ -186,16 +168,23 @@ module TTY
186
168
  # @api public
187
169
  def finish
188
170
  # reenable cursor if it is turned off
189
- if @hide_cursor && @last_render_width != 0
171
+ if hide_cursor && @last_render_width != 0
190
172
  write(ECMA_CSI + DEC_TCEM + DEC_SET, false)
191
173
  end
192
174
  return if @done
193
- @current = @width if @no_width
175
+ @current = width if no_width
194
176
  render
195
- write("\n", false)
177
+ clear ? clear_line : write("\n", false)
196
178
  @done = true
197
179
  end
198
180
 
181
+ # Clear current line
182
+ #
183
+ # @api public
184
+ def clear_line
185
+ output.print(ECMA_CSI + '0m' + ECMA_CSI + '1000D' + ECMA_CSI + ECMA_CLR)
186
+ end
187
+
199
188
  # Check if progress is finised
200
189
  #
201
190
  # @return [Boolean]
@@ -224,6 +213,13 @@ module TTY
224
213
  render
225
214
  end
226
215
 
216
+ # Determine terminal width
217
+ #
218
+ # @api public
219
+ def max_columns
220
+ TTY::Screen.new.width
221
+ end
222
+
227
223
  private
228
224
 
229
225
  # Pad message out with spaces
@@ -237,26 +233,17 @@ module TTY
237
233
  message
238
234
  end
239
235
 
240
- # Prepare default pipeline formatters
241
- #
242
- # @api private
243
- def default_pipeline
244
- @pipeline.use TTY::ProgressBar::CurrentFormatter
245
- @pipeline.use TTY::ProgressBar::TotalFormatter
246
- @pipeline.use TTY::ProgressBar::ElapsedFormatter
247
- @pipeline.use TTY::ProgressBar::EstimatedFormatter
248
- @pipeline.use TTY::ProgressBar::PercentFormatter
249
- @pipeline.use TTY::ProgressBar::BarFormatter
250
- end
251
-
252
236
  # Handle resize and kill signals
253
237
  #
254
238
  # @api private
255
239
  def register_callbacks
256
- callback = proc { send(:resize, max_columns) }
257
- Signal.trap('SIGWINCH', &callback)
240
+ callback = proc do
241
+ adjusted_width = width < max_columns ? width : max_columns
242
+ send(:resize, adjusted_width)
243
+ end
244
+ Signal.trap('WINCH', &callback)
258
245
 
259
- Signal.trap('KILL') { @finish }
246
+ Signal.trap('INT') { finish }
260
247
  end
261
248
  end # ProgressBar
262
249
  end # TTY
@@ -0,0 +1,44 @@
1
+ # coding: utf-8
2
+
3
+ module TTY
4
+ class ProgressBar
5
+ class Configuration
6
+
7
+ attr_accessor :total
8
+
9
+ attr_accessor :width
10
+
11
+ attr_accessor :no_width
12
+
13
+ attr_accessor :incomplete
14
+
15
+ attr_accessor :complete
16
+
17
+ attr_accessor :hide_cursor
18
+
19
+ attr_accessor :clear
20
+
21
+ attr_accessor :output
22
+
23
+ attr_accessor :frequency
24
+
25
+ def initialize(options)
26
+ self.total = options[:total] if options[:total]
27
+ @width = options.fetch(:width) { total }
28
+ @no_width = options.fetch(:no_width) { false }
29
+ @incomplete = options.fetch(:incomplete) { ' ' }
30
+ @complete = options.fetch(:complete) { '=' }
31
+ @hide_cursor = options.fetch(:hide_cursor) { false }
32
+ @clear = options.fetch(:clear) { false }
33
+ @output = options.fetch(:output) { $stderr }
34
+ @frequency = options.fetch(:frequency) { 0 } # 0Hz
35
+ end
36
+
37
+ def total=(value)
38
+ fail ArgumentError unless value
39
+ @total = value
40
+ @width = value unless width
41
+ end
42
+ end # Configuration
43
+ end # ProgressBar
44
+ end # TTY
@@ -6,9 +6,17 @@ module TTY
6
6
  #
7
7
  # @api public
8
8
  class Converter
9
+ HOURSECONDS = 3600
10
+
11
+ # Convert seconds to time notation
12
+ #
13
+ # @param [Numeric] seconds
14
+ # the seconds to convert to time
15
+ #
16
+ # @api public
9
17
  def to_time(seconds)
10
- hours = (seconds / 3600.to_f).floor
11
- seconds -= hours * 3600
18
+ hours = (seconds / HOURSECONDS.to_f).floor
19
+ seconds -= hours * HOURSECONDS
12
20
  minutes = (seconds / 60).floor
13
21
  seconds -= minutes * 60
14
22
 
@@ -22,6 +30,30 @@ module TTY
22
30
  sprintf('%2ds', seconds)
23
31
  end
24
32
  end
33
+
34
+ KILOBYTE = 1024
35
+ MEGABYTE = KILOBYTE * 1024
36
+ GIGABYTE = MEGABYTE * 1024
37
+
38
+ # Convert value to bytes
39
+ #
40
+ # @param [Numeric] value
41
+ # the value to convert to bytes
42
+ #
43
+ # @return [String]
44
+ #
45
+ # @api public
46
+ def to_bytes(value)
47
+ if value >= GIGABYTE
48
+ sprintf('%.2f', value / GIGABYTE.to_f) + 'GB'
49
+ elsif value >= MEGABYTE
50
+ sprintf('%.2f', value / MEGABYTE.to_f) + 'MB'
51
+ elsif value >= KILOBYTE
52
+ sprintf('%.2f', value / KILOBYTE.to_f) + 'KB'
53
+ else
54
+ value.to_s + 'B'
55
+ end
56
+ end
25
57
  end # Converter
26
58
  end # ProgressBar
27
59
  end # TTY
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+
3
+ module TTY
4
+ class ProgressBar
5
+ class Formatter
6
+ extend Forwardable
7
+
8
+ def_delegators :@pipeline, :decorate, :use
9
+
10
+ # @api private
11
+ def initialize(pipeline = nil)
12
+ @pipeline = TTY::ProgressBar::Pipeline.new
13
+ end
14
+
15
+ # Prepare default pipeline formatters
16
+ #
17
+ # @api private
18
+ def load
19
+ @pipeline.use TTY::ProgressBar::CurrentFormatter
20
+ @pipeline.use TTY::ProgressBar::TotalFormatter
21
+ @pipeline.use TTY::ProgressBar::ElapsedFormatter
22
+ @pipeline.use TTY::ProgressBar::EstimatedFormatter
23
+ @pipeline.use TTY::ProgressBar::PercentFormatter
24
+ @pipeline.use TTY::ProgressBar::ByteFormatter
25
+ @pipeline.use TTY::ProgressBar::BarFormatter
26
+ end
27
+ end # Formatter
28
+ end # ProgressBar
29
+ end # TTY
@@ -6,10 +6,23 @@ module TTY
6
6
  #
7
7
  # @api private
8
8
  class BarFormatter
9
+ MATCHER = /:bar/.freeze
10
+
9
11
  def initialize(progress, *args, &block)
10
12
  @progress = progress
11
13
  end
12
14
 
15
+ # Determines whether this formatter is applied or not.
16
+ #
17
+ # @param [Object] value
18
+ #
19
+ # @return [Boolean]
20
+ #
21
+ # @api private
22
+ def matches?(value)
23
+ !!(value.to_s =~ MATCHER)
24
+ end
25
+
13
26
  # Format :bar token
14
27
  #
15
28
  # @param [String] value
@@ -27,7 +40,7 @@ module TTY
27
40
  bar += complete.join
28
41
  bar += incomplete.join
29
42
 
30
- value.gsub(/:bar/, bar)
43
+ value.gsub(MATCHER, bar)
31
44
  end
32
45
  end # BarFormatter
33
46
  end # ProgressBar
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+
3
+ module TTY
4
+ class ProgressBar
5
+ class ByteFormatter
6
+ MATCHER = /:byte/.freeze
7
+ # Used by {Pipeline} to format :byte token
8
+ #
9
+ # @api private
10
+ def initialize(progress)
11
+ @progress = progress
12
+ @converter = Converter.new
13
+ end
14
+
15
+ # Determines whether this formatter is applied or not.
16
+ #
17
+ # @param [Object] value
18
+ #
19
+ # @return [Boolean]
20
+ #
21
+ # @api private
22
+ def matches?(value)
23
+ !!(value.to_s =~ MATCHER)
24
+ end
25
+
26
+ def format(value)
27
+ bytes = @converter.to_bytes(@progress.current)
28
+ value.gsub(MATCHER, bytes)
29
+ end
30
+ end # ByteFormatter
31
+ end # ProgressBar
32
+ end # TTY
@@ -6,10 +6,23 @@ module TTY
6
6
  #
7
7
  # @api private
8
8
  class CurrentFormatter
9
+ MATCHER = /:current/
10
+
9
11
  def initialize(progress)
10
12
  @progress = progress
11
13
  end
12
14
 
15
+ # Determines whether this formatter is applied or not.
16
+ #
17
+ # @param [Object] value
18
+ #
19
+ # @return [Boolean]
20
+ #
21
+ # @api private
22
+ def matches?(value)
23
+ !!(value.to_s =~ MATCHER)
24
+ end
25
+
13
26
  # Format :current token
14
27
  #
15
28
  # @param [String] value
@@ -17,7 +30,7 @@ module TTY
17
30
  #
18
31
  # @api public
19
32
  def format(value)
20
- value.gsub(/:current/, @progress.current.to_s)
33
+ value.gsub(MATCHER, @progress.current.to_s)
21
34
  end
22
35
  end # CurrentFormatter
23
36
  end # ProgressBar
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+
3
+ module TTY
4
+ class ProgressBar
5
+ # Used by {Pipeline} to format :elapsed token
6
+ #
7
+ # @api private
8
+ class ElapsedFormatter
9
+ MATCHER = /:elapsed/.freeze
10
+
11
+ def initialize(progress, *args, &block)
12
+ @progress = progress
13
+ @converter = Converter.new
14
+ end
15
+
16
+ # Determines whether this formatter is applied or not.
17
+ #
18
+ # @param [Object] value
19
+ #
20
+ # @return [Boolean]
21
+ #
22
+ # @api private
23
+ def matches?(value)
24
+ !!(value.to_s =~ MATCHER)
25
+ end
26
+
27
+ def format(value)
28
+ elapsed = (Time.now - @progress.start_at)
29
+ value.gsub(MATCHER, @converter.to_time(elapsed))
30
+ end
31
+ end # ElapsedFormatter
32
+ end # ProgressBar
33
+ end # TTY
@@ -6,16 +6,29 @@ module TTY
6
6
  #
7
7
  # @api private
8
8
  class EstimatedFormatter
9
+ MATCHER = /:eta/.freeze
10
+
9
11
  def initialize(progress)
10
- @progress = progress
11
- @converter = TTY::ProgressBar::Converter.new
12
+ @progress = progress
13
+ @converter = Converter.new
14
+ end
15
+
16
+ # Determines whether this formatter is applied or not.
17
+ #
18
+ # @param [Object] value
19
+ #
20
+ # @return [Boolean]
21
+ #
22
+ # @api private
23
+ def matches?(value)
24
+ !!(value.to_s =~ MATCHER)
12
25
  end
13
26
 
14
27
  def format(value)
15
28
  elapsed = Time.now - @progress.start_at
16
29
  estimated = (elapsed / @progress.ratio).to_f - elapsed
17
30
  estimated = (estimated.infinite? || estimated < 0) ? 0.0 : estimated
18
- value.gsub(/:eta/, @converter.to_time(estimated))
31
+ value.gsub(MATCHER, @converter.to_time(estimated))
19
32
  end
20
33
  end # ElapsedFormatter
21
34
  end # ProgressBar
@@ -6,13 +6,26 @@ module TTY
6
6
  #
7
7
  # @api private
8
8
  class PercentFormatter
9
+ MATCHER = /:percent/.freeze
10
+
9
11
  def initialize(progress, *args, &block)
10
12
  @progress = progress
11
13
  end
12
14
 
15
+ # Determines whether this formatter is applied or not.
16
+ #
17
+ # @param [Object] value
18
+ #
19
+ # @return [Boolean]
20
+ #
21
+ # @api private
22
+ def matches?(value)
23
+ !!(value.to_s =~ MATCHER)
24
+ end
25
+
13
26
  def format(value)
14
27
  percent = @progress.width == 0 ? 100 : (@progress.ratio * 100).to_i
15
- value.gsub(/:percent/, percent.to_s + '%')
28
+ value.gsub(MATCHER, percent.to_s + '%')
16
29
  end
17
30
  end # PercentFormatter
18
31
  end # ProgressBar
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+
3
+ module TTY
4
+ class ProgressBar
5
+ class TotalFormatter
6
+ MATCHER = /:total/.freeze
7
+
8
+ def initialize(progress, *args, &block)
9
+ @progress = progress
10
+ end
11
+
12
+ # Determines whether this formatter is applied or not.
13
+ #
14
+ # @param [Object] value
15
+ #
16
+ # @return [Boolean]
17
+ #
18
+ # @api private
19
+ def matches?(value)
20
+ !!(value.to_s =~ MATCHER)
21
+ end
22
+
23
+ def format(value)
24
+ value.gsub(MATCHER, @progress.total.to_s)
25
+ end
26
+ end # TotalFormatter
27
+ end # ProgressBar
28
+ end # TTY
@@ -22,7 +22,12 @@ module TTY
22
22
  def decorate(progress, tokenized)
23
23
  base = tokenized.dup
24
24
  formatters.inject(base) do |formatted, formatter|
25
- formatter.call(progress).format(formatted)
25
+ instance = formatter.call(progress)
26
+ if instance.respond_to?(:matches?) && instance.matches?(formatted)
27
+ instance.format(formatted)
28
+ else
29
+ formatted
30
+ end
26
31
  end
27
32
  end
28
33
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module TTY
4
4
  class ProgressBar
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end # ProgressBar
7
7
  end # TTY
@@ -0,0 +1,21 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::ProgressBar, 'clear' do
6
+ let(:output) { StringIO.new('', 'w+') }
7
+
8
+ it "clears progress bar when finished" do
9
+ progress = TTY::ProgressBar.new("[:bar]", output: output, total: 5,
10
+ clear: true)
11
+ 5.times { progress.advance }
12
+ output.rewind
13
+ expect(output.read).to eq([
14
+ "\e[1G[= ]",
15
+ "\e[1G[== ]",
16
+ "\e[1G[=== ]",
17
+ "\e[1G[==== ]",
18
+ "\e[1G[=====]\e[0m\e[1000D\e[K"
19
+ ].join)
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::ProgressBar::Converter, '.to_bytes' do
6
+ subject(:converter) { described_class.new }
7
+
8
+ it "converts 1000 to bytes" do
9
+ expect(converter.to_bytes(1000)).to eq('1000B')
10
+ end
11
+
12
+ it "converts 1024 to bytes" do
13
+ expect(converter.to_bytes(1024)).to eq('1.00KB')
14
+ end
15
+
16
+ it "converts 2000 to bytes" do
17
+ expect(converter.to_bytes(2000)).to eq('1.95KB')
18
+ end
19
+
20
+ it "converts 10_000_000 to bytes" do
21
+ expect(converter.to_bytes(10_000_000)).to eq('9.54MB')
22
+ end
23
+
24
+ it "convert 10_000_000_000 to bytes" do
25
+ expect(converter.to_bytes(10_000_000_000)).to eq('9.31GB')
26
+ end
27
+ end
@@ -13,6 +13,10 @@ RSpec.describe TTY::ProgressBar, 'custom' do
13
13
  @progress = progress
14
14
  end
15
15
 
16
+ def matches?(value)
17
+ value.to_s =~ /:hi/
18
+ end
19
+
16
20
  def format(value)
17
21
  value.gsub(/:hi/, "Hello")
18
22
  end
@@ -0,0 +1,20 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::ProgressBar, '.new' do
6
+ let(:output) { StringIO.new('', 'w+') }
7
+
8
+ it "displays bytes processed" do
9
+ progress = described_class.new(":byte", output: output, total: 102_400)
10
+ 5.times { progress.advance(20_480) }
11
+ output.rewind
12
+ expect(output.read).to eq([
13
+ "\e[1G20.00KB",
14
+ "\e[1G40.00KB",
15
+ "\e[1G60.00KB",
16
+ "\e[1G80.00KB",
17
+ "\e[1G100.00KB\n"
18
+ ].join)
19
+ end
20
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- RSpec.describe TTY::ProgressBar, '.new' do
5
+ RSpec.describe TTY::ProgressBar, 'elapsed' do
6
6
  let(:output) { StringIO.new('', 'w+') }
7
7
 
8
8
  before { Timecop.safe_mode = false }
@@ -12,6 +12,18 @@ RSpec.describe TTY::ProgressBar, '.new' do
12
12
  expect(output.read).to eq("\e[1G[= ]")
13
13
  end
14
14
 
15
+ it "yields configuration to block" do
16
+ progress = TTY::ProgressBar.new "[:bar]" do |config|
17
+ config.output = output
18
+ config.total = 10
19
+ config.clear = true
20
+ end
21
+ expect(progress.output).to eq(output)
22
+ expect(progress.total).to eq(10)
23
+ expect(progress.width).to eq(10)
24
+ expect(progress.clear).to eq(true)
25
+ end
26
+
15
27
  it "displays output where width > total" do
16
28
  progress = TTY::ProgressBar.new("[:bar]", output: output, total: 5, width: 10)
17
29
  5.times { progress.advance }
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::ProgressBar, '.resize' do
6
+ let(:output) { StringIO.new('', 'w+') }
7
+
8
+ it "resizes output down by x2" do
9
+ progress = TTY::ProgressBar.new("[:bar]", output: output, total: 5, width: 10)
10
+ 2.times { progress.advance }
11
+ progress.resize(5)
12
+ 3.times { progress.advance }
13
+ output.rewind
14
+ expect(output.read).to eq([
15
+ "\e[1G[== ]",
16
+ "\e[1G[==== ]",
17
+ "\e[1G[== ]",
18
+ "\e[1G[=== ]",
19
+ "\e[1G[==== ]",
20
+ "\e[1G[=====]\n"
21
+ ].join)
22
+ end
23
+
24
+ it "resizes output up by x2" do
25
+ progress = TTY::ProgressBar.new("[:bar]", output: output, total: 5, width: 10)
26
+ 2.times { progress.advance }
27
+ progress.resize(20)
28
+ 3.times { progress.advance }
29
+ output.rewind
30
+ expect(output.read).to eq([
31
+ "\e[1G[== ]",
32
+ "\e[1G[==== ]",
33
+ "\e[1G[======== ]",
34
+ "\e[1G[============ ]",
35
+ "\e[1G[================ ]",
36
+ "\e[1G[====================]\n"
37
+ ].join)
38
+ end
39
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe TTY::ProgressBar, 'width' do
6
+ let(:output) { StringIO.new('', 'w+') }
7
+
8
+ it "handles width exceeding terminal width" do
9
+ progress = TTY::ProgressBar.new "[:bar]" do |config|
10
+ config.output = output
11
+ config.total = 5
12
+ config.width = 1024
13
+ end
14
+ screen = double(:screen, width: 20)
15
+ allow(TTY::Screen).to receive(:new).and_return(screen)
16
+ 5.times { progress.advance }
17
+ output.rewind
18
+ expect(output.read).to eq([
19
+ "\e[1G[==== ]",
20
+ "\e[1G[======= ]",
21
+ "\e[1G[=========== ]",
22
+ "\e[1G[============== ]",
23
+ "\e[1G[==================]\n"
24
+ ].join)
25
+ end
26
+ end
data/tasks/console.rake CHANGED
@@ -4,7 +4,7 @@ desc 'Load gem inside irb console'
4
4
  task :console do
5
5
  require 'irb'
6
6
  require 'irb/completion'
7
- require File.join(__FILE__, '../../lib/tty-progresbar')
7
+ require File.join(__FILE__, '../../lib/tty-progressbar')
8
8
  ARGV.clear
9
9
  IRB.start
10
10
  end
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.6"
22
- spec.add_development_dependency "rake", "~> 10.0"
21
+ spec.add_dependency "tty-screen", '~> 0.1.0'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.6'
23
24
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tty-progressbar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-09 00:00:00.000000000 Z
11
+ date: 2014-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: tty-screen
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: '1.6'
20
- type: :development
19
+ version: 0.1.0
20
+ type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: '1.6'
26
+ version: 0.1.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ~>
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '1.6'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ~>
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '1.6'
41
41
  description: A flexible progress bars drawing in terminal emulators.
42
42
  email:
43
43
  - ''
@@ -58,30 +58,38 @@ files:
58
58
  - examples/simple.rb
59
59
  - lib/tty-progressbar.rb
60
60
  - lib/tty/progressbar.rb
61
- - lib/tty/progressbar/bar_formatter.rb
61
+ - lib/tty/progressbar/configuration.rb
62
62
  - lib/tty/progressbar/converter.rb
63
- - lib/tty/progressbar/current_formatter.rb
64
- - lib/tty/progressbar/elapsed_formatter.rb
65
- - lib/tty/progressbar/estimated_formatter.rb
66
- - lib/tty/progressbar/percent_formatter.rb
63
+ - lib/tty/progressbar/formatter.rb
64
+ - lib/tty/progressbar/formatter/bar.rb
65
+ - lib/tty/progressbar/formatter/byte.rb
66
+ - lib/tty/progressbar/formatter/current.rb
67
+ - lib/tty/progressbar/formatter/elapsed.rb
68
+ - lib/tty/progressbar/formatter/estimated.rb
69
+ - lib/tty/progressbar/formatter/percent.rb
70
+ - lib/tty/progressbar/formatter/total.rb
67
71
  - lib/tty/progressbar/pipeline.rb
68
- - lib/tty/progressbar/total_formatter.rb
69
72
  - lib/tty/progressbar/version.rb
70
73
  - spec/spec_helper.rb
71
74
  - spec/unit/advance_spec.rb
72
- - spec/unit/bar_formatter_spec.rb
75
+ - spec/unit/clear_spec.rb
73
76
  - spec/unit/complete_spec.rb
74
- - spec/unit/converter_spec.rb
75
- - spec/unit/current_formatter_spec.rb
77
+ - spec/unit/converter/to_bytes_spec.rb
78
+ - spec/unit/converter/to_time_spec.rb
76
79
  - spec/unit/custom_formatter_spec.rb
77
- - spec/unit/elapsed_formatter_spec.rb
78
- - spec/unit/estimated_formatter_spec.rb
80
+ - spec/unit/formatter/bar_spec.rb
81
+ - spec/unit/formatter/byte_spec.rb
82
+ - spec/unit/formatter/current_spec.rb
83
+ - spec/unit/formatter/elapsed_spec.rb
84
+ - spec/unit/formatter/estimated_spec.rb
85
+ - spec/unit/formatter/percent_spec.rb
79
86
  - spec/unit/frequency_spec.rb
80
87
  - spec/unit/hide_cursor_spec.rb
81
88
  - spec/unit/log_spec.rb
82
89
  - spec/unit/new_spec.rb
83
- - spec/unit/percent_formatter_spec.rb
84
90
  - spec/unit/pipeline_spec.rb
91
+ - spec/unit/resize_spec.rb
92
+ - spec/unit/width_spec.rb
85
93
  - tasks/console.rake
86
94
  - tasks/coverage.rake
87
95
  - tasks/spec.rake
@@ -113,17 +121,22 @@ summary: A flexible progress bars drawing in terminal emulators.
113
121
  test_files:
114
122
  - spec/spec_helper.rb
115
123
  - spec/unit/advance_spec.rb
116
- - spec/unit/bar_formatter_spec.rb
124
+ - spec/unit/clear_spec.rb
117
125
  - spec/unit/complete_spec.rb
118
- - spec/unit/converter_spec.rb
119
- - spec/unit/current_formatter_spec.rb
126
+ - spec/unit/converter/to_bytes_spec.rb
127
+ - spec/unit/converter/to_time_spec.rb
120
128
  - spec/unit/custom_formatter_spec.rb
121
- - spec/unit/elapsed_formatter_spec.rb
122
- - spec/unit/estimated_formatter_spec.rb
129
+ - spec/unit/formatter/bar_spec.rb
130
+ - spec/unit/formatter/byte_spec.rb
131
+ - spec/unit/formatter/current_spec.rb
132
+ - spec/unit/formatter/elapsed_spec.rb
133
+ - spec/unit/formatter/estimated_spec.rb
134
+ - spec/unit/formatter/percent_spec.rb
123
135
  - spec/unit/frequency_spec.rb
124
136
  - spec/unit/hide_cursor_spec.rb
125
137
  - spec/unit/log_spec.rb
126
138
  - spec/unit/new_spec.rb
127
- - spec/unit/percent_formatter_spec.rb
128
139
  - spec/unit/pipeline_spec.rb
140
+ - spec/unit/resize_spec.rb
141
+ - spec/unit/width_spec.rb
129
142
  has_rdoc:
@@ -1,20 +0,0 @@
1
- # coding: utf-8
2
-
3
- module TTY
4
- class ProgressBar
5
- # Used by {Pipeline} to format :elapsed token
6
- #
7
- # @api private
8
- class ElapsedFormatter
9
- def initialize(progress, *args, &block)
10
- @progress = progress
11
- @converter = TTY::ProgressBar::Converter.new
12
- end
13
-
14
- def format(value)
15
- elapsed = (Time.now - @progress.start_at)
16
- value.gsub(/:elapsed/, @converter.to_time(elapsed))
17
- end
18
- end # ElapsedFormatter
19
- end # ProgressBar
20
- end # TTY
@@ -1,15 +0,0 @@
1
- # coding: utf-8
2
-
3
- module TTY
4
- class ProgressBar
5
- class TotalFormatter
6
- def initialize(progress, *args, &block)
7
- @progress = progress
8
- end
9
-
10
- def format(value)
11
- value.gsub(/:total/, @progress.total.to_s)
12
- end
13
- end # TotalFormatter
14
- end # ProgressBar
15
- end # TTY