progress 3.3.2 → 3.6.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.
data/lib/progress.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # encoding: UTF-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'singleton'
4
5
  require 'thread'
@@ -51,6 +52,7 @@ class Progress
51
52
  attr_reader :current
52
53
  attr_reader :title
53
54
  attr_accessor :note
55
+
54
56
  def initialize(total, title)
55
57
  if !total.is_a?(Numeric) && (title.nil? || title.is_a?(Numeric))
56
58
  total, title = title, total
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'progress'
2
4
 
3
5
  module ActiveRecord
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Progress
2
4
  # Repeatedly run block of code after time interval
3
5
  class Beeper
@@ -1,4 +1,7 @@
1
1
  # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'progress/elapsed_time'
2
5
 
3
6
  class Progress
4
7
  # Class methods of Progress
@@ -10,8 +13,9 @@ class Progress
10
13
  # start progress indication
11
14
  def start(total = nil, title = nil)
12
15
  init(total, title)
13
- print_message :force => true
16
+ print_message force: true
14
17
  return unless block_given?
18
+
15
19
  begin
16
20
  yield
17
21
  ensure
@@ -44,8 +48,9 @@ class Progress
44
48
  # stop progress
45
49
  def stop
46
50
  return unless running?
51
+
47
52
  if @levels.length == 1
48
- print_message :force => true, :finish => true
53
+ print_message force: true, finish: true
49
54
  stop_beeper
50
55
  end
51
56
  @levels.pop
@@ -59,6 +64,7 @@ class Progress
59
64
  # set note
60
65
  def note=(note)
61
66
  return unless running?
67
+
62
68
  @levels.last.note = note
63
69
  end
64
70
 
@@ -82,7 +88,7 @@ class Progress
82
88
  @highlight = true && value
83
89
  end
84
90
 
85
- # show progerss in terminal title
91
+ # show progress in terminal title
86
92
  def terminal_title?
87
93
  @terminal_title.nil? ? io_tty? : @terminal_title
88
94
  end
@@ -102,6 +108,15 @@ class Progress
102
108
  io.tty? || ENV['PROGRESS_TTY']
103
109
  end
104
110
 
111
+ # don't refresh progress (eta) periodically for the duration of the block
112
+ def without_beeper
113
+ old_state = @without_beeper
114
+ @without_beeper = true
115
+ yield
116
+ ensure
117
+ @without_beeper = old_state
118
+ end
119
+
105
120
  private
106
121
 
107
122
  attr_reader :eta
@@ -139,7 +154,7 @@ class Progress
139
154
 
140
155
  def start_beeper
141
156
  @beeper = Beeper.new(10) do
142
- print_message
157
+ print_message unless @without_beeper
143
158
  end
144
159
  end
145
160
 
@@ -152,14 +167,14 @@ class Progress
152
167
  end
153
168
 
154
169
  def time_to_print?
155
- !@next_time_to_print || @next_time_to_print <= Time.now
170
+ !@next_time_to_print || @next_time_to_print <= ElapsedTime.now
156
171
  end
157
172
 
158
173
  def print_message(options = {})
159
174
  force = options[:force]
160
175
  lock force do
161
176
  if force || time_to_print?
162
- @next_time_to_print = Time.now + 0.3
177
+ @next_time_to_print = ElapsedTime.now + 0.3
163
178
  restart_beeper
164
179
  io << message_for_output(options)
165
180
  end
@@ -169,7 +184,7 @@ class Progress
169
184
  def message_for_output(options)
170
185
  message = build_message(options)
171
186
 
172
- out = ''
187
+ out = ''.dup
173
188
  out << "\r" if stay_on_line?
174
189
  out << message
175
190
  out << "\e[K" if stay_on_line?
@@ -188,7 +203,7 @@ class Progress
188
203
 
189
204
  def build_message(options)
190
205
  current = 0
191
- message = @levels.reverse.map do |level|
206
+ reverse_parts = @levels.reverse.map do |level|
192
207
  current = level.to_f(current)
193
208
 
194
209
  part = current.zero? ? '......' : format('%5.1f%%', current * 100.0)
@@ -198,7 +213,8 @@ class Progress
198
213
  end
199
214
 
200
215
  level.title ? "#{level.title}: #{part}" : part
