tty 0.0.3 → 0.0.4

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/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