columnist 1.0.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.
@@ -0,0 +1,301 @@
1
+ require 'spec_helper'
2
+
3
+ describe Columnist::NestedFormatter do
4
+ subject { Columnist::NestedFormatter.instance }
5
+
6
+ let(:controls) do
7
+ {
8
+ :clear => "\e[0m",
9
+ :bold => "\e[1m",
10
+ :red => "\e[31m",
11
+ }
12
+ end
13
+
14
+ describe '#method default values' do
15
+ describe '#message_string' do
16
+ subject { super().message_string }
17
+ it { should == 'working' }
18
+ end
19
+
20
+ describe '#complete_string' do
21
+ subject { super().complete_string }
22
+ it { should == 'complete' }
23
+ end
24
+
25
+ describe '#indent_size' do
26
+ subject { super().indent_size }
27
+ it { should eq 2 }
28
+ end
29
+ end
30
+
31
+ describe '#format' do
32
+ context 'argument validation' do
33
+ before :each do
34
+ @indent_size = subject.indent_size
35
+ end
36
+
37
+ # Restore the singleton to its original state to ensure clean tests
38
+ after :each do
39
+ subject.indent_size = @indent_size
40
+ end
41
+
42
+ it 'raises exception when there is an invalid argument' do
43
+ expect {
44
+ subject.format({:asdf => true}, lambda { })
45
+ }.to raise_exception ArgumentError
46
+ end
47
+
48
+ it 'raises an exception when a block is not given' do
49
+ expect {
50
+ subject.format({:message => 'test'})
51
+ }.to raise_exception
52
+ end
53
+
54
+ it 'accepts valid arguments' do
55
+ expect(subject).to receive(:puts).with('test')
56
+ expect(subject).to receive(:puts).with('complete')
57
+
58
+ expect {
59
+ subject.format({:message => 'test'}, lambda { }) do
60
+ end
61
+ }.not_to raise_exception
62
+ end
63
+ end
64
+
65
+ context 'not nested' do
66
+ before :each do
67
+ @complete_string = subject.complete_string
68
+ @indent_size = subject.indent_size
69
+ end
70
+
71
+ # Restore the singleton to its original state to ensure clean tests
72
+ after :each do
73
+ subject.complete_string = @complete_string
74
+ subject.indent_size = @indent_size
75
+ end
76
+
77
+ it 'performs a wrapped report' do
78
+ expect(subject).to receive(:puts).with('working')
79
+ expect(subject).to receive(:puts).with('complete')
80
+
81
+ subject.format({ }, lambda { })
82
+ end
83
+
84
+ it 'performs a wrapped report with color' do
85
+ expect(subject).to receive(:puts).with("#{controls[:red]}working#{controls[:clear]}")
86
+ expect(subject).to receive(:puts).with("#{controls[:red]}complete#{controls[:clear]}")
87
+
88
+ subject.format({:color => 'red'}, lambda { })
89
+ end
90
+
91
+ it 'performs a wrapped report with color' do
92
+ expect(subject).to receive(:puts).with("#{controls[:bold]}working#{controls[:clear]}")
93
+ expect(subject).to receive(:puts).with("#{controls[:bold]}complete#{controls[:clear]}")
94
+
95
+ subject.format({:bold => true}, lambda { })
96
+ end
97
+
98
+ it 'performs a wrapped report overriding the message' do
99
+ expect(subject).to receive(:puts).with('test')
100
+ expect(subject).to receive(:puts).with('complete')
101
+
102
+ subject.format({:message => 'test'}, lambda { })
103
+ end
104
+
105
+ it 'performs an inline report' do
106
+ expect(subject).to receive(:print).with('test...')
107
+ expect(subject).to receive(:puts).with('complete')
108
+ expect(subject).to receive(:print).with('test2...')
109
+ expect(subject).to receive(:puts).with('complete')
110
+
111
+ subject.format({:message => 'test', :type => 'inline'}, lambda { })
112
+ subject.format({:message => 'test2', :type => 'inline'}, lambda { })
113
+ end
114
+
115
+ it 'overrides the default for all invocations of a wrapped report' do
116
+ subject.complete_string = 'done'
117
+ expect(subject).to receive(:puts).with('test')
118
+ expect(subject).to receive(:puts).with('done')
119
+ expect(subject).to receive(:puts).with('test2')
120
+ expect(subject).to receive(:puts).with('done')
121
+
122
+ subject.format({:message => 'test'}, lambda { })
123
+ subject.format({:message => 'test2'}, lambda { })
124
+ end
125
+
126
+ it 'overrides the default complete string for a wrapped report' do
127
+ expect(subject).to receive(:puts).with('test')
128
+ expect(subject).to receive(:puts).with('done')
129
+ expect(subject).to receive(:puts).with('test2')
130
+ expect(subject).to receive(:puts).with('finally')
131
+
132
+ subject.format({:message => 'test', :complete => 'done'}, lambda { })
133
+ subject.format({:message => 'test2', :complete => 'finally'}, lambda { })
134
+ end
135
+
136
+ it 'overrides the default complete string for an inline report' do
137
+ expect(subject).to receive(:print).with('test...')
138
+ expect(subject).to receive(:puts).with('done')
139
+ expect(subject).to receive(:print).with('test2...')
140
+ expect(subject).to receive(:puts).with('finally')
141
+
142
+ subject.format({:message => 'test', :type => 'inline', :complete => 'done'}, lambda { })
143
+ subject.format({:message => 'test2', :type => 'inline', :complete => 'finally'}, lambda { })
144
+ end
145
+
146
+ it 'performs another wrapped report to ensure defaul behavior' do
147
+ expect(subject).to receive(:puts).with('test')
148
+ expect(subject).to receive(:puts).with('complete')
149
+ expect(subject).to receive(:puts).with('test2')
150
+ expect(subject).to receive(:puts).with('complete')
151
+
152
+ subject.format({:message => 'test'}, lambda { })
153
+ subject.format({:message => 'test2'}, lambda { })
154
+ end
155
+ end
156
+
157
+ context 'nested commands' do
158
+ before :each do
159
+ @indent_size = subject.indent_size
160
+ end
161
+
162
+ # Restore the singleton to its original state to ensure clean tests
163
+ after :each do
164
+ subject.indent_size = @indent_size
165
+ end
166
+
167
+ it 'indents the nested wrapped messages' do
168
+ expect(subject).to receive(:puts).with('test')
169
+ expect(subject).to receive(:puts).with(' test2')
170
+ expect(subject).to receive(:puts).with(' complete')
171
+ expect(subject).to receive(:puts).with('complete')
172
+
173
+ subject.format({:message => 'test'}, lambda {
174
+ subject.format({:message => 'test2'}, lambda {})
175
+ })
176
+ end
177
+
178
+ it 'indents the nested wrapped messages and outputs color' do
179
+ expect(subject).to receive(:puts).with("#{controls[:red]}test#{controls[:clear]}")
180
+ expect(subject).to receive(:puts).with("#{controls[:red]} test2#{controls[:clear]}")
181
+ expect(subject).to receive(:puts).with("#{controls[:red]} complete#{controls[:clear]}")
182
+ expect(subject).to receive(:puts).with("#{controls[:red]}complete#{controls[:clear]}")
183
+
184
+ subject.format({:message => 'test', :color => 'red'}, lambda {
185
+ subject.format({:message => 'test2', :color => 'red'}, lambda {})
186
+ })
187
+ end
188
+
189
+ it 'indents the nested wrapped messages and outputs bold' do
190
+ expect(subject).to receive(:puts).with("#{controls[:bold]}test#{controls[:clear]}")
191
+ expect(subject).to receive(:puts).with("#{controls[:bold]} test2#{controls[:clear]}")
192
+ expect(subject).to receive(:puts).with("#{controls[:bold]} complete#{controls[:clear]}")
193
+ expect(subject).to receive(:puts).with("#{controls[:bold]}complete#{controls[:clear]}")
194
+
195
+ subject.format({:message => 'test', :bold => true}, lambda {
196
+ subject.format({:message => 'test2', :bold => true}, lambda {})
197
+ })
198
+ end
199
+
200
+ it 'indents the multiple nested wrapped messages' do
201
+ expect(subject).to receive(:puts).with('test')
202
+ expect(subject).to receive(:puts).with(' test2')
203
+ expect(subject).to receive(:puts).with(' test3')
204
+ expect(subject).to receive(:puts).with(' complete')
205
+ expect(subject).to receive(:puts).with(' complete')
206
+ expect(subject).to receive(:puts).with('complete')
207
+
208
+ subject.format({:message => 'test'}, lambda {
209
+ subject.format({:message => 'test2'}, lambda {
210
+ subject.format({:message => 'test3'}, lambda { })
211
+ })
212
+ })
213
+ end
214
+
215
+ it 'indents the nested wrapped and inline messages' do
216
+ expect(subject).to receive(:puts).with('test')
217
+ expect(subject).to receive(:print).with(' test2...')
218
+ expect(subject).to receive(:puts).with('complete')
219
+ expect(subject).to receive(:puts).with('complete')
220
+
221
+ subject.format({:message => 'test'}, lambda {
222
+ subject.format({:message => 'test2', :type => 'inline'}, lambda { })
223
+ })
224
+ end
225
+
226
+ it 'indents the multiple nested wrapped messages' do
227
+ expect(subject).to receive(:puts).with('test')
228
+ expect(subject).to receive(:puts).with(' test2')
229
+ expect(subject).to receive(:print).with(' test3...')
230
+ expect(subject).to receive(:puts).with('complete')
231
+ expect(subject).to receive(:puts).with(' complete')
232
+ expect(subject).to receive(:puts).with('complete')
233
+
234
+ subject.format({:message => 'test'}, lambda {
235
+ subject.format({:message => 'test2'}, lambda {
236
+ subject.format({:message => 'test3', :type => 'inline'}, lambda { })
237
+ })
238
+ })
239
+ end
240
+
241
+ it 'overrides the indent spacing of all messages' do
242
+ expect(subject).to receive(:puts).with('test')
243
+ expect(subject).to receive(:puts).with(' test2')
244
+ expect(subject).to receive(:print).with(' test3...')
245
+ expect(subject).to receive(:puts).with('complete')
246
+ expect(subject).to receive(:puts).with(' complete')
247
+ expect(subject).to receive(:puts).with('complete')
248
+
249
+ subject.indent_size = 4
250
+
251
+ subject.format({:message => 'test'}, lambda {
252
+ subject.format({:message => 'test2'}, lambda {
253
+ subject.format({:message => 'test3', :type => 'inline'}, lambda { })
254
+ })
255
+ })
256
+ end
257
+
258
+ it 'overrides the indent spacing of specific message' do
259
+ expect(subject).to receive(:puts).with('test')
260
+ expect(subject).to receive(:puts).with(' test2')
261
+ expect(subject).to receive(:print).with(' test3...')
262
+ expect(subject).to receive(:puts).with('complete')
263
+ expect(subject).to receive(:puts).with(' complete')
264
+ expect(subject).to receive(:puts).with('complete')
265
+
266
+ subject.indent_size = 4
267
+
268
+ subject.format({:message => 'test'}, lambda {
269
+ subject.format({:message => 'test2', :indent_size => 6}, lambda {
270
+ subject.format({:message => 'test3', :type => 'inline', :indent_size => 3}, lambda { })
271
+ })
272
+ })
273
+ end
274
+
275
+ it 'performs the sums specified in the block' do
276
+ x,y,z = 0,0,0
277
+
278
+ expect(subject).to receive(:puts).with('sum x and 10')
279
+ expect(subject).to receive(:puts).with(' y is the difference of 20 and x')
280
+ expect(subject).to receive(:puts).with(' z = x + y')
281
+ expect(subject).to receive(:puts).with(' complete')
282
+ expect(subject).to receive(:puts).with(' complete')
283
+ expect(subject).to receive(:puts).with('complete')
284
+
285
+ subject.format({:message => 'sum x and 10'}, lambda {
286
+ x = x + 10
287
+ subject.format({:message => 'y is the difference of 20 and x'}, lambda {
288
+ y = 20 - x
289
+ subject.format({:message => 'z = x + y'}, lambda {
290
+ z = x + y
291
+ })
292
+ })
293
+ })
294
+
295
+ expect(x).to eq(10)
296
+ expect(y).to eq(10)
297
+ expect(z).to eq(20)
298
+ end
299
+ end
300
+ end
301
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptionsValidator do
4
+ subject { Class.new.extend(OptionsValidator) }
5
+
6
+ it 'accepts single key options' do
7
+ expect {
8
+ subject.validate_options({:valid => true}, :valid)
9
+ }.to_not raise_error
10
+ end
11
+
12
+ it 'rejects invalid option hashes' do
13
+ expect {
14
+ subject.validate_options({:wrong => true}, :valid)
15
+ }.to raise_error ArgumentError
16
+ end
17
+
18
+ it 'accepts multi-key options' do
19
+ expect {
20
+ valid = [:valid, :another]
21
+ subject.validate_options({:valid => true, :another => true}, *valid)
22
+ }.to_not raise_error
23
+ end
24
+ end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ describe Columnist::ProgressFormatter do
4
+ subject { Columnist::ProgressFormatter.instance }
5
+
6
+ describe '#method default values' do
7
+ describe '#indicator' do
8
+ subject { super().indicator }
9
+ it { should == '.' }
10
+ end
11
+ end
12
+
13
+ let :controls do
14
+ {
15
+ :clear => "\e[0m",
16
+ :bold => "\e[1m",
17
+ :red => "\e[31m",
18
+ }
19
+ end
20
+
21
+ describe '#format' do
22
+ it 'displays dots for the indicator' do
23
+ expect(subject).to receive(:print).exactly(10).times.with('.')
24
+ expect(subject).to receive(:puts).exactly(1).times
25
+
26
+ subject.format({}, lambda {
27
+ 10.times {subject.progress}
28
+ })
29
+ end
30
+
31
+ it 'displays colored red dots for the indicator' do
32
+ expect(subject).to receive(:print).exactly(10).times.with("#{controls[:red]}.#{controls[:clear]}")
33
+ expect(subject).to receive(:puts).exactly(1).times
34
+
35
+ subject.format({:color => 'red'}, lambda {
36
+ 10.times {subject.progress}
37
+ })
38
+ end
39
+
40
+ it 'displays BOLD dots for the indicator' do
41
+ expect(subject).to receive(:print).exactly(10).times.with("#{controls[:bold]}.#{controls[:clear]}")
42
+ expect(subject).to receive(:puts).exactly(1).times
43
+
44
+ subject.format({:bold => true}, lambda {
45
+ 10.times {subject.progress}
46
+ })
47
+ end
48
+
49
+ it 'uses the defined indicator' do
50
+ subject.indicator = '+'
51
+ expect(subject).to receive(:print).exactly(10).times.with('+')
52
+ expect(subject).to receive(:puts)
53
+
54
+ subject.format({}, lambda {
55
+ 10.times {subject.progress}
56
+ })
57
+
58
+ end
59
+
60
+ it 'allows override of the indicator' do
61
+ expect(subject).to receive(:print).exactly(10).times.with('=')
62
+ expect(subject).to receive(:puts)
63
+
64
+ subject.format({:indicator => '='}, lambda {
65
+ 10.times {subject.progress}
66
+ })
67
+ end
68
+ end
69
+
70
+ describe '#progress' do
71
+ it 'allows override of the indicator' do
72
+ expect(subject).to receive(:print).exactly(10).times.with('+')
73
+ expect(subject).to receive(:puts)
74
+
75
+ subject.format({}, lambda {
76
+ 10.times {subject.progress('+')}
77
+ })
78
+ end
79
+
80
+ it 'allows any indicator' do
81
+ expect(subject).to receive(:print).exactly(10).times
82
+ expect(subject).to receive(:puts)
83
+
84
+ subject.format({}, lambda {
85
+ 10.times {|i| subject.progress("#{i}")}
86
+ })
87
+ end
88
+ end
89
+ end
data/spec/row_spec.rb ADDED
@@ -0,0 +1,129 @@
1
+ require 'spec_helper'
2
+
3
+ describe Columnist::Row do
4
+ let(:cols) { 10.times.map {|v| Columnist::Column.new("test#{v}")} }
5
+
6
+ describe '#initialize' do
7
+ it 'accepts header' do
8
+ expect(Columnist::Row.new(:header => true).header).to be_true
9
+ end
10
+
11
+ it 'accepts color' do
12
+ expect(Columnist::Row.new(:color => 'red').color).to eq('red')
13
+ end
14
+
15
+ it 'accepts bold' do
16
+ expect(Columnist::Row.new(:bold => true).bold).to be_true
17
+ end
18
+
19
+ it 'output encoding should be ascii' do
20
+ expect(Columnist::Row.new(:encoding => :ascii).encoding).to eq(:ascii)
21
+ end
22
+
23
+ it 'output encoding should be unicode' do
24
+ expect(Columnist::Row.new.encoding).to eq(:unicode)
25
+ end
26
+
27
+ end
28
+
29
+ describe '#add' do
30
+ subject { Columnist::Row.new }
31
+
32
+ it 'columns' do
33
+ subject.add(cols[0])
34
+ expect(subject.columns.size).to eq(1)
35
+ expect(subject.columns[0]).to eq(cols[0])
36
+ subject.add(cols[1])
37
+ expect(subject.columns).to eq(cols[0,2])
38
+ end
39
+
40
+ it 'defaults colors on columns' do
41
+ row = Columnist::Row.new(:color => 'red')
42
+ row.add(cols[0])
43
+ expect(row.columns[0].color).to eq('red')
44
+ row.add(cols[1])
45
+ expect(row.columns[1].color).to eq('red')
46
+ end
47
+
48
+ it 'allows columns to override the row color' do
49
+ col = Columnist::Column.new('test', :color => 'blue')
50
+ row = Columnist::Row.new(:color => 'red')
51
+ row.add(col)
52
+ expect(row.columns[0].color).to eq('blue')
53
+ end
54
+
55
+ it 'supercedes bold on columns' do
56
+ row = Columnist::Row.new(:bold => true)
57
+ row.add(cols[0])
58
+ expect(row.columns[0].bold).to be_true
59
+ row.add(cols[1])
60
+ expect(row.columns[1].bold).to be_true
61
+ end
62
+ end
63
+
64
+ describe '#output' do
65
+ let :cols do
66
+ [
67
+ Columnist::Column.new('asdf'),
68
+ Columnist::Column.new('qwer', :align => 'center'),
69
+ Columnist::Column.new('zxcv', :align => 'right'),
70
+ Columnist::Column.new('x' * 25, :align => 'left', :width => 10),
71
+ Columnist::Column.new('x' * 25, :align => 'center', :width => 10),
72
+ Columnist::Column.new('x' * 35, :align => 'left', :width => 10),
73
+ ]
74
+ end
75
+
76
+ let(:one_space) { ' ' }
77
+ let(:three_spaces) { ' {3,3}' }
78
+ let(:six_spaces) { ' {6,6}' }
79
+ let(:nine_spaces) { ' {9,9}' }
80
+ let(:five_xs) { 'x{5,5}' }
81
+ let(:ten_xs) { 'x{10,10}' }
82
+
83
+ context 'no border' do
84
+ context 'no wrap' do
85
+ it 'outputs a single column' do
86
+ subject.add(cols[0])
87
+ expect(subject).to receive(:puts).with(/^asdf#{@six_pieces}/)
88
+ subject.output
89
+ end
90
+ it 'outputs three columns' do
91
+ subject.add(cols[0])
92
+ subject.add(cols[1])
93
+ subject.add(cols[2])
94
+ expect(subject).to receive(:puts).with(/^asdf#{six_spaces}#{one_space}#{three_spaces}qwer#{three_spaces}#{one_space}#{six_spaces}zxcv $/)
95
+ subject.output
96
+ end
97
+ end
98
+
99
+ context 'with wrapping' do
100
+ it 'outputs a single column' do
101
+ subject.add(cols[3])
102
+ expect(subject).to receive(:puts).with(/^#{ten_xs}#{one_space}$/)
103
+ expect(subject).to receive(:puts).with(/^#{ten_xs}#{one_space}$/)
104
+ expect(subject).to receive(:puts).with(/^#{five_xs}#{six_spaces}$/)
105
+ subject.output
106
+ end
107
+
108
+ it 'outputs multiple columns of the same size' do
109
+ subject.add(cols[3])
110
+ subject.add(cols[4])
111
+ expect(subject).to receive(:puts).with(/^#{ten_xs}#{one_space}#{ten_xs}#{one_space}$/)
112
+ expect(subject).to receive(:puts).with(/^#{ten_xs}#{one_space}#{ten_xs}#{one_space}$/)
113
+ expect(subject).to receive(:puts).with(/^#{five_xs}#{nine_spaces}#{five_xs}#{three_spaces}$/)
114
+ subject.output
115
+ end
116
+
117
+ it 'outputs multiple columns with different sizes' do
118
+ subject.add(cols[5])
119
+ subject.add(cols[3])
120
+ expect(subject).to receive(:puts).with(/^#{ten_xs}#{one_space}#{ten_xs}#{one_space}$/)
121
+ expect(subject).to receive(:puts).with(/^#{ten_xs}#{one_space}#{ten_xs}#{one_space}$/)
122
+ expect(subject).to receive(:puts).with(/^#{ten_xs}#{one_space}#{five_xs}#{six_spaces}$/)
123
+ expect(subject).to receive(:puts).with(/^#{five_xs} {5,17}$/)
124
+ subject.output
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,6 @@
1
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'columnist'
4
+
5
+ require_relative 'support/helpers/stdout'
6
+ require_relative 'support/matchers/argument'
@@ -0,0 +1,8 @@
1
+ require 'stringio'
2
+
3
+ def capture_stdout
4
+ $stdout = StringIO.new
5
+ yield
6
+ ensure
7
+ $stdout = STDOUT
8
+ end
@@ -0,0 +1,17 @@
1
+ RSpec::Matchers.define :accepts_argument do |expected|
2
+ match do
3
+ expected.call.should raise_exception ArgumentError
4
+ end
5
+
6
+ # failure_message_for_should do |actual|
7
+ # 'should raise an ArgumentError'
8
+ # end
9
+
10
+ # failure_message_for_should_not do |actual|
11
+ # 'should not raise ArgumentError'
12
+ # end
13
+
14
+ # description do
15
+ # 'validates options argument'
16
+ # end
17
+ end