progress 3.3.2 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
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