command_kit 0.3.0 → 0.4.1

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +6 -6
  3. data/.rubocop.yml +16 -0
  4. data/ChangeLog.md +30 -0
  5. data/Gemfile +2 -0
  6. data/LICENSE.txt +1 -1
  7. data/README.md +18 -9
  8. data/command_kit.gemspec +0 -1
  9. data/examples/printing/tables.rb +141 -0
  10. data/examples/subcommands/cli/config/get.rb +47 -0
  11. data/examples/subcommands/cli/config/set.rb +44 -0
  12. data/examples/subcommands/cli/config.rb +23 -0
  13. data/examples/subcommands/cli/list.rb +35 -0
  14. data/examples/subcommands/cli/update.rb +47 -0
  15. data/examples/subcommands/cli.rb +55 -0
  16. data/gemspec.yml +3 -3
  17. data/lib/command_kit/bug_report.rb +105 -0
  18. data/lib/command_kit/colors.rb +4 -4
  19. data/lib/command_kit/edit.rb +54 -0
  20. data/lib/command_kit/env/home.rb +1 -1
  21. data/lib/command_kit/env.rb +1 -1
  22. data/lib/command_kit/inflector.rb +1 -1
  23. data/lib/command_kit/options/option.rb +5 -1
  24. data/lib/command_kit/options/option_value.rb +2 -2
  25. data/lib/command_kit/options/parser.rb +2 -2
  26. data/lib/command_kit/options/quiet.rb +1 -1
  27. data/lib/command_kit/options/verbose.rb +2 -2
  28. data/lib/command_kit/options/version.rb +10 -0
  29. data/lib/command_kit/options.rb +1 -1
  30. data/lib/command_kit/os/linux.rb +1 -1
  31. data/lib/command_kit/os.rb +2 -2
  32. data/lib/command_kit/printing/fields.rb +56 -0
  33. data/lib/command_kit/printing/indent.rb +1 -1
  34. data/lib/command_kit/printing/lists.rb +91 -0
  35. data/lib/command_kit/printing/tables/border_style.rb +169 -0
  36. data/lib/command_kit/printing/tables/cell_builder.rb +93 -0
  37. data/lib/command_kit/printing/tables/row_builder.rb +111 -0
  38. data/lib/command_kit/printing/tables/style.rb +198 -0
  39. data/lib/command_kit/printing/tables/table_builder.rb +145 -0
  40. data/lib/command_kit/printing/tables/table_formatter.rb +254 -0
  41. data/lib/command_kit/printing/tables.rb +208 -0
  42. data/lib/command_kit/stdio.rb +5 -1
  43. data/lib/command_kit/version.rb +1 -1
  44. data/lib/command_kit/xdg.rb +1 -1
  45. data/spec/bug_report_spec.rb +266 -0
  46. data/spec/colors_spec.rb +6 -0
  47. data/spec/command_name_spec.rb +1 -1
  48. data/spec/commands_spec.rb +26 -0
  49. data/spec/edit_spec.rb +72 -0
  50. data/spec/options/option_spec.rb +12 -2
  51. data/spec/options/parser_spec.rb +19 -0
  52. data/spec/options/quiet_spec.rb +51 -0
  53. data/spec/options/verbose_spec.rb +51 -0
  54. data/spec/options/version_spec.rb +146 -0
  55. data/spec/pager_spec.rb +1 -1
  56. data/spec/printing/fields_spec.rb +167 -0
  57. data/spec/printing/lists_spec.rb +99 -0
  58. data/spec/printing/tables/border_style.rb +43 -0
  59. data/spec/printing/tables/cell_builer_spec.rb +135 -0
  60. data/spec/printing/tables/row_builder_spec.rb +165 -0
  61. data/spec/printing/tables/style_spec.rb +377 -0
  62. data/spec/printing/tables/table_builder_spec.rb +252 -0
  63. data/spec/printing/tables/table_formatter_spec.rb +1190 -0
  64. data/spec/printing/tables_spec.rb +1069 -0
  65. metadata +39 -7
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandKit
4
+ module Printing
5
+ module Tables
6
+ #
7
+ # Build's a cell and calculates it's dimensions.
8
+ #
9
+ # @api private
10
+ #
11
+ class CellBuilder
12
+
13
+ # The lines within the cell.
14
+ #
15
+ # @return [Array<String>]
16
+ attr_reader :lines
17
+
18
+ # The height (in lines) of the cell.
19
+ #
20
+ # @return [Integer]
21
+ attr_reader :height
22
+
23
+ # The with (in characters) of the cell.
24
+ #
25
+ # @return [Integer]
26
+ attr_reader :width
27
+
28
+ #
29
+ # Initializes the cell.
30
+ #
31
+ # @param [#to_s] value
32
+ # The value for the cell.
33
+ #
34
+ def initialize(value=nil)
35
+ @lines = []
36
+ @height = 0
37
+ @width = 0
38
+
39
+ if value
40
+ value.to_s.each_line(chomp: true) do |line|
41
+ self << line
42
+ end
43
+ end
44
+ end
45
+
46
+ #
47
+ # Calculates the width of a string, sans any ASNI escape sequences.
48
+ #
49
+ # @param [String] string
50
+ # The string to calculate the width of.
51
+ #
52
+ # @return [Integer]
53
+ # The display width of the string.
54
+ #
55
+ def self.line_width(string)
56
+ string.gsub(/\e\[\d+m/,'').length
57
+ end
58
+
59
+ #
60
+ # Adds a line to the cell.
61
+ #
62
+ # @param [String] line
63
+ # A line to add to the cell.
64
+ #
65
+ # @return [self]
66
+ #
67
+ def <<(line)
68
+ line_width = self.class.line_width(line)
69
+
70
+ @height += 1
71
+ @width = line_width if line_width > @width
72
+
73
+ @lines << line
74
+ return self
75
+ end
76
+
77
+ #
78
+ # Fetches a line from the cell.
79
+ #
80
+ # @param [Integer] line_index
81
+ # The line index to fetch.
82
+ #
83
+ # @return [String]
84
+ # The line at the line index or an empty String if the cell is empty.
85
+ #
86
+ def [](line_index)
87
+ @lines.fetch(line_index,'')
88
+ end
89
+
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,111 @@
1
+ require 'command_kit/printing/tables/cell_builder'
2
+
3
+ module CommandKit
4
+ module Printing
5
+ module Tables
6
+ #
7
+ # Builds a table row and calculates it's dimensions.
8
+ #
9
+ # @api private
10
+ #
11
+ class RowBuilder
12
+
13
+ include Enumerable
14
+
15
+ # The cells within the row.
16
+ #
17
+ # @return [Array<CellBuilder>]
18
+ attr_reader :cells
19
+
20
+ # The height (in lines) for the row.
21
+ #
22
+ # @return [Integer]
23
+ attr_reader :height
24
+
25
+ # The width (in characters) for the row.
26
+ #
27
+ # @return [Integer]
28
+ attr_reader :width
29
+
30
+ # The number of columns in the row.
31
+ #
32
+ # @return [Integer]
33
+ attr_reader :columns
34
+
35
+ #
36
+ # Initializes the row.
37
+ #
38
+ # @param [Array, nil] cells
39
+ # The cells for the row.
40
+ #
41
+ def initialize(cells=nil)
42
+ @cells = []
43
+
44
+ @height = 0
45
+ @width = 0
46
+
47
+ @columns = 0
48
+
49
+ if cells
50
+ cells.each { |value| self << value }
51
+ end
52
+ end
53
+
54
+ # An empty cell.
55
+ EMPTY_CELL = CellBuilder.new
56
+
57
+ #
58
+ # Appends a value to the row.
59
+ #
60
+ # @param [#to_s] value
61
+ # The cell value to add to the row.
62
+ #
63
+ # @return [self]
64
+ #
65
+ def <<(value)
66
+ new_cell = if value then CellBuilder.new(value)
67
+ else EMPTY_CELL
68
+ end
69
+
70
+ @height = new_cell.height if new_cell.height > @height
71
+ @width += new_cell.width
72
+ @columns += 1
73
+
74
+ @cells << new_cell
75
+ return self
76
+ end
77
+
78
+ #
79
+ # Fetches a cell from the row.
80
+ #
81
+ # @param [Integer] column_index
82
+ # The column index.
83
+ #
84
+ # @return [CellBuilder]
85
+ # The cell at the given column index or an empty cell if the row
86
+ # does not have a cell at the given column index.
87
+ #
88
+ def [](column_index)
89
+ @cells.fetch(column_index,EMPTY_CELL)
90
+ end
91
+
92
+ #
93
+ # Enumerates over each cell in the row.
94
+ #
95
+ # @yield [cell]
96
+ # The given block will be passed each cell within the row.
97
+ #
98
+ # @yieldparam [CellBuilder] cell
99
+ # A cell within the row.
100
+ #
101
+ # @return [Enumerator]
102
+ # If no block is given, an Enumerator will be returned.
103
+ #
104
+ def each(&block)
105
+ @cells.each(&block)
106
+ end
107
+
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,198 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'command_kit/printing/tables/border_style'
4
+
5
+ module CommandKit
6
+ module Printing
7
+ module Tables
8
+ #
9
+ # Contains the table's style configuration.
10
+ #
11
+ # @api private
12
+ #
13
+ class Style
14
+
15
+ # Built-in border styles.
16
+ BORDER_STYLES = {
17
+ ascii: BorderStyle.new(
18
+ top_left_corner: '+',
19
+ top_border: '-',
20
+ top_joined_border: '+',
21
+ top_right_corner: '+',
22
+ left_border: '|',
23
+ left_joined_border: '+',
24
+ horizontal_separator: '-',
25
+ vertical_separator: '|',
26
+ inner_joined_border: '+',
27
+ right_border: '|',
28
+ right_joined_border: '+',
29
+ bottom_border: '-',
30
+ bottom_left_corner: '+',
31
+ bottom_joined_border: '+',
32
+ bottom_right_corner: '+'
33
+ ),
34
+
35
+ line: BorderStyle.new(
36
+ top_left_corner: '┌',
37
+ top_border: '─',
38
+ top_joined_border: '┬',
39
+ top_right_corner: '┐',
40
+ left_border: '│',
41
+ left_joined_border: '├',
42
+ horizontal_separator: '─',
43
+ vertical_separator: '│',
44
+ inner_joined_border: '┼',
45
+ right_border: '│',
46
+ right_joined_border: '┤',
47
+ bottom_border: '─',
48
+ bottom_left_corner: '└',
49
+ bottom_joined_border: '┴',
50
+ bottom_right_corner: '┘'
51
+ ),
52
+
53
+ double_line: BorderStyle.new(
54
+ top_left_corner: '╔',
55
+ top_border: '═',
56
+ top_joined_border: '╦',
57
+ top_right_corner: '╗',
58
+ left_border: '║',
59
+ left_joined_border: '╠',
60
+ horizontal_separator: '═',
61
+ vertical_separator: '║',
62
+ inner_joined_border: '╬',
63
+ right_border: '║',
64
+ right_joined_border: '╣',
65
+ bottom_border: '═',
66
+ bottom_left_corner: '╚',
67
+ bottom_joined_border: '╩',
68
+ bottom_right_corner: '╝'
69
+ )
70
+ }
71
+
72
+ # The border style.
73
+ #
74
+ # @return [BorderStyle]
75
+ attr_reader :border
76
+
77
+ # The padding to use for cells.
78
+ #
79
+ # @return [Integer]
80
+ attr_reader :padding
81
+
82
+ # The justification to use for cells.
83
+ #
84
+ # @return [:left, :right, :center]
85
+ attr_reader :justify
86
+
87
+ # The justification to use for header cells.
88
+ #
89
+ # @return [:left, :right, :center]
90
+ attr_reader :justify_header
91
+
92
+ # Specifies whether to separate rows with a border row.
93
+ #
94
+ # @return [Boolean]
95
+ attr_reader :separate_rows
96
+
97
+ #
98
+ # Initializes the style.
99
+ #
100
+ # @param [:line, :double_line, nil, Hash{Symbol => String}, :ascii] border
101
+ # The border style or a custom Hash of border characters.
102
+ #
103
+ # @option border [String] :top_left_corner (' ')
104
+ # The top-left-corner border character.
105
+ #
106
+ # @option border [String] :top_border (' ')
107
+ # The top-border character.
108
+ #
109
+ # @option border [String] :top_joined_border (' ')
110
+ # The top-joined-border character.
111
+ #
112
+ # @option border [String] :top_right_corner (' ')
113
+ # The top-right-corner border character.
114
+ #
115
+ # @option border [String] :left_border (' ')
116
+ # The left-hand-side border character.
117
+ #
118
+ # @option border [String] :left_joined_border (' ')
119
+ # The left-hand-side-joined-border character.
120
+ #
121
+ # @option border [String] :horizontal_separator (' ')
122
+ # The horizontal-separator character.
123
+ #
124
+ # @option border [String] :vertical_separator (' ')
125
+ # The vertical-separator character.
126
+ #
127
+ # @option border [String] :inner_joined_border (' ')
128
+ # The inner-joined border character.
129
+ #
130
+ # @option border [String] :right_border (' ')
131
+ # The right-hand-side border character.
132
+ #
133
+ # @option border [String] :right_joined_border (' ')
134
+ # The right-hand-side joined border character.
135
+ #
136
+ # @option border [String] :bottom_border (' ')
137
+ # The bottom border character.
138
+ #
139
+ # @option border [String] :bottom_left_corner (' ')
140
+ # The bottom-left-corner border character.
141
+ #
142
+ # @option border [String] :bottom_joined_border (' ')
143
+ # The bottom-joined border character.
144
+ #
145
+ # @option border [String] :bottom_right_corner (' ')
146
+ # The bottom-right-corner border character.
147
+ #
148
+ # @param [Integer] padding
149
+ # The number of characters to pad table cell values with.
150
+ #
151
+ # @param [:left, :right, :center] justify
152
+ # Specifies how to justify the table cell values.
153
+ #
154
+ # @param [:left, :right, :center] justify_header
155
+ # Specifies how to justify the table header cell values.
156
+ #
157
+ # @param [Boolean] separate_rows
158
+ # Specifies whether to add separator rows in between the rows.
159
+ #
160
+ def initialize(border: nil,
161
+ padding: 1,
162
+ justify: :left,
163
+ justify_header: :center,
164
+ separate_rows: false)
165
+ @border = case border
166
+ when Hash
167
+ BorderStyle.new(**border)
168
+ when Symbol
169
+ BORDER_STYLES.fetch(border) do
170
+ raise(ArgumentError,"unknown border style (#{border.inspect}) must be either #{BORDER_STYLES.keys.map(&:inspect).join(', ')}")
171
+ end
172
+ when nil then nil
173
+ else
174
+ raise(ArgumentError,"invalid border value (#{border.inspect}) must be either #{BORDER_STYLES.keys.map(&:inspect).join(', ')}, Hash, or nil")
175
+ end
176
+
177
+ @padding = padding
178
+
179
+ @justify = justify
180
+ @justify_header = justify_header
181
+
182
+ @separate_rows = separate_rows
183
+ end
184
+
185
+ #
186
+ # Determines if the rows should be separated.
187
+ #
188
+ # @return [Boolean]
189
+ # Specifies whether each row should be separated with a separator row.
190
+ #
191
+ def separate_rows?
192
+ @separate_rows
193
+ end
194
+
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,145 @@
1
+ require 'command_kit/printing/tables/row_builder'
2
+
3
+ module CommandKit
4
+ module Printing
5
+ module Tables
6
+ #
7
+ # Builds a table and calculates it's dimensions.
8
+ #
9
+ # @api private
10
+ #
11
+ class TableBuilder
12
+
13
+ include Enumerable
14
+
15
+ # The rows within the table.
16
+ #
17
+ # @return [Array<RowBuilder>]
18
+ attr_reader :rows
19
+
20
+ # Indicates whether the table has a header row.
21
+ #
22
+ # @return [Boolean]
23
+ attr_reader :header
24
+
25
+ # The height (in lines) of the table.
26
+ #
27
+ # @return [Integer]
28
+ attr_reader :height
29
+
30
+ # The width (in characters) of the table.
31
+ #
32
+ # @return [Integer]
33
+ attr_reader :width
34
+
35
+ # The maximum number of rows in the table.
36
+ #
37
+ # @return [Integer]
38
+ attr_reader :max_rows
39
+
40
+ # The maximum number of columns in the table.
41
+ #
42
+ # @return [Integer]
43
+ attr_reader :max_columns
44
+
45
+ # The widths of the columns in the table.
46
+ #
47
+ # @return [Array<Integer>]
48
+ attr_reader :column_widths
49
+
50
+ #
51
+ # Initializes the table.
52
+ #
53
+ # @param [Array<Array>] rows
54
+ # The rows for the table.
55
+ #
56
+ # @param [Array] header
57
+ # The header row.
58
+ #
59
+ def initialize(rows=[], header: nil)
60
+ @rows = []
61
+ @height = 0
62
+ @width = 0
63
+ @column_widths = []
64
+
65
+ @max_rows = 0
66
+ @max_columns = 0
67
+
68
+ @header = !header.nil?
69
+
70
+ self << header if header
71
+ rows.each { |row| self << row }
72
+ end
73
+
74
+ #
75
+ # Determines whether the table has a header row.
76
+ #
77
+ # @return [Boolean]
78
+ # Indicates whether the first row of the table is a header row.
79
+ #
80
+ def header?
81
+ @header
82
+ end
83
+
84
+ #
85
+ # Appends a row to the table.
86
+ #
87
+ # @param [Array] row
88
+ # The row to append.
89
+ #
90
+ # @return [self]
91
+ #
92
+ def <<(row)
93
+ new_row = RowBuilder.new(row)
94
+
95
+ new_row.each_with_index do |cell,index|
96
+ column_width = @column_widths[index] || 0
97
+
98
+ if cell.width > column_width
99
+ @column_widths[index] = cell.width
100
+ end
101
+ end
102
+
103
+ @height += new_row.height
104
+ @width = new_row.width if new_row.width > @width
105
+
106
+ @max_rows += 1
107
+ @max_columns = new_row.columns if new_row.columns > @max_columns
108
+
109
+ @rows << new_row
110
+ return self
111
+ end
112
+
113
+ #
114
+ # Fetches a row from the table.
115
+ #
116
+ # @param [Integer] row_index
117
+ # The row index to fetch the row at.
118
+ #
119
+ # @return [RowBuilder, nil]
120
+ # The row or `nil` if no row exists at the given row index.
121
+ #
122
+ def [](row_index)
123
+ @rows[row_index]
124
+ end
125
+
126
+ #
127
+ # Enumerates over each row in the table.
128
+ #
129
+ # @yield [row]
130
+ # If a block is given, it will be passed each row within the table.
131
+ #
132
+ # @yieldparam [RowBuilder] row
133
+ # A row within the table.
134
+ #
135
+ # @return [Enumerator]
136
+ # If no block is given, an Enumerator will be returned.
137
+ #
138
+ def each(&block)
139
+ @rows.each(&block)
140
+ end
141
+
142
+ end
143
+ end
144
+ end
145
+ end