tty 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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