command_kit 0.2.2 → 0.4.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +4 -5
  3. data/.rubocop.yml +14 -1
  4. data/ChangeLog.md +82 -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/gemspec.yml +3 -3
  11. data/lib/command_kit/arguments/argument.rb +2 -2
  12. data/lib/command_kit/arguments.rb +27 -2
  13. data/lib/command_kit/bug_report.rb +105 -0
  14. data/lib/command_kit/colors.rb +488 -15
  15. data/lib/command_kit/command.rb +1 -2
  16. data/lib/command_kit/edit.rb +54 -0
  17. data/lib/command_kit/env.rb +1 -1
  18. data/lib/command_kit/file_utils.rb +46 -0
  19. data/lib/command_kit/options/option.rb +45 -22
  20. data/lib/command_kit/options/option_value.rb +2 -2
  21. data/lib/command_kit/options/parser.rb +1 -4
  22. data/lib/command_kit/options/quiet.rb +1 -1
  23. data/lib/command_kit/options/verbose.rb +2 -2
  24. data/lib/command_kit/options/version.rb +10 -0
  25. data/lib/command_kit/options.rb +89 -14
  26. data/lib/command_kit/os.rb +1 -1
  27. data/lib/command_kit/printing/fields.rb +56 -0
  28. data/lib/command_kit/printing/indent.rb +1 -1
  29. data/lib/command_kit/printing/lists.rb +91 -0
  30. data/lib/command_kit/printing/tables/border_style.rb +169 -0
  31. data/lib/command_kit/printing/tables/cell_builder.rb +93 -0
  32. data/lib/command_kit/printing/tables/row_builder.rb +111 -0
  33. data/lib/command_kit/printing/tables/style.rb +198 -0
  34. data/lib/command_kit/printing/tables/table_builder.rb +145 -0
  35. data/lib/command_kit/printing/tables/table_formatter.rb +254 -0
  36. data/lib/command_kit/printing/tables.rb +208 -0
  37. data/lib/command_kit/program_name.rb +9 -0
  38. data/lib/command_kit/stdio.rb +5 -1
  39. data/lib/command_kit/version.rb +1 -1
  40. data/spec/arguments_spec.rb +33 -0
  41. data/spec/bug_report_spec.rb +266 -0
  42. data/spec/colors_spec.rb +232 -195
  43. data/spec/command_name_spec.rb +1 -1
  44. data/spec/command_spec.rb +2 -2
  45. data/spec/edit_spec.rb +72 -0
  46. data/spec/file_utils_spec.rb +59 -0
  47. data/spec/fixtures/template.erb +5 -0
  48. data/spec/options/option_spec.rb +48 -2
  49. data/spec/options/parser_spec.rb +0 -10
  50. data/spec/options/quiet_spec.rb +51 -0
  51. data/spec/options/verbose_spec.rb +51 -0
  52. data/spec/options/version_spec.rb +146 -0
  53. data/spec/options_spec.rb +46 -0
  54. data/spec/pager_spec.rb +1 -1
  55. data/spec/printing/fields_spec.rb +167 -0
  56. data/spec/printing/lists_spec.rb +99 -0
  57. data/spec/printing/tables/border_style.rb +43 -0
  58. data/spec/printing/tables/cell_builer_spec.rb +135 -0
  59. data/spec/printing/tables/row_builder_spec.rb +165 -0
  60. data/spec/printing/tables/style_spec.rb +377 -0
  61. data/spec/printing/tables/table_builder_spec.rb +252 -0
  62. data/spec/printing/tables/table_formatter_spec.rb +1180 -0
  63. data/spec/printing/tables_spec.rb +1069 -0
  64. data/spec/program_name_spec.rb +8 -0
  65. metadata +36 -7
