tty 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/tty/table.rb CHANGED
@@ -8,7 +8,7 @@ require 'tty/table/validatable'
8
8
  module TTY
9
9
  class Table
10
10
  include Comparable, Enumerable, Renderer, Conversion
11
- include Validatable
11
+ include Validatable, Equatable
12
12
  extend Forwardable
13
13
 
14
14
  # The table header
@@ -26,9 +26,23 @@ module TTY
26
26
  attr_reader :rows
27
27
  private :rows
28
28
 
29
+ # The table enforced column widths
30
+ #
31
+ # @return [Array]
32
+ #
33
+ # @api public
34
+ attr_accessor :column_widths
35
+
36
+ # The table column alignments
37
+ #
38
+ # @return [Operation::AlignmentSet]
39
+ #
40
+ # @api private
41
+ attr_reader :alignments
42
+
29
43
  # Subset of safe methods that both Array and Hash implement
30
44
  def_delegators(:@rows, :[], :assoc, :flatten, :include?, :index,
31
- :inspect, :length, :select, :to_a, :values_at, :pretty_print, :rassoc)
45
+ :length, :select, :to_a, :values_at, :pretty_print, :rassoc)
32
46
 
33
47
  # The table orientation
34
48
  #
@@ -81,23 +95,25 @@ module TTY
81
95
  # @option options [String] :header
82
96
  # column names to be displayed
83
97
  # @option options [String] :rows
84
- # Array of Arrays expressin the rows
98
+ # Array of Arrays expressing the rows
85
99
  # @option options [String] :renderer
86
100
  # used to format table output
87
- # @option options [String] :alignments
101
+ # @option options [String] :column_aligns
88
102
  # used to format table individual column alignment
89
103
  # @option options [String] :column_widths
90
104
  # used to format table individula column width
91
105
  #
92
- # @return [Table]
106
+ # @return [TTY::Table]
93
107
  #
94
108
  # @api private
95
109
  def initialize(options={}, &block)
96
- @header = options.fetch :header, []
110
+ @header = options.fetch :header, nil
97
111
  @rows = coerce(options.fetch :rows, [])
98
112
  @renderer = pick_renderer options[:renderer]
99
- @column_aligns = options.fetch :column_aligns, []
113
+ # TODO: assert that row_size is the same as column widths & aligns
100
114
  @column_widths = options.fetch :column_widths, []
115
+ @alignments = Operation::AlignmentSet.new options[:column_aligns]
116
+
101
117
  assert_row_sizes @rows
102
118
  yield_or_eval &block if block_given?
103
119
  end
@@ -109,9 +125,10 @@ module TTY
109
125
  if i >= 0 && j >= 0
110
126
  rows.fetch(i){return nil}[j]
111
127
  else
112
- raise IndexError.new("index #{index} not found")
128
+ raise IndexError.new("element at(#{i},#{j}) not found")
113
129
  end
114
130
  end
131
+ alias at []
115
132
  alias element []
116
133
  alias component []
117
134
 
@@ -178,11 +195,14 @@ module TTY
178
195
  #
179
196
  # @param [Array] row
180
197
  #
198
+ # @return [self]
199
+ #
181
200
  # @api public
182
201
  def <<(row)
183
202
  rows_copy = rows.dup
184
203
  assert_row_sizes rows_copy << row
185
204
  rows << row
205
+ self
186
206
  end
187
207
 
188
208
  # Iterate over each tuple in the set
@@ -247,7 +267,7 @@ module TTY
247
267
  #
248
268
  # @api public
249
269
  def width
250
- extract_column_widths(rows)
270
+ render(self)
251
271
  total_width
252
272
  end
253
273
 
@@ -261,39 +281,13 @@ module TTY
261
281
  column_size == 0 || row_size == 0
262
282
  end
263
283
 
264
- # Compare this table with other table for equality
265
- #
266
- # @param [TTY::Table] other
267
- #
268
- # @return [Boolean]
269
- #
270
- # @api public
271
- def eql?(other)
272
- instance_of?(other.class)
273
- end
274
-
275
- # Compare the table with other table for equivalency
276
- #
277
- # @example
278
- # table == other # => true or false
279
- #
280
- # @param [TTY::Table] other
281
- # the other table to compare with
282
- #
283
- # @return [Boolean]
284
- def ==(other)
285
- header == other.header &&
286
- to_a == other.to_a
287
- end
288
-
289
284
  # Return string representation of table
