tty 0.0.7 → 0.0.8

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 (66) hide show
  1. data/.travis.yml +0 -1
  2. data/README.md +141 -23
  3. data/benchmarks/table.rb +5 -0
  4. data/lib/tty/logger.rb +71 -0
  5. data/lib/tty/support/equatable.rb +6 -2
  6. data/lib/tty/system/which.rb +41 -0
  7. data/lib/tty/system.rb +8 -0
  8. data/lib/tty/table/border/ascii.rb +15 -16
  9. data/lib/tty/table/border/null.rb +9 -4
  10. data/lib/tty/table/border/unicode.rb +15 -16
  11. data/lib/tty/table/border.rb +69 -9
  12. data/lib/tty/table/border_dsl.rb +247 -0
  13. data/lib/tty/table/border_options.rb +52 -0
  14. data/lib/tty/table/field.rb +101 -0
  15. data/lib/tty/table/header.rb +115 -0
  16. data/lib/tty/table/operation/alignment_set.rb +14 -10
  17. data/lib/tty/table/operation/truncation.rb +19 -13
  18. data/lib/tty/table/operation/wrapped.rb +20 -9
  19. data/lib/tty/table/operations.rb +69 -0
  20. data/lib/tty/table/renderer/basic.rb +35 -26
  21. data/lib/tty/table/renderer.rb +13 -1
  22. data/lib/tty/table/row.rb +174 -0
  23. data/lib/tty/table.rb +96 -21
  24. data/lib/tty/text/truncation.rb +1 -1
  25. data/lib/tty/vector.rb +8 -7
  26. data/lib/tty/version.rb +1 -1
  27. data/lib/tty.rb +21 -0
  28. data/spec/tty/logger/new_spec.rb +36 -0
  29. data/spec/tty/logger/valid_level_spec.rb +33 -0
  30. data/spec/tty/system/platform_spec.rb +8 -0
  31. data/spec/tty/system/which_spec.rb +41 -0
  32. data/spec/tty/table/access_spec.rb +12 -1
  33. data/spec/tty/table/add_row_spec.rb +28 -0
  34. data/spec/tty/table/border/new_spec.rb +9 -4
  35. data/spec/tty/table/border/null/rendering_spec.rb +39 -1
  36. data/spec/tty/table/border/options/from_spec.rb +39 -0
  37. data/spec/tty/table/border/options/new_spec.rb +15 -0
  38. data/spec/tty/table/border/style_spec.rb +70 -0
  39. data/spec/tty/table/border_spec.rb +107 -0
  40. data/spec/tty/table/each_with_index_spec.rb +30 -0
  41. data/spec/tty/table/field/equality_spec.rb +51 -0
  42. data/spec/tty/table/field/new_spec.rb +29 -0
  43. data/spec/tty/table/field/width_spec.rb +21 -0
  44. data/spec/tty/table/header/call_spec.rb +30 -0
  45. data/spec/tty/table/header/new_spec.rb +25 -0
  46. data/spec/tty/table/header/set_spec.rb +15 -0
  47. data/spec/tty/table/header/to_ary_spec.rb +14 -0
  48. data/spec/tty/table/header_spec.rb +14 -0
  49. data/spec/tty/table/operation/alignment_set/align_rows_spec.rb +8 -1
  50. data/spec/tty/table/operation/truncation/call_spec.rb +27 -0
  51. data/spec/tty/table/operation/wrapped/call_spec.rb +27 -0
  52. data/spec/tty/table/operations/new_spec.rb +32 -0
  53. data/spec/tty/table/options_spec.rb +17 -9
  54. data/spec/tty/table/renderer/basic/alignment_spec.rb +76 -29
  55. data/spec/tty/table/renderer/basic/separator_spec.rb +99 -0
  56. data/spec/tty/table/renderer_spec.rb +7 -1
  57. data/spec/tty/table/renders_with_spec.rb +35 -28
  58. data/spec/tty/table/rotate_spec.rb +1 -0
  59. data/spec/tty/table/row/access_spec.rb +25 -0
  60. data/spec/tty/table/row/call_spec.rb +41 -0
  61. data/spec/tty/table/row/data_spec.rb +26 -0
  62. data/spec/tty/table/row/equality_spec.rb +73 -0
  63. data/spec/tty/table/row/new_spec.rb +41 -0
  64. data/spec/tty/table/row/to_ary_spec.rb +14 -0
  65. data/spec/tty/text/truncation/truncate_spec.rb +6 -0
  66. metadata +72 -10
