samovar 2.3.0 → 2.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -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 +10 -158
- data.tar.gz.sig +0 -0
- metadata +6 -9
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85e0df9ca2ed86284ab62c49ef724f93818ecb8571a33cccca98759f0a6b2612
|
4
|
+
data.tar.gz: 833c98f6619c7fb73243f6bbbbbb84e25a8d100c03af55392bd97f20a4246803
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0aff1d4bc6f8c2154dcd7e216a8f082fc50b44239321a122b139edff54e0749ab7109a45ab87e2615677e181426717b4912d81118f87f01cd4bf7032653d2041
|
7
|
+
data.tar.gz: d3c94b2986da89eba71d18d06df18dd0541775f2f1c2ba2c327e8407a38e05757e073b5f97c137c8c0241ab29811fd9e0a3fed3c2613034ca7db15ef0e5aff12
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/samovar/command.rb
CHANGED
@@ -1,54 +1,85 @@
|
|
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
|
8
|
-
require_relative
|
9
|
-
require_relative
|
10
|
-
require_relative
|
11
|
-
require_relative
|
6
|
+
require_relative "table"
|
7
|
+
require_relative "options"
|
8
|
+
require_relative "nested"
|
9
|
+
require_relative "one"
|
10
|
+
require_relative "many"
|
11
|
+
require_relative "split"
|
12
12
|
|
13
|
-
require_relative 'output'
|
14
13
|
|
15
|
-
require_relative
|
14
|
+
require_relative "output"
|
15
|
+
|
16
|
+
require_relative "error"
|
16
17
|
|
17
18
|
module Samovar
|
19
|
+
# Represents a command in the command-line interface.
|
20
|
+
#
|
21
|
+
# Commands are the main building blocks of Samovar applications. Each command is a class that can parse command-line arguments, options, and sub-commands.
|
18
22
|
class Command
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# The
|
26
|
-
def self.
|
27
|
-
self.
|
23
|
+
# Parse and execute the command with the given input.
|
24
|
+
#
|
25
|
+
# This is the high-level entry point for CLI applications. It handles errors gracefully by printing usage and returning nil.
|
26
|
+
#
|
27
|
+
# @parameter input [Array(String)] The command-line arguments to parse.
|
28
|
+
# @parameter output [IO] The output stream for error messages.
|
29
|
+
# @returns [Object | Nil] The result of the command's call method, or nil if parsing/execution failed.
|
30
|
+
def self.call(input = ARGV, output: $stderr)
|
31
|
+
self.parse(input).call
|
28
32
|
rescue Error => error
|
29
|
-
error.command.print_usage(output:
|
33
|
+
error.command.print_usage(output: output) do |formatter|
|
30
34
|
formatter.map(error)
|
31
35
|
end
|
32
36
|
|
33
37
|
return nil
|
34
38
|
end
|
35
39
|
|
40
|
+
# Parse the command-line input and create a command instance.
|
41
|
+
#
|
42
|
+
# This is the low-level parsing primitive. It raises {Error} exceptions on parsing failures.
|
43
|
+
# For CLI applications, use {call} instead which handles errors gracefully.
|
44
|
+
#
|
45
|
+
# @parameter input [Array(String)] The command-line arguments to parse.
|
46
|
+
# @returns [Command] The parsed command instance.
|
47
|
+
# @raises [Error] If parsing fails.
|
48
|
+
def self.parse(input)
|
49
|
+
self.new(input)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Create a new command instance with the given arguments.
|
53
|
+
#
|
54
|
+
# This is a convenience method for creating command instances with explicit arguments.
|
55
|
+
#
|
56
|
+
# @parameter input [Array(String)] The command-line arguments to parse.
|
57
|
+
# @parameter options [Hash] Additional options to pass to the command.
|
58
|
+
# @returns [Command] The command instance.
|
36
59
|
def self.[](*input, **options)
|
37
60
|
self.new(input, **options)
|
38
61
|
end
|
39
62
|
|
40
63
|
class << self
|
64
|
+
# A description of the command's purpose.
|
65
|
+
#
|
66
|
+
# @attribute [String]
|
41
67
|
attr_accessor :description
|
42
68
|
end
|
43
69
|
|
70
|
+
# The table of rows for parsing command-line arguments.
|
71
|
+
#
|
72
|
+
# @returns [Table] The table of parsing rows.
|
44
73
|
def self.table
|
45
74
|
@table ||= Table.nested(self)
|
46
75
|
end
|
47
76
|
|
77
|
+
# Append a row to the parsing table.
|
78
|
+
#
|
79
|
+
# @parameter row The row to append to the table.
|
48
80
|
def self.append(row)
|
49
81
|
if method_defined?(row.key, false)
|
50
|
-
|
51
|
-
# raise ArgumentError, "Method for key #{row.key} is already defined!"
|
82
|
+
raise ArgumentError, "Method for key #{row.key} is already defined!"
|
52
83
|
end
|
53
84
|
|
54
85
|
attr_accessor(row.key) if row.respond_to?(:key)
|
@@ -56,26 +87,51 @@ module Samovar
|
|
56
87
|
self.table << row
|
57
88
|
end
|
58
89
|
|
90
|
+
# Define command-line options for this command.
|
91
|
+
#
|
92
|
+
# @parameter arguments [Array] The arguments for the options.
|
93
|
+
# @parameter options [Hash] Additional options.
|
94
|
+
# @yields {|...| ...} A block that defines the options using {Options}.
|
59
95
|
def self.options(*arguments, **options, &block)
|
60
96
|
append Options.parse(*arguments, **options, &block)
|
61
97
|
end
|
62
98
|
|
99
|
+
# Define a nested sub-command.
|
100
|
+
#
|
101
|
+
# @parameter arguments [Array] The arguments for the nested command.
|
102
|
+
# @parameter options [Hash] A hash mapping command names to command classes.
|
63
103
|
def self.nested(*arguments, **options)
|
64
104
|
append Nested.new(*arguments, **options)
|
65
105
|
end
|
66
106
|
|
107
|
+
# Define a single required positional argument.
|
108
|
+
#
|
109
|
+
# @parameter arguments [Array] The arguments for the positional parameter.
|
110
|
+
# @parameter options [Hash] Additional options.
|
67
111
|
def self.one(*arguments, **options)
|
68
112
|
append One.new(*arguments, **options)
|
69
113
|
end
|
70
114
|
|
115
|
+
# Define multiple positional arguments.
|
116
|
+
#
|
117
|
+
# @parameter arguments [Array] The arguments for the positional parameters.
|
118
|
+
# @parameter options [Hash] Additional options.
|
71
119
|
def self.many(*arguments, **options)
|
72
120
|
append Many.new(*arguments, **options)
|
73
121
|
end
|
74
122
|
|
123
|
+
# Define a split point in the argument list (typically `--`).
|
124
|
+
#
|
125
|
+
# @parameter arguments [Array] The arguments for the split.
|
126
|
+
# @parameter options [Hash] Additional options.
|
75
127
|
def self.split(*arguments, **options)
|
76
128
|
append Split.new(*arguments, **options)
|
77
129
|
end
|
78
130
|
|
131
|
+
# Generate usage information for this command.
|
132
|
+
#
|
133
|
+
# @parameter rows [Output::Rows] The rows to append usage information to.
|
134
|
+
# @parameter name [String] The name of the command.
|
79
135
|
def self.usage(rows, name)
|
80
136
|
rows.nested(name, self) do |rows|
|
81
137
|
return unless table = self.table.merged
|
@@ -90,14 +146,22 @@ module Samovar
|
|
90
146
|
end
|
91
147
|
end
|
92
148
|
|
149
|
+
# Generate a command-line usage string.
|
150
|
+
#
|
151
|
+
# @parameter name [String] The name of the command.
|
152
|
+
# @returns [String] The command-line usage string.
|
93
153
|
def self.command_line(name)
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
name
|
98
|
-
end
|
154
|
+
table = self.table.merged
|
155
|
+
|
156
|
+
return "#{name} #{table.usage}"
|
99
157
|
end
|
100
158
|
|
159
|
+
# Initialize a new command instance.
|
160
|
+
#
|
161
|
+
# @parameter input [Array(String) | Nil] The command-line arguments to parse.
|
162
|
+
# @parameter name [String] The name of the command (defaults to the script name).
|
163
|
+
# @parameter parent [Command | Nil] The parent command, if this is a nested command.
|
164
|
+
# @parameter output [IO | Nil] The output stream for usage information.
|
101
165
|
def initialize(input = nil, name: File.basename($0), parent: nil, output: nil)
|
102
166
|
@name = name
|
103
167
|
@parent = parent
|
@@ -106,23 +170,47 @@ module Samovar
|
|
106
170
|
parse(input) if input
|
107
171
|
end
|
108
172
|
|
173
|
+
# The output stream for usage information.
|
174
|
+
#
|
175
|
+
# @attribute [IO]
|
109
176
|
attr :output
|
110
177
|
|
178
|
+
# The output stream for usage information, defaults to `$stdout`.
|
179
|
+
#
|
180
|
+
# @returns [IO] The output stream.
|
111
181
|
def output
|
112
182
|
@output || $stdout
|
113
183
|
end
|
114
184
|
|
185
|
+
# Generate a string representation of the command.
|
186
|
+
#
|
187
|
+
# @returns [String] The class name.
|
115
188
|
def to_s
|
116
189
|
self.class.name
|
117
190
|
end
|
118
191
|
|
192
|
+
# The name of the command.
|
193
|
+
#
|
194
|
+
# @attribute [String]
|
119
195
|
attr :name
|
196
|
+
|
197
|
+
# The parent command, if this is a nested command.
|
198
|
+
#
|
199
|
+
# @attribute [Command | Nil]
|
120
200
|
attr :parent
|
121
201
|
|
202
|
+
# Duplicate the command with additional arguments.
|
203
|
+
#
|
204
|
+
# @parameter input [Array(String)] The additional command-line arguments to parse.
|
205
|
+
# @returns [Command] The duplicated command instance.
|
122
206
|
def [](*input)
|
123
207
|
self.dup.tap{|command| command.parse(input)}
|
124
208
|
end
|
125
209
|
|
210
|
+
# Parse the command-line input.
|
211
|
+
#
|
212
|
+
# @parameter input [Array(String)] The command-line arguments to parse.
|
213
|
+
# @returns [Command] The command instance.
|
126
214
|
def parse(input)
|
127
215
|
self.class.table.merged.parse(input, self)
|
128
216
|
|
@@ -133,6 +221,11 @@ module Samovar
|
|
133
221
|
end
|
134
222
|
end
|
135
223
|
|
224
|
+
# Print usage information for this command.
|
225
|
+
#
|
226
|
+
# @parameter output [IO] The output stream to print to.
|
227
|
+
# @parameter formatter [Class] The formatter class to use for output.
|
228
|
+
# @yields {|formatter| ...} A block to customize the output.
|
136
229
|
def print_usage(output: self.output, formatter: Output::UsageFormatter, &block)
|
137
230
|
rows = Output::Rows.new
|
138
231
|
|
data/lib/samovar/error.rb
CHANGED
@@ -1,13 +1,19 @@
|
|
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
|
+
# The base class for all Samovar errors.
|
7
8
|
class Error < StandardError
|
8
9
|
end
|
9
|
-
|
10
|
+
|
11
|
+
# Raised when invalid input is provided on the command line.
|
10
12
|
class InvalidInputError < Error
|
13
|
+
# Initialize a new invalid input error.
|
14
|
+
#
|
15
|
+
# @parameter command [Command] The command that encountered the error.
|
16
|
+
# @parameter input [Array(String)] The remaining input that could not be parsed.
|
11
17
|
def initialize(command, input)
|
12
18
|
@command = command
|
13
19
|
@input = input
|
@@ -15,19 +21,37 @@ module Samovar
|
|
15
21
|
super "Could not parse token #{input.first.inspect}"
|
16
22
|
end
|
17
23
|
|
24
|
+
# The token that could not be parsed.
|
25
|
+
#
|
26
|
+
# @returns [String] The first unparsed token.
|
18
27
|
def token
|
19
28
|
@input.first
|
20
29
|
end
|
21
30
|
|
31
|
+
# Check if the error was caused by a help request.
|
32
|
+
#
|
33
|
+
# @returns [Boolean] True if the token is `--help`.
|
22
34
|
def help?
|
23
35
|
self.token == "--help"
|
24
36
|
end
|
25
37
|
|
38
|
+
# The command that encountered the error.
|
39
|
+
#
|
40
|
+
# @attribute [Command]
|
26
41
|
attr :command
|
42
|
+
|
43
|
+
# The remaining input that could not be parsed.
|
44
|
+
#
|
45
|
+
# @attribute [Array(String)]
|
27
46
|
attr :input
|
28
47
|
end
|
29
48
|
|
49
|
+
# Raised when a required value is missing.
|
30
50
|
class MissingValueError < Error
|
51
|
+
# Initialize a new missing value error.
|
52
|
+
#
|
53
|
+
# @parameter command [Command] The command that encountered the error.
|
54
|
+
# @parameter field [Symbol] The name of the missing field.
|
31
55
|
def initialize(command, field)
|
32
56
|
@command = command
|
33
57
|
@field = field
|
@@ -35,7 +59,14 @@ module Samovar
|
|
35
59
|
super "#{field} is required"
|
36
60
|
end
|
37
61
|
|
62
|
+
# The command that encountered the error.
|
63
|
+
#
|
64
|
+
# @attribute [Command]
|
38
65
|
attr :command
|
66
|
+
|
67
|
+
# The name of the missing field.
|
68
|
+
#
|
69
|
+
# @attribute [Symbol]
|
39
70
|
attr :field
|
40
71
|
end
|
41
72
|
end
|
data/lib/samovar/failure.rb
CHANGED
data/lib/samovar/flags.rb
CHANGED
@@ -1,37 +1,61 @@
|
|
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 collection of flag alternatives for an option.
|
8
|
+
#
|
9
|
+
# Flags parse text like `-f/--flag <value>` into individual flag parsers.
|
7
10
|
class Flags
|
11
|
+
# Initialize a new flags parser.
|
12
|
+
#
|
13
|
+
# @parameter text [String] The flags specification string (e.g., `-f/--flag <value>`).
|
8
14
|
def initialize(text)
|
9
15
|
@text = text
|
10
16
|
|
11
17
|
@ordered = text.split(/\s+\|\s+/).map{|part| Flag.parse(part)}
|
12
18
|
end
|
13
19
|
|
20
|
+
# Iterate over each flag.
|
21
|
+
#
|
22
|
+
# @yields {|flag| ...} Each flag in the collection.
|
14
23
|
def each(&block)
|
15
24
|
@ordered.each(&block)
|
16
25
|
end
|
17
26
|
|
27
|
+
# Get the first flag.
|
28
|
+
#
|
29
|
+
# @returns [Flag] The first flag.
|
18
30
|
def first
|
19
31
|
@ordered.first
|
20
32
|
end
|
21
33
|
|
22
|
-
# Whether
|
34
|
+
# Whether this flag should have a true/false value if not specified otherwise.
|
35
|
+
#
|
36
|
+
# @returns [Boolean] True if this is a boolean flag.
|
23
37
|
def boolean?
|
24
38
|
@ordered.count == 1 and @ordered.first.boolean?
|
25
39
|
end
|
26
40
|
|
41
|
+
# The number of flag alternatives.
|
42
|
+
#
|
43
|
+
# @returns [Integer] The count of flags.
|
27
44
|
def count
|
28
45
|
return @ordered.count
|
29
46
|
end
|
30
47
|
|
48
|
+
# Generate a string representation for usage output.
|
49
|
+
#
|
50
|
+
# @returns [String] The usage string.
|
31
51
|
def to_s
|
32
52
|
"[#{@ordered.join(' | ')}]"
|
33
53
|
end
|
34
54
|
|
55
|
+
# Parse a flag from the input.
|
56
|
+
#
|
57
|
+
# @parameter input [Array(String)] The command-line arguments.
|
58
|
+
# @returns [Object | Nil] The parsed value, or nil if no match.
|
35
59
|
def parse(input)
|
36
60
|
@ordered.each do |flag|
|
37
61
|
result = flag.parse(input)
|
@@ -44,7 +68,14 @@ module Samovar
|
|
44
68
|
end
|
45
69
|
end
|
46
70
|
|
71
|
+
# Represents a single command-line flag.
|
72
|
+
#
|
73
|
+
# A flag can be a simple boolean flag or a flag that accepts a value.
|
47
74
|
class Flag
|
75
|
+
# Parse a flag specification string into a flag instance.
|
76
|
+
#
|
77
|
+
# @parameter text [String] The flag specification (e.g., `-f <value>` or `--flag`).
|
78
|
+
# @returns [Flag] A flag instance (either {ValueFlag} or {BooleanFlag}).
|
48
79
|
def self.parse(text)
|
49
80
|
if text =~ /(.*?)\s(\<.*?\>)/
|
50
81
|
ValueFlag.new(text, $1, $2)
|
@@ -55,54 +86,107 @@ module Samovar
|
|
55
86
|
end
|
56
87
|
end
|
57
88
|
|
89
|
+
# Initialize a new flag.
|
90
|
+
#
|
91
|
+
# @parameter text [String] The full flag specification text.
|
92
|
+
# @parameter prefix [String] The primary flag prefix (e.g., `--flag`).
|
93
|
+
# @parameter alternatives [Array(String) | Nil] Alternative flag prefixes.
|
58
94
|
def initialize(text, prefix, alternatives = nil)
|
59
95
|
@text = text
|
60
96
|
@prefix = prefix
|
61
97
|
@alternatives = alternatives
|
62
98
|
end
|
63
99
|
|
100
|
+
# The full flag specification text.
|
101
|
+
#
|
102
|
+
# @attribute [String]
|
64
103
|
attr :text
|
104
|
+
|
105
|
+
# The primary flag prefix.
|
106
|
+
#
|
107
|
+
# @attribute [String]
|
65
108
|
attr :prefix
|
109
|
+
|
110
|
+
# Alternative flag prefixes.
|
111
|
+
#
|
112
|
+
# @attribute [Array(String) | Nil]
|
66
113
|
attr :alternatives
|
67
114
|
|
115
|
+
# Generate a string representation for usage output.
|
116
|
+
#
|
117
|
+
# @returns [String] The flag text.
|
68
118
|
def to_s
|
69
119
|
@text
|
70
120
|
end
|
71
121
|
|
122
|
+
# Generate a key name for this flag.
|
123
|
+
#
|
124
|
+
# @returns [Symbol] The key name.
|
72
125
|
def key
|
73
|
-
@key ||= @prefix.sub(/^-*/,
|
126
|
+
@key ||= @prefix.sub(/^-*/, "").gsub("-", "_").to_sym
|
74
127
|
end
|
75
128
|
|
129
|
+
# Whether this is a boolean flag.
|
130
|
+
#
|
131
|
+
# @returns [Boolean] False by default.
|
76
132
|
def boolean?
|
77
133
|
false
|
78
134
|
end
|
79
135
|
end
|
80
136
|
|
137
|
+
# Represents a flag that accepts a value or acts as a boolean.
|
81
138
|
class ValueFlag < Flag
|
139
|
+
# Initialize a new value flag.
|
140
|
+
#
|
141
|
+
# @parameter text [String] The full flag specification text.
|
142
|
+
# @parameter prefix [String] The primary flag prefix with alternatives (e.g., `-f/--flag`).
|
143
|
+
# @parameter value [String | Nil] The value placeholder (e.g., `<file>`).
|
82
144
|
def initialize(text, prefix, value)
|
83
145
|
super(text, prefix)
|
84
146
|
|
85
147
|
@value = value
|
86
148
|
|
87
|
-
*@alternatives, @prefix = @prefix.split(
|
149
|
+
*@alternatives, @prefix = @prefix.split("/")
|
88
150
|
end
|
89
151
|
|
152
|
+
# Alternative flag prefixes.
|
153
|
+
#
|
154
|
+
# @attribute [Array(String)]
|
90
155
|
attr :alternatives
|
156
|
+
|
157
|
+
# The value placeholder.
|
158
|
+
#
|
159
|
+
# @attribute [String | Nil]
|
91
160
|
attr :value
|
92
161
|
|
162
|
+
# Whether this is a boolean flag (no value required).
|
163
|
+
#
|
164
|
+
# @returns [Boolean] True if no value is required.
|
93
165
|
def boolean?
|
94
166
|
@value.nil?
|
95
167
|
end
|
96
168
|
|
169
|
+
# Check if the token matches this flag.
|
170
|
+
#
|
171
|
+
# @parameter token [String] The token to check.
|
172
|
+
# @returns [Boolean] True if the token matches.
|
97
173
|
def prefix?(token)
|
98
174
|
@prefix == token or @alternatives.include?(token)
|
99
175
|
end
|
100
176
|
|
177
|
+
# Parse this flag from the input.
|
178
|
+
#
|
179
|
+
# @parameter input [Array(String)] The command-line arguments.
|
180
|
+
# @returns [String | Symbol | Nil] The parsed value.
|
101
181
|
def parse(input)
|
102
182
|
if prefix?(input.first)
|
183
|
+
# Whether we are expecting to parse a value from input:
|
103
184
|
if @value
|
104
|
-
|
185
|
+
# Get the actual value from input:
|
186
|
+
flag, value = input.shift(2)
|
187
|
+
return value
|
105
188
|
else
|
189
|
+
# Otherwise, we are just a boolean flag:
|
106
190
|
input.shift
|
107
191
|
return key
|
108
192
|
end
|
@@ -110,20 +194,34 @@ module Samovar
|
|
110
194
|
end
|
111
195
|
end
|
112
196
|
|
197
|
+
# Represents a boolean flag with `--flag` and `--no-flag` variants.
|
113
198
|
class BooleanFlag < Flag
|
199
|
+
# Initialize a new boolean flag.
|
200
|
+
#
|
201
|
+
# @parameter text [String] The full flag specification text.
|
202
|
+
# @parameter prefix [String] The primary flag prefix (e.g., `--flag`).
|
203
|
+
# @parameter value [Object | Nil] Reserved for future use.
|
114
204
|
def initialize(text, prefix, value = nil)
|
115
205
|
super(text, prefix)
|
116
206
|
|
117
207
|
@value = value
|
118
208
|
|
119
|
-
@negated = @prefix.sub(/^--/,
|
209
|
+
@negated = @prefix.sub(/^--/, "--no-")
|
120
210
|
@alternatives = [@negated]
|
121
211
|
end
|
122
212
|
|
213
|
+
# Check if the token matches this flag.
|
214
|
+
#
|
215
|
+
# @parameter token [String] The token to check.
|
216
|
+
# @returns [Boolean] True if the token matches.
|
123
217
|
def prefix?(token)
|
124
218
|
@prefix == token or @negated == token
|
125
219
|
end
|
126
220
|
|
221
|
+
# Parse this flag from the input.
|
222
|
+
#
|
223
|
+
# @parameter input [Array(String)] The command-line arguments.
|
224
|
+
# @returns [Boolean | Nil] True, false, or nil.
|
127
225
|
def parse(input)
|
128
226
|
if input.first == @prefix
|
129
227
|
input.shift
|
data/lib/samovar/many.rb
CHANGED
@@ -4,7 +4,17 @@
|
|
4
4
|
# Copyright, 2016-2023, by Samuel Williams.
|
5
5
|
|
6
6
|
module Samovar
|
7
|
+
# Represents multiple positional arguments in a command.
|
8
|
+
#
|
9
|
+
# A `Many` parser extracts all arguments from the command line until it encounters a stop pattern (typically an option flag).
|
7
10
|
class Many
|
11
|
+
# Initialize a new multi-argument parser.
|
12
|
+
#
|
13
|
+
# @parameter key [Symbol] The name of the attribute to store the values in.
|
14
|
+
# @parameter description [String | Nil] A description of the arguments for help output.
|
15
|
+
# @parameter stop [Regexp] A pattern that indicates the end of this argument list.
|
16
|
+
# @parameter default [Object] The default value if no arguments are provided.
|
17
|
+
# @parameter required [Boolean] Whether at least one argument is required.
|
8
18
|
def initialize(key, description = nil, stop: /^-/, default: nil, required: false)
|
9
19
|
@key = key
|
10
20
|
@description = description
|
@@ -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 in.
|
27
|
+
#
|
28
|
+
# @attribute [Symbol]
|
16
29
|
attr :key
|
30
|
+
|
31
|
+
# A description of the arguments for help output.
|
32
|
+
#
|
33
|
+
# @attribute [String | Nil]
|
17
34
|
attr :description
|
35
|
+
|
36
|
+
# A pattern that indicates the end of this argument list.
|
37
|
+
#
|
38
|
+
# @attribute [Regexp]
|
18
39
|
attr :stop
|
40
|
+
|
41
|
+
# The default value if no arguments are provided.
|
42
|
+
#
|
43
|
+
# @attribute [Object]
|
19
44
|
attr :default
|
45
|
+
|
46
|
+
# Whether at least one argument 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
|
"<#{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,6 +70,12 @@ module Samovar
|
|
35
70
|
return usage
|
36
71
|
end
|
37
72
|
|
73
|
+
# Parse multiple arguments from the input.
|
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 parsed values, or the default if none match.
|
38
79
|
def parse(input, parent = nil, default = nil)
|
39
80
|
if @stop and stop_index = input.index{|item| @stop === item}
|
40
81
|
input.shift(stop_index)
|
@@ -43,7 +84,7 @@ module Samovar
|
|
43
84
|
elsif default ||= @default
|
44
85
|
return default
|
45
86
|
elsif @required
|
46
|
-
raise MissingValueError.new(parent,
|
87
|
+
raise MissingValueError.new(parent, @key)
|
47
88
|
end
|
48
89
|
end
|
49
90
|
end
|