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.
Files changed (148) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +22 -0
  6. data/Gemfile +19 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +389 -0
  9. data/Rakefile +8 -0
  10. data/benchmarks/speed.rb +36 -0
  11. data/lib/tty/table/border/ascii.rb +32 -0
  12. data/lib/tty/table/border/null.rb +37 -0
  13. data/lib/tty/table/border/row_line.rb +18 -0
  14. data/lib/tty/table/border/unicode.rb +32 -0
  15. data/lib/tty/table/border.rb +219 -0
  16. data/lib/tty/table/border_dsl.rb +251 -0
  17. data/lib/tty/table/border_options.rb +53 -0
  18. data/lib/tty/table/column_set.rb +121 -0
  19. data/lib/tty/table/columns.rb +170 -0
  20. data/lib/tty/table/empty.rb +26 -0
  21. data/lib/tty/table/error.rb +39 -0
  22. data/lib/tty/table/field.rb +139 -0
  23. data/lib/tty/table/header.rb +163 -0
  24. data/lib/tty/table/indentation.rb +52 -0
  25. data/lib/tty/table/operation/alignment_set.rb +103 -0
  26. data/lib/tty/table/operation/escape.rb +30 -0
  27. data/lib/tty/table/operation/filter.rb +34 -0
  28. data/lib/tty/table/operation/padding.rb +95 -0
  29. data/lib/tty/table/operation/truncation.rb +41 -0
  30. data/lib/tty/table/operation/wrapped.rb +43 -0
  31. data/lib/tty/table/operations.rb +69 -0
  32. data/lib/tty/table/options.rb +30 -0
  33. data/lib/tty/table/orientation/horizontal.rb +48 -0
  34. data/lib/tty/table/orientation/vertical.rb +38 -0
  35. data/lib/tty/table/orientation.rb +57 -0
  36. data/lib/tty/table/padder.rb +180 -0
  37. data/lib/tty/table/renderer/ascii.rb +16 -0
  38. data/lib/tty/table/renderer/basic.rb +294 -0
  39. data/lib/tty/table/renderer/color.rb +12 -0
  40. data/lib/tty/table/renderer/unicode.rb +21 -0
  41. data/lib/tty/table/renderer.rb +101 -0
  42. data/lib/tty/table/row.rb +248 -0
  43. data/lib/tty/table/transformation.rb +39 -0
  44. data/lib/tty/table/validatable.rb +64 -0
  45. data/lib/tty/table/version.rb +7 -0
  46. data/lib/tty/table.rb +469 -0
  47. data/lib/tty-table.rb +48 -0
  48. data/spec/spec_helper.rb +51 -0
  49. data/spec/unit/access_spec.rb +86 -0
  50. data/spec/unit/add_row_spec.rb +28 -0
  51. data/spec/unit/border/ascii/rendering_spec.rb +90 -0
  52. data/spec/unit/border/new_spec.rb +27 -0
  53. data/spec/unit/border/null/rendering_spec.rb +130 -0
  54. data/spec/unit/border/options/from_spec.rb +38 -0
  55. data/spec/unit/border/options/new_spec.rb +14 -0
  56. data/spec/unit/border/unicode/rendering_spec.rb +63 -0
  57. data/spec/unit/border_options/new_spec.rb +20 -0
  58. data/spec/unit/border_options/update_spec.rb +18 -0
  59. data/spec/unit/column_set/extract_widths_spec.rb +15 -0
  60. data/spec/unit/column_set/total_width_spec.rb +15 -0
  61. data/spec/unit/column_set/widths_from_spec.rb +51 -0
  62. data/spec/unit/columns/enforce_spec.rb +67 -0
  63. data/spec/unit/columns/widths_spec.rb +35 -0
  64. data/spec/unit/data_spec.rb +14 -0
  65. data/spec/unit/each_spec.rb +41 -0
  66. data/spec/unit/each_with_index_spec.rb +57 -0
  67. data/spec/unit/empty_spec.rb +23 -0
  68. data/spec/unit/eql_spec.rb +34 -0
  69. data/spec/unit/field/equality_spec.rb +51 -0
  70. data/spec/unit/field/length_spec.rb +21 -0
  71. data/spec/unit/field/lines_spec.rb +21 -0
  72. data/spec/unit/field/new_spec.rb +29 -0
  73. data/spec/unit/field/width_spec.rb +23 -0
  74. data/spec/unit/filter_spec.rb +23 -0
  75. data/spec/unit/header/call_spec.rb +30 -0
  76. data/spec/unit/header/color_spec.rb +19 -0
  77. data/spec/unit/header/equality_spec.rb +51 -0
  78. data/spec/unit/header/height_spec.rb +27 -0
  79. data/spec/unit/header/new_spec.rb +25 -0
  80. data/spec/unit/header/set_spec.rb +20 -0
  81. data/spec/unit/header/to_ary_spec.rb +14 -0
  82. data/spec/unit/header_spec.rb +13 -0
  83. data/spec/unit/indentation/insert_indent_spec.rb +27 -0
  84. data/spec/unit/initialize_spec.rb +88 -0
  85. data/spec/unit/operation/alignment_set/call_spec.rb +39 -0
  86. data/spec/unit/operation/alignment_set/each_spec.rb +17 -0
  87. data/spec/unit/operation/alignment_set/new_spec.rb +27 -0
  88. data/spec/unit/operation/alignment_set/to_ary_spec.rb +14 -0
  89. data/spec/unit/operation/escape/call_spec.rb +16 -0
  90. data/spec/unit/operation/filter/call_spec.rb +17 -0
  91. data/spec/unit/operation/truncation/call_spec.rb +32 -0
  92. data/spec/unit/operation/wrapped/call_spec.rb +33 -0
  93. data/spec/unit/operations/new_spec.rb +30 -0
  94. data/spec/unit/options/access_spec.rb +14 -0
  95. data/spec/unit/options_spec.rb +25 -0
  96. data/spec/unit/orientation_spec.rb +145 -0
  97. data/spec/unit/padder/parse_spec.rb +45 -0
  98. data/spec/unit/padder/to_s_spec.rb +14 -0
  99. data/spec/unit/padding_spec.rb +120 -0
  100. data/spec/unit/properties_spec.rb +25 -0
  101. data/spec/unit/render_spec.rb +63 -0
  102. data/spec/unit/render_with_spec.rb +106 -0
  103. data/spec/unit/renderer/ascii/indentation_spec.rb +41 -0
  104. data/spec/unit/renderer/ascii/padding_spec.rb +61 -0
  105. data/spec/unit/renderer/ascii/render_spec.rb +68 -0
  106. data/spec/unit/renderer/ascii/resizing_spec.rb +114 -0
  107. data/spec/unit/renderer/ascii/separator_spec.rb +28 -0
  108. data/spec/unit/renderer/basic/alignment_spec.rb +88 -0
  109. data/spec/unit/renderer/basic/coloring_spec.rb +46 -0
  110. data/spec/unit/renderer/basic/extract_column_widths_spec.rb +28 -0
  111. data/spec/unit/renderer/basic/filter_spec.rb +53 -0
  112. data/spec/unit/renderer/basic/indentation_spec.rb +48 -0
  113. data/spec/unit/renderer/basic/multiline_content_spec.rb +135 -0
  114. data/spec/unit/renderer/basic/new_spec.rb +26 -0
  115. data/spec/unit/renderer/basic/options_spec.rb +52 -0
  116. data/spec/unit/renderer/basic/padding_spec.rb +52 -0
  117. data/spec/unit/renderer/basic/render_spec.rb +57 -0
  118. data/spec/unit/renderer/basic/resizing_spec.rb +96 -0
  119. data/spec/unit/renderer/basic/separator_spec.rb +43 -0
  120. data/spec/unit/renderer/basic/truncation_spec.rb +35 -0
  121. data/spec/unit/renderer/basic/wrapping_spec.rb +40 -0
  122. data/spec/unit/renderer/border_spec.rb +104 -0
  123. data/spec/unit/renderer/render_spec.rb +36 -0
  124. data/spec/unit/renderer/select_spec.rb +22 -0
  125. data/spec/unit/renderer/style_spec.rb +72 -0
  126. data/spec/unit/renderer/unicode/indentation_spec.rb +41 -0
  127. data/spec/unit/renderer/unicode/padding_spec.rb +61 -0
  128. data/spec/unit/renderer/unicode/render_spec.rb +68 -0
  129. data/spec/unit/renderer/unicode/separator_spec.rb +26 -0
  130. data/spec/unit/renderer_spec.rb +19 -0
  131. data/spec/unit/rotate_spec.rb +86 -0
  132. data/spec/unit/row/access_spec.rb +25 -0
  133. data/spec/unit/row/call_spec.rb +45 -0
  134. data/spec/unit/row/data_spec.rb +26 -0
  135. data/spec/unit/row/each_spec.rb +31 -0
  136. data/spec/unit/row/equality_spec.rb +73 -0
  137. data/spec/unit/row/height_spec.rb +27 -0
  138. data/spec/unit/row/new_spec.rb +41 -0
  139. data/spec/unit/row/to_ary_spec.rb +14 -0
  140. data/spec/unit/to_s_spec.rb +63 -0
  141. data/spec/unit/transformation/extract_tuples_spec.rb +35 -0
  142. data/spec/unit/validatable/validate_options_spec.rb +33 -0
  143. data/spec/unit/validatable_spec.rb +32 -0
  144. data/tasks/console.rake +10 -0
  145. data/tasks/coverage.rake +11 -0
  146. data/tasks/spec.rake +29 -0
  147. data/tty-table.gemspec +28 -0
  148. 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