@@ -0,0 +1,247 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module TTY
4
+ class Table
5
+
6
+ # A class responsible for bulding and modifying border
7
+ class BorderDSL
8
+ extend TTY::Delegatable
9
+
10
+ attr_reader :options
11
+
12
+ delegatable_method :options, :characters, :style, :separator
13
+
14
+ # Initialize a BorderDSL
15
+ #
16
+ # @param [Hash] characters
17
+ # the border characters
18
+ #
19
+ # @return [undefined]
20
+ #
21
+ # @api private
22
+ def initialize(characters=nil, &block)
23
+ @options = TTY::Table::BorderOptions.new
24
+ @options.characters = characters if characters
25
+ yield_or_eval &block if block_given?
26
+ end
27
+
28
+ # Apply style color to the border
29
+ #
30
+ # @param [Symbol] style
31
+ # the style color for the border
32
+ #
33
+ # @return [undefined]
34
+ #
35
+ # @api public
36
+ def style(value)
37
+ options.style = value
38
+ end
39
+
40
+ # Apply table tuple separator
41
+ #
42
+ # @param [Symbol] separator
43
+ # the table tuple separator
44
+ #
45
+ # @return [undefined]
46
+ #
47
+ # @api public
48
+ def separator(value)
49
+ options.separator = value
50
+ end
51
+
52
+ # Set top border character
53
+ #
54
+ # @param [String] value
55
+ # the character value
56
+ #
57
+ # @return [undefined]
58
+ #
59
+ # @api public
60
+ def top(value)
61
+ options.characters['top'] = value
62
+ end
63
+
64
+ # Set top middle border character
65
+ #
66
+ # @param [String] value
67
+ # the character value
68
+ #
69
+ # @return [undefined]
70
+ #
71
+ # @api public
72
+ def top_mid(value)
73
+ options.characters['top_mid'] = value
74
+ end
75
+
76
+ # Set top left corner border character
77
+ #
78
+ # @param [String] value
79
+ # the character value
80
+ #
81
+ # @return [undefined]
82
+ #
83
+ # @api public
84
+ def top_left(value)
85
+ options.characters['top_left'] = value
86
+ end
87
+
88
+ # Set top right corner border character
89
+ #
90
+ # @param [String] value
91
+ # the character value
92
+ #
93
+ # @return [undefined]
94
+ #
95
+ # @api public
96
+ def top_right(value)
97
+ options.characters['top_right'] = value
98
+ end
99
+
100
+ # Set bottom border character
101
+ #
102
+ # @param [String] value
103
+ # the character value
104
+ #
105
+ # @return [undefined]
106
+ #
107
+ # @api public
108
+ def bottom(value)
109
+ options.characters['bottom'] = value
110
+ end
111
+
112
+ # Set bottom middle border character
113
+ #
114
+ # @param [String] value
115
+ # the character value
116
+ #
117
+ # @return [undefined]
118
+ #
119
+ # @api public
120
+ def bottom_mid(value)
121
+ options.characters['bottom_mid'] = value
122
+ end
123
+
124
+ # Set bottom left corner border character
125
+ #
126
+ # @param [String] value
127
+ # the character value
128
+ #
129
+ # @return [undefined]
130
+ #
131
+ # @api public
132
+ def bottom_left(value)
133
+ options.characters['bottom_left'] = value
134
+ end
135
+
136
+ # Set bottom right corner border character
137
+ #
138
+ # @param [String] value
139
+ # the character value
140
+ #
141
+ # @return [undefined]
142
+ #
143
+ # @api public
144
+ def bottom_right(value)
145
+ options.characters['bottom_right'] = value
146
+ end
147
+
148
+ # Set middle border character
149
+ #
150
+ # @param [String] value
151
+ # the character value
152
+ #
153
+ # @return [undefined]
154
+ #
155
+ # @api public
156
+ def mid(value)
157
+ options.characters['mid'] = value
158
+ end
159
+
160
+ # Set middle border character
161
+ #
162
+ # @param [String] value
163
+ # the character value
164
+ #
165
+ # @return [undefined]
166
+ #
167
+ # @api public
168
+ def mid_mid(value)
169
+ options.characters['mid_mid'] = value
170
+ end
171
+
172
+ # Set middle left corner border character
173
+ #
174
+ # @param [String] value
175
+ # the character value
176
+ #
177
+ # @return [undefined]
178
+ #
179
+ # @api public
180
+ def mid_left(value)
181
+ options.characters['mid_left'] = value
182
+ end
183
+
184
+ # Set middle right corner border character
185
+ #
186
+ # @param [String] value
187
+ # the character value
188
+ #
189
+ # @return [undefined]
190
+ #
191
+ # @api public
192
+ def mid_right(value)
193
+ options.characters['mid_right'] = value
194
+ end
195
+
196
+ # Set left border character
197
+ #
198
+ # @param [String] value
199
+ # the character value
200
+ #
201
+ # @return [undefined]
202
+ #
203
+ # @api public
204
+ def left(value)
205
+ options.characters['left'] = value
206
+ end
207
+
208
+ # Set center border character
209
+ #
210
+ # @param [String] value
211
+ # the character value
212
+ #
213
+ # @return [undefined]
214
+ #
215
+ # @api public
216
+ def center(value)
217
+ options.characters['center'] = value
218
+ end
219
+
220
+ # Set right border character
221
+ #
222
+ # @param [String] value
223
+ # the character value
224
+ #
225
+ # @return [undefined]
226
+ #
227
+ # @api public
228
+ def right(value)
229
+ options.characters['right'] = value
230
+ end
231
+
232
+ private
233
+
234
+ # Evaluate block
235
+ #
236
+ # @return [Table]
237
+ #
238
+ # @api private
239
+ def yield_or_eval(&block)
240
+ return unless block
241
+ block.arity > 0 ? yield(self) : self.instance_eval(&block)
242
+ end
243
+
244
+ end # BorderDSL
245
+
246
+ end # Table
247
+ end # TTY
@@ -0,0 +1,52 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module TTY
4
+ class Table
5
+
6
+ # A class that represents table border options
7
+ class BorderOptions < Struct.new(:characters, :separator, :style)
8
+
9
+ # Initialize a BorderOptions
10
+ #
11
+ # @api public
12
+ def initialize(*args)
13
+ super(*args)
14
+ self.characters = {} unless characters
15
+ end
16
+
17
+ # Create options instance from hash
18
+ #
19
+ # @api public
20
+ def self.from(value)
21
+ value ? new.update(value) : new
22
+ end
23
+
24
+ # Set all accessors with hash attributes
25
+ #
26
+ # @param [Hash, BorderOptions] obj
27
+ #
28
+ # @return [BorderOptions]
29
+ #
30
+ # @api public
31
+ def update(obj)
32
+ obj.each_pair do |key, value|
33
+ self.send("#{key}=", value)
34
+ end
35
+ self
36
+ end
37
+
38
+ # Convert to hash
39
+ #
40
+ # @api public
41
+ def to_hash
42
+ hash = {}
43
+ members.each do |key|
44
+ value = send(key)
45
+ hash[key.to_sym] = value if value
46
+ end
47
+ hash
48
+ end
49
+
50
+ end # BorderOptions
51
+ end # Table
52
+ end # TTY
@@ -0,0 +1,101 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module TTY
4
+ class Table
5
+
6
+ # A class that represents a unique element in a table.
7
+ class Field
8
+ include Equatable
9
+
10
+ # The value inside the field
11
+ #
12
+ # @api public
13
+ attr_reader :value
14
+
15
+ # TODO: Change to :content to separate value from formatted string
16
+ attr_writer :value
17
+
18
+ # The field value width
19
+ #
20
+ # @api public
21
+ attr_reader :width
22
+
23
+ # Number of columns this field spans. Defaults to 1.
24
+ #
25
+ attr_reader :colspan
26
+
27
+ # Number of rows this field spans. Defaults to 1.
28
+ #
29
+ attr_reader :rowspan
30
+
31
+ attr_reader :align
32
+
33
+ # Initialize a Field
34
+ #
35
+ # @example
36
+ # field = new TTY::Table::Field 'a1'
37
+ # field.value # => a1
38
+ #
39
+ # field = new TTY::Table::Field {:value => 'a1'}
40
+ # field.value # => a1
41
+ #
42
+ # field = new TTY::Table::Field {:value => 'a1', :align => :center}
43
+ # field.value # => a1
44
+ # field.align # => :center
45
+ #
46
+ # @api private
47
+ def initialize(value)
48
+ if value.class <= Hash
49
+ options = value
50
+ @value = options.fetch(:value)
51
+ else
52
+ @value = value
53
+ options = {}
54
+ end
55
+ @width = options.fetch(:width) { @value.to_s.size }
56
+ @align = options.fetch(:align) { nil }
57
+ @colspan = options.fetch(:colspan) { 1 }
58
+ @rowspan = options.fetch(:rowspan) { 1 }
59
+ end
60
+
61
+ # Return the width this field would normally have bar other contraints
62
+ #
63
+ # @api public
64
+ def value_width
65
+ @width
66
+ end
67
+
68
+ def value_height
69
+ @height
70
+ end
71
+
72
+ def height
73
+ lines.size
74
+ end
75
+
76
+ # Return number of lines this value spans
77
+ #
78
+ def lines
79
+ value.to_s.split(/\n/)
80
+ end
81
+
82
+ def longest_line
83
+ lines.max_by(&:length).size
84
+ end
85
+
86
+ # Render value inside this field box
87
+ #
88
+ # @api public
89
+ def render
90
+ end
91
+
92
+ # Return field value
93
+ #
94
+ # @api public
95
+ def to_s
96
+ value
97
+ end
98
+
99
+ end # Field
100
+ end # Table
101
+ end # TTY
@@ -0,0 +1,115 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'tty/vector'
4
+ require 'forwardable'
5
+
6
+ module TTY
7
+ class Table
8
+
9
+ # Convert an Array row into Header
10
+ #
11
+ # @return [TTY::Table::Header]
12
+ #
13
+ # @api private
14
+ def to_header(row)
15
+ Header.new(row)
16
+ end
17
+
18
+ # A set of header elements that correspond with values in each row
19
+ class Header < Vector
20
+ include Equatable
21
+ extend Forwardable
22
+
23
+ def_delegators :@attributes, :join, :map, :map!
24
+
25
+ # The header attributes
26
+ #
27
+ # @return [Array]
28
+ #
29
+ # @api private
30
+ attr_reader :attributes
31
+
32
+ # Initialize a Header
33
+ #
34
+ # @return [undefined]
35
+ #
36
+ # @api public
37
+ def initialize(attributes=[])
38
+ @attributes = attributes.map { |attr| to_field(attr) }
39
+ @attribute_for = Hash[@attributes.each_with_index.map.to_a]
40
+ end
41
+
42
+ # Instantiates a new field
43
+ #
44
+ # @api public
45
+ def to_field(options=nil)
46
+ Field.new(options)
47
+ end
48
+
49
+ # Filter header names including styling background,
50
+ # text color and capitalization
51
+ #
52
+ #
53
+ def filter
54
+ end
55
+
56
+ # Lookup a column in the header given a name
57
+ #
58
+ # @api public
59
+ def [](attribute)
60
+ case attribute
61
+ when Integer
62
+ @attributes[attribute].value
63
+ else
64
+ @attribute_for.fetch(to_field(attribute)) do |header_name|
65
+ raise UnknownAttributeError, "the header '#{header_name.value}' is unknown"
66
+ end
67
+ end
68
+ end
69
+ alias :call :[]
70
+
71
+ # Set value at index
72
+ #
73
+ # @example
74
+ # header[attribute] = value
75
+ #
76
+ # @api public
77
+ def []=(attribute, value)
78
+ self.attributes[attribute] = to_field(value)
79
+ end
80
+
81
+ # Size of the header
82
+ #
83
+ # @api public
84
+ def size
85
+ to_ary.size
86
+ end
87
+ alias :length :size
88
+
89
+ # Convert the Header into an Array
90
+ #
91
+ # @api public
92
+ def to_ary
93
+ attributes.map { |attr| attr.value if attr.value }
94
+ end
95
+
96
+ # Check if this header is equivalent to another header
97
+ #
98
+ # @return [Boolean]
99
+ #
100
+ # @api public
101
+ def ==(other)
102
+ to_a == other.to_a
103
+ end
104
+ alias :eql? :==
105
+
106
+ # Provide an unique hash value
107
+ #
108
+ # @api public
109
+ def to_hash
110
+ to_a.hash
111
+ end
112
+
113
+ end # Header
114
+ end # Table
115
+ end # TTY
@@ -27,13 +27,18 @@ module TTY
27
27
  map { |alignment| alignment }