201
- end.reverse * ' > '
216
+ end
217
+ message = reverse_parts.reverse * ' > '
202
218
 
203
219
  if options[:finish]
204
220
  message << " (elapsed: #{eta.elapsed})"
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Progress
4
+ # Use Process.clock_gettime if available to get time more fitting to calculate elapsed time
5
+ module ElapsedTime
6
+ CLOCK_NAME = %w[
7
+ CLOCK_UPTIME_RAW
8
+ CLOCK_UPTIME
9
+ CLOCK_MONOTONIC_RAW
10
+ CLOCK_MONOTONIC
11
+ CLOCK_REALTIME
12
+ ].find{ |name| Process.const_defined?(name) }
13
+
14
+ CLOCK_ID = CLOCK_NAME && Process.const_get(CLOCK_NAME)
15
+
16
+ module_function
17
+
18
+ def now
19
+ if CLOCK_ID
20
+ Process.clock_gettime(CLOCK_ID)
21
+ else
22
+ Time.now.to_f
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'enumerator'
2
4
  require 'progress/with_progress'
3
5
 
data/lib/progress/eta.rb CHANGED
@@ -1,24 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'progress/elapsed_time'
4
+
1
5
  class Progress
2
6
  # Estimate time of arrival
3
7
  class Eta
4
8
  def initialize
5
- @started_at = Time.now
9
+ @started_at = ElapsedTime.now
6
10
  end
7
11
 
8
12
  def left(completed)
9
13
  seconds = seconds_left(completed)
10
14
  return unless seconds && seconds > 0
15
+
11
16
  seconds_to_string(seconds)
12
17
  end
13
18
 
14
19
  def elapsed
15
- seconds_to_string(Time.now - @started_at)
20
+ seconds_to_string(ElapsedTime.now - @started_at)
16
21
  end
17
22
 
18
23
  private
19
24
 
20
25
  def seconds_to_string(seconds)
21
26
  return unless seconds
27
+
22
28
  case seconds
23
29
  when 0...60
24
30
  format '%.0fs', seconds
@@ -32,8 +38,9 @@ class Progress
32
38
  end
33
39
 
34
40
  def seconds_left(completed)
35
- now = Time.now
41
+ now = ElapsedTime.now
36
42
  return unless completed > 0 && now - @started_at >= 1
43
+
37
44
  current_eta = @started_at + (now - @started_at) / completed
38
45
  @left = if @left
39
46
  @left + (current_eta - @left) * (1 + completed) * 0.5
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'progress'
2
4
 
3
5
  # Add times_with_progress method to Integer
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'progress'
2
4
 
3
5
  # Add Progress method as alias to Progress.start
4
6
  module Kernel
7
+ private
8
+
5
9
  define_method :Progress do |*args, &block|
6
10
  Progress.start(*args, &block)
7
11
  end
8
- private :Progress
9
12
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'progress'
2
4
  require 'stringio'
3
5
 
@@ -64,8 +66,6 @@ class Progress
64
66
  run_for_string(method, *args, &block)
65
67
  when io?
66
68
  run_for_io(method, *args, &block)
67
- when defined?(CSV::Reader) && @enum.is_a?(CSV::Reader)
68
- run_for_ruby18_csv(method, *args, &block)
69
69
  else
70
70
  run_with_length(@enum, enum_length(@enum), method, *args, &block)
71
71
  end
@@ -88,13 +88,6 @@ class Progress
88
88
  end
89
89
  end
90
90
 
91
- def run_for_ruby18_csv(method, *args, &block)
92
- warn "Progress: #{@enum.class} doesn't expose IO, collecting elements"
93
- with_substitute(@enum.to_a) do |lines|
94
- run_with_length(lines, lines.length, method, *args, &block)
95
- end
96
- end
97
-
98
91
  def run_without_block(enum, method, *args)
99
92
  Progress.start(@title) do
100
93
  Progress.step do
@@ -103,22 +96,22 @@ class Progress
103
96
  end
104
97
  end
105
98
 
106
- def run_with_length(enum, length, method, *args, &block)
99
+ def run_with_length(enum, length, method, *args)
107
100
  Progress.start(@title, length) do
108
101
  enum.send(method, *args) do |*block_args|
109
102
  Progress.step do
