tty-table 0.1.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.
- 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
|