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.
@@ -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 all?) will not show 100%
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
@@ -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
- if seconds && seconds > 0
10
- seconds_to_string(seconds)
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
- if seconds
22
- case seconds
23
- when 0...60
24
- '%.0fs' % seconds
25
- when 60...3600
26
- '%.1fm' % (seconds / 60)
27
- when 3600...86400
28
- '%.1fh' % (seconds / 3600)
29
- else
30
- '%.1fd' % (seconds / 86400)
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
- if completed > 0 && now - @started_at >= 1
38
- current_eta = @started_at + (now - @started_at) / completed
39
- @left = if @left
40
- @left + (current_eta - @left) * (1 + completed) * 0.5
41
- else
42
- current_eta
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
@@ -1,5 +1,6 @@
1
1
  require 'progress'
2
2
 
3
+ # Add times_with_progress method to Integer
3
4
  class Integer
4
5
  # run `times` with progress
5
6
  # 100.times_with_progress('Numbers') do |number|
@@ -0,0 +1,9 @@
1
+ require 'progress'
2
+
3
+ # Add Progress method as alias to Progress.start
4
+ module Kernel
5
+ define_method :Progress do |*args, &block|
6
+ Progress.start(*args, &block)
7
+ end
8
+ private :Progress
9
+ end
@@ -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 :enumerable, :title
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(enumerable, title, length = nil, &block)
10
- @enumerable, @title, @length = enumerable, title, length
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(@enumerable, title, length || @length, &block)
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
- @enumerable.in_threads(*args).with_progress(@title, @length, &block)
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?(sym, include_private = false)
27
- enumerable_method?(method) || super(sym, include_private)
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
- enumerable = case
52
+ case
53
+ when !block
54
+ run_without_block(@enum, method, *args)
46
55
  when @length
47
- @enumerable
48
- when
49
- @enumerable.is_a?(String),
50
- @enumerable.is_a?(IO),
51
- Object.const_defined?(:StringIO) && @enumerable.is_a?(StringIO),
52
- Object.const_defined?(:TempFile) && @enumerable.is_a?(TempFile)
53
- warn "Progress: collecting elements for instance of class #{@enumerable.class}"
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
- @enumerable
64
+ run_with_length(@enum, enum_length(@enum), method, *args, &block)
59
65
  end
66
+ end
60
67
 
61
- length = case
62
- when @length
63
- @length
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
- if block
73
- result = Progress.start(@title, length) do
74
- enumerable.send(method, *args) do |*block_args|
75
- Progress.step do
76
- block.call(*block_args)
77
- end
78
- end
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
- if result.eql?(enumerable)
81
- @enumerable
82
- else
83
- result
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
- else
86
- Progress.start(@title) do
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
- enumerable.send(method, *args)
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'progress'
5
- s.version = '3.0.2'
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
@@ -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.set_terminal_title = true
11
+ Progress.terminal_title = true
11
12
 
12
- Progress.stub(:start_beeper)
13
- Progress.stub(:time_to_print?).and_return(true)
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
- describe "integrity" do
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
- @io = double(:<< => nil, :tty? => true)
22
- Progress.stub(:io).and_return(@io)
22
+ io = double(:<< => nil, :tty? => true)
23
+ allow(Progress).to receive(:io).and_return(io)
23
24
  end
24
25
 
25
- it "should return result from start block" do
26
- Progress.start('Test') do
26
+ it 'returns result from start block' do
27
+ expect(Progress.start('Test') do
27
28
  'test'
28
- end.should == 'test'
29
+ end).to eq('test')
29
30
  end
30
31
 
31
- it "should return result from step block" do
32
+ it 'returns result from step block' do
32
33
  Progress.start 1 do
33
- Progress.step{ 'test' }.should == 'test'
34
+ expect(Progress.step{ 'test' }).to eq('test')
34
35
  end
35
36
  end
36
37
 
37
- it "should return result from set block" do
38
+ it 'returns result from set block' do
38
39
  Progress.start 1 do
39
- Progress.set(1){ 'test' }.should == 'test'
40
+ expect(Progress.set(1){ 'test' }).to eq('test')
40
41
  end
41
42
  end
42
43
 
43
- it "should return result from nested block" do
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.should == [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
49
+ end).to eq([[1, 2, 3], [2, 4, 6], [3, 6, 9]])
49
50
  end
50
51
 
51
- it "should not raise errors on extra step or stop" do
52
- proc{
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
- }.should_not raise_error
65
+ end.not_to raise_error
65
66
  end