28
28
  end
29
29
 
30
- # Align table header
30
+ # Evaluate alignment of the provided row
31
+ #
32
+ # @param [Array] row
33
+ # the table row
34
+ # @param [Hash] options
35
+ # the table options
31
36
  #
32
37
  # @return [Array[String]]
33
38
  #
34
39
  # @api public
35
- def align_header(header, options={})
36
- align_row(header, options)
40
+ def call(row, options={})
41
+ align_row(row, options)
37
42
  end
38
43
 
39
44
  # Align the supplied rows with the correct alignment.
@@ -50,7 +55,7 @@ module TTY
50
55
 
51
56
  private
52
57
 
53
- # Align each cell in a row
58
+ # Align each field in a row
54
59
  #
55
60
  # @param [Object] row
56
61
  #
@@ -60,14 +65,13 @@ module TTY
60
65
  #
61
66
  # @api private
62
67
  def align_row(row, options={})
63
- line = []
64
- row.each_with_index do |cell, index|
68
+ index = 0
69
+ row.map! do |field|
65
70
  column_width = options[:column_widths][index]
66
- alignment = Alignment.new self[index]
67
-
68
- line << alignment.format(cell, column_width)
71
+ alignment = Alignment.new(field.align || self[index])
72
+ index += 1
73
+ alignment.format(field, column_width)
69
74
  end