110
- block.call(*block_args)
103
+ yield(*block_args)
111
104
  end
112
105
  end
113
106
  end
114
107
  end
115
108
 
116
- def run_with_pos(io, method, *args, &block)
109
+ def run_with_pos(io, method, *args)
117
110
  size = io.respond_to?(:size) ? io.size : io.stat.size
118
111
  Progress.start(@title, size) do
119
112
  io.send(method, *args) do |*block_args|
120
113
  Progress.set(io.pos) do
121
- block.call(*block_args)
114
+ yield(*block_args)
122
115
  end
123
116
  end
124
117
  end
@@ -140,6 +133,7 @@ class Progress
140
133
  false
141
134
  rescue Errno::EPIPE
142
135
  raise unless defined?(JRUBY_VERSION)
136
+
143
137
  false
144
138
  end
145
139
 
data/progress.gemspec CHANGED
@@ -2,13 +2,20 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'progress'
5
- s.version = '3.3.2'
5
+ s.version = '3.6.0'
6
6
  s.summary = %q{Show progress of long running tasks}
7
- s.homepage = "http://github.com/toy/#{s.name}"
7
+ s.homepage = "https://github.com/toy/#{s.name}"
8
8
  s.authors = ['Ivan Kuchin']
9
9
  s.license = 'MIT'
10
10
 
11
- s.rubyforge_project = s.name
11
+ s.required_ruby_version = '>= 1.9.3'
12
+
13
+ s.metadata = {
14
+ 'bug_tracker_uri' => "https://github.com/toy/#{s.name}/issues",
15
+ 'changelog_uri' => "https://github.com/toy/#{s.name}/blob/master/CHANGELOG.markdown",
16
+ 'documentation_uri' => "https://www.rubydoc.info/gems/#{s.name}/#{s.version}",
17
+ 'source_code_uri' => "https://github.com/toy/#{s.name}",
18
+ }
12
19
 
13
20
  s.files = `git ls-files`.split("\n")
14
21
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -16,7 +23,7 @@ Gem::Specification.new do |s|
16
23
  s.require_paths = %w[lib]
17
24
 
18
25
  s.add_development_dependency 'rspec', '~> 3.0'
19
- if RUBY_VERSION >= '2.0'
20
- s.add_development_dependency 'rubocop', '~> 0.27'
26
+ if RUBY_VERSION >= '2.4'
27
+ s.add_development_dependency 'rubocop', '~> 1.0'
21
28
  end
22
29
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'progress/elapsed_time'
4
+
5
+ describe Progress::ElapsedTime do
6
+ let(:timeout){ 0.01 }
7
+
8
+ describe '.now' do
9
+ it 'returns incrementing value' do
10
+ expect{ sleep timeout }.to change{ described_class.now }.by_at_least(timeout)
11
+ end
12
+ end
13
+ end
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rspec'
2
4
  require 'progress'
3
5
  require 'tempfile'
4
6
  require 'shellwords'
5
7
  require 'csv'
8
+ require 'set'
6
9
 
7
10
  describe Progress do
8
11
  before do
@@ -13,7 +16,7 @@ describe Progress do
13
16
  allow(Progress).to receive(:start_beeper)
14
17
  allow(Progress).to receive(:time_to_print?).and_return(true)
15
18
 
16
- eta = instance_double(Progress::Eta, :left => nil, :elapsed => '0s')
19
+ eta = instance_double(Progress::Eta, left: nil, elapsed: '0s')
17
20
  allow(Progress).to receive(:eta).and_return(eta)
18
21
  end
19
22
 
@@ -84,7 +87,7 @@ describe Progress do
84
87
 
85
88
  describe 'with_progress' do
86
89
  it 'returns with block same as when called with each' do
87
- expect(enum.with_progress{}).to eq(enum.with_progress.each{})
90
+ expect(enum.with_progress(&:to_s)).to eq(enum.with_progress.each(&:to_s))
88
91
  end
89
92
 
90
93
  it 'does not break each' do
@@ -178,7 +181,7 @@ describe Progress do
178
181
  describe enum.class do
179
182
  it 'calls each only once' do
180
183
  expect(enum).to receive(:each).once.and_call_original