66
67
 
67
68
  describe Enumerable do
69
+ let(:enum){ 0...1000 }
68
70
 
69
- before :each do
70
- @a = 0...1000
71
- end
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 "should not break each" do
76
- reference = @a.each
77
- @a.with_progress.each do |n|
78
- n.should == reference.next
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
- proc{ reference.next }.should raise_error(StopIteration)
81
+ expect{ reference.next }.to raise_error(StopIteration)
81
82
  end
82
83
 
83
- it "should not break find" do
84
+ it 'does not break find' do
84
85
  default = proc{ 'default' }
85
- @a.with_progress.find{ |n| n == 100 }.should == @a.find{ |n| n == 100 }
86
- @a.with_progress.find{ |n| n == 10000 }.should == @a.find{ |n| n == 10000 }
87
- @a.with_progress.find(default){ |n| n == 10000 }.should == @a.find(default){ |n| n == 10000 }
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 "should not break map" do
91
- @a.with_progress.map{ |n| n * n }.should == @a.map{ |n| n * n }
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 "should not break grep" do
95
- @a.with_progress.grep(100).should == @a.grep(100)
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 "should not break each_cons" do
99
- reference = @a.each_cons(3)
100
- @a.with_progress.each_cons(3) do |values|
101
- values.should == reference.next
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
- proc{ reference.next }.should raise_error(StopIteration)
107
+ expect{ reference.next }.to raise_error(StopIteration)
104
108
  end
105
109
 
106
- describe "with_progress.with_progress" do
107
-
108
- it "should not change existing instance" do
109
- wp = @a.with_progress('hello')
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 "should create new instance with different title when called on WithProgress" do
114
- wp = @a.with_progress('hello')
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.should == 'hello'
117
- wp_wp.title.should == 'world'
118
- wp_wp.should_not == wp
119
- wp_wp.enumerable.should == 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 "calls to each" do
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
- def without_warnings
127
- verbosity = $VERBOSE
128
- $VERBOSE = nil
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
- it "should call each only once for Array" do
135
- enum = [1, 2, 3]
136
- enum.should_receive(:each).once.and_return(enum)
137
- enum.with_progress.each{ }.should == enum
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
- it "should call each only once for Hash" do
141
- enum = {1 => 1, 2 => 2, 3 => 3}
142
- enum.should_receive(:each).once.and_return(enum)
143
- enum.with_progress.each{ }.should == 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 "should call each only once for Set" do
147
- enum = [1, 2, 3].to_set
148
- enum.should_receive(:each).once.and_return(enum)
149
- enum.with_progress.each{ }.should == 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
- if ''.is_a?(Enumerable) # ruby1.8
153
- it "should call each only once for String" do
154
- enum = "a\nb\nc"
155
- enum.should_receive(:each).once.and_return(enum)
156
- without_warnings do
157
- enum.with_progress.each{ }.should == enum
158
- end
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 "should call each only once for File (IO)" do
163
- enum = File.open(__FILE__)
164
- enum.should_receive(:each).once.and_return(enum)
165
- without_warnings do
166
- enum.with_progress.each{ }.should == enum
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
- it "should call each only once for StringIO" do
171
- enum = StringIO.new("a\nb\nc")
172
- enum.should_receive(:each).once.and_return(enum)
173
- without_warnings do
174
- enum.with_progress.each{ }.should == 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 "should not break times_with_progress" do
259
+ it 'does not break times_with_progress' do
187
260
  reference = count.times
188
261
  count.times_with_progress do |i|
189
- i.should == reference.next
262
+ expect(i).to eq(reference.next)
190
263
  end
191
- proc{ reference.next }.should raise_error(StopIteration)
264
+ expect{ reference.next }.to raise_error(StopIteration)
192
265
  end
193
266
 
194
- it "should not break times.with_progress" do
267
+ it 'does not break times.with_progress' do
195
268
  reference = count.times
196
269
  count.times.with_progress do |i|
197
- i.should == reference.next
270
+ expect(i).to eq(reference.next)
198
271
  end
199
- proc{ reference.next }.should raise_error(StopIteration)
272
+ expect{ reference.next }.to raise_error(StopIteration)
200
273
  end
201
-
202
274
  end
203
-
204
275
  end
205
276
 
206
- describe "output" do
207
-
208
- class ChunkIo
209
- attr_reader :chunks
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
- def stub_progress_io(klass)
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
- it "should produce valid output when staying on line" do
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
- @io = stub_progress_io(ChunkIo)
329
+ stub_progress_io(io = StringIO.new)
262
330
  run_example_progress
