tty-table 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +22 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +22 -0
- data/README.md +389 -0
- data/Rakefile +8 -0
- data/benchmarks/speed.rb +36 -0
- data/lib/tty/table/border/ascii.rb +32 -0
- data/lib/tty/table/border/null.rb +37 -0
- data/lib/tty/table/border/row_line.rb +18 -0
- data/lib/tty/table/border/unicode.rb +32 -0
- data/lib/tty/table/border.rb +219 -0
- data/lib/tty/table/border_dsl.rb +251 -0
- data/lib/tty/table/border_options.rb +53 -0
- data/lib/tty/table/column_set.rb +121 -0
- data/lib/tty/table/columns.rb +170 -0
- data/lib/tty/table/empty.rb +26 -0
- data/lib/tty/table/error.rb +39 -0
- data/lib/tty/table/field.rb +139 -0
- data/lib/tty/table/header.rb +163 -0
- data/lib/tty/table/indentation.rb +52 -0
- data/lib/tty/table/operation/alignment_set.rb +103 -0
- data/lib/tty/table/operation/escape.rb +30 -0
- data/lib/tty/table/operation/filter.rb +34 -0
- data/lib/tty/table/operation/padding.rb +95 -0
- data/lib/tty/table/operation/truncation.rb +41 -0
- data/lib/tty/table/operation/wrapped.rb +43 -0
- data/lib/tty/table/operations.rb +69 -0
- data/lib/tty/table/options.rb +30 -0
- data/lib/tty/table/orientation/horizontal.rb +48 -0
- data/lib/tty/table/orientation/vertical.rb +38 -0
- data/lib/tty/table/orientation.rb +57 -0
- data/lib/tty/table/padder.rb +180 -0
- data/lib/tty/table/renderer/ascii.rb +16 -0
- data/lib/tty/table/renderer/basic.rb +294 -0
- data/lib/tty/table/renderer/color.rb +12 -0
- data/lib/tty/table/renderer/unicode.rb +21 -0
- data/lib/tty/table/renderer.rb +101 -0
- data/lib/tty/table/row.rb +248 -0
- data/lib/tty/table/transformation.rb +39 -0
- data/lib/tty/table/validatable.rb +64 -0
- data/lib/tty/table/version.rb +7 -0
- data/lib/tty/table.rb +469 -0
- data/lib/tty-table.rb +48 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/unit/access_spec.rb +86 -0
- data/spec/unit/add_row_spec.rb +28 -0
- data/spec/unit/border/ascii/rendering_spec.rb +90 -0
- data/spec/unit/border/new_spec.rb +27 -0
- data/spec/unit/border/null/rendering_spec.rb +130 -0
- data/spec/unit/border/options/from_spec.rb +38 -0
- data/spec/unit/border/options/new_spec.rb +14 -0
- data/spec/unit/border/unicode/rendering_spec.rb +63 -0
- data/spec/unit/border_options/new_spec.rb +20 -0
- data/spec/unit/border_options/update_spec.rb +18 -0
- data/spec/unit/column_set/extract_widths_spec.rb +15 -0
- data/spec/unit/column_set/total_width_spec.rb +15 -0
- data/spec/unit/column_set/widths_from_spec.rb +51 -0
- data/spec/unit/columns/enforce_spec.rb +67 -0
- data/spec/unit/columns/widths_spec.rb +35 -0
- data/spec/unit/data_spec.rb +14 -0
- data/spec/unit/each_spec.rb +41 -0
- data/spec/unit/each_with_index_spec.rb +57 -0
- data/spec/unit/empty_spec.rb +23 -0
- data/spec/unit/eql_spec.rb +34 -0
- data/spec/unit/field/equality_spec.rb +51 -0
- data/spec/unit/field/length_spec.rb +21 -0
- data/spec/unit/field/lines_spec.rb +21 -0
- data/spec/unit/field/new_spec.rb +29 -0
- data/spec/unit/field/width_spec.rb +23 -0
- data/spec/unit/filter_spec.rb +23 -0
- data/spec/unit/header/call_spec.rb +30 -0
- data/spec/unit/header/color_spec.rb +19 -0
- data/spec/unit/header/equality_spec.rb +51 -0
- data/spec/unit/header/height_spec.rb +27 -0
- data/spec/unit/header/new_spec.rb +25 -0
- data/spec/unit/header/set_spec.rb +20 -0
- data/spec/unit/header/to_ary_spec.rb +14 -0
- data/spec/unit/header_spec.rb +13 -0
- data/spec/unit/indentation/insert_indent_spec.rb +27 -0
- data/spec/unit/initialize_spec.rb +88 -0
- data/spec/unit/operation/alignment_set/call_spec.rb +39 -0
- data/spec/unit/operation/alignment_set/each_spec.rb +17 -0
- data/spec/unit/operation/alignment_set/new_spec.rb +27 -0
- data/spec/unit/operation/alignment_set/to_ary_spec.rb +14 -0
- data/spec/unit/operation/escape/call_spec.rb +16 -0
- data/spec/unit/operation/filter/call_spec.rb +17 -0
- data/spec/unit/operation/truncation/call_spec.rb +32 -0
- data/spec/unit/operation/wrapped/call_spec.rb +33 -0
- data/spec/unit/operations/new_spec.rb +30 -0
- data/spec/unit/options/access_spec.rb +14 -0
- data/spec/unit/options_spec.rb +25 -0
- data/spec/unit/orientation_spec.rb +145 -0
- data/spec/unit/padder/parse_spec.rb +45 -0
- data/spec/unit/padder/to_s_spec.rb +14 -0
- data/spec/unit/padding_spec.rb +120 -0
- data/spec/unit/properties_spec.rb +25 -0
- data/spec/unit/render_spec.rb +63 -0
- data/spec/unit/render_with_spec.rb +106 -0
- data/spec/unit/renderer/ascii/indentation_spec.rb +41 -0
- data/spec/unit/renderer/ascii/padding_spec.rb +61 -0
- data/spec/unit/renderer/ascii/render_spec.rb +68 -0
- data/spec/unit/renderer/ascii/resizing_spec.rb +114 -0
- data/spec/unit/renderer/ascii/separator_spec.rb +28 -0
- data/spec/unit/renderer/basic/alignment_spec.rb +88 -0
- data/spec/unit/renderer/basic/coloring_spec.rb +46 -0
- data/spec/unit/renderer/basic/extract_column_widths_spec.rb +28 -0
- data/spec/unit/renderer/basic/filter_spec.rb +53 -0
- data/spec/unit/renderer/basic/indentation_spec.rb +48 -0
- data/spec/unit/renderer/basic/multiline_content_spec.rb +135 -0
- data/spec/unit/renderer/basic/new_spec.rb +26 -0
- data/spec/unit/renderer/basic/options_spec.rb +52 -0
- data/spec/unit/renderer/basic/padding_spec.rb +52 -0
- data/spec/unit/renderer/basic/render_spec.rb +57 -0
- data/spec/unit/renderer/basic/resizing_spec.rb +96 -0
- data/spec/unit/renderer/basic/separator_spec.rb +43 -0
- data/spec/unit/renderer/basic/truncation_spec.rb +35 -0
- data/spec/unit/renderer/basic/wrapping_spec.rb +40 -0
- data/spec/unit/renderer/border_spec.rb +104 -0
- data/spec/unit/renderer/render_spec.rb +36 -0
- data/spec/unit/renderer/select_spec.rb +22 -0
- data/spec/unit/renderer/style_spec.rb +72 -0
- data/spec/unit/renderer/unicode/indentation_spec.rb +41 -0
- data/spec/unit/renderer/unicode/padding_spec.rb +61 -0
- data/spec/unit/renderer/unicode/render_spec.rb +68 -0
- data/spec/unit/renderer/unicode/separator_spec.rb +26 -0
- data/spec/unit/renderer_spec.rb +19 -0
- data/spec/unit/rotate_spec.rb +86 -0
- data/spec/unit/row/access_spec.rb +25 -0
- data/spec/unit/row/call_spec.rb +45 -0
- data/spec/unit/row/data_spec.rb +26 -0
- data/spec/unit/row/each_spec.rb +31 -0
- data/spec/unit/row/equality_spec.rb +73 -0
- data/spec/unit/row/height_spec.rb +27 -0
- data/spec/unit/row/new_spec.rb +41 -0
- data/spec/unit/row/to_ary_spec.rb +14 -0
- data/spec/unit/to_s_spec.rb +63 -0
- data/spec/unit/transformation/extract_tuples_spec.rb +35 -0
- data/spec/unit/validatable/validate_options_spec.rb +33 -0
- data/spec/unit/validatable_spec.rb +32 -0
- data/tasks/console.rake +10 -0
- data/tasks/coverage.rake +11 -0
- data/tasks/spec.rake +29 -0
- data/tty-table.gemspec +28 -0
- metadata +371 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
# A class responsible for enforcing column constraints.
|
6
|
+
#
|
7
|
+
# Used internally by {Renderer::Basic} to enforce correct column widths.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class Columns
|
11
|
+
|
12
|
+
attr_reader :table
|
13
|
+
|
14
|
+
attr_reader :renderer
|
15
|
+
|
16
|
+
MIN_WIDTH = 1
|
17
|
+
|
18
|
+
BORDER_WIDTH = 1
|
19
|
+
|
20
|
+
# Initialize a Columns
|
21
|
+
#
|
22
|
+
# @param [TTY::Table::Renderer] renderer
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
def initialize(renderer)
|
26
|
+
@renderer = renderer
|
27
|
+
@table = renderer.table
|
28
|
+
end
|
29
|
+
|
30
|
+
# Estimate outside border size
|
31
|
+
#
|
32
|
+
# @return [Integer]
|
33
|
+
#
|
34
|
+
# @api public
|
35
|
+
def outside_border_size
|
36
|
+
renderer.border_class == TTY::Table::Border::Null ? 0 : 2
|
37
|
+
end
|
38
|
+
|
39
|
+
# Total border size
|
40
|
+
#
|
41
|
+
# @return [Integer]
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
def border_size
|
45
|
+
BORDER_WIDTH * (table.column_size - 1) + outside_border_size
|
46
|
+
end
|
47
|
+
|
48
|
+
# Estimate minimum table width to be able to display content
|
49
|
+
#
|
50
|
+
# @return [Integer]
|
51
|
+
#
|
52
|
+
# @api public
|
53
|
+
def minimum_width
|
54
|
+
table.column_size * MIN_WIDTH + border_size
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return column's natural unconstrained widths
|
58
|
+
#
|
59
|
+
# @return [Integer]
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
def natural_width
|
63
|
+
renderer.column_widths.inject(0, &:+) + border_size
|
64
|
+
end
|
65
|
+
|
66
|
+
# Return the constrained column widths.
|
67
|
+
#
|
68
|
+
# Account for table field widths and any user defined
|
69
|
+
# constraints on the table width.
|
70
|
+
#
|
71
|
+
# @api public
|
72
|
+
def enforce
|
73
|
+
assert_minimum_width
|
74
|
+
|
75
|
+
unless renderer.padding.empty?
|
76
|
+
renderer.column_widths = adjust_padding
|
77
|
+
end
|
78
|
+
|
79
|
+
if natural_width <= renderer.width
|
80
|
+
renderer.column_widths = expand if renderer.resize
|
81
|
+
else
|
82
|
+
if renderer.resize
|
83
|
+
renderer.column_widths = shrink
|
84
|
+
else
|
85
|
+
rotate
|
86
|
+
renderer.column_widths = ColumnSet.widths_from(table)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Adjust column widths to account for padding whitespace
|
92
|
+
#
|
93
|
+
# @api private
|
94
|
+
def adjust_padding
|
95
|
+
padding = renderer.padding
|
96
|
+
column_size = table.column_size
|
97
|
+
|
98
|
+
(0...column_size).reduce([]) do |lengths, col|
|
99
|
+
lengths + [padding.left + renderer.column_widths[col] + padding.right]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Rotate table to vertical orientation and print information to stdout
|
104
|
+
#
|
105
|
+
# @api private
|
106
|
+
def rotate
|
107
|
+
Kernel.warn 'The table size exceeds the currently set width.' \
|
108
|
+
'To avoid error either. Defaulting to vertical ' \
|
109
|
+
'orientation.'
|
110
|
+
table.orientation = :vertical
|
111
|
+
table.rotate
|
112
|
+
end
|
113
|
+
|
114
|
+
# Expand column widths to match the requested width
|
115
|
+
#
|
116
|
+
# @api private
|
117
|
+
def expand
|
118
|
+
column_size = table.column_size
|
119
|
+
ratio = ((renderer.width - natural_width) / column_size.to_f).floor
|
120
|
+
|
121
|
+
widths = (0...column_size).reduce([]) do |lengths, col|
|
122
|
+
lengths + [renderer.column_widths[col] + ratio]
|
123
|
+
end
|
124
|
+
distribute_extra_width(widths)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Shrink column widths to match the requested width
|
128
|
+
#
|
129
|
+
# @api private
|
130
|
+
def shrink
|
131
|
+
column_size = table.column_size
|
132
|
+
ratio = ((natural_width - renderer.width) / column_size.to_f).ceil
|
133
|
+
|
134
|
+
widths = (0...column_size).reduce([]) do |lengths, col|
|
135
|
+
lengths + [renderer.column_widths[col] - ratio]
|
136
|
+
end
|
137
|
+
distribute_extra_width(widths)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Assert minimum width for the table content
|
141
|
+
#
|
142
|
+
# @raise [TTY::ResizeError]
|
143
|
+
#
|
144
|
+
# @api private
|
145
|
+
def assert_minimum_width
|
146
|
+
width = renderer.width
|
147
|
+
return unless width <= minimum_width
|
148
|
+
fail ResizeError, "Table's width is too small to contain the content " \
|
149
|
+
"(min width #{minimum_width}, currently set #{width})"
|
150
|
+
end
|
151
|
+
|
152
|
+
# Distribute remaining width to meet the total width requirement.
|
153
|
+
#
|
154
|
+
# @param [Array[Integer]] widths
|
155
|
+
#
|
156
|
+
# @api private
|
157
|
+
def distribute_extra_width(widths)
|
158
|
+
column_size = table.column_size
|
159
|
+
extra_width = renderer.width - (widths.reduce(:+) + border_size)
|
160
|
+
per_field_width = extra_width / column_size
|
161
|
+
remaining_width = extra_width % column_size
|
162
|
+
extra = [1] * remaining_width + [0] * (column_size - remaining_width)
|
163
|
+
|
164
|
+
widths.map.with_index do |width, index|
|
165
|
+
width + per_field_width + extra[index]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end # Columns
|
169
|
+
end # Table
|
170
|
+
end # TTY
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module TTY
|
2
|
+
class Table
|
3
|
+
class Empty < TTY::Table
|
4
|
+
|
5
|
+
ZERO_ROW = [].freeze
|
6
|
+
|
7
|
+
def self.new(header, rows = ZERO_ROW)
|
8
|
+
super.new(header, rows)
|
9
|
+
end
|
10
|
+
|
11
|
+
def each
|
12
|
+
return to_enum unless block_given?
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def size
|
17
|
+
0
|
18
|
+
end
|
19
|
+
|
20
|
+
def width
|
21
|
+
0
|
22
|
+
end
|
23
|
+
|
24
|
+
end # Empty
|
25
|
+
end # Table
|
26
|
+
end # TTY
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
# Raised when inserting into table with a mismatching row(s)
|
6
|
+
class DimensionMismatchError < ArgumentError; end
|
7
|
+
|
8
|
+
# Raised when reading non-existent element from a table
|
9
|
+
class TupleMissing < IndexError
|
10
|
+
attr_reader :i, :j
|
11
|
+
|
12
|
+
def initialize(i, j)
|
13
|
+
@i, @j = i, j
|
14
|
+
super("element at(#{i},#{j}) not found")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Raised when the table orientation is unkown
|
19
|
+
class InvalidOrientationError < ArgumentError; end
|
20
|
+
|
21
|
+
# Raised when the table cannot be resized
|
22
|
+
class ResizeError < ArgumentError; end
|
23
|
+
|
24
|
+
# Raised when the operation is not implemented
|
25
|
+
class NoImplementationError < NotImplementedError; end
|
26
|
+
|
27
|
+
# Raised when the argument type is different from expected
|
28
|
+
class TypeError < ArgumentError; end
|
29
|
+
|
30
|
+
# Raised when the required argument is not supplied
|
31
|
+
class ArgumentRequired < ArgumentError; end
|
32
|
+
|
33
|
+
# Raised when the argument is not expected
|
34
|
+
class InvalidArgument < ArgumentError; end
|
35
|
+
|
36
|
+
# Raised when the attribute is unknown
|
37
|
+
class UnknownAttributeError < IndexError; end
|
38
|
+
end # Table
|
39
|
+
end # TTY
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
# A class that represents a unique element in a table.
|
6
|
+
#
|
7
|
+
# Used internally by {Table::Row} to define internal structure.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class Field
|
11
|
+
include Equatable
|
12
|
+
|
13
|
+
# The value inside the field
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
attr_reader :value
|
17
|
+
|
18
|
+
# The name for the value
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
attr_reader :name
|
22
|
+
|
23
|
+
# TODO: Change to :content to separate value from formatted string
|
24
|
+
attr_writer :value
|
25
|
+
|
26
|
+
# The field value width
|
27
|
+
#
|
28
|
+
# @api public
|
29
|
+
attr_reader :width
|
30
|
+
|
31
|
+
# Number of columns this field spans. Defaults to 1.
|
32
|
+
#
|
33
|
+
attr_reader :colspan
|
34
|
+
|
35
|
+
# Number of rows this field spans. Defaults to 1.
|
36
|
+
#
|
37
|
+
attr_reader :rowspan
|
38
|
+
|
39
|
+
attr_reader :align
|
40
|
+
|
41
|
+
# Initialize a Field
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# field = TTY::Table::Field.new 'a1'
|
45
|
+
# field.value # => a1
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# field = TTY::Table::Field.new value: 'a1'
|
49
|
+
# field.value # => a1
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# field = TTY::Table::Field.new value: 'a1', align: :center
|
53
|
+
# field.value # => a1
|
54
|
+
# field.align # => :center
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
def initialize(value)
|
58
|
+
options = extract_options(value)
|
59
|
+
@width = options.fetch(:width) { @value.to_s.size }
|
60
|
+
@align = options.fetch(:align) { nil }
|
61
|
+
@colspan = options.fetch(:colspan) { 1 }
|
62
|
+
@rowspan = options.fetch(:rowspan) { 1 }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Extract options and set value
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
def extract_options(value)
|
69
|
+
if value.class <= Hash
|
70
|
+
options = value
|
71
|
+
@value = options.fetch(:value)
|
72
|
+
else
|
73
|
+
@value = value
|
74
|
+
options = {}
|
75
|
+
end
|
76
|
+
options
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return the width this field would normally have bar other contraints
|
80
|
+
#
|
81
|
+
# @api public
|
82
|
+
def value_width
|
83
|
+
@width
|
84
|
+
end
|
85
|
+
|
86
|
+
def value_height
|
87
|
+
@height
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return number of lines this value spans.
|
91
|
+
#
|
92
|
+
# A distinction is being made between escaped and non-escaped strings.
|
93
|
+
#
|
94
|
+
# @return [Array[String]]
|
95
|
+
#
|
96
|
+
# @api public
|
97
|
+
def lines
|
98
|
+
escaped = value.to_s.scan(/(\\n|\\t|\\r)/)
|
99
|
+
escaped.empty? ? value.to_s.split(/\n/, -1) : [value.to_s]
|
100
|
+
end
|
101
|
+
|
102
|
+
# If the string contains unescaped new lines then the longest token
|
103
|
+
# deterimines the actual field length.
|
104
|
+
#
|
105
|
+
# @return [Integer]
|
106
|
+
#
|
107
|
+
# @api public
|
108
|
+
def length
|
109
|
+
(lines.max_by(&:length) || '').size
|
110
|
+
end
|
111
|
+
|
112
|
+
# Extract the number of lines this value spans
|
113
|
+
#
|
114
|
+
# @return [Integer]
|
115
|
+
#
|
116
|
+
# @api public
|
117
|
+
def height
|
118
|
+
lines.size
|
119
|
+
end
|
120
|
+
|
121
|
+
def chars
|
122
|
+
value.chars
|
123
|
+
end
|
124
|
+
|
125
|
+
# Render value inside this field box
|
126
|
+
#
|
127
|
+
# @api public
|
128
|
+
def render
|
129
|
+
end
|
130
|
+
|
131
|
+
# Return field value
|
132
|
+
#
|
133
|
+
# @api public
|
134
|
+
def to_s
|
135
|
+
value
|
136
|
+
end
|
137
|
+
end # Field
|
138
|
+
end # Table
|
139
|
+
end # TTY
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
# Convert an Array row into Header
|
6
|
+
#
|
7
|
+
# @return [TTY::Table::Header]
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
def to_header(row)
|
11
|
+
Header.new(row)
|
12
|
+
end
|
13
|
+
|
14
|
+
# A set of header elements that correspond to values in each row
|
15
|
+
class Header
|
16
|
+
include Equatable, Enumerable
|
17
|
+
extend Forwardable
|
18
|
+
|
19
|
+
def_delegators :@attributes, :join, :map, :map!
|
20
|
+
|
21
|
+
# The header attributes
|
22
|
+
#
|
23
|
+
# @return [Array]
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
attr_reader :attributes
|
27
|
+
alias :fields :attributes
|
28
|
+
|
29
|
+
# Initialize a Header
|
30
|
+
#
|
31
|
+
# @return [undefined]
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def initialize(attributes = [])
|
35
|
+
@attributes = attributes.map { |attr| to_field(attr) }
|
36
|
+
@attribute_for = Hash[@attributes.each_with_index.map.to_a]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Iterate over each element in the vector
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# vec = Vector[1,2,3]
|
43
|
+
# vec.each { |element| ... }
|
44
|
+
#
|
45
|
+
# @return [self]
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def each
|
49
|
+
return to_enum unless block_given?
|
50
|
+
to_ary.each { |element| yield element }
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
# Instantiates a new field
|
55
|
+
#
|
56
|
+
# @param [String,Hash] attribute
|
57
|
+
# the attribute value to convert to field object
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
def to_field(attribute = nil)
|
61
|
+
Field.new(attribute)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Lookup a column in the header given a name
|
65
|
+
#
|
66
|
+
# @param [Integer, String] attribute
|
67
|
+
# the attribute to look up by
|
68
|
+
#
|
69
|
+
# @api public
|
70
|
+
def [](attribute)
|
71
|
+
case attribute
|
72
|
+
when Integer
|
73
|
+
@attributes[attribute].value
|
74
|
+
else
|
75
|
+
@attribute_for.fetch(to_field(attribute)) do |header_name|
|
76
|
+
fail UnknownAttributeError,
|
77
|
+
"the header '#{header_name.value}' is unknown"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Lookup attribute without evaluation
|
83
|
+
#
|
84
|
+
# @api public
|
85
|
+
def call(attribute)
|
86
|
+
@attributes[attribute]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Set value at index
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# header[attribute] = value
|
93
|
+
#
|
94
|
+
# @api public
|
95
|
+
def []=(attribute, value)
|
96
|
+
attributes[attribute] = to_field(value)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Size of the header
|
100
|
+
#
|
101
|
+
# @return [Integer]
|
102
|
+
#
|
103
|
+
# @api public
|
104
|
+
def size
|
105
|
+
to_ary.size
|
106
|
+
end
|
107
|
+
alias :length :size
|
108
|
+
|
109
|
+
# Find maximum header height
|
110
|
+
#
|
111
|
+
# @return [Integer]
|
112
|
+
#
|
113
|
+
# @api public
|
114
|
+
def height
|
115
|
+
attributes.map { |field| field.height }.max
|
116
|
+
end
|
117
|
+
|
118
|
+
# Convert the Header into an Array
|
119
|
+
#
|
120
|
+
# @return [Array]
|
121
|
+
#
|
122
|
+
# @api public
|
123
|
+
def to_ary
|
124
|
+
attributes.map { |attr| attr.value if attr }
|
125
|
+
end
|
126
|
+
|
127
|
+
# Return the header elements in an array.
|
128
|
+
#
|
129
|
+
# @return [Array]
|
130
|
+
#
|
131
|
+
# @api public
|
132
|
+
def to_a
|
133
|
+
to_ary.dup
|
134
|
+
end
|
135
|
+
|
136
|
+
# Check if there are no elements.
|
137
|
+
#
|
138
|
+
# @return [Boolean]
|
139
|
+
#
|
140
|
+
# @api public
|
141
|
+
def empty?
|
142
|
+
to_ary.empty?
|
143
|
+
end
|
144
|
+
|
145
|
+
# Check if this header is equivalent to another header
|
146
|
+
#
|
147
|
+
# @return [Boolean]
|
148
|
+
#
|
149
|
+
# @api public
|
150
|
+
def ==(other)
|
151
|
+
to_a == other.to_a
|
152
|
+
end
|
153
|
+
alias :eql? :==
|
154
|
+
|
155
|
+
# Provide an unique hash value
|
156
|
+
#
|
157
|
+
# @api public
|
158
|
+
def to_hash
|
159
|
+
to_a.hash
|
160
|
+
end
|
161
|
+
end # Header
|
162
|
+
end # Table
|
163
|
+
end # TTY
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
# A class responsible for indenting table representation
|
6
|
+
class Indentation
|
7
|
+
|
8
|
+
attr_reader :renderer
|
9
|
+
|
10
|
+
# Initialize an Indentation
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
def initialize(renderer)
|
14
|
+
@renderer = renderer
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create indentation
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def indentation
|
21
|
+
' ' * renderer.indent
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return a table part with indentation inserted
|
25
|
+
#
|
26
|
+
# @param [#map, #to_s] part
|
27
|
+
# the rendered table part
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def insert_indent(part)
|
31
|
+
if part.respond_to?(:to_a)
|
32
|
+
part.map { |line| insert_indentation(line) }
|
33
|
+
else
|
34
|
+
insert_indentation(part)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Insert indentation into a table renderd line
|
41
|
+
#
|
42
|
+
# @param [#to_a, #to_s] line
|
43
|
+
# the rendered table line
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
def insert_indentation(line)
|
47
|
+
line = line.is_a?(Array) ? line[0] : line
|
48
|
+
line.insert(0, indentation) if line
|
49
|
+
end
|
50
|
+
end # Indentation
|
51
|
+
end # Table
|
52
|
+
end # TTY
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
module Operation
|
6
|
+
# A class which responsiblity is to align table rows and header.
|
7
|
+
class AlignmentSet
|
8
|
+
include Enumerable
|
9
|
+
# Initialize an AlignmentSet
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
def initialize(aligns, widths = nil)
|
13
|
+
@converter = Necromancer.new
|
14
|
+
@elements = @converter.convert(aligns).to(:array)
|
15
|
+
@widths = widths
|
16
|
+
end
|
17
|
+
|
18
|
+
# Iterate over each element in the alignment set
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# alignment = AlignmentSet.new [1,2,3]
|
22
|
+
# alignment.each { |element| ... }
|
23
|
+
#
|
24
|
+
# @return [self]
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
def each
|
28
|
+
return to_enum unless block_given?
|
29
|
+
to_ary.each { |element| yield element }
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Lookup an alignment by index
|
34
|
+
#
|
35
|
+
# @param [Integer]
|
36
|
+
#
|
37
|
+
# @return [Symbol] alignment
|
38
|
+
#
|
39
|
+
# @api public
|
40
|
+
def [](index)
|
41
|
+
elements.fetch(index, :left)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return each alignment in an Array
|
45
|
+
#
|
46
|
+
# @return [Array]
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
def alignments
|
50
|
+
map { |alignment| alignment }
|
51
|
+
end
|
52
|
+
|
53
|
+
# Evaluate alignment of the provided row
|
54
|
+
#
|
55
|
+
# @param [Array] row
|
56
|
+
# the table row
|
57
|
+
# @param [Hash] options
|
58
|
+
# the table options
|
59
|
+
#
|
60
|
+
# @return [TTY::Table::Field]
|
61
|
+
#
|
62
|
+
# @api public
|
63
|
+
def call(field, row, col)
|
64
|
+
align_field(field, col)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Convert to array
|
68
|
+
#
|
69
|
+
# @return [Array]
|
70
|
+
#
|
71
|
+
# @api public
|
72
|
+
def to_ary
|
73
|
+
@elements
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
attr_reader :widths
|
79
|
+
|
80
|
+
attr_reader :elements
|
81
|
+
|
82
|
+
# Align each field in a row
|
83
|
+
#
|
84
|
+
# @param [TTY::Table::Field] field
|
85
|
+
# the table field
|
86
|
+
#
|
87
|
+
# @param [Integer] col
|
88
|
+
# the table column index
|
89
|
+
#
|
90
|
+
# @param [Hash] options
|
91
|
+
#
|
92
|
+
# @return [TTY::Table::Field]
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
def align_field(field, col)
|
96
|
+
column_width = widths[col]
|
97
|
+
direction = field.align || self[col]
|
98
|
+
field.value = Verse.align(field.to_s, column_width, direction)
|
99
|
+
end
|
100
|
+
end # AlignmentSet
|
101
|
+
end # Operation
|
102
|
+
end # Table
|
103
|
+
end # TTY
|