70
- line
71
75
  end
72
76
 
73
77
  end # AlignmentSet
@@ -6,7 +6,23 @@ module TTY
6
6
 
7
7
  # A class responsible for shortening text.
8
8
  class Truncation
9
- include Unicode
9
+
10
+ # Apply truncation to a row
11
+ #
12
+ # @param [Array] row
13
+ # the table row
14
+ #
15
+ # @return [Array[String]]
16
+ #
17
+ # @api public
18
+ def call(row, options={})
19
+ index = 0
20
+ row.map! do |field|
21
+ width = options.fetch(:column_widths, {})[index] || field.width
22
+ index += 1
23
+ field.value = truncate(field.value, width)
24
+ end
25
+ end
10
26
 
11
27
  # Shorten given string with traling character.
12
28
  #
@@ -14,22 +30,12 @@ module TTY
14
30
  # the string to truncate
15
31
  # @param [Integer] width
16
32
  # the maximum width
17
- # @param [String] trailing
18
- # the trailing character
19
33
  #
20
34
  # @return [String]
21
35
  #
22
36
  # @api public
23
- def truncate(string, width, trailing = '…')
24
- as_unicode do
25
- chars = string.chars.to_a
26
- if chars.length < width
27
- chars.join
28
- else
29
- traling_size = trailing.chars.to_a.size
30
- ( chars[0, width - traling_size].join ) + trailing
31
- end
32
- end
37
+ def truncate(string, width)
38
+ TTY::Text.truncate(string, width)
33
39
  end