263
331
 
264
- @io.chunks.should == [
265
- on_line("Test: #{hl '......'}"), title('Test: ......'),
266
- on_line("Test: #{hl ' 40.0%'} - simle"), title('Test: 40.0% - simle'),
267
- on_line("Test: #{hl ' 40.0%'} > #{hl '......'}"), title('Test: 40.0% > ......'),
268
- on_line("Test: #{hl ' 53.3%'} > #{hl ' 33.3%'}"), title('Test: 53.3% > 33.3%'),
269
- on_line("Test: #{hl ' 66.7%'} > #{hl ' 66.7%'}"), title('Test: 66.7% > 66.7%'),
270
- on_line("Test: #{hl ' 80.0%'} > 100.0%"), title('Test: 80.0% > 100.0%'),
271
- on_line("Test: #{hl ' 80.0%'} - times"), title('Test: 80.0% - times'),
272
- on_line("Test: #{hl ' 80.0%'} > #{hl '......'}"), title('Test: 80.0% > ......'),
273
- on_line("Test: #{hl ' 86.7%'} > #{hl ' 33.3%'}"), title('Test: 86.7% > 33.3%'),
274
- on_line("Test: #{hl ' 93.3%'} > #{hl ' 66.7%'}"), title('Test: 93.3% > 66.7%'),
275
- on_line("Test: 100.0% > 100.0%"), title('Test: 100.0% > 100.0%'),
276
- on_line("Test: 100.0% - enum"), title('Test: 100.0% - enum'),
277
- on_line("Test: 100.0% (elapsed: 0s) - enum") + "\n", title(''),
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 "should produce valid output when not staying on line" do
350
+ it 'produces valid output when not staying on line' do
282
351
  Progress.stay_on_line = false
283
352
 
284
- @io = stub_progress_io(ChunkIo)
353
+ stub_progress_io(io = StringIO.new)
285
354
  run_example_progress
286
355
 
287
- @io.chunks.should == [
288
- line("Test: #{hl '......'}"), title('Test: ......'),
289
- line("Test: #{hl ' 40.0%'} - simle"), title('Test: 40.0% - simle'),
290
- line("Test: #{hl ' 40.0%'} > #{hl '......'}"), title('Test: 40.0% > ......'),
291
- line("Test: #{hl ' 53.3%'} > #{hl ' 33.3%'}"), title('Test: 53.3% > 33.3%'),
292
- line("Test: #{hl ' 66.7%'} > #{hl ' 66.7%'}"), title('Test: 66.7% > 66.7%'),
293
- line("Test: #{hl ' 80.0%'} > 100.0%"), title('Test: 80.0% > 100.0%'),
294
- line("Test: #{hl ' 80.0%'} - times"), title('Test: 80.0% - times'),
295
- line("Test: #{hl ' 80.0%'} > #{hl '......'}"), title('Test: 80.0% > ......'),
296
- line("Test: #{hl ' 86.7%'} > #{hl ' 33.3%'}"), title('Test: 86.7% > 33.3%'),
297
- line("Test: #{hl ' 93.3%'} > #{hl ' 66.7%'}"), title('Test: 93.3% > 66.7%'),
298
- line("Test: 100.0% > 100.0%"), title('Test: 100.0% > 100.0%'),
299
- line("Test: 100.0% - enum"), title('Test: 100.0% - enum'),
300
- line("Test: 100.0% (elapsed: 0s) - enum"), title(''),
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 "different call styles" do
307
-
375
+ describe 'different call styles' do
308
376
  let(:count_a){ 13 }
309
377
  let(:count_b){ 17 }
310
-
311
- before do
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
- @reference_output = reference_io.string
383
+ reference_io.string
384
+ end
385
+ let(:io){ StringIO.new }
317
386
 
318
- @io = stub_progress_io(StringIO)
387
+ before do
388
+ stub_progress_io(io)
319
389
  end
320
390
 
321
- it "should output same when called without block" do
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
- @io.string.should == @reference_output
403
+ expect(io.string).to eq(reference_output)
334
404
  end
335
405
 
336
- it "should output same when called with block" do
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
- @io.string.should == @reference_output
418
+ expect(io.string).to eq(reference_output)
349
419
  end
350
420
 
351
- it "should output same when called using with_progress on list" do
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
- @io.string.should == @reference_output
425
+ expect(io.string).to eq(reference_output)
356
426
  end
357
-
358
427
  end
359
-
360
428
  end
361
-
362
429
  end