samovar 2.3.0 → 2.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +215 -0
- data/context/index.yaml +14 -0
- data/lib/samovar/command.rb +119 -26
- data/lib/samovar/error.rb +33 -2
- data/lib/samovar/failure.rb +1 -0
- data/lib/samovar/flags.rb +104 -6
- data/lib/samovar/many.rb +42 -1
- data/lib/samovar/nested.rb +40 -2
- data/lib/samovar/one.rb +42 -1
- data/lib/samovar/option.rb +66 -6
- data/lib/samovar/options.rb +73 -6
- data/lib/samovar/output/columns.rb +19 -0
- data/lib/samovar/output/header.rb +18 -0
- data/lib/samovar/output/row.rb +15 -2
- data/lib/samovar/output/rows.rb +40 -4
- data/lib/samovar/output/usage_formatter.rb +32 -14
- data/lib/samovar/output.rb +2 -2
- data/lib/samovar/split.rb +44 -3
- data/lib/samovar/table.rb +44 -2
- data/lib/samovar/version.rb +3 -2
- data/lib/samovar.rb +3 -3
- data/license.md +1 -1
- data/readme.md +23 -147
- data/releases.md +21 -0
- data.tar.gz.sig +0 -0
- metadata +9 -9
- metadata.gz.sig +0 -0
data/lib/samovar/options.rb
CHANGED
@@ -1,12 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2016-
|
4
|
+
# Copyright, 2016-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "option"
|
7
7
|
|
8
8
|
module Samovar
|
9
|
+
# Represents a collection of command-line options.
|
10
|
+
#
|
11
|
+
# Options provide a DSL for defining multiple option flags in a single block.
|
9
12
|
class Options
|
13
|
+
# Parse and create an options collection from a block.
|
14
|
+
#
|
15
|
+
# @parameter arguments [Array] The arguments for the options collection.
|
16
|
+
# @parameter options [Hash] Additional options.
|
17
|
+
# @yields {|...| ...} A block that defines options using {#option}.
|
18
|
+
# @returns [Options] The frozen options collection.
|
10
19
|
def self.parse(*arguments, **options, &block)
|
11
20
|
options = self.new(*arguments, **options)
|
12
21
|
|
@@ -15,6 +24,10 @@ module Samovar
|
|
15
24
|
return options.freeze
|
16
25
|
end
|
17
26
|
|
27
|
+
# Initialize a new options collection.
|
28
|
+
#
|
29
|
+
# @parameter title [String] The title for this options group in usage output.
|
30
|
+
# @parameter key [Symbol] The key to use for storing parsed options.
|
18
31
|
def initialize(title = "Options", key: :options)
|
19
32
|
@title = title
|
20
33
|
@ordered = []
|
@@ -27,6 +40,9 @@ module Samovar
|
|
27
40
|
@defaults = {}
|
28
41
|
end
|
29
42
|
|
43
|
+
# Initialize a duplicate of this options collection.
|
44
|
+
#
|
45
|
+
# @parameter source [Options] The source options to duplicate.
|
30
46
|
def initialize_dup(source)
|
31
47
|
super
|
32
48
|
|
@@ -35,12 +51,29 @@ module Samovar
|
|
35
51
|
@defaults = @defaults.dup
|
36
52
|
end
|
37
53
|
|
54
|
+
# The title for this options group in usage output.
|
55
|
+
#
|
56
|
+
# @attribute [String]
|
38
57
|
attr :title
|
58
|
+
|
59
|
+
# The ordered list of options.
|
60
|
+
#
|
61
|
+
# @attribute [Array(Option)]
|
39
62
|
attr :ordered
|
40
63
|
|
64
|
+
# The key to use for storing parsed options.
|
65
|
+
#
|
66
|
+
# @attribute [Symbol]
|
41
67
|
attr :key
|
68
|
+
|
69
|
+
# The default values for options.
|
70
|
+
#
|
71
|
+
# @attribute [Hash]
|
42
72
|
attr :defaults
|
43
73
|
|
74
|
+
# Freeze this options collection.
|
75
|
+
#
|
76
|
+
# @returns [Options] The frozen options collection.
|
44
77
|
def freeze
|
45
78
|
return self if frozen?
|
46
79
|
|
@@ -53,24 +86,41 @@ module Samovar
|
|
53
86
|
super
|
54
87
|
end
|
55
88
|
|
89
|
+
# Iterate over each option.
|
90
|
+
#
|
91
|
+
# @yields {|option| ...} Each option in the collection.
|
56
92
|
def each(&block)
|
57
93
|
@ordered.each(&block)
|
58
94
|
end
|
59
95
|
|
96
|
+
# Check if this options collection is empty.
|
97
|
+
#
|
98
|
+
# @returns [Boolean] True if there are no options.
|
60
99
|
def empty?
|
61
100
|
@ordered.empty?
|
62
101
|
end
|
63
102
|
|
103
|
+
# Define a new option in this collection.
|
104
|
+
#
|
105
|
+
# @parameter arguments [Array] The arguments for the option.
|
106
|
+
# @parameter options [Hash] Additional options.
|
107
|
+
# @yields {|value| ...} An optional block to transform the parsed value.
|
64
108
|
def option(*arguments, **options, &block)
|
65
109
|
self << Option.new(*arguments, **options, &block)
|
66
110
|
end
|
67
111
|
|
112
|
+
# Merge another options collection into this one.
|
113
|
+
#
|
114
|
+
# @parameter options [Options] The options to merge.
|
68
115
|
def merge!(options)
|
69
116
|
options.each do |option|
|
70
117
|
self << option
|
71
118
|
end
|
72
119
|
end
|
73
120
|
|
121
|
+
# Add an option to this collection.
|
122
|
+
#
|
123
|
+
# @parameter option [Option] The option to add.
|
74
124
|
def << option
|
75
125
|
@ordered << option
|
76
126
|
option.flags.each do |flag|
|
@@ -86,24 +136,41 @@ module Samovar
|
|
86
136
|
end
|
87
137
|
end
|
88
138
|
|
139
|
+
# Parse options from the input.
|
140
|
+
#
|
141
|
+
# @parameter input [Array(String)] The command-line arguments.
|
142
|
+
# @parameter parent [Command | Nil] The parent command.
|
143
|
+
# @parameter default [Hash | Nil] Default values to use.
|
144
|
+
# @returns [Hash] The parsed option values.
|
89
145
|
def parse(input, parent = nil, default = nil)
|
90
146
|
values = (default || @defaults).dup
|
91
147
|
|
92
148
|
while option = @keyed[input.first]
|
93
|
-
prefix = input.first
|
149
|
+
# prefix = input.first
|
94
150
|
result = option.parse(input)
|
95
151
|
if result != nil
|
96
152
|
values[option.key] = result
|
97
153
|
end
|
98
154
|
end
|
99
155
|
|
156
|
+
# Validate required options
|
157
|
+
@ordered.each do |option|
|
158
|
+
if option.required && !values.key?(option.key)
|
159
|
+
raise MissingValueError.new(parent, option.key)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
100
163
|
return values
|
101
|
-
end
|
102
|
-
|
164
|
+
end # Generate a string representation for usage output.
|
165
|
+
#
|
166
|
+
# @returns [String] The usage string.
|
103
167
|
def to_s
|
104
|
-
@ordered.collect(&:to_s).join(
|
168
|
+
@ordered.collect(&:to_s).join(" ")
|
105
169
|
end
|
106
170
|
|
171
|
+
# Generate usage information for this options collection.
|
172
|
+
#
|
173
|
+
# @parameter rows [Output::Rows] The rows to append usage information to.
|
107
174
|
def usage(rows)
|
108
175
|
@ordered.each do |option|
|
109
176
|
rows << option
|
@@ -4,15 +4,34 @@
|
|
4
4
|
# Copyright, 2016-2023, by Samuel Williams.
|
5
5
|
|
6
6
|
module Samovar
|
7
|
+
# Namespace for output formatting classes.
|
7
8
|
module Output
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Samovar
|
13
|
+
module Output
|
14
|
+
# Represents column widths for aligned output formatting.
|
15
|
+
#
|
16
|
+
# Calculates the maximum width of each column across all rows for proper text alignment.
|
8
17
|
class Columns
|
18
|
+
# Initialize column width calculator.
|
19
|
+
#
|
20
|
+
# @parameter rows [Array(Array)] The rows to calculate column widths from.
|
9
21
|
def initialize(rows)
|
10
22
|
@rows = rows
|
11
23
|
@widths = calculate_widths(rows)
|
12
24
|
end
|
13
25
|
|
26
|
+
# The calculated column widths.
|
27
|
+
#
|
28
|
+
# @attribute [Array(Integer)]
|
14
29
|
attr :widths
|
15
30
|
|
31
|
+
# Calculate the maximum width for each column.
|
32
|
+
#
|
33
|
+
# @parameter rows [Array(Array)] The rows to analyze.
|
34
|
+
# @returns [Array(Integer)] The maximum width of each column.
|
16
35
|
def calculate_widths(rows)
|
17
36
|
widths = []
|
18
37
|
|
@@ -5,15 +5,33 @@
|
|
5
5
|
|
6
6
|
module Samovar
|
7
7
|
module Output
|
8
|
+
# Represents a header row in usage output.
|
9
|
+
#
|
10
|
+
# Headers display command names and their descriptions.
|
8
11
|
class Header
|
12
|
+
# Initialize a new header.
|
13
|
+
#
|
14
|
+
# @parameter name [String] The command name.
|
15
|
+
# @parameter object [Command] The command class.
|
9
16
|
def initialize(name, object)
|
10
17
|
@name = name
|
11
18
|
@object = object
|
12
19
|
end
|
13
20
|
|
21
|
+
# The command name.
|
22
|
+
#
|
23
|
+
# @attribute [String]
|
14
24
|
attr :name
|
25
|
+
|
26
|
+
# The command class.
|
27
|
+
#
|
28
|
+
# @attribute [Command]
|
15
29
|
attr :object
|
16
30
|
|
31
|
+
# Generate an aligned header string.
|
32
|
+
#
|
33
|
+
# @parameter columns [Columns] The columns for alignment (unused for headers).
|
34
|
+
# @returns [String] The command line usage string.
|
17
35
|
def align(columns)
|
18
36
|
@object.command_line(@name)
|
19
37
|
end
|
data/lib/samovar/output/row.rb
CHANGED
@@ -1,22 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
module Samovar
|
7
7
|
module Output
|
8
|
+
# Represents a row in usage output.
|
9
|
+
#
|
10
|
+
# Rows display formatted option or argument information with proper column alignment.
|
8
11
|
class Row < Array
|
12
|
+
# Initialize a new row.
|
13
|
+
#
|
14
|
+
# @parameter object [Object] The object to convert to a row (must respond to `to_a`).
|
9
15
|
def initialize(object)
|
10
16
|
@object = object
|
11
17
|
super object.to_a.collect(&:to_s)
|
12
18
|
end
|
13
19
|
|
20
|
+
# The source object for this row.
|
21
|
+
#
|
22
|
+
# @attribute [Object]
|
14
23
|
attr :object
|
15
24
|
|
25
|
+
# Generate an aligned row string.
|
26
|
+
#
|
27
|
+
# @parameter columns [Columns] The columns for alignment.
|
28
|
+
# @returns [String] The aligned row string.
|
16
29
|
def align(columns)
|
17
30
|
self.collect.with_index do |value, index|
|
18
31
|
value.ljust(columns.widths[index])
|
19
|
-
end.join(
|
32
|
+
end.join(" ")
|
20
33
|
end
|
21
34
|
end
|
22
35
|
end
|
data/lib/samovar/output/rows.rb
CHANGED
@@ -1,40 +1,65 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
6
|
+
require_relative "header"
|
7
|
+
require_relative "columns"
|
8
|
+
require_relative "row"
|
9
9
|
|
10
10
|
module Samovar
|
11
11
|
module Output
|
12
|
+
# Represents a collection of rows for usage output.
|
13
|
+
#
|
14
|
+
# Manages hierarchical usage information with support for nesting and formatting.
|
12
15
|
class Rows
|
13
16
|
include Enumerable
|
14
17
|
|
18
|
+
# Initialize a new rows collection.
|
19
|
+
#
|
20
|
+
# @parameter level [Integer] The indentation level for this collection.
|
15
21
|
def initialize(level = 0)
|
16
22
|
@level = level
|
17
23
|
@rows = []
|
18
24
|
end
|
19
25
|
|
26
|
+
# The indentation level.
|
27
|
+
#
|
28
|
+
# @attribute [Integer]
|
20
29
|
attr :level
|
21
30
|
|
31
|
+
# Check if this collection is empty.
|
32
|
+
#
|
33
|
+
# @returns [Boolean] True if there are no rows.
|
22
34
|
def empty?
|
23
35
|
@rows.empty?
|
24
36
|
end
|
25
37
|
|
38
|
+
# Get the first row.
|
39
|
+
#
|
40
|
+
# @returns [Object | Nil] The first row.
|
26
41
|
def first
|
27
42
|
@rows.first
|
28
43
|
end
|
29
44
|
|
45
|
+
# Get the last row.
|
46
|
+
#
|
47
|
+
# @returns [Object | Nil] The last row.
|
30
48
|
def last
|
31
49
|
@rows.last
|
32
50
|
end
|
33
51
|
|
52
|
+
# Get the indentation string for this level.
|
53
|
+
#
|
54
|
+
# @returns [String] The indentation string.
|
34
55
|
def indentation
|
35
56
|
@indentation ||= "\t" * @level
|
36
57
|
end
|
37
58
|
|
59
|
+
# Iterate over each row.
|
60
|
+
#
|
61
|
+
# @parameter ignore_nested [Boolean] Whether to skip nested rows.
|
62
|
+
# @yields {|row, rows| ...} Each row with its parent collection.
|
38
63
|
def each(ignore_nested: false, &block)
|
39
64
|
return to_enum(:each, ignore_nested: ignore_nested) unless block_given?
|
40
65
|
|
@@ -47,16 +72,27 @@ module Samovar
|
|
47
72
|
end
|
48
73
|
end
|
49
74
|
|
75
|
+
# Add a row to this collection.
|
76
|
+
#
|
77
|
+
# @parameter object [Object] The object to add as a row.
|
78
|
+
# @returns [Rows] Self.
|
50
79
|
def << object
|
51
80
|
@rows << Row.new(object)
|
52
81
|
|
53
82
|
return self
|
54
83
|
end
|
55
84
|
|
85
|
+
# Get the columns for alignment.
|
86
|
+
#
|
87
|
+
# @returns [Columns] The columns calculator.
|
56
88
|
def columns
|
57
89
|
@columns ||= Columns.new(@rows.select{|row| row.is_a? Array})
|
58
90
|
end
|
59
91
|
|
92
|
+
# Create a nested section in the output.
|
93
|
+
#
|
94
|
+
# @parameter arguments [Array] Arguments for the header.
|
95
|
+
# @yields {|rows| ...} A block that populates the nested rows.
|
60
96
|
def nested(*arguments)
|
61
97
|
@rows << Header.new(*arguments)
|
62
98
|
|
@@ -1,33 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
6
|
+
require "mapping/model"
|
7
|
+
require "console/terminal"
|
8
8
|
|
9
|
-
require_relative
|
9
|
+
require_relative "../error"
|
10
10
|
|
11
|
-
require_relative
|
11
|
+
require_relative "header"
|
12
12
|
|
13
|
-
require_relative
|
14
|
-
require_relative
|
13
|
+
require_relative "row"
|
14
|
+
require_relative "rows"
|
15
15
|
|
16
16
|
module Samovar
|
17
17
|
module Output
|
18
|
+
# Formats and prints usage information to a terminal.
|
19
|
+
#
|
20
|
+
# Uses the `mapping` gem to handle different output object types with custom formatting rules.
|
18
21
|
class UsageFormatter < Mapping::Model
|
22
|
+
# Print usage information to the output.
|
23
|
+
#
|
24
|
+
# @parameter rows [Rows] The rows to format and print.
|
25
|
+
# @parameter output [IO] The output stream to print to.
|
26
|
+
# @yields {|formatter| ...} Optional block to customize the formatter.
|
19
27
|
def self.print(rows, output)
|
20
|
-
formatter = self.new(
|
28
|
+
formatter = self.new(output)
|
21
29
|
|
22
30
|
yield formatter if block_given?
|
23
31
|
|
24
|
-
formatter.print
|
32
|
+
formatter.print(rows)
|
25
33
|
end
|
26
34
|
|
27
|
-
|
28
|
-
|
35
|
+
# Initialize a new usage formatter.
|
36
|
+
#
|
37
|
+
# @parameter rows [Rows] The rows to format.
|
38
|
+
# @parameter output [IO] The output stream to print to.
|
39
|
+
def initialize(output)
|
29
40
|
@output = output
|
30
41
|
@width = 80
|
42
|
+
@first = true
|
31
43
|
|
32
44
|
@terminal = Console::Terminal.for(@output)
|
33
45
|
@terminal[:header] = @terminal.style(nil, nil, :bright)
|
@@ -45,7 +57,11 @@ module Samovar
|
|
45
57
|
end
|
46
58
|
|
47
59
|
map(Header) do |header, rows|
|
48
|
-
|
60
|
+
if @first
|
61
|
+
@first = false
|
62
|
+
else
|
63
|
+
@terminal.puts
|
64
|
+
end
|
49
65
|
|
50
66
|
command_line = header.object.command_line(header.name)
|
51
67
|
@terminal.puts "#{rows.indentation}#{command_line}", style: :header
|
@@ -64,8 +80,10 @@ module Samovar
|
|
64
80
|
items.collect{|row, rows| map(row, rows)}
|
65
81
|
end
|
66
82
|
|
67
|
-
|
68
|
-
|
83
|
+
# Print the formatted usage output.
|
84
|
+
def print(rows, first: @first)
|
85
|
+
@first = first
|
86
|
+
map(rows)
|
69
87
|
end
|
70
88
|
end
|
71
89
|
end
|
data/lib/samovar/output.rb
CHANGED
data/lib/samovar/split.rb
CHANGED
@@ -1,11 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2016-
|
4
|
+
# Copyright, 2016-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
module Samovar
|
7
|
+
# Represents a split point in the command-line arguments.
|
8
|
+
#
|
9
|
+
# A `Split` parser divides the argument list at a marker (typically `--`), allowing you to separate arguments meant for your command from those passed to another tool.
|
7
10
|
class Split
|
8
|
-
|
11
|
+
# Initialize a new split parser.
|
12
|
+
#
|
13
|
+
# @parameter key [Symbol] The name of the attribute to store the values after the split.
|
14
|
+
# @parameter description [String] A description of the split for help output.
|
15
|
+
# @parameter marker [String] The marker that indicates the split point.
|
16
|
+
# @parameter default [Object] The default value if no split is present.
|
17
|
+
# @parameter required [Boolean] Whether the split is required.
|
18
|
+
def initialize(key, description, marker: "--", default: nil, required: false)
|
9
19
|
@key = key
|
10
20
|
@description = description
|
11
21
|
@marker = marker
|
@@ -13,16 +23,41 @@ module Samovar
|
|
13
23
|
@required = required
|
14
24
|
end
|
15
25
|
|
26
|
+
# The name of the attribute to store the values after the split.
|
27
|
+
#
|
28
|
+
# @attribute [Symbol]
|
16
29
|
attr :key
|
30
|
+
|
31
|
+
# A description of the split for help output.
|
32
|
+
#
|
33
|
+
# @attribute [String]
|
17
34
|
attr :description
|
35
|
+
|
36
|
+
# The marker that indicates the split point.
|
37
|
+
#
|
38
|
+
# @attribute [String]
|
18
39
|
attr :marker
|
40
|
+
|
41
|
+
# The default value if no split is present.
|
42
|
+
#
|
43
|
+
# @attribute [Object]
|
19
44
|
attr :default
|
45
|
+
|
46
|
+
# Whether the split is required.
|
47
|
+
#
|
48
|
+
# @attribute [Boolean]
|
20
49
|
attr :required
|
21
50
|
|
51
|
+
# Generate a string representation for usage output.
|
52
|
+
#
|
53
|
+
# @returns [String] The usage string.
|
22
54
|
def to_s
|
23
55
|
"#{@marker} <#{@key}...>"
|
24
56
|
end
|
25
57
|
|
58
|
+
# Generate an array representation for usage output.
|
59
|
+
#
|
60
|
+
# @returns [Array] The usage array.
|
26
61
|
def to_a
|
27
62
|
usage = [to_s, @description]
|
28
63
|
|
@@ -35,13 +70,19 @@ module Samovar
|
|
35
70
|
return usage
|
36
71
|
end
|
37
72
|
|
73
|
+
# Parse arguments after the split marker.
|
74
|
+
#
|
75
|
+
# @parameter input [Array(String)] The command-line arguments.
|
76
|
+
# @parameter parent [Command | Nil] The parent command.
|
77
|
+
# @parameter default [Object | Nil] An override for the default value.
|
78
|
+
# @returns [Array(String) | Object | Nil] The arguments after the split, or the default if no split.
|
38
79
|
def parse(input, parent = nil, default = nil)
|
39
80
|
if offset = input.index(@marker)
|
40
81
|
input.pop(input.size - offset).tap(&:shift)
|
41
82
|
elsif default ||= @default
|
42
83
|
return default
|
43
84
|
elsif @required
|
44
|
-
raise MissingValueError.new(parent,
|
85
|
+
raise MissingValueError.new(parent, @key)
|
45
86
|
end
|
46
87
|
end
|
47
88
|
end
|
data/lib/samovar/table.rb
CHANGED
@@ -1,10 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2016-
|
4
|
+
# Copyright, 2016-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
module Samovar
|
7
|
+
# Represents a table of parsing rows for a command.
|
8
|
+
#
|
9
|
+
# A table manages the collection of options, arguments, and nested commands that define how to parse a command line.
|
7
10
|
class Table
|
11
|
+
# Create a nested table that inherits from the parent class's table.
|
12
|
+
#
|
13
|
+
# @parameter klass [Class] The command class to create a table for.
|
14
|
+
# @parameter parent [Table | Nil] The parent table to inherit from.
|
15
|
+
# @returns [Table] The new table.
|
8
16
|
def self.nested(klass, parent = nil)
|
9
17
|
if klass.superclass.respond_to?(:table)
|
10
18
|
parent = klass.superclass.table
|
@@ -13,12 +21,19 @@ module Samovar
|
|
13
21
|
self.new(parent, name: klass.name)
|
14
22
|
end
|
15
23
|
|
24
|
+
# Initialize a new table.
|
25
|
+
#
|
26
|
+
# @parameter parent [Table | Nil] The parent table to inherit from.
|
27
|
+
# @parameter name [String | Nil] The name of the command this table belongs to.
|
16
28
|
def initialize(parent = nil, name: nil)
|
17
29
|
@parent = parent
|
18
30
|
@name = name
|
19
31
|
@rows = {}
|
20
32
|
end
|
21
33
|
|
34
|
+
# Freeze this table.
|
35
|
+
#
|
36
|
+
# @returns [Table] The frozen table.
|
22
37
|
def freeze
|
23
38
|
return self if frozen?
|
24
39
|
|
@@ -27,14 +42,24 @@ module Samovar
|
|
27
42
|
super
|
28
43
|
end
|
29
44
|
|
45
|
+
# Get a row by key.
|
46
|
+
#
|
47
|
+
# @parameter key [Symbol] The key to look up.
|
48
|
+
# @returns [Object | Nil] The row with the given key.
|
30
49
|
def [] key
|
31
50
|
@rows[key]
|
32
51
|
end
|
33
52
|
|
53
|
+
# Iterate over each row.
|
54
|
+
#
|
55
|
+
# @yields {|row| ...} Each row in the table.
|
34
56
|
def each(&block)
|
35
57
|
@rows.each_value(&block)
|
36
58
|
end
|
37
59
|
|
60
|
+
# Add a row to the table.
|
61
|
+
#
|
62
|
+
# @parameter row The row to add.
|
38
63
|
def << row
|
39
64
|
if existing_row = @rows[row.key] and existing_row.respond_to?(:merge!)
|
40
65
|
existing_row.merge!(row)
|
@@ -44,10 +69,17 @@ module Samovar
|
|
44
69
|
end
|
45
70
|
end
|
46
71
|
|
72
|
+
# Check if this table is empty.
|
73
|
+
#
|
74
|
+
# @returns [Boolean] True if this table and its parent are empty.
|
47
75
|
def empty?
|
48
76
|
@rows.empty? && @parent&.empty?
|
49
77
|
end
|
50
78
|
|
79
|
+
# Merge this table's rows into another table.
|
80
|
+
#
|
81
|
+
# @parameter table [Table] The table to merge into.
|
82
|
+
# @returns [Table] The merged table.
|
51
83
|
def merge_into(table)
|
52
84
|
@parent&.merge_into(table)
|
53
85
|
|
@@ -58,6 +90,9 @@ module Samovar
|
|
58
90
|
return table
|
59
91
|
end
|
60
92
|
|
93
|
+
# Get a merged table that includes parent rows.
|
94
|
+
#
|
95
|
+
# @returns [Table] The merged table.
|
61
96
|
def merged
|
62
97
|
if @parent.nil? or @parent.empty?
|
63
98
|
return self
|
@@ -66,10 +101,17 @@ module Samovar
|
|
66
101
|
end
|
67
102
|
end
|
68
103
|
|
104
|
+
# Generate a usage string from all rows.
|
105
|
+
#
|
106
|
+
# @returns [String] The usage string.
|
69
107
|
def usage
|
70
|
-
@rows.each_value.collect(&:to_s).reject(&:empty?).join(
|
108
|
+
@rows.each_value.collect(&:to_s).reject(&:empty?).join(" ")
|
71
109
|
end
|
72
110
|
|
111
|
+
# Parse the input according to the rows in this table.
|
112
|
+
#
|
113
|
+
# @parameter input [Array(String)] The command-line arguments.
|
114
|
+
# @parameter parent [Command] The parent command to store results in.
|
73
115
|
def parse(input, parent)
|
74
116
|
@rows.each do |key, row|
|
75
117
|
next unless row.respond_to?(:parse)
|
data/lib/samovar/version.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2016-
|
4
|
+
# Copyright, 2016-2025, by Samuel Williams.
|
5
5
|
# Copyright, 2018, by Gabriel Mazetto.
|
6
6
|
|
7
|
+
# @namespace
|
7
8
|
module Samovar
|
8
|
-
VERSION = "2.
|
9
|
+
VERSION = "2.4.1"
|
9
10
|
end
|
data/lib/samovar.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2016-
|
4
|
+
# Copyright, 2016-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
6
|
+
require_relative "samovar/version"
|
7
|
+
require_relative "samovar/command"
|