34
40
 
35
41
  end # Truncation
@@ -6,7 +6,23 @@ module TTY
6
6
 
7
7
  # A class responsible for wrapping text.
8
8
  class Wrapped
9
- include Unicode
9
+
10
+ # Apply truncation to a row
11
+ #
12
+ # @param [Array] row
13
+ # the table row
14
+ #
15
+ # @return [Array[String]]
16
+ #
17
+ # @api public
18
+ def call(row, options={})
19
+ index = 0
20
+ row.map! do |field|
21
+ width = options.fetch(:column_widths, {})[index] || field.width
22
+ index += 1
23
+ field.value = wrap(field.value, width)
24
+ end
25
+ end
10
26
 
11
27
  # Wrap a long string according to the width.
12
28
  #
@@ -14,17 +30,12 @@ module TTY
14
30
  # the string to wrap
15
31
  # @param [Integer] width
16
32
  # the maximum width
17
- # @param [Boolean] addnewline
18
- # add new line add the end
33
+ #
34
+ # @return [String]
19
35
  #
20
36
  # @api public
21
37
  def wrap(string, width)
22
- as_unicode do
23
- chars = string.chars.to_a
24
- return string if chars.length <= width
25
- idx = width
26
- return chars[0, idx].join + "\n" + wrap(chars[idx..-1].join, width)
27
- end
38
+ TTY::Text.wrap(string, width)
28
39
  end
29
40
 
30
41
  end # Wrapped