command_kit 0.2.2 → 0.4.0

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