progress 3.0.2 → 3.1.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.
- 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
|