290
285
  #
291
286
  # @return [String]
292
287
  #
293
288
  # @api public
294
289
  def to_s
295
- render(rows, :column_widths => @column_widths,
296
- :column_aligns => @column_aligns)
290
+ render(self)
297
291
  end
298
292
 
299
293
  # Coerce an Enumerable into a Table
data/lib/tty/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  module TTY
4
- VERSION = "0.0.3"
4
+ VERSION = "0.0.4"
5
5
  end
data/lib/tty.rb CHANGED
@@ -6,6 +6,7 @@ require 'tty/support/utils'
6
6
  require 'tty/support/delegatable'
7
7
  require 'tty/support/conversion'
8
8
  require 'tty/support/coercion'
9
+ require 'tty/support/equatable'
9
10
 
10
11
  require 'tty/color'
11
12
  require 'tty/terminal'
@@ -14,6 +15,7 @@ require 'tty/table'
14
15
 
15
16
  require 'tty/table/operation/alignment_set'
16
17
  require 'tty/table/operation/alignment'
18
+ require 'tty/table/operation/truncation'
17
19
 
18
20
  module TTY
19
21
 
@@ -0,0 +1,206 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+
5
+ describe TTY::Equatable do
6
+ let(:name) { 'Value' }
7
+
8
+ context 'without attributes' do
9
+ let(:klass) { ::Class.new }
10
+
11
+ subject { klass.new }
12
+
13
+ before {
14
+ klass.stub(:name).and_return 'Value'
15
+ klass.send :include, described_class
16
+ }
17
+
18
+ it { should respond_to :compare? }
19
+
20
+ it { should be_instance_of klass }
21
+
22
+ describe '#eql?' do
23
+ context 'when objects are similar' do
24
+ let(:other) { subject.dup }
25
+
26
+ it { subject.eql?(other).should be_true }
27
+ end
28
+
29
+ context 'when objects are different' do
30
+ let(:other) { stub('other') }
31
+
32
+ it { subject.eql?(other).should be_false }
33
+ end
34
+ end
35
+
36
+ describe '#==' do
37
+ context 'when objects are similar' do
38
+ let(:other) { subject.dup }
39
+
40
+ it { (subject == other).should be_true }
41
+ end
42
+
43
+ context 'when objects are different' do
44
+ let(:other) { stub('other') }
45
+
46
+ it { (subject == other)}
47
+ end
48
+ end
49
+
50
+ describe '#inspect' do
51
+ it { subject.inspect.should eql('#<Value>') }
52
+ end
53
+
54
+ describe '#hash' do
55
+ it { subject.hash.should eql([klass].hash) }
56
+ end
57
+
58
+
59
+ end
60
+
61
+ context 'with attributes' do
62
+ let(:value) { 11 }
63
+ let(:klass) {
64
+ ::Class.new do
65
+ include TTY::Equatable
66
+
67
+ attr_reader :value
68
+
69
+ def initialize(value)
70
+ @value = value
71
+ end
72
+ end
73
+ }
74
+
75
+ before {
76
+ klass.stub(:name).and_return name
77
+ }
78
+
79
+ subject { klass.new(value) }
80
+
81
+ it 'dynamically defines #hash method' do
82
+ klass.method_defined?(:hash).should be_true
83
+ end
84
+
85
+ it 'dynamically defines #inspect method' do
86
+ klass.method_defined?(:inspect).should be_true
87
+ end
88
+
89
+ it { should respond_to :compare? }
90
+
91
+ it { should respond_to :eql? }
92
+
93
+ describe '#eql?' do
94
+ context 'when objects are similar' do
95
+ let(:other) { subject.dup }
96
+
97
+ it { subject.eql?(other).should be_true }
98
+ end
99
+
100
+ context 'when objects are different' do
101
+ let(:other) { stub('other') }
102
+
103
+ it { subject.eql?(other).should be_false }
104
+ end
105
+ end
106
+
107
+ describe '#==' do
108
+ context 'when objects are similar' do
109
+ let(:other) { subject.dup }
110
+
111
+ it { (subject == other).should be_true }
112
+ end
113
+
114
+ context 'when objects are different' do
115
+ let(:other) { stub('other') }
116
+
117
+ it { (subject == other).should be_false }
118
+ end
119
+ end
120
+
121
+ describe '#inspect' do
122
+ it { subject.inspect.should eql('#<Value value=11>') }
123
+ end
124
+
125
+ describe '#hash' do
126
+ it { subject.hash.should eql( ([klass] + [value]).hash) }
127
+ end
128
+
129
+ context 'equivalence relation' do
130
+ let(:other) { subject.dup }
131
+
132
+ it 'is not equal to nil reference' do
133
+ subject.eql?(nil).should be_false
134
+ end
135
+
136
+ it 'is reflexive' do
137
+ subject.eql?(subject).should be_true
138
+ end
139
+
140
+ it 'is symmetric' do
141
+ (subject.eql?(other)).should eql( other.eql?(subject) )
142
+ end
143
+
144
+ it 'is transitive'
145
+ end
146
+ end
147
+
148
+ context 'subclass' do
149
+ let(:value) { 11 }
150
+ let(:klass) {
151
+ ::Class.new do
152
+ include TTY::Equatable
153
+
154
+ attr_reader :value
155
+
156
+ def initialize(value)
157
+ @value = value
158
+ end
159
+ end
160
+ }
161
+ let(:subclass) { ::Class.new(klass) }
162
+
163
+ before {
164
+ klass.stub(:name).and_return name
165
+ }
166
+
167
+ subject { subclass.new(value) }
168
+
169
+ it { subclass.superclass.should == klass }
170
+
171
+ it { should respond_to :value }
172
+
173
+ describe '#inspect' do
174
+ it { subject.inspect.should eql('#<Value value=11>') }
175
+ end
176
+
177
+ describe '#eql?' do
178
+ context 'when objects are similar' do
179
+ let(:other) { subject.dup }
180
+
181
+ it { subject.eql?(other).should be_true }
182
+ end
183
+
184
+ context 'when objects are different' do
185
+ let(:other) { stub('other') }
186
+
187
+ it { subject.eql?(other).should be_false }
188
+ end
189
+ end
190
+
191
+ describe '#==' do
192
+ context 'when objects are similar' do
193
+ let(:other) { subject.dup }
194
+
195
+ it { (subject == other).should be_true }
196
+ end
197
+
198
+ context 'when objects are different' do
199
+ let(:other) { stub('other') }
200
+
201
+ it { (subject == other)}
202
+ end
203
+ end
204
+ end
205
+
206
+ end
@@ -10,10 +10,17 @@ describe TTY::Table, 'access' do
10
10
 
