command_line_reporter 1.1.0 → 2.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.
- data/README.md +119 -3
- data/examples/table.rb +49 -0
- data/lib/column.rb +52 -0
- data/lib/command_line_reporter.rb +21 -6
- data/lib/options_validator.rb +5 -0
- data/lib/row.rb +60 -0
- data/lib/table.rb +44 -0
- data/lib/version.rb +1 -1
- data/spec/column_spec.rb +148 -0
- data/spec/command_line_reporter_spec.rb +80 -0
- data/spec/options_validator_spec.rb +25 -0
- data/spec/row_spec.rb +88 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/matchers/argument.rb +17 -0
- data/spec/table_spec.rb +45 -0
- metadata +20 -5
data/README.md
CHANGED
@@ -4,6 +4,9 @@ This gem provides an simple way to add RSpec like formatting of the output of yo
|
|
4
4
|
eliminates the need to litter your code with *puts* statements instead providing a cleaner, more
|
5
5
|
ruby like way of reporting progress to the user throught the command line interface.
|
6
6
|
|
7
|
+
With the release of Version 2.0, it is now possible to produce tables with and without borders. See
|
8
|
+
the section on *Tables* for more examples.
|
9
|
+
|
7
10
|
### Installation
|
8
11
|
|
9
12
|
It is up on rubygems.org so add it to your bundle or do it the old fashioned way:
|
@@ -20,7 +23,7 @@ The gem provides a mixin that can be included in your scripts.
|
|
20
23
|
include CommandLineReporter
|
21
24
|
```
|
22
25
|
|
23
|
-
|
26
|
+
##### Standard Methods
|
24
27
|
|
25
28
|
There are several methods the mixin provides that do not depend on the formatter used:
|
26
29
|
|
@@ -58,6 +61,18 @@ There are several methods the mixin provides that do not depend on the formatter
|
|
58
61
|
* _text_ - String to display
|
59
62
|
* _:align_ - 'left'|'right'|'center' align the string text. _Default: 'left'_
|
60
63
|
* _:width_ - The width in characters of the string text. _Default: 100_
|
64
|
+
* _table(hash) {block}_
|
65
|
+
* The first argument is a hash that defines properties of the table.
|
66
|
+
* _:border_ - true|false indicates whether to include borders around the table cells
|
67
|
+
* The second argument is a block which includes calls the to the _row_ method
|
68
|
+
* _row {block}_
|
69
|
+
* Only argument is a block with calls to _column_ allowed
|
70
|
+
* _column(string, hash)_
|
71
|
+
* _text_ - String to display in the table cell
|
72
|
+
* _options_ - The options to define the column
|
73
|
+
* :width - defines the width of the column
|
74
|
+
* :padding - The number of spaces to put on both the left and right of the text.
|
75
|
+
* :align - Allowed values are left|right|center
|
61
76
|
|
62
77
|
### Progress Formatter
|
63
78
|
|
@@ -228,7 +243,108 @@ report(:message => 'running', :complete => 'finished', :type => 'inline', :inden
|
|
228
243
|
end
|
229
244
|
```
|
230
245
|
|
246
|
+
### Tables
|
247
|
+
|
248
|
+
Examples are always helpful so let's look at the following:
|
249
|
+
|
250
|
+
```ruby
|
251
|
+
require 'command_line_reporter'
|
252
|
+
|
253
|
+
class Example
|
254
|
+
include CommandLineReporter
|
255
|
+
|
256
|
+
def run
|
257
|
+
table(:border => true) do
|
258
|
+
row do
|
259
|
+
column('NAME', :width => 20)
|
260
|
+
column('ADDRESS', :width => 30, :align => 'right', :padding => 5)
|
261
|
+
column('CITY', :width => 15)
|
262
|
+
end
|
263
|
+
row do
|
264
|
+
column('Ceaser')
|
265
|
+
column('1 Appian Way')
|
266
|
+
column('Rome')
|
267
|
+
end
|
268
|
+
row do
|
269
|
+
column('Richard Feynman')
|
270
|
+
column('1 Golden Gate')
|
271
|
+
column('Quantum Field')
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
Example.new.run
|
278
|
+
```
|
279
|
+
|
280
|
+
This produces the very simple output with 2 rows and 3 columns of data:
|
281
|
+
|
282
|
+
```bash
|
283
|
+
+----------------------+--------------------------------+-----------------+
|
284
|
+
| NAME | ADDRESS | CITY |
|
285
|
+
+----------------------+--------------------------------+-----------------+
|
286
|
+
| Ceaser | 1 Appian Way | Rome |
|
287
|
+
+----------------------+--------------------------------+-----------------+
|
288
|
+
| Richard Feynman | 1 Golden Gate | Quantum Field |
|
289
|
+
+----------------------+--------------------------------+-----------------+
|
290
|
+
```
|
291
|
+
|
292
|
+
Notice how the properties of the columns for the second and third rows have been inherited from the
|
293
|
+
first like in HTML. This makes it a lot easier to write in freeform. What if you have data to
|
294
|
+
iterate over and have text that is wider than the column width you have selected? Not a problem as
|
295
|
+
the following example demonstrates all of the combinations of the various options:
|
296
|
+
|
297
|
+
```ruby
|
298
|
+
require 'command_line_reporter'
|
299
|
+
|
300
|
+
class Example
|
301
|
+
include CommandLineReporter
|
302
|
+
|
303
|
+
def run
|
304
|
+
table(:border => true) do
|
305
|
+
3.times do
|
306
|
+
row do
|
307
|
+
i = 0
|
308
|
+
3.times do
|
309
|
+
i += 10
|
310
|
+
column('x' * (0 + rand(50)), :align => %w[left right center][rand(3)], :width => i, :padding => rand(5))
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
```
|
318
|
+
|
319
|
+
This randomly produces a table with a border that has 3 rows and 3 columns. Each column gets wider
|
320
|
+
by 10 characters. The alignment of the column is demonstrated and you can see where some data
|
321
|
+
elements have padding around them.
|
322
|
+
|
323
|
+
```bash
|
324
|
+
+------------+----------------------+--------------------------------+
|
325
|
+
| xxxxxxxxxx | xxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxx |
|
326
|
+
| xxxxxx | xxxxxxxxx | xxxxxx |
|
327
|
+
+------------+----------------------+--------------------------------+
|
328
|
+
| xxxxxxxxxx | xxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxx |
|
329
|
+
| xxxxxxxxxx | xxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxx |
|
330
|
+
| xxxxxxxxxx | xxxxxxxxxxxxxx | |
|
331
|
+
| xxxxxxx | xxxx | |
|
332
|
+
+------------+----------------------+--------------------------------+
|
333
|
+
| xxxxxxxxxx | xxxx | xxxxxxxxxxxxxxxxxxx |
|
334
|
+
| xxxxxx | | |
|
335
|
+
+------------+----------------------+--------------------------------+
|
336
|
+
```
|
337
|
+
|
338
|
+
This is all generated randomly to illustrate the features but you get the idea of how to use
|
339
|
+
alignment, width and padding.
|
340
|
+
|
341
|
+
The best feature is *wrapping*. If the text you are display in a cell is larger than the width it
|
342
|
+
was given, it will automatically wrap it for you. Padding and alignment are preserved. It palso
|
343
|
+
properly handles the case where the data in one cell causes the wrapping but other cells my not have
|
344
|
+
the same number of lines to wrap.
|
345
|
+
|
231
346
|
### To Do
|
232
347
|
|
233
|
-
|
234
|
-
|
348
|
+
* Add the ability for a column to span across others
|
349
|
+
* Add the progress method to the top level mixin so that there is no need to invoke through the
|
350
|
+
formatter.
|
data/examples/table.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'command_line_reporter'
|
2
|
+
|
3
|
+
class Example
|
4
|
+
include CommandLineReporter
|
5
|
+
|
6
|
+
def run
|
7
|
+
header(:title => 'TABLE EXAMPLES - Borders, Wrapping, Alignment and Padding', :align => 'center', :width => 70)
|
8
|
+
|
9
|
+
2.times do |j|
|
10
|
+
header(:title => "Table #{j}", :align => 'center', :width => 65)
|
11
|
+
|
12
|
+
table(:border => j % 2 == 0) do
|
13
|
+
3.times do
|
14
|
+
row do
|
15
|
+
i = 0
|
16
|
+
3.times do
|
17
|
+
i += 10
|
18
|
+
column('x' * (0 + rand(50)), :align => %w[left right center][rand(3)], :width => i, :padding => rand(5))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
vertical_spacing(2)
|
25
|
+
end
|
26
|
+
|
27
|
+
header(:title => 'A simple example of how column properties are inhereted from the first row')
|
28
|
+
|
29
|
+
table(:border => true) do
|
30
|
+
row do
|
31
|
+
column('NAME', :width => 20)
|
32
|
+
column('ADDRESS', :width => 30, :align => 'right', :padding => 5)
|
33
|
+
column('CITY', :width => 15)
|
34
|
+
end
|
35
|
+
row do
|
36
|
+
column('Ceaser')
|
37
|
+
column('1 Appian Way')
|
38
|
+
column('Rome')
|
39
|
+
end
|
40
|
+
row do
|
41
|
+
column('Richard Feynman')
|
42
|
+
column('1 Golden Gate')
|
43
|
+
column('Quantum Field')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
Example.new.run
|
data/lib/column.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'options_validator'
|
2
|
+
|
3
|
+
class Column
|
4
|
+
include OptionsValidator
|
5
|
+
|
6
|
+
VALID_OPTIONS = [:width, :padding, :align]
|
7
|
+
attr_accessor :text, :size, *VALID_OPTIONS
|
8
|
+
|
9
|
+
def initialize(text = nil, options = {})
|
10
|
+
self.validate_options(options, *VALID_OPTIONS)
|
11
|
+
|
12
|
+
self.text = text
|
13
|
+
|
14
|
+
self.width = options[:width] || 10
|
15
|
+
self.align = options[:align] || 'left'
|
16
|
+
self.padding = options[:padding] || 0
|
17
|
+
|
18
|
+
raise ArgumentError unless self.width > 0
|
19
|
+
raise ArgumentError unless self.padding.to_s.match(/^\d+$/)
|
20
|
+
|
21
|
+
self.size = self.width - 2 * self.padding
|
22
|
+
|
23
|
+
# self.freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
def screen_rows
|
27
|
+
if self.text.nil? || self.text.empty?
|
28
|
+
[' ' * self.width]
|
29
|
+
else
|
30
|
+
self.text.scan(/.{1,#{self.size}}/m).map {|s| to_cell(s)}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def to_cell(str)
|
37
|
+
cell = if str.empty?
|
38
|
+
' ' * self.size
|
39
|
+
else
|
40
|
+
case self.align
|
41
|
+
when 'left'
|
42
|
+
str.ljust(self.size)
|
43
|
+
when 'right'
|
44
|
+
str.rjust(self.size)
|
45
|
+
when 'center'
|
46
|
+
str.ljust((self.size - str.size)/2.0 + str.size).rjust(self.size)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
' ' * self.padding + cell + ' ' * self.padding
|
51
|
+
end
|
52
|
+
end
|
@@ -1,6 +1,10 @@
|
|
1
|
+
require 'table'
|
2
|
+
|
1
3
|
Dir[File.join(File.dirname(__FILE__), '*_formatter.rb')].each {|r| require r}
|
2
4
|
|
3
5
|
module CommandLineReporter
|
6
|
+
include OptionsValidator
|
7
|
+
|
4
8
|
attr_reader :formatter
|
5
9
|
|
6
10
|
DEFAULTS = {
|
@@ -23,10 +27,8 @@ module CommandLineReporter
|
|
23
27
|
end
|
24
28
|
|
25
29
|
def report(options = {}, &block)
|
30
|
+
self.formatter ||= 'nested'
|
26
31
|
self.formatter.format(options, block)
|
27
|
-
rescue NoMethodError
|
28
|
-
self.formatter = 'nested'
|
29
|
-
retry
|
30
32
|
end
|
31
33
|
|
32
34
|
def footer(options = {})
|
@@ -80,12 +82,25 @@ module CommandLineReporter
|
|
80
82
|
end
|
81
83
|
end
|
82
84
|
|
83
|
-
|
85
|
+
def table(options = {})
|
86
|
+
@table = Table.new(options)
|
87
|
+
yield
|
88
|
+
@table.to_s
|
89
|
+
end
|
84
90
|
|
85
|
-
def
|
86
|
-
|
91
|
+
def row(options = {})
|
92
|
+
@row = Row.new
|
93
|
+
yield
|
94
|
+
@table.add(@row)
|
87
95
|
end
|
88
96
|
|
97
|
+
def column(text, options = {})
|
98
|
+
col = Column.new(text, options)
|
99
|
+
@row.add(col)
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
89
104
|
def section(type, options)
|
90
105
|
validate_options(options, :title, :width, :align, :spacing, :timestamp, :rule)
|
91
106
|
|
data/lib/row.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'column'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
class Row
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_accessor :columns, :border
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
self.columns = []
|
11
|
+
self.border = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def_delegator :@columns, :push, :add
|
15
|
+
|
16
|
+
def separator
|
17
|
+
@sep ||= '+' + self.columns.map {|c| '-' * (c.width + 2)}.join('+') + '+'
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
screen_count.times do |sr|
|
22
|
+
line = (self.border) ? '| ' : ''
|
23
|
+
self.columns.size.times do |mc|
|
24
|
+
col = self.columns[mc]
|
25
|
+
# Account for the fact that some columns will have more screen rows than their
|
26
|
+
# counterparts in the row. An example being:
|
27
|
+
# c1 = Column.new('x' * 50, :width => 10)
|
28
|
+
# c2 = Column.new('x' * 20, :width => 10)
|
29
|
+
#
|
30
|
+
# c1.screen_rows.size == 5
|
31
|
+
# c2.screen_rows.size == 2
|
32
|
+
#
|
33
|
+
# So when we don't have a screen row for c2 we need to fill the screen with the
|
34
|
+
# proper number of blanks so the layout looks like (parenthesis on the right just
|
35
|
+
# indicate screen row index)
|
36
|
+
#
|
37
|
+
# +-------------+------------+
|
38
|
+
# | xxxxxxxxxxx | xxxxxxxxxx | (0)
|
39
|
+
# | xxxxxxxxxxx | xxxxxxxxxx | (1)
|
40
|
+
# | xxxxxxxxxxx | | (2)
|
41
|
+
# | xxxxxxxxxxx | | (3)
|
42
|
+
# | xxxxxxxxxxx | | (4)
|
43
|
+
# +-------------+------------+
|
44
|
+
if col.screen_rows[sr].nil?
|
45
|
+
line << ' ' * col.width
|
46
|
+
else
|
47
|
+
line << self.columns[mc].screen_rows[sr]
|
48
|
+
end
|
49
|
+
line << ' ' + ((self.border) ? '| ' : '')
|
50
|
+
end
|
51
|
+
puts line
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def screen_count
|
58
|
+
@sc ||= self.columns.inject(0) {|max,column| column.screen_rows.size > max ? column.screen_rows.size : max}
|
59
|
+
end
|
60
|
+
end
|
data/lib/table.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'row'
|
2
|
+
require 'options_validator'
|
3
|
+
|
4
|
+
class Table
|
5
|
+
include OptionsValidator
|
6
|
+
|
7
|
+
VALID_OPTIONS = [:border]
|
8
|
+
attr_accessor :rows, *VALID_OPTIONS
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
self.validate_options(options, *VALID_OPTIONS)
|
12
|
+
|
13
|
+
self.border = options[:border] || false
|
14
|
+
|
15
|
+
@rows = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(row)
|
19
|
+
# Inheritance from the table
|
20
|
+
row.border = self.border
|
21
|
+
|
22
|
+
# Inherit properties from the first row
|
23
|
+
if self.rows[0]
|
24
|
+
row.columns.each_with_index do |c,i|
|
25
|
+
c.align = self.rows[0].columns[i].align
|
26
|
+
c.padding = self.rows[0].columns[i].padding
|
27
|
+
c.width = self.rows[0].columns[i].width
|
28
|
+
c.size = c.width - 2 * c.padding
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
self.rows << row
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
return if self.rows.size == 0 # we got here with nothing to print to the screen
|
37
|
+
|
38
|
+
puts self.rows[0].separator if self.border
|
39
|
+
self.rows.each do |row|
|
40
|
+
row.to_s
|
41
|
+
puts self.rows[0].separator if self.border
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/version.rb
CHANGED
data/spec/column_spec.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'column'
|
3
|
+
|
4
|
+
describe Column do
|
5
|
+
context 'creation' do
|
6
|
+
it 'rejects invalid options' do
|
7
|
+
expect {
|
8
|
+
Column.new('test', :asdf => '1234')
|
9
|
+
}.to raise_error ArgumentError
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'defaults options hash' do
|
13
|
+
expect {
|
14
|
+
Column.new('test')
|
15
|
+
}.to_not raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'defaults the width' do
|
19
|
+
Column.new('test').width.should == 10
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'accepts the width' do
|
23
|
+
Column.new('test', :width => 50).width.should == 50
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'requires valid width' do
|
27
|
+
expect {
|
28
|
+
Column.new('test', :width => 'asdf')
|
29
|
+
}.to raise_error ArgumentError
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'accepts text' do
|
33
|
+
Column.new('asdf').text.should == 'asdf'
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'defaults the padding' do
|
37
|
+
Column.new('test').padding.should == 0
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'accepts the padding' do
|
41
|
+
Column.new('test', :padding => 5).padding.should == 5
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'requires valid width' do
|
45
|
+
expect {
|
46
|
+
Column.new('test', :padding => 'asdf')
|
47
|
+
}.to raise_error ArgumentError
|
48
|
+
end
|
49
|
+
|
50
|
+
# it 'is immutable' do
|
51
|
+
# c = Column.new('test')
|
52
|
+
|
53
|
+
# expect {
|
54
|
+
# c.text = 'asdf'
|
55
|
+
# }.to raise_error RuntimeError, /frozen object/
|
56
|
+
|
57
|
+
# expect {
|
58
|
+
# c.width = 123
|
59
|
+
# }.to raise_error RuntimeError, /frozen object/
|
60
|
+
|
61
|
+
# expect {
|
62
|
+
# c.padding = 123
|
63
|
+
# }.to raise_error RuntimeError, /frozen object/
|
64
|
+
# end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#screen_rows' do
|
68
|
+
context 'no wrapping' do
|
69
|
+
context 'no padding' do
|
70
|
+
it 'gives a single row' do
|
71
|
+
c = Column.new('x' * 5)
|
72
|
+
c.screen_rows.size == 1
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'handles empty text' do
|
76
|
+
c = Column.new
|
77
|
+
c.screen_rows[0].should == ' ' * 10
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'left justifies' do
|
81
|
+
c = Column.new('x' * 10, :width => 20)
|
82
|
+
c.screen_rows[0].should == 'x' * 10 + ' ' * 10
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'right justifies' do
|
86
|
+
c = Column.new('x' * 10, :align => 'right', :width => 20)
|
87
|
+
c.screen_rows[0].should == ' ' * 10 + 'x' * 10
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'center justifies' do
|
91
|
+
c = Column.new('x' * 10, :align => 'center', :width => 20)
|
92
|
+
c.screen_rows[0].should == ' ' * 5 + 'x' * 10 + ' ' * 5
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'accounts for padding' do
|
97
|
+
it 'left justifies' do
|
98
|
+
c = Column.new('x' * 10, :padding => 5, :width => 30)
|
99
|
+
c.screen_rows[0].should == ' ' * 5 + 'x' * 10 + ' ' * 15
|
100
|
+
end
|
101
|
+
it 'right justifies' do
|
102
|
+
c = Column.new('x' * 10, :align => 'right', :padding => 5, :width => 30)
|
103
|
+
c.screen_rows[0].should == ' ' * 15 + 'x' * 10 + ' ' * 5
|
104
|
+
end
|
105
|
+
it 'right justifies' do
|
106
|
+
c = Column.new('x' * 10, :align => 'center', :padding => 5, :width => 30)
|
107
|
+
c.screen_rows[0].should == ' ' * 10 + 'x' * 10 + ' ' * 10
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'with wrapping' do
|
113
|
+
context 'no padding' do
|
114
|
+
it 'left justifies' do
|
115
|
+
c = Column.new('x' * 25, :width => 10)
|
116
|
+
c.screen_rows.should == ['x' * 10, 'x' * 10, 'x' * 5 + ' ' * 5]
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'left justifies' do
|
120
|
+
c = Column.new('x' * 25, :align => 'right', :width => 10)
|
121
|
+
c.screen_rows.should == ['x' * 10, 'x' * 10, ' ' * 5 + 'x' * 5]
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'left justifies' do
|
125
|
+
c = Column.new('x' * 25, :align => 'center', :width => 10)
|
126
|
+
c.screen_rows.should == ['x' * 10, 'x' * 10, ' ' * 3 + 'x' * 5 + ' ' * 2]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'account for padding' do
|
131
|
+
it 'left justifies' do
|
132
|
+
c = Column.new('x' * 25, :padding => 2, :width => 20)
|
133
|
+
c.screen_rows.should == [' ' * 2 + 'x' * 16 + ' ' * 2, ' ' * 2 + 'x' * 9 + ' ' * 9]
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'right justifies' do
|
137
|
+
c = Column.new('x' * 25, :padding => 2, :align => 'right', :width => 20)
|
138
|
+
c.screen_rows.should == [' ' * 2 + 'x' * 16 + ' ' * 2, ' ' * 9 + 'x' * 9 + ' ' * 2]
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'center justifies' do
|
142
|
+
c = Column.new('x' * 25, :padding => 2, :align => 'center', :width => 20)
|
143
|
+
c.screen_rows.should == [' ' * 2 + 'x' * 16 + ' ' * 2, ' ' * 6 + 'x' * 9 + ' ' * 5]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -49,6 +49,14 @@ describe CommandLineReporter do
|
|
49
49
|
|
50
50
|
subject.formatter.class.should == CommandLineReporter::ProgressFormatter
|
51
51
|
end
|
52
|
+
|
53
|
+
it 'does not mask other application errors when a formatter is not set' do
|
54
|
+
capture_stdout {
|
55
|
+
subject.report {
|
56
|
+
lambda {self.some_method_that_does_not_exist}.should raise_error(NoMethodError)
|
57
|
+
}
|
58
|
+
}
|
59
|
+
end
|
52
60
|
end
|
53
61
|
|
54
62
|
describe '#header' do
|
@@ -509,4 +517,76 @@ describe CommandLineReporter do
|
|
509
517
|
end
|
510
518
|
end
|
511
519
|
end
|
520
|
+
|
521
|
+
describe '#table' do
|
522
|
+
it 'instantiates the table class' do
|
523
|
+
subject.should_receive(:puts).any_number_of_times
|
524
|
+
subject.should_receive(:table).once
|
525
|
+
subject.table { }
|
526
|
+
# subject.instance_variable_get(:@table).should_not be_nil
|
527
|
+
end
|
528
|
+
|
529
|
+
it 'requires a row to be defined' do
|
530
|
+
expect {
|
531
|
+
subject.table
|
532
|
+
}.to raise_error LocalJumpError
|
533
|
+
end
|
534
|
+
|
535
|
+
it 'accepts valid options' do
|
536
|
+
expect {
|
537
|
+
subject.table(:border => true) { }
|
538
|
+
}.to_not raise_error
|
539
|
+
end
|
540
|
+
|
541
|
+
it 'rejects invalid options' do
|
542
|
+
expect {
|
543
|
+
subject.table(:asdf => '100') { }
|
544
|
+
}.to raise_error ArgumentError
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
describe '#row' do
|
549
|
+
it 'instantiates a row class' do
|
550
|
+
subject.should_receive(:row).once
|
551
|
+
subject.should_receive(:puts).any_number_of_times
|
552
|
+
|
553
|
+
subject.table do
|
554
|
+
subject.row do
|
555
|
+
end
|
556
|
+
end
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
describe '#column' do
|
561
|
+
it 'instantiates multiple columns' do
|
562
|
+
subject.should_receive(:column).exactly(3).times
|
563
|
+
subject.should_receive(:puts).any_number_of_times
|
564
|
+
|
565
|
+
subject.table do
|
566
|
+
subject.row do
|
567
|
+
subject.column('asdf')
|
568
|
+
subject.column('qwer')
|
569
|
+
subject.column('zxcv')
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
it 'accepts valid options' do
|
575
|
+
subject.table do
|
576
|
+
subject.row do
|
577
|
+
subject.column('asdf', :width => 30)
|
578
|
+
end
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
it 'rejects invalid options' do
|
583
|
+
expect {
|
584
|
+
subject.table do
|
585
|
+
subject.row do
|
586
|
+
subject.column('asdf', :asdf => 30)
|
587
|
+
end
|
588
|
+
end
|
589
|
+
}.to raise_error ArgumentError
|
590
|
+
end
|
591
|
+
end
|
512
592
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'options_validator'
|
3
|
+
|
4
|
+
describe OptionsValidator do
|
5
|
+
subject { Class.new.extend(OptionsValidator) }
|
6
|
+
|
7
|
+
it 'accepts single key options' do
|
8
|
+
expect {
|
9
|
+
subject.validate_options({:valid => true}, :valid)
|
10
|
+
}.to_not raise_error
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'rejects invalid option hashes' do
|
14
|
+
expect {
|
15
|
+
subject.validate_options({:wrong => true}, :valid)
|
16
|
+
}.to raise_error ArgumentError
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'accepts multi-key options' do
|
20
|
+
expect {
|
21
|
+
valid = [:valid, :another]
|
22
|
+
subject.validate_options({:valid => true, :another => true}, *valid)
|
23
|
+
}.to_not raise_error
|
24
|
+
end
|
25
|
+
end
|
data/spec/row_spec.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'row'
|
3
|
+
|
4
|
+
describe Row do
|
5
|
+
before :each do
|
6
|
+
@cols = 10.times.map {|v| Column.new("test#{v}")}
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#add' do
|
10
|
+
subject { Row.new }
|
11
|
+
|
12
|
+
it 'adds columns' do
|
13
|
+
subject.add(@cols[0])
|
14
|
+
subject.columns.size.should == 1
|
15
|
+
subject.columns[0].should == @cols[0]
|
16
|
+
subject.add(@cols[1])
|
17
|
+
subject.columns.should == @cols[0,2]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#to_s' do
|
22
|
+
before :each do
|
23
|
+
@cols = [
|
24
|
+
Column.new('asdf'),
|
25
|
+
Column.new('qwer', :align => 'center'),
|
26
|
+
Column.new('zxcv', :align => 'right'),
|
27
|
+
Column.new('x' * 25, :align => 'left', :width => 10),
|
28
|
+
Column.new('x' * 25, :align => 'center', :width => 10),
|
29
|
+
Column.new('x' * 35, :align => 'left', :width => 10),
|
30
|
+
]
|
31
|
+
end
|
32
|
+
|
33
|
+
before :all do
|
34
|
+
@one_space = ' '
|
35
|
+
@three_spaces = ' {3,3}'
|
36
|
+
@six_spaces = ' {6,6}'
|
37
|
+
@nine_spaces = ' {9,9}'
|
38
|
+
@five_xs = 'x{5,5}'
|
39
|
+
@ten_xs = 'x{10,10}'
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'no border' do
|
43
|
+
context 'no wrap' do
|
44
|
+
it 'prints a single column' do
|
45
|
+
subject.add(@cols[0])
|
46
|
+
subject.should_receive(:puts).with(/^asdf#{@six_pieces}/)
|
47
|
+
subject.to_s
|
48
|
+
end
|
49
|
+
it 'prints three columns' do
|
50
|
+
subject.add(@cols[0])
|
51
|
+
subject.add(@cols[1])
|
52
|
+
subject.add(@cols[2])
|
53
|
+
subject.should_receive(:puts).with(/^asdf#{@six_spaces}#{@one_space}#{@three_spaces}qwer#{@three_spaces}#{@one_space}#{@six_spaces}zxcv $/)
|
54
|
+
subject.to_s
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with wrapping' do
|
59
|
+
it 'prints a single column' do
|
60
|
+
subject.add(@cols[3])
|
61
|
+
subject.should_receive(:puts).with(/^#{@ten_xs}#{@one_space}$/)
|
62
|
+
subject.should_receive(:puts).with(/^#{@ten_xs}#{@one_space}$/)
|
63
|
+
subject.should_receive(:puts).with(/^#{@five_xs}#{@six_spaces}$/)
|
64
|
+
subject.to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'prints multiple columns of the same size' do
|
68
|
+
subject.add(@cols[3])
|
69
|
+
subject.add(@cols[4])
|
70
|
+
subject.should_receive(:puts).with(/^#{@ten_xs}#{@one_space}#{@ten_xs}#{@one_space}$/)
|
71
|
+
subject.should_receive(:puts).with(/^#{@ten_xs}#{@one_space}#{@ten_xs}#{@one_space}$/)
|
72
|
+
subject.should_receive(:puts).with(/^#{@five_xs}#{@nine_spaces}#{@five_xs}#{@three_spaces}$/)
|
73
|
+
subject.to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'prints multiple columns with different size' do
|
77
|
+
subject.add(@cols[5])
|
78
|
+
subject.add(@cols[3])
|
79
|
+
subject.should_receive(:puts).with(/^#{@ten_xs}#{@one_space}#{@ten_xs}#{@one_space}$/)
|
80
|
+
subject.should_receive(:puts).with(/^#{@ten_xs}#{@one_space}#{@ten_xs}#{@one_space}$/)
|
81
|
+
subject.should_receive(:puts).with(/^#{@ten_xs}#{@one_space}#{@five_xs}#{@six_spaces}$/)
|
82
|
+
subject.should_receive(:puts).with(/^#{@five_xs} {5,17}$/)
|
83
|
+
subject.to_s
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -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
|
data/spec/table_spec.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'table'
|
3
|
+
|
4
|
+
describe Table do
|
5
|
+
context 'creation' do
|
6
|
+
it 'defaults options hash' do
|
7
|
+
expect {
|
8
|
+
Table.new
|
9
|
+
}.to_not raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'defaults the border' do
|
13
|
+
Table.new.border.should == false
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'accepts the border' do
|
17
|
+
Table.new(:border => true).border.should == true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'rows' do
|
22
|
+
it 'allows addition' do
|
23
|
+
cols = [Column.new('test1'), Column.new('test2')]
|
24
|
+
row = Row.new
|
25
|
+
cols.each {|c| row.add(c)}
|
26
|
+
expect {
|
27
|
+
Table.new.add(row)
|
28
|
+
}.to_not raise_error
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'normalizes all column width and sizes' do
|
32
|
+
cols = [Column.new('asdf', :width => 5), Column.new('qwer')]
|
33
|
+
row = Row.new
|
34
|
+
row.add(cols[0])
|
35
|
+
t = Table.new
|
36
|
+
t.add(row)
|
37
|
+
row = Row.new
|
38
|
+
row.add(cols[1])
|
39
|
+
t.add(row)
|
40
|
+
output = capture_stdout {
|
41
|
+
t.to_s
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: command_line_reporter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: '2.0'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2012-01-11 00:00:00.000000000Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
17
|
-
requirement: &
|
17
|
+
requirement: &2152892720 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,7 +22,7 @@ dependencies:
|
|
22
22
|
version: 1.0.0
|
23
23
|
type: :development
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *2152892720
|
26
26
|
description: This gem makes it easy to provide a report while your ruby script is
|
27
27
|
executing
|
28
28
|
email: baywes@gmail.com
|
@@ -33,16 +33,26 @@ files:
|
|
33
33
|
- examples/nested.rb
|
34
34
|
- examples/progress.rb
|
35
35
|
- examples/simple.rb
|
36
|
+
- examples/table.rb
|
37
|
+
- lib/column.rb
|
36
38
|
- lib/command_line_reporter.rb
|
37
39
|
- lib/nested_formatter.rb
|
40
|
+
- lib/options_validator.rb
|
38
41
|
- lib/progress_formatter.rb
|
42
|
+
- lib/row.rb
|
43
|
+
- lib/table.rb
|
39
44
|
- lib/version.rb
|
40
45
|
- README.md
|
46
|
+
- spec/column_spec.rb
|
41
47
|
- spec/command_line_reporter_spec.rb
|
42
48
|
- spec/nested_formatter_spec.rb
|
49
|
+
- spec/options_validator_spec.rb
|
43
50
|
- spec/progress_formatter_spec.rb
|
51
|
+
- spec/row_spec.rb
|
44
52
|
- spec/spec_helper.rb
|
45
53
|
- spec/support/helpers/stdout.rb
|
54
|
+
- spec/support/matchers/argument.rb
|
55
|
+
- spec/table_spec.rb
|
46
56
|
homepage: http://github.com/wbailey/command_line_reporter
|
47
57
|
licenses: []
|
48
58
|
post_install_message:
|
@@ -57,7 +67,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
67
|
version: '0'
|
58
68
|
segments:
|
59
69
|
- 0
|
60
|
-
hash:
|
70
|
+
hash: 3958532185567160349
|
61
71
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
72
|
none: false
|
63
73
|
requirements:
|
@@ -71,8 +81,13 @@ signing_key:
|
|
71
81
|
specification_version: 3
|
72
82
|
summary: A tool for providing interactive command line applications
|
73
83
|
test_files:
|
84
|
+
- spec/column_spec.rb
|
74
85
|
- spec/command_line_reporter_spec.rb
|
75
86
|
- spec/nested_formatter_spec.rb
|
87
|
+
- spec/options_validator_spec.rb
|
76
88
|
- spec/progress_formatter_spec.rb
|
89
|
+
- spec/row_spec.rb
|
77
90
|
- spec/spec_helper.rb
|
78
91
|
- spec/support/helpers/stdout.rb
|
92
|
+
- spec/support/matchers/argument.rb
|
93
|
+
- spec/table_spec.rb
|