@@ -0,0 +1,254 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandKit
4
+ module Printing
5
+ module Tables
6
+ #
7
+ # @api private
8
+ #
9
+ class TableFormatter
10
+
11
+ #
12
+ # Initialies the table formatter.
13
+ #
14
+ # @param [TableBuilder] table
15
+ # The table data to print.
16
+ #
17
+ # @param [Style] style
18
+ # The table's style configuration.
19
+ #
20
+ def initialize(table,style)
21
+ @table = table
22
+ @style = style
23
+
24
+ @padding = ' ' * @style.padding
25
+ @extra_padding = @style.padding * 2
26
+
27
+ @last_column = @table.max_columns - 1
28
+ @last_row = @table.max_rows - 1
29
+ end
30
+
31
+ #
32
+ # Formats the table.
33
+ #
34
+ # @yield [line]
35
+ # The given block will be passed each formatted line of the table.
36
+ #
37
+ # @yieldparam [String]
38
+ # A formatted line of the table.
39
+ #
40
+ def format(&block)
41
+ yield format_top_border if @style.border
42
+
43
+ separator_row = if @table.header? || @style.separate_rows?
44
+ format_separator_row
45
+ end
46
+
47
+ @table.max_rows.times do |row_index|
48
+ row = @table[row_index]
49
+
50
+ if @table.header? && row_index == 0
51
+ format_header_row(row,&block)
52
+
53
+ yield separator_row
54
+ else
55
+ format_row(row,&block)
56
+
57
+ if @style.separate_rows? && row_index < @last_row
58
+ yield separator_row
59
+ end
60
+ end
61
+ end
62
+
63
+ yield format_bottom_border if @style.border
64
+ end
65
+
66
+ private
67
+
68
+ #
69
+ # Builds a horizontal border row.
70
+ #
71
+ # @param [String] left_border
72
+ # The left-hand side/corner border character.
73
+ #
74
+ # @param [String] column_border
75
+ # The top/bottom/horizontal column border character.
76
+ #
77
+ # @param [String] joined_border
78
+ # A joined border character.
79
+ #
80
+ # @param [String] right_border
81
+ # The right-hand side/corner border character.
82
+ #
83
+ # @return [String]
84
+ # The formatted border row.
85
+ #
86
+ def format_border_row(left_border: ,
87
+ column_border: ,
88
+ joined_border: ,
89
+ right_border: )
90
+ line = String.new
91
+ line << left_border
92
+
93
+ @table.max_columns.times do |column_index|
94
+ column_width = @table.column_widths[column_index] + @extra_padding
95
+
96
+ line << (column_border * column_width)
97
+
98
+ line << if column_index < @last_column
99
+ joined_border
100
+ else
101
+ right_border
102
+ end
103
+ end
104
+
105
+ return line
106
+ end
107
+
108
+ #
109
+ # Builds the top border row.
110
+ #
111
+ # @return [String]
112
+ # The formatted top border row.
113
+ #
114
+ def format_top_border
115
+ format_border_row(left_border: @style.border.top_left_corner,
116
+ column_border: @style.border.top_border,
117
+ joined_border: @style.border.top_joined_border,
118
+ right_border: @style.border.top_right_corner)
119
+ end
120
+
121
+ #
122
+ # Builds the separator row.
123
+ #
124
+ # @return [String]
125
+ # The formatted separator row.
126
+ #
127
+ def format_separator_row
128
+ if @style.border
129
+ format_border_row(left_border: @style.border.left_joined_border,
130
+ column_border: @style.border.horizontal_separator,
131
+ joined_border: @style.border.inner_joined_border,
132
+ right_border: @style.border.right_joined_border)
133
+ else
134
+ ''
135
+ end
136
+ end
137
+
138
+ #
139
+ # Builds the top border row.
140
+ #
141
+ # @return [String]
142
+ # The formatted bottom border row.
143
+ #
144
+ def format_bottom_border
145
+ format_border_row(left_border: @style.border.bottom_left_corner,
146
+ column_border: @style.border.bottom_border,
147
+ joined_border: @style.border.bottom_joined_border,
148
+ right_border: @style.border.bottom_right_corner)
149
+ end
150
+
151
+ #
152
+ # Formats a cell value.
153
+ #
154
+ # @param [String] value
155
+ # The value from the cell.
156
+ #
157
+ # @param [Integer] width
158
+ # The desired width of the cell.
159
+ #
160
+ # @param [:left, :right, :center] justify
161
+ # How to justify the cell's value.
162
+ #
163
+ # @return [String]
164
+ # The formatted cell.
165
+ #
166
+ def format_cell(value, width: , justify: @style.justify)
167
+ justified_value = case justify
168
+ when :center then value.center(width)
169
+ when :left then value.ljust(width)
170
+ when :right then value.rjust(width)
171
+ else
172
+ raise(ArgumentError,"invalid justify value (#{justify.inspect}), must be :left, :right, or :center")
173
+ end
174
+
175
+ return "#{@padding}#{justified_value}#{@padding}"
176
+ end
177
+
178
+ #
179
+ # Formats a specific line within a row.
180
+ #
181
+ # @param [RowBuilder] row
182
+ # The table row to format.
183
+ #
184
+ # @param [Integer] line_index
185
+ # The line index within the row to specifically format.
186
+ #
187
+ # @param [:left, :right, :center] justify
188
+ # How to justify each cell within the row.
189
+ #
190
+ # @return [String]
191
+ # The formatted row line.
192
+ #
193
+ def format_row_line(row,line_index, justify: @style.justify)
194
+ line = String.new
195
+ line << @style.border.left_border if @style.border
196
+
197
+ @table.max_columns.times do |column_index|
198
+ column_width = @table.column_widths[column_index]
199
+ cell_line = row[column_index][line_index]
200
+
201
+ line << format_cell(cell_line, width: column_width,
202
+ justify: justify)
203
+
204
+ if (@style.border && (column_index < @last_column))
205
+ line << @style.border.vertical_separator
206
+ end
207
+ end
208
+
209
+ line << @style.border.right_border if @style.border
210
+ return line
211
+ end
212
+
213
+ #
214
+ # Formats a row.
215
+ #
216
+ # @param [RowBuilder] row
217
+ # The table row to format.
218
+ #
219
+ # @param [:left, :right, :center] justify
220
+ # How to justify each cell within the row.
221
+ #
222
+ # @yield [line]
223
+ # The given block will be passed each formatted line within the row.
224
+ #
225
+ # @yieldparam [String] line
226
+ # A formatted line of the row.
227
+ #
228
+ def format_row(row, justify: @style.justify)
229
+ row.height.times do |line_index|
230
+ yield format_row_line(row,line_index, justify: justify)
231
+ end
232
+ end
233
+
234
+ #
235
+ # Formats a header row.
236
+ #
237
+ # @param [RowBuilder] row
238
+ # The header row to format.
239
+ #
240
+ # @yield [line]
241
+ # The given block will be passed each formatted line within the header
242
+ # row.
243
+ #
244
+ # @yieldparam [String] line
245
+ # A formatted line of the header row.
246
+ #
247
+ def format_header_row(row,&block)
248
+ format_row(row, justify: @style.justify_header, &block)
249
+ end
250
+
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'command_kit/printing/indent'
4
+ require 'command_kit/printing/tables/table_builder'
5
+ require 'command_kit/printing/tables/style'
6
+ require 'command_kit/printing/tables/table_formatter'
7
+
8
+ module CommandKit
9
+ module Printing
10
+ #
11
+ # Methods for printing tables.
12
+ #
13
+ # ## Examples
14
+ #
15
+ # include CommandKit::Printing::Tables
16
+ #
17
+ # def run
18
+ # header = ['A', 'B', 'C']
19
+ # table = [
20
+ # ['AAAA', 'BBBB', 'CCCC'],
21
+ # ['AAAA', 'BBBB', 'CCCC'],
22
+ # ['AAAA', 'BBBB', 'CCCC']
23
+ # ]
24
+ #
25
+ # print_table table
26
+ # # AAAA BBBB CCCC
27
+ # # AAAA BBBB CCCC
28
+ # # AAAA BBBB CCCC
29
+ #
30
+ # print_table table, header: header
31
+ # # A B C
32
+ # #
33
+ # # AAAA BBBB CCCC
34
+ # # AAAA BBBB CCCC
35
+ # # AAAA BBBB CCCC
36
+ #
37
+ # print_table table, header: header,
38
+ # border: :ascii
39
+ # # +------+------+------+
40
+ # # | A | B | C |
41
+ # # +------+------+------+
42
+ # # | AAAA | BBBB | CCCC |
43
+ # # | AAAA | BBBB | CCCC |
44
+ # # | AAAA | BBBB | CCCC |
45
+ # # +------+------+------+
46
+ #
47
+ # print_table table, header: header,
48
+ # border: :ascii,
49
+ # separate_rows: true
50
+ # # +------+------+------+
51
+ # # | A | B | C |
52
+ # # +------+------+------+
53
+ # # | AAAA | BBBB | CCCC |
54
+ # # +------+------+------+
55
+ # # | AAAA | BBBB | CCCC |
56
+ # # +------+------+------+
57
+ # # | AAAA | BBBB | CCCC |
58
+ # # +------+------+------+
59
+ #
60
+ # print_table table, header: header,
61
+ # border: :line
62
+ # # ┌──────┬──────┬──────┐
63
+ # # │ A │ B │ C │
64
+ # # ├──────┼──────┼──────┤
65
+ # # │ AAAA │ BBBB │ CCCC │
66
+ # # │ AAAA │ BBBB │ CCCC │
67
+ # # │ AAAA │ BBBB │ CCCC │
68
+ # # └──────┴──────┴──────┘
69
+ #
70
+ # print_table table, header: header,
71
+ # border: :double_line
72
+ # # ╔══════╦══════╦══════╗
73
+ # # ║ A ║ B ║ C ║
74
+ # # ╠══════╬══════╬══════╣
75
+ # # ║ AAAA ║ BBBB ║ CCCC ║
76
+ # # ║ AAAA ║ BBBB ║ CCCC ║
77
+ # # ║ AAAA ║ BBBB ║ CCCC ║
78
+ # # ╚══════╩══════╩══════╝
79
+ #
80
+ # uneven_table = [
81
+ # ['AAAAAA', 'B', 'CCCCCCC'],
82
+ # ['AAA', 'BBBB', 'CCC' ],
83
+ # ['A', 'BBBBBBB', 'C' ]
84
+ # ]
85
+ #
86
+ # print_table uneven_table, header: header,
87
+ # justify: :left,
88
+ # justify_header: :left,
89
+ # border: :line
90
+ # # ┌────────┬─────────┬─────────┐
91
+ # # │ A │ B │ C │
92
+ # # ├────────┼─────────┼─────────┤
93
+ # # │ AAAAAA │ B │ CCCCCCC │
94
+ # # │ AAA │ BBBB │ CCC │
95
+ # # │ A │ BBBBBBB │ C │
96
+ # # └────────┴─────────┴─────────┘
97
+ #
98
+ # print_table uneven_table, header: header,
99
+ # justify: :right,
100
+ # justify_header: :right,
101
+ # border: :line
102
+ # # ┌────────┬─────────┬─────────┐
103
+ # # │ A │ B │ C │
104
+ # # ├────────┼─────────┼─────────┤
105
+ # # │ AAAAAA │ B │ CCCCCCC │
106
+ # # │ AAA │ BBBB │ CCC │
107
+ # # │ A │ BBBBBBB │ C │
108
+ # # └────────┴─────────┴─────────┘
109
+ #
110
+ # print_table uneven_table, header: header,
111
+ # justify: :center,
112
+ # justify_header: :center,
113
+ # border: :line
114
+ # # ┌────────┬─────────┬─────────┐
115
+ # # │ A │ B │ C │
116
+ # # ├────────┼─────────┼─────────┤
117
+ # # │ AAAAAA │ B │ CCCCCCC │
118
+ # # │ AAA │ BBBB │ CCC │
119
+ # # │ A │ BBBBBBB │ C │
120
+ # # └────────┴─────────┴─────────┘
121
+ #
122
+ # table_with_empty_cells = [
123
+ # ['AAAA', 'BBBB', 'CCCC'],
124
+ # ['AAAA', nil, 'CCCC'],
125
+ # ['AAAA', 'BBBB']
126
+ # ]
127
+ #
128
+ # print_table table_with_empty_cells, header: header,
129
+ # justify: :left,
130
+ # border: :line
131
+ # # ┌──────┬──────┬──────┐
132
+ # # │ A │ B │ C │
133
+ # # ├──────┼──────┼──────┤
134
+ # # │ AAAA │ BBBB │ CCCC │
135
+ # # │ AAAA │ │ CCCC │
136
+ # # │ AAAA │ BBBB │ │
137
+ # # └──────┴──────┴──────┘
138
+ #
139
+ # multi_line_table = [
140
+ # ['AAAA', 'BBBB', "CCCC\nCC"],
141
+ # ['AAAA', "BBBB\nB", 'CCCC'],
142
+ # ["AAAA\nAA\nA", "BBBB", "CCCC"]
143
+ # ]
144
+ #
145
+ # print_table multi_line_table, header: header,
146
+ # justify: :left,
147
+ # border: :line
148
+ # # ┌──────┬──────┬──────┐
149
+ # # │ A │ B │ C │
150
+ # # ├──────┼──────┼──────┤
151
+ # # │ AAAA │ BBBB │ CCCC │
152
+ # # │ │ │ CC │
153
+ # # │ AAAA │ BBBB │ CCCC │
154
+ # # │ │ B │ │
155
+ # # │ AAAA │ BBBB │ CCCC │
156
+ # # │ AA │ │ │
157
+ # # │ A │ │ │
158
+ # # └──────┴──────┴──────┘
159
+ # end
160
+ #
161
+ # @since 0.4.0
162
+ #
163
+ module Tables
164
+ include Indent
165
+
166
+ #
167
+ # Prints a table of rows.
168
+ #
169
+ # @param [Array<Array>] rows
170
+ # The table rows.
171
+ #
172
+ # @param [Array, nil] header
173
+ # The optional header row.
174
+ #
175
+ # @param [Hash{Symbol => Object}] kwargs
176
+ # Additional keyword arguments.
177
+ #
178
+ # @option kwargs [:line, :double_line, nil, Hash{Symbol => String}, :ascii] :border
179
+ # The border style or a custom Hash of border characters.
180
+ #
181
+ # @option [Integer] :padding (1)
182
+ # The number of characters to pad table cell values with.
183
+ #
184
+ # @option [:left, :right, :center] :justify (:left)
185
+ # Specifies how to justify the table cell values.
186
+ #
187
+ # @option [:left, :right, :center] :justify_header (:center)
188
+ # Specifies how to justify the table header cell values.
189
+ #
190
+ # @option [Boolean] :separate_rows (false)
191
+ # Specifies whether to add separator rows in between the rows.
192
+ #
193
+ # @api public
194
+ #
195
+ def print_table(rows, header: nil, **kwargs)
196
+ table = TableBuilder.new(rows, header: header)
197
+ style = Style.new(**kwargs)
198
+ formatter = TableFormatter.new(table,style)
199
+
200
+ formatter.format do |line|
201
+ puts line
202
+ end
203
+
204
+ return nil
205
+ end
206
+ end
207
+ end
208
+ end
@@ -60,5 +60,14 @@ module CommandKit
60
60
  def program_name