11
11
  it { should respond_to(:component) }
12
12
 
13
+ it { should respond_to(:at) }
14
+
13
15
  its([0,0]) { should == 'a1'}
14
16
 
15
- context '#row' do
17
+ its([5,5]) { should be_nil }
16
18
 
19
+ it 'raises error for negative indices' do
20
+ expect { subject[-5,-5] }.to raise_error(IndexError)
21
+ end
22
+
23
+ context '#row' do
17
24
  it 'returns nil for wrong index' do
18
25
  subject.row(11).should be_nil
19
26
  end
@@ -3,8 +3,14 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe TTY::Table, '#eql?' do
6
+ let(:rows) { [['a1', 'a2'], ['b1', 'b2']] }
7
+ let(:object) { described_class.new rows }
8
+
6
9
  subject { object.eql?(other) }
7
- let(:object) { described_class.new }
10
+
11
+ describe '#inspect' do
12
+ it { object.inspect.should =~ /#<TTY::Table/ }
13
+ end
8
14
 
9
15
  context 'with the same object' do
10
16
  let(:other) { object }
@@ -12,53 +12,61 @@ describe TTY::Table, '#initialize' do
12
12
 
13
13
  it { (Enumerable === subject).should be_true }
14
14
 
15
- it 'initializes table header' do
16
- table = TTY::Table.new :header => header
17
- table.header.should == header
18
- end
15
+ context 'with rows only' do
16
+ it 'initializes with rows as arguments' do
17
+ table = TTY::Table[*rows]
18
+ table.to_a.should == rows
19
+ end
19
20
 
20
- it 'initializes table header as an option' do
21
- table = TTY::Table.new :header => header
22
- table.header.should == header
23
- end
21
+ it 'initializes with rows' do
22
+ table = TTY::Table.new rows
23
+ table.to_a.should == rows
24
+ end
24
25
 
25
- it 'initializes with rows as arguments' do
26
- table = TTY::Table[*rows]
27
- table.to_a.should == rows
28
- end
26
+ it 'initializes table rows as an option' do
27
+ table = TTY::Table.new :rows => rows
28
+ table.to_a.should == rows
29
+ end
29
30
 
30
- it 'initializes ' do
31
- table = TTY::Table.new rows
32
- table.to_a.should == rows
33
- end
31
+ it 'initializes table rows in a block with param' do
32
+ table = TTY::Table.new do |t|
33
+ t << rows[0]
34
+ t << rows[1]
35
+ end
36
+ table.to_a.should == rows
37
+ end
34
38
 
35
- it 'initializes table rows as an option' do
36
- table = TTY::Table.new :rows => rows
37
- table.to_a.should == rows
38
- end
39
+ it 'initializes table and adds rows' do
40
+ table = TTY::Table.new
41
+ table << rows[0]
42
+ table << rows[1]
43
+ table.to_a.should == rows
44
+ end
39
45
 
40
- it 'initializes table rows as an argument' do
41
- table = TTY::Table.new header, rows
42
- table.to_a.should == rows
46
+ it 'chains rows' do
47
+ table = TTY::Table.new
48
+ table << rows[0] << rows[1]
49
+ table.to_a.should == rows
50
+ end
43
51
  end
44
52
 
45
- it 'initializes table rows in a block with param' do
46
- table = TTY::Table.new do |t|
47
- t << rows[0]
48
- t << rows[1]
53
+ context 'with header and rows' do
54
+ it 'initializes header as an option' do
55
+ table = TTY::Table.new :header => header
56
+ table.header.should == header
49
57
  end
50
- table.to_a.should == rows
51
- end
52
58
 
53
- it 'initializes table and adds rows' do
54
- table = TTY::Table.new
55
- table << rows[0]
56
- table << rows[1]
57
- table.to_a.should == rows
59
+ it 'initializes table rows as an argument' do
60
+ table = TTY::Table.new header, rows
61
+ table.header.should == header
62
+ table.to_a.should == rows
63
+ end
58
64
  end
59
65
 
60
- it 'converts row arguments from hash to array' do
61
- table = TTY::Table.new :rows => {:a => 1, :b => 2}
62
- table.to_a.should include [:a, 1 ]
66
+ context 'coercion' do
67
+ it 'converts row arguments from hash to array' do
68
+ table = TTY::Table.new :rows => {:a => 1, :b => 2}
69
+ table.to_a.should include [:a, 1 ]
70
+ end
63
71
  end
64
72
  end
@@ -4,21 +4,22 @@ require 'spec_helper'
4
4
 
5
5
  describe TTY::Table::Operation::Alignment, '#new' do
6
6
  let(:object) { described_class.new alignment }
7
- let(:cell) { "aaaa"}
7
+ let(:field) { "aaaa"}
8
+ let(:space) { '' }
8
9
  let(:column_width) { 8 }
9
10
 
10
- subject { object.format cell, column_width }
11
+ subject { object.format field, column_width, space }
11
12
 
12
13
  shared_examples_for 'lower column width' do
13
14
  let(:column_width) { 2 }
14
15
 
15
- it { should == cell }
16
+ it { should == field }
16
17
  end
17
18
 
18
19
  context 'left aligend' do
19
20
  let(:alignment) { :left }
20
21
 
21
- it { should == cell + ' ' }
22
+ it { should == field + ' ' }
22
23
 
23
24
  it_should_behave_like 'lower column width'
24
25
  end
@@ -26,7 +27,7 @@ describe TTY::Table::Operation::Alignment, '#new' do
26
27
  context 'center aligned' do
27
28
  let(:alignment) { :center }
28
29
 
29
- it { should == ' ' + cell + ' ' }
30
+ it { should == ' ' + field + ' ' }
30
31
 
31
32
  it_should_behave_like 'lower column width'
32
33
  end
@@ -36,4 +37,11 @@ describe TTY::Table::Operation::Alignment, '#new' do
36
37
 
37
38
  it_should_behave_like 'lower column width'
38
39
  end
40
+
41
+ context 'with space' do
42
+ let(:alignment) { :center }
43
+ let(:space) { ' '}
44
+
45
+ it { should == ' ' + field + ' ' }
46
+ end
39
47
  end
@@ -14,7 +14,7 @@ describe TTY::Table::Operation::AlignmentSet, '#align_rows' do
14
14
 
15
15
  it { should be_instance_of(Array) }
16
16
 
17
- it { should == ['a1 a2 ', 'b1 b2 '] }
17
+ it { should == [['a1 ', 'a2 '], ['b1 ', 'b2 ']] }
18
18
  end
19
19
 
20
20
  context 'aligned with column widths and alignments' do
@@ -23,7 +23,7 @@ describe TTY::Table::Operation::AlignmentSet, '#align_rows' do
23
23
 
24
24
  it { should be_instance_of(Array) }
25
25
 
26
- it { should == [' a1 a2 ', ' b1 b2 '] }
26
+ it { should == [[' a1 ', 'a2 '], [' b1 ', 'b2 ']] }
27
27
  end
28
28
 
29
29
  context 'aligned with no column widths and no alignments' do
@@ -32,7 +32,7 @@ describe TTY::Table::Operation::AlignmentSet, '#align_rows' do
32
32
 
33
33
  it { should be_instance_of(Array) }
34
34
 
35
- it { should == ['a1 a2', 'b1 b2'] }
35
+ it { should == [['a1 ', 'a2'], ['b1 ', 'b2']] }
36
36
  end
37
37
 
38
38
  context 'aligned with no column widths and alignments' do
@@ -41,6 +41,6 @@ describe TTY::Table::Operation::AlignmentSet, '#align_rows' do
41
41
 
42
42
  it { should be_instance_of(Array) }
43
43
 
44
- it { should == ['a1 a2', 'b1 b2'] }
44
+ it { should == [['a1 ', 'a2'], ['b1 ', 'b2']] }
45
45
  end
46
46
  end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+
5
+ describe TTY::Table::Operation::Truncation, '#truncate' do
6
+ let(:instance) { described_class.new }
7
+ let(:text) { '太丸ゴシック体' }
8
+
9
+ subject { instance.truncate(text, width) }
10
+
11
+ context 'without shortening' do
12
+ let(:width) { 8 }
13
+
14
+ it { should == text }
15
+ end
16
+
17
+ context 'with shortening' do
18
+ let(:width) { 5 }
19
+
20
+ it { should == '太丸ゴシ…' }
21
+ end
22
+ end
@@ -17,7 +17,11 @@ describe TTY::Table, 'options' do
17
17
 
18
18
  its(:column_widths) { should == [2,2] }
19
19
 
20
- its(:column_aligns) { should == [] }
20
+ its(:alignments) { should be_kind_of TTY::Table::Operation::AlignmentSet }
21
+
22
+ it 'is empty' do
23
+ subject.alignments.to_a.should be_empty
24
+ end
21
25
 
22
26
  context '#column_widths' do
23
27
  let(:widths) { [10, 10] }
@@ -28,6 +32,8 @@ describe TTY::Table, 'options' do
28
32
  context '#column_aligns' do
29
33
  let(:aligns) { [:center, :center] }
30
34
 
31
- its(:column_aligns) { should == aligns }
35
+ it 'unwraps original array' do
36
+ subject.alignments.to_a.should == aligns
37
+ end
32
38
  end
33
39
  end
@@ -0,0 +1,40 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+
5
+ describe TTY::Table::Renderer::Basic, 'alignment' do
6
+ let(:header) { ['h1', 'h2', 'h3'] }
7
+ let(:rows) { [['a1', 'a2', 'a3'], ['b1', 'b2', 'b3']] }
8
+
9
+ it 'aligns left by default' do
10
+ header = ['h1', 'h2']
11
+ rows = [['aaaaa', 'a'], ['b', 'bbbbb']]
12
+ table = TTY::Table.new header, rows, :renderer => :basic
13
+ table.to_s.should == <<-EOS.normalize
14
+ h1 h2
15
+ aaaaa a
16
+ b bbbbb
17
+ EOS
18
+ end
19
+
20
+ it 'aligns table headers' do
21
+ header = ['header1', 'head2', 'h3']
22
+ table = TTY::Table.new header, rows, :render => :basic,
23
+ :column_aligns => [:left, :center, :right]
24
+ table.to_s.should == <<-EOS.normalize
25
+ header1 head2 h3
26
+ a1 a2 a3
27
+ b1 b2 b3
28
+ EOS
29
+ end
30
+
31
+ it 'aligns table rows' do
32
+ rows = [['aaaaa', 'a'], ['b', 'bbbbb']]
33
+ table = TTY::Table.new rows, :renderer => :basic,
34
+ :column_aligns => [:left, :right]
35
+ table.to_s.should == <<-EOS.normalize
36
+ aaaaa a
37
+ b bbbbb
38
+ EOS
39
+ end
40
+ end