tty-progressbar 0.2.0 → 0.3.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.
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