181
- expect(enum.with_progress.each{}).to eq(enum)
184
+ expect(enum.with_progress.each(&:to_s)).to eq(enum)
182
185
  end
183
186
 
184
187
  include_examples 'yielding', enum
@@ -187,13 +190,13 @@ describe Progress do
187
190
 
188
191
  [
189
192
  100.times,
190
- 'a'..'z',
193
+ ('a'..'z').dup,
191
194
  ].each do |enum|
192
195
  describe enum.class do
193
196
  it 'calls each twice' do
194
- enum_each = enum.each{}
197
+ enum_each = enum.each(&:to_s)
195
198
  expect(enum).to receive(:each).at_most(:twice).and_call_original
196
- expect(enum.with_progress.each{}).to eq(enum_each)
199
+ expect(enum.with_progress.each(&:to_s)).to eq(enum_each)
197
200
  end
198
201
 
199
202
  include_examples 'yielding', enum
@@ -202,7 +205,7 @@ describe Progress do
202
205
 
203
206
  describe String do
204
207
  it 'calls each only once on StringIO' do
205
- enum = "a\nb\nc"
208
+ enum = "a\nb\nc".dup
206
209
  expect(enum).not_to receive(:each)
207
210
  io = StringIO.new(enum)
208
211
  expect(StringIO).to receive(:new).with(enum).and_return(io)
@@ -210,7 +213,7 @@ describe Progress do
210
213
 
211
214
  with_progress = Progress::WithProgress.new(enum)
212
215
  expect(with_progress).not_to receive(:warn)
213
- expect(with_progress.each{}).to eq(enum)
216
+ expect(with_progress.each(&:to_s)).to eq(enum)
214
217
  end
215
218
 
216
219
  it 'yields same lines' do
@@ -231,18 +234,18 @@ describe Progress do
231
234
 
232
235
  with_progress = enum.with_progress
233
236
  expect(with_progress).not_to receive(:warn)
234
- expect(with_progress.each{}).to eq(enum)
237
+ expect(with_progress.each(&:to_s)).to eq(enum)
235
238
  end
236
239
  end
237
240
 
238
241
  it 'calls each only once for Tempfile' do
239
242
  enum = Tempfile.open('progress')
240
- enum_each = enum.each{} # returns underlying File
243
+ enum_each = enum.each(&:to_s) # returns underlying File
241
244
  expect(enum_each).to receive(:each).once.and_call_original
242
245
 
243
246
  with_progress = enum.with_progress
244
247
  expect(with_progress).not_to receive(:warn)
245
- expect(with_progress.each{}).to eq(enum_each)
248
+ expect(with_progress.each(&:to_s)).to eq(enum_each)
246
249
  end
247
250
 
248
251
  it 'calls each only once for IO and shows warning' do
@@ -251,7 +254,7 @@ describe Progress do
251
254
 
252
255
  with_progress = enum.with_progress
253
256
  expect(with_progress).to receive(:warn)
254
- expect(with_progress.each{}).to eq(enum)
257
+ expect(with_progress.each(&:to_s)).to eq(enum)
255
258
  end
256
259
 