61
61
  self.class.program_name
62
62
  end
63
+
64
+ #
65
+ # @see #program_name
66
+ #
67
+ # @since 0.3.0
68
+ #
69
+ def command_name
70
+ program_name
71
+ end
63
72
  end
64
73
  end
@@ -7,7 +7,11 @@ module CommandKit
7
7
  # class MyCmd
8
8
  # include CommandKit::Stdio
9
9
  #
10
- # def main
10
+ # def run
11
+ # print 'Name: '
12
+ # name = gets
13
+ #
14
+ # puts "Hello #{name}!"
11
15
  # end
12
16
  # end
13
17
  #
@@ -1,4 +1,4 @@
1
1
  module CommandKit
2
2
  # command_kit version
3
- VERSION = "0.2.2"
3
+ VERSION = "0.4.0"
4
4
  end
@@ -233,6 +233,39 @@ describe CommandKit::Arguments do
233
233
  ].join($/)
234
234
  ).to_stdout
235
235
  end
236
+
237
+ context "when one the argument has an Array for a description" do
238
+ module TestArguments
239
+ class MultiLineArgumentDescription
240
+ include CommandKit::Arguments
241
+
242
+ argument :foo, desc: "Foo option"
243
+ argument :bar, desc: [
244
+ "Bar option",
245
+ "Line 2",
246
+ "Line 3"
247
+ ]
248
+ argument :baz, desc: "Baz option"
249
+ end
250
+ end
251
+
252
+ let(:command_class) { TestArguments::MultiLineArgumentDescription }
253
+
254
+ it "must print out each line of a multi-line argument description" do
255
+ expect { subject.help_arguments }.to output(
256
+ [
257
+ '',
258
+ "Arguments:",
259
+ " #{foo_argument.usage.ljust(33)}#{foo_argument.desc}",
260
+ " #{bar_argument.usage.ljust(33)}#{bar_argument.desc[0]}",
261
+ " #{' '.ljust(33)}#{bar_argument.desc[1]}",
262
+ " #{' '.ljust(33)}#{bar_argument.desc[2]}",
263
+ " #{baz_argument.usage.ljust(33)}#{baz_argument.desc}",
264
+ ''
265
+ ].join($/)
266
+ ).to_stdout
267
+ end
268
+ end
236
269
  end
237
270
  end
238
271