progress 3.0.2 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.rubocop.yml +65 -0
- data/.travis.yml +13 -2
- data/Gemfile +1 -1
- data/README.markdown +6 -2
- data/lib/progress.rb +12 -221
- data/lib/progress/active_record.rb +3 -5
- data/lib/progress/beeper.rb +1 -0
- data/lib/progress/class_methods.rb +214 -0
- data/lib/progress/enumerable.rb +3 -1
- data/lib/progress/eta.rb +20 -22
- data/lib/progress/integer.rb +1 -0
- data/lib/progress/kernel.rb +9 -0
- data/lib/progress/with_progress.rb +91 -43
- data/progress.gemspec +5 -2
- data/spec/progress_spec.rb +246 -179
- data/spec/test.csv +8 -0
- metadata +25 -6
data/lib/progress/enumerable.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'enumerator'
|
2
2
|
require 'progress/with_progress'
|
3
3
|
|
4
|
+
# Add with_progress method to Enumerable
|
4
5
|
module Enumerable
|
5
6
|
# run any Enumerable method with progress
|
6
|
-
# methods which don't necessarily go through all items (like find, any? or
|
7
|
+
# methods which don't necessarily go through all items (like find, any? or
|
8
|
+
# all?) will not show 100%
|
7
9
|
# ==== Example
|
8
10
|
# [1, 2, 3].with_progress('Numbers').each do |number|
|
9
11
|
# # code
|
data/lib/progress/eta.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
class Progress
|
2
|
+
# Estimate time of arrival
|
2
3
|
class Eta
|
3
4
|
def initialize
|
4
5
|
@started_at = Time.now
|
@@ -6,9 +7,8 @@ class Progress
|
|
6
7
|
|
7
8
|
def left(completed)
|
8
9
|
seconds = seconds_left(completed)
|
9
|
-
|
10
|
-
|
11
|
-
end
|
10
|
+
return unless seconds && seconds > 0
|
11
|
+
seconds_to_string(seconds)
|
12
12
|
end
|
13
13
|
|
14
14
|
def elapsed
|
@@ -18,31 +18,29 @@ class Progress
|
|
18
18
|
private
|
19
19
|
|
20
20
|
def seconds_to_string(seconds)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
21
|
+
return unless seconds
|
22
|
+
case seconds
|
23
|
+
when 0...60
|
24
|
+
format '%.0fs', seconds
|
25
|
+
when 60...3600
|
26
|
+
format '%.1fm', seconds / 60
|
27
|
+
when 3600...86_400
|
28
|
+
format '%.1fh', seconds / 3600
|
29
|
+
else
|
30
|
+
format '%.1fd', seconds / 86_400
|
32
31
|
end
|
33
32
|
end
|
34
33
|
|
35
34
|
def seconds_left(completed)
|
36
35
|
now = Time.now
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
@left - now
|
36
|
+
return unless completed > 0 && now - @started_at >= 1
|
37
|
+
current_eta = @started_at + (now - @started_at) / completed
|
38
|
+
@left = if @left
|
39
|
+
@left + (current_eta - @left) * (1 + completed) * 0.5
|
40
|
+
else
|
41
|
+
current_eta
|
45
42
|
end
|
43
|
+
@left - now
|
46
44
|
end
|
47
45
|
end
|
48
46
|
end
|
data/lib/progress/integer.rb
CHANGED
@@ -1,30 +1,37 @@
|
|
1
1
|
require 'progress'
|
2
|
+
require 'stringio'
|
2
3
|
|
3
4
|
class Progress
|
5
|
+
# Handling with_progress
|
4
6
|
class WithProgress
|
5
|
-
attr_reader :
|
7
|
+
attr_reader :enum, :title
|
8
|
+
alias_method :enumerable, :enum
|
9
|
+
|
10
|
+
# If block given run each on instance otherwise return instance
|
11
|
+
def self.new(*args, &block)
|
12
|
+
block ? super.each(&block) : super
|
13
|
+
end
|
6
14
|
|
7
15
|
# initialize with object responding to each, title and optional length
|
8
16
|
# if block is provided, it is passed to each
|
9
|
-
def initialize(
|
10
|
-
@
|
11
|
-
each(&block) if block
|
17
|
+
def initialize(enum, title = nil, length = nil)
|
18
|
+
@enum, @title, @length = enum, title, length
|
12
19
|
end
|
13
20
|
|
14
21
|
# returns self but changes title
|
15
22
|
def with_progress(title = nil, length = nil, &block)
|
16
|
-
self.class.new(@
|
23
|
+
self.class.new(@enum, title, length || @length, &block)
|
17
24
|
end
|
18
25
|
|
19
26
|
# befriend with in_threads gem
|
20
27
|
def in_threads(*args, &block)
|
21
|
-
@
|
28
|
+
@enum.in_threads(*args).with_progress(@title, @length, &block)
|
22
29
|
rescue
|
23
30
|
super
|
24
31
|
end
|
25
32
|
|
26
|
-
def respond_to?(
|
27
|
-
enumerable_method?(method) || super
|
33
|
+
def respond_to?(method, include_private = false)
|
34
|
+
enumerable_method?(method) || super
|
28
35
|
end
|
29
36
|
|
30
37
|
def method_missing(method, *args, &block)
|
@@ -42,54 +49,95 @@ class Progress
|
|
42
49
|
end
|
43
50
|
|
44
51
|
def run(method, *args, &block)
|
45
|
-
|
52
|
+
case
|
53
|
+
when !block
|
54
|
+
run_without_block(@enum, method, *args)
|
46
55
|
when @length
|
47
|
-
@
|
48
|
-
when
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
lines = []
|
55
|
-
@enumerable.each{ |line| lines << line }
|
56
|
-
lines
|
56
|
+
run_with_length(@enum, @length, method, *args, &block)
|
57
|
+
when @enum.is_a?(String)
|
58
|
+
run_for_string(method, *args, &block)
|
59
|
+
when io?
|
60
|
+
run_for_io(method, *args, &block)
|
61
|
+
when defined?(CSV::Reader) && @enum.is_a?(CSV::Reader)
|
62
|
+
run_for_ruby18_csv(method, *args, &block)
|
57
63
|
else
|
58
|
-
@
|
64
|
+
run_with_length(@enum, enum_length(@enum), method, *args, &block)
|
59
65
|
end
|
66
|
+
end
|
60
67
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
when enumerable.respond_to?(:size)
|
65
|
-
enumerable.size
|
66
|
-
when enumerable.respond_to?(:length)
|
67
|
-
enumerable.length
|
68
|
-
else
|
69
|
-
enumerable.count
|
68
|
+
def run_for_string(method, *args, &block)
|
69
|
+
with_substitute(StringIO.new(@enum)) do |io|
|
70
|
+
run_with_pos(io, method, *args, &block)
|
70
71
|
end
|
72
|
+
end
|
71
73
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
74
|
+
def run_for_io(method, *args, &block)
|
75
|
+
if io_pos?
|
76
|
+
run_with_pos(@enum, method, *args, &block)
|
77
|
+
else
|
78
|
+
warn "Progress: can't get #{@enum.class} pos, collecting elements"
|
79
|
+
with_substitute(@enum.to_a) do |lines|
|
80
|
+
run_with_length(lines, lines.length, method, *args, &block)
|
79
81
|
end
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def run_for_ruby18_csv(method, *args, &block)
|
86
|
+
warn "Progress: #{@enum.class} doesn't expose IO, collecting elements"
|
87
|
+
with_substitute(@enum.to_a) do |lines|
|
88
|
+
run_with_length(lines, lines.length, method, *args, &block)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def run_without_block(enum, method, *args)
|
93
|
+
Progress.start(@title) do
|
94
|
+
Progress.step do
|
95
|
+
enum.send(method, *args)
|
84
96
|
end
|
85
|
-
|
86
|
-
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def run_with_length(enum, length, method, *args, &block)
|
101
|
+
Progress.start(@title, length) do
|
102
|
+
enum.send(method, *args) do |*block_args|
|
87
103
|
Progress.step do
|
88
|
-
|
104
|
+
block.call(*block_args)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def run_with_pos(io, method, *args, &block)
|
111
|
+
size = io.respond_to?(:size) ? io.size : io.stat.size
|
112
|
+
Progress.start(@title, size) do
|
113
|
+
io.send(method, *args) do |*block_args|
|
114
|
+
Progress.set(io.pos) do
|
115
|
+
block.call(*block_args)
|
89
116
|
end
|
90
117
|
end
|
91
118
|
end
|
92
119
|
end
|
93
120
|
|
121
|
+
def with_substitute(enum)
|
122
|
+
result = yield enum
|
123
|
+
result.eql?(enum) ? @enum : result
|
124
|
+
end
|
125
|
+
|
126
|
+
def io?
|
127
|
+
@enum.respond_to?(:pos) &&
|
128
|
+
(@enum.respond_to?(:size) || @enum.respond_to?(:stat))
|
129
|
+
end
|
130
|
+
|
131
|
+
def io_pos?
|
132
|
+
@enum.pos; true
|
133
|
+
rescue Errno::ESPIPE
|
134
|
+
false
|
135
|
+
end
|
136
|
+
|
137
|
+
def enum_length(enum)
|
138
|
+
enum.respond_to?(:size) && enum.size ||
|
139
|
+
enum.respond_to?(:length) && enum.length ||
|
140
|
+
enum.count
|
141
|
+
end
|
94
142
|
end
|
95
143
|
end
|
data/progress.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'progress'
|
5
|
-
s.version = '3.0
|
5
|
+
s.version = '3.1.0'
|
6
6
|
s.summary = %q{Show progress of long running tasks}
|
7
7
|
s.homepage = "http://github.com/toy/#{s.name}"
|
8
8
|
s.authors = ['Ivan Kuchin']
|
@@ -15,5 +15,8 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
16
|
s.require_paths = %w[lib]
|
17
17
|
|
18
|
-
s.add_development_dependency 'rspec'
|
18
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
19
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9.3')
|
20
|
+
s.add_development_dependency 'rubocop', '~> 0.27'
|
21
|
+
end
|
19
22
|
end
|
data/spec/progress_spec.rb
CHANGED
@@ -1,55 +1,56 @@
|
|
1
|
-
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
2
1
|
require 'rspec'
|
3
2
|
require 'progress'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'shellwords'
|
5
|
+
require 'csv'
|
4
6
|
|
5
7
|
describe Progress do
|
6
|
-
|
7
8
|
before do
|
8
9
|
Progress.stay_on_line = true
|
9
10
|
Progress.highlight = true
|
10
|
-
Progress.
|
11
|
+
Progress.terminal_title = true
|
11
12
|
|
12
|
-
Progress.
|
13
|
-
Progress.
|
14
|
-
Progress.stub(:eta)
|
15
|
-
Progress.stub(:elapsed).and_return('0s')
|
16
|
-
end
|
13
|
+
allow(Progress).to receive(:start_beeper)
|
14
|
+
allow(Progress).to receive(:time_to_print?).and_return(true)
|
17
15
|
|
18
|
-
|
16
|
+
eta = instance_double(Progress::Eta, :left => nil, :elapsed => '0s')
|
17
|
+
allow(Progress).to receive(:eta).and_return(eta)
|
18
|
+
end
|
19
19
|
|
20
|
+
describe 'integrity' do
|
20
21
|
before do
|
21
|
-
|
22
|
-
Progress.
|
22
|
+
io = double(:<< => nil, :tty? => true)
|
23
|
+
allow(Progress).to receive(:io).and_return(io)
|
23
24
|
end
|
24
25
|
|
25
|
-
it
|
26
|
-
Progress.start('Test') do
|
26
|
+
it 'returns result from start block' do
|
27
|
+
expect(Progress.start('Test') do
|
27
28
|
'test'
|
28
|
-
end.
|
29
|
+
end).to eq('test')
|
29
30
|
end
|
30
31
|
|
31
|
-
it
|
32
|
+
it 'returns result from step block' do
|
32
33
|
Progress.start 1 do
|
33
|
-
Progress.step{ 'test' }.
|
34
|
+
expect(Progress.step{ 'test' }).to eq('test')
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
37
|
-
it
|
38
|
+
it 'returns result from set block' do
|
38
39
|
Progress.start 1 do
|
39
|
-
Progress.set(1){ 'test' }.
|
40
|
+
expect(Progress.set(1){ 'test' }).to eq('test')
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
43
|
-
it
|
44
|
-
[1, 2, 3].with_progress.map do |a|
|
44
|
+
it 'returns result from nested block' do
|
45
|
+
expect([1, 2, 3].with_progress.map do |a|
|
45
46
|
[1, 2, 3].with_progress.map do |b|
|
46
47
|
a * b
|
47
48
|
end
|
48
|
-
end.
|
49
|
+
end).to eq([[1, 2, 3], [2, 4, 6], [3, 6, 9]])
|
49
50
|
end
|
50
51
|
|
51
|
-
it
|
52
|
-
|
52
|
+
it 'does not raise errors on extra step or stop' do
|
53
|
+
expect do
|
53
54
|
3.times_with_progress do
|
54
55
|
Progress.start 'simple' do
|
55
56
|
Progress.step
|
@@ -61,180 +62,235 @@ describe Progress do
|
|
61
62
|
end
|
62
63
|
Progress.step
|
63
64
|
Progress.stop
|
64
|
-
|
65
|
+
end.not_to raise_error
|
65
66
|
end
|
66
67
|
|
67
68
|
describe Enumerable do
|
69
|
+
let(:enum){ 0...1000 }
|
68
70
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
describe "with_progress" do
|
71
|
+
describe 'with_progress' do
|
72
|
+
it 'returns with block same as when called with each' do
|
73
|
+
expect(enum.with_progress{}).to eq(enum.with_progress.each{})
|
74
|
+
end
|
74
75
|
|
75
|
-
it
|
76
|
-
reference =
|
77
|
-
|
78
|
-
n.
|
76
|
+
it 'does not break each' do
|
77
|
+
reference = enum.each
|
78
|
+
enum.with_progress.each do |n|
|
79
|
+
expect(n).to eq(reference.next)
|
79
80
|
end
|
80
|
-
|
81
|
+
expect{ reference.next }.to raise_error(StopIteration)
|
81
82
|
end
|
82
83
|
|
83
|
-
it
|
84
|
+
it 'does not break find' do
|
84
85
|
default = proc{ 'default' }
|
85
|
-
|
86
|
-
|
87
|
-
|
86
|
+
expect(enum.with_progress.find{ |n| n == 100 }).
|
87
|
+
to eq(enum.find{ |n| n == 100 })
|
88
|
+
expect(enum.with_progress.find{ |n| n == 10_000 }).
|
89
|
+
to eq(enum.find{ |n| n == 10_000 })
|
90
|
+
expect(enum.with_progress.find(default){ |n| n == 10_000 }).
|
91
|
+
to eq(enum.find(default){ |n| n == 10_000 })
|
88
92
|
end
|
89
93
|
|
90
|
-
it
|
91
|
-
|
94
|
+
it 'does not break map' do
|
95
|
+
expect(enum.with_progress.map{ |n| n**2 }).to eq(enum.map{ |n| n**2 })
|
92
96
|
end
|
93
97
|
|
94
|
-
it
|
95
|
-
|
98
|
+
it 'does not break grep' do
|
99
|
+
expect(enum.with_progress.grep(100)).to eq(enum.grep(100))
|
96
100
|
end
|
97
101
|
|
98
|
-
it
|
99
|
-
reference =
|
100
|
-
|
101
|
-
values.
|
102
|
+
it 'does not break each_cons' do
|
103
|
+
reference = enum.each_cons(3)
|
104
|
+
enum.with_progress.each_cons(3) do |values|
|
105
|
+
expect(values).to eq(reference.next)
|
102
106
|
end
|
103
|
-
|
107
|
+
expect{ reference.next }.to raise_error(StopIteration)
|
104
108
|
end
|
105
109
|
|
106
|
-
describe
|
107
|
-
|
108
|
-
|
109
|
-
wp
|
110
|
-
proc{ wp.with_progress('world') }.should_not change(wp, :title)
|
110
|
+
describe 'with_progress.with_progress' do
|
111
|
+
it 'does not change existing instance' do
|
112
|
+
wp = enum.with_progress('hello')
|
113
|
+
expect{ wp.with_progress('world') }.not_to change(wp, :title)
|
111
114
|
end
|
112
115
|
|
113
|
-
it
|
114
|
-
wp =
|
116
|
+
it 'returns new instance with different title' do
|
117
|
+
wp = enum.with_progress('hello')
|
115
118
|
wp_wp = wp.with_progress('world')
|
116
|
-
wp.title.
|
117
|
-
wp_wp.title.
|
118
|
-
wp_wp.
|
119
|
-
wp_wp.enumerable.
|
119
|
+
expect(wp.title).to eq('hello')
|
120
|
+
expect(wp_wp.title).to eq('world')
|
121
|
+
expect(wp_wp).not_to eq(wp)
|
122
|
+
expect(wp_wp.enumerable).to eq(wp.enumerable)
|
120
123
|
end
|
121
|
-
|
122
124
|
end
|
123
125
|
|
124
|
-
describe
|
126
|
+
describe 'collections' do
|
127
|
+
[
|
128
|
+
[1, 2, 3],
|
129
|
+
{1 => 1, 2 => 2, 3 => 3},
|
130
|
+
[1, 2, 3].to_set,
|
131
|
+
].each do |enum|
|
132
|
+
it "calls each only once for #{enum.class}" do
|
133
|
+
expect(enum).to receive(:each).once.and_call_original
|
134
|
+
expect(enum.with_progress.each{}).to eq(enum)
|
135
|
+
end
|
125
136
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
result = yield
|
130
|
-
$VERBOSE = verbosity
|
131
|
-
result
|
137
|
+
it "yields same objects for #{enum.class}" do
|
138
|
+
expect(enum.with_progress.entries).to eq(enum.entries)
|
139
|
+
end
|
132
140
|
end
|
133
141
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
142
|
+
[
|
143
|
+
100.times,
|
144
|
+
'a'..'z',
|
145
|
+
].each do |enum|
|
146
|
+
it "calls each twice for #{enum.class}" do
|
147
|
+
enum_each = enum.each{}
|
148
|
+
expect(enum).to receive(:each).at_most(:twice).and_call_original
|
149
|
+
expect(enum.with_progress.each{}).to eq(enum_each)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "yields same objects for #{enum.class}" do
|
153
|
+
expect(enum.with_progress.entries).to eq(enum.entries)
|
154
|
+
end
|
138
155
|
end
|
156
|
+
end
|
139
157
|
|
140
|
-
|
141
|
-
|
142
|
-
enum
|
143
|
-
enum.
|
158
|
+
describe String do
|
159
|
+
it 'calls each only once on StringIO' do
|
160
|
+
enum = "a\nb\nc"
|
161
|
+
expect(enum).not_to receive(:each)
|
162
|
+
io = StringIO.new(enum)
|
163
|
+
expect(StringIO).to receive(:new).with(enum).and_return(io)
|
164
|
+
expect(io).to receive(:each).once.and_call_original
|
165
|
+
|
166
|
+
with_progress = Progress::WithProgress.new(enum)
|
167
|
+
expect(with_progress).not_to receive(:warn)
|
168
|
+
expect(with_progress.each{}).to eq(enum)
|
144
169
|
end
|
145
170
|
|
146
|
-
it
|
147
|
-
enum =
|
148
|
-
|
149
|
-
enum.
|
171
|
+
it 'yields same lines' do
|
172
|
+
enum = "a\nb\nc"
|
173
|
+
lines = []
|
174
|
+
Progress::WithProgress.new(enum).each{ |line| lines << line }
|
175
|
+
expect(lines).to eq(enum.lines.to_a)
|
150
176
|
end
|
177
|
+
end
|
151
178
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
179
|
+
describe IO do
|
180
|
+
[
|
181
|
+
File.open(__FILE__),
|
182
|
+
StringIO.new(File.read(__FILE__)),
|
183
|
+
].each do |enum|
|
184
|
+
it "calls each only once for #{enum.class}" do
|
185
|
+
expect(enum).to receive(:each).once.and_call_original
|
186
|
+
|
187
|
+
with_progress = enum.with_progress
|
188
|
+
expect(with_progress).not_to receive(:warn)
|
189
|
+
expect(with_progress.each{}).to eq(enum)
|
159
190
|
end
|
160
191
|
end
|
161
192
|
|
162
|
-
it
|
163
|
-
enum =
|
164
|
-
enum.
|
165
|
-
|
166
|
-
|
193
|
+
it 'calls each only once for Tempfile' do
|
194
|
+
enum = Tempfile.open('progress')
|
195
|
+
enum_each = enum.each{} # returns underlying File
|
196
|
+
expect(enum_each).to receive(:each).once.and_call_original
|
197
|
+
|
198
|
+
with_progress = enum.with_progress
|
199
|
+
expect(with_progress).not_to receive(:warn)
|
200
|
+
expect(with_progress.each{}).to eq(enum_each)
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'calls each only once for IO and shows warning' do
|
204
|
+
enum = IO.popen("cat #{__FILE__.shellescape}")
|
205
|
+
expect(enum).to receive(:each).once.and_call_original
|
206
|
+
|
207
|
+
with_progress = enum.with_progress
|
208
|
+
expect(with_progress).to receive(:warn)
|
209
|
+
expect(with_progress.each{}).to eq(enum)
|
210
|
+
end
|
211
|
+
|
212
|
+
[
|
213
|
+
File.open(__FILE__),
|
214
|
+
StringIO.new(File.read(__FILE__)),
|
215
|
+
Tempfile.open('progress').tap do |f|
|
216
|
+
f.write(File.read(__FILE__))
|
217
|
+
f.rewind
|
218
|
+
end,
|
219
|
+
IO.popen("cat #{__FILE__.shellescape}"),
|
220
|
+
].each do |enum|
|
221
|
+
it "yields same lines for #{enum.class}" do
|
222
|
+
expect(enum.with_progress.entries).to eq(File.readlines(__FILE__))
|
167
223
|
end
|
168
224
|
end
|
225
|
+
end
|
169
226
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
enum.
|
227
|
+
describe CSV do
|
228
|
+
if CSV.method_defined?(:pos)
|
229
|
+
it 'calls each only once for CSV' do
|
230
|
+
enum = CSV.open('spec/test.csv')
|
231
|
+
expect(enum).to receive(:each).once.and_call_original
|
232
|
+
|
233
|
+
with_progress = enum.with_progress
|
234
|
+
expect(with_progress).not_to receive(:warn)
|
235
|
+
expect(with_progress.each{}).to eq(nil)
|
236
|
+
end
|
237
|
+
else
|
238
|
+
it 'calls each only once for CSV and shows warning' do
|
239
|
+
enum = CSV.open('spec/test.csv', 'r')
|
240
|
+
expect(enum).to receive(:each).once.and_call_original
|
241
|
+
|
242
|
+
with_progress = enum.with_progress
|
243
|
+
expect(with_progress).to receive(:warn)
|
244
|
+
expect(with_progress.each{}).to eq(enum)
|
175
245
|
end
|
176
246
|
end
|
177
247
|
|
248
|
+
it 'yields same lines for CSV' do
|
249
|
+
csv = proc{ CSV.open('spec/test.csv', 'r') }
|
250
|
+
expect(csv[].with_progress.entries).to eq(csv[].entries)
|
251
|
+
end
|
178
252
|
end
|
179
253
|
end
|
180
254
|
end
|
181
255
|
|
182
256
|
describe Integer do
|
183
|
-
|
184
257
|
let(:count){ 108 }
|
185
258
|
|
186
|
-
it
|
259
|
+
it 'does not break times_with_progress' do
|
187
260
|
reference = count.times
|
188
261
|
count.times_with_progress do |i|
|
189
|
-
i.
|
262
|
+
expect(i).to eq(reference.next)
|
190
263
|
end
|
191
|
-
|
264
|
+
expect{ reference.next }.to raise_error(StopIteration)
|
192
265
|
end
|
193
266
|
|
194
|
-
it
|
267
|
+
it 'does not break times.with_progress' do
|
195
268
|
reference = count.times
|
196
269
|
count.times.with_progress do |i|
|
197
|
-
i.
|
270
|
+
expect(i).to eq(reference.next)
|
198
271
|
end
|
199
|
-
|
272
|
+
expect{ reference.next }.to raise_error(StopIteration)
|
200
273
|
end
|
201
|
-
|
202
274
|
end
|
203
|
-
|
204
275
|
end
|
205
276
|
|
206
|
-
describe
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
def initialize
|
211
|
-
@chunks = []
|
212
|
-
end
|
213
|
-
|
214
|
-
def <<(data)
|
215
|
-
@chunks << data.to_s
|
216
|
-
end
|
277
|
+
describe 'output' do
|
278
|
+
def stub_progress_io(io)
|
279
|
+
allow(io).to receive(:tty?).and_return(true)
|
280
|
+
allow(Progress).to receive(:io).and_return(io)
|
217
281
|
end
|
218
282
|
|
219
|
-
|
220
|
-
io = klass.new
|
221
|
-
io.stub(:tty?).and_return(true)
|
222
|
-
Progress.stub(:io).and_return(io)
|
223
|
-
io
|
224
|
-
end
|
225
|
-
|
226
|
-
describe "validity" do
|
227
|
-
|
283
|
+
describe 'validity' do
|
228
284
|
def run_example_progress
|
229
285
|
Progress.start 5, 'Test' do
|
230
286
|
Progress.step 2, 'simle'
|
231
287
|
|
232
288
|
Progress.step 2, 'times' do
|
233
|
-
3.times.with_progress
|
289
|
+
3.times.with_progress{}
|
234
290
|
end
|
235
291
|
|
236
292
|
Progress.step 'enum' do
|
237
|
-
3.times.to_a.with_progress
|
293
|
+
3.times.to_a.with_progress{}
|
238
294
|
end
|
239
295
|
end
|
240
296
|
end
|
@@ -247,6 +303,10 @@ describe Progress do
|
|
247
303
|
"\e[1m#{s}\e[0m"
|
248
304
|
end
|
249
305
|
|
306
|
+
def unhl(s)
|
307
|
+
s.gsub(/\e\[\dm/, '')
|
308
|
+
end
|
309
|
+
|
250
310
|
def on_line(s)
|
251
311
|
"\r" + s + "\e[K"
|
252
312
|
end
|
@@ -255,70 +315,80 @@ describe Progress do
|
|
255
315
|
s + "\n"
|
256
316
|
end
|
257
317
|
|
258
|
-
|
318
|
+
def on_line_n_title(s)
|
319
|
+
[on_line(s), title(unhl(s))]
|
320
|
+
end
|
321
|
+
|
322
|
+
def line_n_title(s)
|
323
|
+
[line(s), title(unhl(s))]
|
324
|
+
end
|
325
|
+
|
326
|
+
it 'produces valid output when staying on line' do
|
259
327
|
Progress.stay_on_line = true
|
260
328
|
|
261
|
-
|
329
|
+
stub_progress_io(io = StringIO.new)
|
262
330
|
run_example_progress
|
263
331
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
on_line(
|
278
|
-
|
332
|
+
expect(io.string).to eq([
|
333
|
+
on_line_n_title("Test: #{hl '......'}"),
|
334
|
+
on_line_n_title("Test: #{hl ' 40.0%'} - simle"),
|
335
|
+
on_line_n_title("Test: #{hl ' 40.0%'} > #{hl '......'}"),
|
336
|
+
on_line_n_title("Test: #{hl ' 53.3%'} > #{hl ' 33.3%'}"),
|
337
|
+
on_line_n_title("Test: #{hl ' 66.7%'} > #{hl ' 66.7%'}"),
|
338
|
+
on_line_n_title("Test: #{hl ' 80.0%'} > 100.0%"),
|
339
|
+
on_line_n_title("Test: #{hl ' 80.0%'} - times"),
|
340
|
+
on_line_n_title("Test: #{hl ' 80.0%'} > #{hl '......'}"),
|
341
|
+
on_line_n_title("Test: #{hl ' 86.7%'} > #{hl ' 33.3%'}"),
|
342
|
+
on_line_n_title("Test: #{hl ' 93.3%'} > #{hl ' 66.7%'}"),
|
343
|
+
on_line_n_title('Test: 100.0% > 100.0%'),
|
344
|
+
on_line_n_title('Test: 100.0% - enum'),
|
345
|
+
on_line('Test: 100.0% (elapsed: 0s) - enum') + "\n",
|
346
|
+
title(''),
|
347
|
+
].flatten.join)
|
279
348
|
end
|
280
349
|
|
281
|
-
it
|
350
|
+
it 'produces valid output when not staying on line' do
|
282
351
|
Progress.stay_on_line = false
|
283
352
|
|
284
|
-
|
353
|
+
stub_progress_io(io = StringIO.new)
|
285
354
|
run_example_progress
|
286
355
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
line(
|
301
|
-
|
356
|
+
expect(io.string).to eq([
|
357
|
+
line_n_title("Test: #{hl '......'}"),
|
358
|
+
line_n_title("Test: #{hl ' 40.0%'} - simle"),
|
359
|
+
line_n_title("Test: #{hl ' 40.0%'} > #{hl '......'}"),
|
360
|
+
line_n_title("Test: #{hl ' 53.3%'} > #{hl ' 33.3%'}"),
|
361
|
+
line_n_title("Test: #{hl ' 66.7%'} > #{hl ' 66.7%'}"),
|
362
|
+
line_n_title("Test: #{hl ' 80.0%'} > 100.0%"),
|
363
|
+
line_n_title("Test: #{hl ' 80.0%'} - times"),
|
364
|
+
line_n_title("Test: #{hl ' 80.0%'} > #{hl '......'}"),
|
365
|
+
line_n_title("Test: #{hl ' 86.7%'} > #{hl ' 33.3%'}"),
|
366
|
+
line_n_title("Test: #{hl ' 93.3%'} > #{hl ' 66.7%'}"),
|
367
|
+
line_n_title('Test: 100.0% > 100.0%'),
|
368
|
+
line_n_title('Test: 100.0% - enum'),
|
369
|
+
line('Test: 100.0% (elapsed: 0s) - enum'),
|
370
|
+
title(''),
|
371
|
+
].flatten.join)
|
302
372
|
end
|
303
|
-
|
304
373
|
end
|
305
374
|
|
306
|
-
describe
|
307
|
-
|
375
|
+
describe 'different call styles' do
|
308
376
|
let(:count_a){ 13 }
|
309
377
|
let(:count_b){ 17 }
|
310
|
-
|
311
|
-
|
312
|
-
reference_io = stub_progress_io(StringIO)
|
378
|
+
let(:reference_output) do
|
379
|
+
stub_progress_io(reference_io = StringIO.new)
|
313
380
|
count_a.times.with_progress('Test') do
|
314
|
-
count_b.times.with_progress
|
381
|
+
count_b.times.with_progress{}
|
315
382
|
end
|
316
|
-
|
383
|
+
reference_io.string
|
384
|
+
end
|
385
|
+
let(:io){ StringIO.new }
|
317
386
|
|
318
|
-
|
387
|
+
before do
|
388
|
+
stub_progress_io(io)
|
319
389
|
end
|
320
390
|
|
321
|
-
it
|
391
|
+
it 'outputs same when called without block' do
|
322
392
|
Progress(count_a, 'Test')
|
323
393
|
count_a.times do
|
324
394
|
Progress.step do
|
@@ -330,10 +400,10 @@ describe Progress do
|
|
330
400
|
end
|
331
401
|
end
|
332
402
|
Progress.stop
|
333
|
-
|
403
|
+
expect(io.string).to eq(reference_output)
|
334
404
|
end
|
335
405
|
|
336
|
-
it
|
406
|
+
it 'outputs same when called with block' do
|
337
407
|
Progress(count_a, 'Test') do
|
338
408
|
count_a.times do
|
339
409
|
Progress.step do
|
@@ -345,18 +415,15 @@ describe Progress do
|
|
345
415
|
end
|
346
416
|
end
|
347
417
|
end
|
348
|
-
|
418
|
+
expect(io.string).to eq(reference_output)
|
349
419
|
end
|
350
420
|
|
351
|
-
it
|
421
|
+
it 'outputs same when called using with_progress on list' do
|
352
422
|
count_a.times.to_a.with_progress('Test') do
|
353
|
-
count_b.times.to_a.with_progress
|
423
|
+
count_b.times.to_a.with_progress{}
|
354
424
|
end
|
355
|
-
|
425
|
+
expect(io.string).to eq(reference_output)
|
356
426
|
end
|
357
|
-
|
358
427
|
end
|
359
|
-
|
360
428
|
end
|
361
|
-
|
362
429
|
end
|