257
260
  [
@@ -277,7 +280,8 @@ describe Progress do
277
280
 
278
281
  with_progress = enum.with_progress
279
282
  expect(with_progress).not_to receive(:warn)
280
- expect(with_progress.each{}).to eq(nil)
283
+ expect(with_progress.each(&:to_s)).
284
+ to eq(CSV.open('spec/test.csv').each(&:to_s))
281
285
  end
282
286
  else
283
287
  it 'calls each only once for CSV and shows warning' do
@@ -286,7 +290,7 @@ describe Progress do
286
290
 
287
291
  with_progress = enum.with_progress
288
292
  expect(with_progress).to receive(:warn)
289
- expect(with_progress.each{}).to eq(enum)
293
+ expect(with_progress.each(&:to_s)).to eq(enum)
290
294
  end
291
295
  end
292
296
 
@@ -331,41 +335,41 @@ describe Progress do
331
335
  Progress.step 2, 'simle'
332
336
 
333
337
  Progress.step 2, 'times' do
334
- 3.times.with_progress{}
338
+ 3.times.with_progress(&:to_s)
335
339
  end
336
340
 
337
341
  Progress.step 'enum' do
338
- 3.times.to_a.with_progress{}
342
+ 3.times.to_a.with_progress(&:to_s)
339
343
  end
340
344
  end
341
345
  end
342
346
 
343
- def title(s)
344
- "\e]0;#{s}\a"
347
+ def title(str)
348
+ "\e]0;#{str}\a"
345
349
  end
346
350
 
347
- def hl(s)
348
- "\e[1m#{s}\e[0m"
351
+ def hl(str)
352
+ "\e[1m#{str}\e[0m"
349
353
  end
350
354
 
351
- def unhl(s)
352
- s.gsub(/\e\[\dm/, '')
355
+ def unhl(str)
356
+ str.gsub(/\e\[\dm/, '')
353
357
  end
354
358
 
355
- def on_line(s)
356
- "\r" + s + "\e[K"
359
+ def on_line(str)
360
+ "\r#{str}\e[K"
357
361
  end
358
362
 
359
- def line(s)
360
- s + "\n"
363
+ def line(str)
364
+ "#{str}\n"
361
365
  end
362
366
 
363
- def on_line_n_title(s)
364
- [on_line(s), title(unhl(s))]
367
+ def on_line_n_title(str)
368
+ [on_line(str), title(unhl(str))]
365
369
  end
366
370
 
367
- def line_n_title(s)
368
- [line(s), title(unhl(s))]
371
+ def line_n_title(str)
372
+ [line(str), title(unhl(str))]
369
373
  end
370
374
 
371
375
  it 'produces valid output when staying on line' do
@@ -387,7 +391,7 @@ describe Progress do
387
391
  on_line_n_title("Test: #{hl ' 93.3%'} > #{hl ' 66.7%'}"),
388
392
  on_line_n_title('Test: 100.0% > 100.0%'),
389
393
  on_line_n_title('Test: 100.0% - enum'),
390
- on_line('Test: 100.0% (elapsed: 0s) - enum') + "\n",
394
+ "#{on_line('Test: 100.0% (elapsed: 0s) - enum')}\n",
391
395
  title(''),
392
396
  ].flatten.join)
393
397
  end
@@ -423,7 +427,7 @@ describe Progress do
423
427
  let(:reference_output) do
424
428
  stub_progress_io(reference_io = StringIO.new)
425
429
  count_a.times.with_progress('Test') do
426
- count_b.times.with_progress{}
430
+ count_b.times.with_progress(&:to_s)
427
431
  end
428
432
  reference_io.string
429
433
  end
@@ -465,7 +469,7 @@ describe Progress do
465
469
 
466
470
  it 'outputs same when called using with_progress on list' do
467
471
  count_a.times.to_a.with_progress('Test') do
468
- count_b.times.to_a.with_progress{}
472
+ count_b.times.to_a.with_progress(&:to_s)
469
473
  end
470
474
  expect(io.string).to eq(reference_output)
471
475
  end
@@ -522,5 +526,50 @@ describe Progress do
522
526
  it{ is_expected.to be_truthy }
523
527
  end
524
528
  end
529
+
530
+ describe '.without_beeper' do
531
+ before do
532
+ allow(Progress).to receive(:start_beeper).and_call_original
533
+ allow(Progress::Beeper).to receive(:new) do |*, &block|
534
+ @block = block
535
+ double(restart: nil, stop: nil)
536
+ end
537
+ expect(Progress).to receive(:print_message).exactly(print_times).times
538
+ end
539
+
540
+ context 'when not used' do
541
+ let(:print_times){ 3 }
542
+
543
+ it 'allows beeper to print progress' do
544
+ Progress.start do
545
+ @block.call
546
+ end
547
+ end
548
+ end
549
+
550
+ context 'when used around progress block' do
551
+ let(:print_times){ 2 }
552
+
553
+ it 'stops beeper from printing progress' do
554
+ Progress.without_beeper do
555
+ Progress.start do
556
+ @block.call
557
+ end
558
+ end
559
+ end
560
+ end
561
+
562
+ context 'when used inside progress block' do
563
+ let(:print_times){ 2 }
564
+
565
+ it 'stops beeper from printing progress' do
566
+ Progress.start do
567
+ Progress.without_beeper do
568
+ @block.call
569
+ end
570
+ end
571
+ end
572
+ end
573
+ end
525
574
  end
526
575
  end