toys-core 0.13.1 → 0.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/README.md +18 -16
- data/docs/guide.md +111 -6
- data/lib/toys/acceptor.rb +64 -58
- data/lib/toys/arg_parser.rb +4 -4
- data/lib/toys/cli.rb +3 -3
- data/lib/toys/completion.rb +5 -1
- data/lib/toys/context.rb +4 -3
- data/lib/toys/core.rb +1 -1
- data/lib/toys/errors.rb +14 -3
- data/lib/toys/flag.rb +10 -2
- data/lib/toys/input_file.rb +8 -8
- data/lib/toys/loader.rb +160 -92
- data/lib/toys/middleware.rb +24 -5
- data/lib/toys/settings.rb +2 -2
- data/lib/toys/source_info.rb +9 -9
- data/lib/toys/standard_middleware/show_help.rb +4 -16
- data/lib/toys/standard_mixins/exec.rb +86 -22
- data/lib/toys/standard_mixins/pager.rb +57 -0
- data/lib/toys/standard_mixins/xdg.rb +7 -7
- data/lib/toys/tool_definition.rb +10 -8
- data/lib/toys/utils/exec.rb +218 -37
- data/lib/toys/utils/help_text.rb +4 -1
- data/lib/toys/utils/pager.rb +138 -0
- data/lib/toys/wrappable_string.rb +12 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49f6342ac835f193218570f20cc534fc7f1d4fab14742602df3a7174cfc8a554
|
4
|
+
data.tar.gz: 27b8298897b5c8fd08319fffec33540c057675ed8655700280b4837f413cc635
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55dc00e57b5c6b3212ed24d45bb3a595dae8b5b6fd076ddefdbe4835d621dfeb70d58b8551ae69a8190dc3884a66968e78b800d93b07eed6b61d42eef70faea7
|
7
|
+
data.tar.gz: eb6b5c306c815947290a8fb99834a4a809052edf7e6c781f1b562c61217cf49c63a9609beb168b0e06c5e09ace51083bbb7da15ad58746e72fc5017e04f9546d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,33 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### v0.14.1 / 2022-10-03
|
4
|
+
|
5
|
+
* (No significant changes)
|
6
|
+
|
7
|
+
### v0.14.0 / 2022-10-03
|
8
|
+
|
9
|
+
Toys-Core 0.14.0 is a major release with pager support, support for tees and pipes in the Exec utility, some cleanup of the behavior of Acceptors, and other improvements.
|
10
|
+
|
11
|
+
Fixes that are potentially breaking:
|
12
|
+
|
13
|
+
* Disallowed acceptors on flags that are explicitly boolean.
|
14
|
+
* Acceptors no longer sometimes apply to the boolean setting of a flag with an optional value.
|
15
|
+
|
16
|
+
New functionality:
|
17
|
+
|
18
|
+
* Implemented a utility class and mixin for output pagers.
|
19
|
+
* Builtin commands that display data can format as either YAML or JSON.
|
20
|
+
* The Exec utility and mixin can tee (i.e. duplicate and split) output streams.
|
21
|
+
* The Exec utility and mixin can take pipes as input and output streams.
|
22
|
+
* The Exec mixin provides a `verbosity_flags` convenience method.
|
23
|
+
* `Loader#list_subtools` takes separate arguments for filtering namespaces and non-runnable tools
|
24
|
+
|
25
|
+
Fixes:
|
26
|
+
|
27
|
+
* Contents of preload directories are loaded in sorted order.
|
28
|
+
* Removed some potential deadlocks if a `Toys::Loader` is accessed from multiple threads
|
29
|
+
* Various clarifications, fixes, and updates to the users guide and documentation.
|
30
|
+
|
3
31
|
### v0.13.1 / 2022-03-01
|
4
32
|
|
5
33
|
* FIXED: Toys::Utils::Gems no longer fails to install a bundle if it had locked to a different version of a builtin gem
|
data/README.md
CHANGED
@@ -25,23 +25,15 @@ recommend first walking through the tutorial in the
|
|
25
25
|
you are running a unix-like system such as Linux or macOS. Some commands might
|
26
26
|
need to be modified if you're running on Windows.
|
27
27
|
|
28
|
-
### Install Toys
|
28
|
+
### Install Toys-Core
|
29
29
|
|
30
30
|
Install the **toys-core** gem using:
|
31
31
|
|
32
32
|
$ gem install toys-core
|
33
33
|
|
34
|
-
You
|
34
|
+
You can also install the **toys** gem, which brings in **toys-core** as a
|
35
35
|
dependency.
|
36
36
|
|
37
|
-
Toys-Core requires Ruby 2.4 or later.
|
38
|
-
|
39
|
-
Most parts of Toys-Core work on JRuby. However, JRuby is not recommended
|
40
|
-
because of JVM boot latency, lack of support for Kernel#fork, and other issues.
|
41
|
-
|
42
|
-
Most parts of Toys-Core work on TruffleRuby. However, TruffleRuby is not
|
43
|
-
recommended because it has a few known bugs that affect Toys.
|
44
|
-
|
45
37
|
### Create a new executable
|
46
38
|
|
47
39
|
We'll start by creating an executable Ruby script. Using your favorite text
|
@@ -77,7 +69,7 @@ use to write Toys files. You could point your executable at a directory
|
|
77
69
|
containing actual Toys files, but the simplest option is to provide the
|
78
70
|
information to the Toys CLI object in a block.
|
79
71
|
|
80
|
-
Let's add some functionality
|
72
|
+
Let's add some functionality to `mycmd`.
|
81
73
|
|
82
74
|
#!/usr/bin/env ruby
|
83
75
|
|
@@ -107,7 +99,7 @@ look familiar. Let's run it now, and experiment with passing flags to it.
|
|
107
99
|
Notice that we did not create a `tool` block, but instead set up description,
|
108
100
|
flags, and functionality directly in the configuration block. This configures
|
109
101
|
the "root tool", i.e. what happens when you run the executable without passing
|
110
|
-
a tool name to it. (
|
102
|
+
a tool name to it. (In fact, it's legal to do this in Toys as well, by setting
|
111
103
|
functionality at the "top level" of a `.toys.rb` file without including any
|
112
104
|
`tool` block.)
|
113
105
|
|
@@ -172,8 +164,8 @@ modifies error handling and delimiter parsing.
|
|
172
164
|
#### Pass some additional options to the CLI constructor ...
|
173
165
|
cli = Toys::CLI.new(
|
174
166
|
extra_delimiters: ":",
|
175
|
-
error_handler: ->(
|
176
|
-
puts "
|
167
|
+
error_handler: ->(err) {
|
168
|
+
puts "Aww shucks, an error happened: #{err.message}"
|
177
169
|
return 1
|
178
170
|
}
|
179
171
|
)
|
@@ -209,8 +201,8 @@ Toys _middleware_ are objects that provide common functionality for all the
|
|
209
201
|
tools in your executable. For example, a middleware adds the `--help` flag to
|
210
202
|
your tools by default.
|
211
203
|
|
212
|
-
The next example
|
213
|
-
functionality.
|
204
|
+
The next example provides a custom middleware stack, resulting in a different
|
205
|
+
set of common tool functionality.
|
214
206
|
|
215
207
|
#!/usr/bin/env ruby
|
216
208
|
|
@@ -339,6 +331,16 @@ handling errors, controlling log output, and providing your own mixins,
|
|
339
331
|
templates, and middleware, in the
|
340
332
|
[Toys-Core User Guide](https://dazuma.github.io/toys/gems/toys-core/latest/file.guide.html).
|
341
333
|
|
334
|
+
## System requirements
|
335
|
+
|
336
|
+
Toys-Core requires Ruby 2.4 or later.
|
337
|
+
|
338
|
+
Most parts of Toys-Core work on JRuby. However, JRuby is not recommended
|
339
|
+
because of JVM boot latency, lack of support for Kernel#fork, and other issues.
|
340
|
+
|
341
|
+
Most parts of Toys-Core work on TruffleRuby. However, TruffleRuby is not
|
342
|
+
recommended because it has a few known bugs that affect Toys.
|
343
|
+
|
342
344
|
## License
|
343
345
|
|
344
346
|
Copyright 2019-2022 Daniel Azuma and the Toys contributors
|
data/docs/guide.md
CHANGED
@@ -29,7 +29,7 @@ sophisticated command line tools.
|
|
29
29
|
|
30
30
|
## Conceptual overview
|
31
31
|
|
32
|
-
Toys-Core is a command line
|
32
|
+
Toys-Core is a **command line framework** in the traditional sense. It is
|
33
33
|
intended to be used to write custom command line executables in Ruby. The
|
34
34
|
framework provides common facilities such as argument parsing and online help,
|
35
35
|
while your executable chooses and configures those facilities, and implements
|
@@ -44,7 +44,7 @@ written in **toys files** or in **blocks** passed to the CLI. It uses the same
|
|
44
44
|
DSL used by Toys itself, and supports tools, subtools, flags, arguments, help
|
45
45
|
text, and all the other features of Toys.
|
46
46
|
|
47
|
-
An executable
|
47
|
+
An executable can customize its own facilities for writing tools by providing
|
48
48
|
**built-in mixins** and **built-in templates**, and can implement default
|
49
49
|
behavior across all tools by providing **middleware**.
|
50
50
|
|
@@ -52,7 +52,7 @@ Most executables will provide a set of **static tools**, but it is possible to
|
|
52
52
|
support user-provided tools as Toys does. Executables can customize how tool
|
53
53
|
definitions are searched and loaded from the file system.
|
54
54
|
|
55
|
-
Finally, an executable
|
55
|
+
Finally, an executable can customize many aspects of its behavior, such as the
|
56
56
|
**logging output**, **error handling**, and even shell **tab completion**.
|
57
57
|
|
58
58
|
## Using the CLI object
|
@@ -96,7 +96,109 @@ Following is a simple "hello world" example using the CLI:
|
|
96
96
|
|
97
97
|
This section provides some detail on how a CLI executes your code.
|
98
98
|
|
99
|
-
|
99
|
+
When you call {Toys::CLI#run}, the CLI runs through three phases:
|
100
|
+
|
101
|
+
* **Loading** in which the CLI identifies which tool to run, and loads the
|
102
|
+
tool from a tool **source**, which could be a block passed to the CLI, or a
|
103
|
+
file loaded from the file system, git, or other location.
|
104
|
+
* **Building context**, in which the CLI parses the command-line arguments
|
105
|
+
according to the flags and arguments declared by the tool, instantiates the
|
106
|
+
tool, and populates the {Toys::Context} object (which is `self` when the
|
107
|
+
tool is executed)
|
108
|
+
* **Execution**, which involves running any initializers defined on the tool,
|
109
|
+
applying middleware, running the tool, and handling errors.
|
110
|
+
|
111
|
+
#### The Loader
|
112
|
+
|
113
|
+
When the CLI needs the definition of a tool, it queries the {Toys::Loader}. The
|
114
|
+
loader object is configured with a set of tool _sources_ representing ways to
|
115
|
+
define a tool. These sources may be blocks passed directly to the CLI,
|
116
|
+
directories and files in the file system, and remote git repositories. When a
|
117
|
+
tool is requested by name, the loader is responsible for locating the tool
|
118
|
+
definition in those sources, and constructing the tool definition object,
|
119
|
+
represented by {Toys::ToolDefinition}.
|
120
|
+
|
121
|
+
One important property of the loader is that it is _lazy_. It queries tool
|
122
|
+
sources only if it has reason to believe that a tool it is looking for may be
|
123
|
+
defined there. For example, if your tools are defined in a directory structure,
|
124
|
+
the `foo bar` tool might live in the file `foo/bar.rb`. The loader will open
|
125
|
+
that file, if it exists, only when the `foo bar` tool is requested. If instead
|
126
|
+
`foo qux` is requested, the `foo/bar.rb` file is never even opened.
|
127
|
+
|
128
|
+
Perhaps more subtly, if you call {Toys::CLI#add_config_block} to define tools,
|
129
|
+
the block is stored in the loader object _but not called immediately_. Only
|
130
|
+
when a tool is requested does the block actually execute. Furthermore, if you
|
131
|
+
have `tool` blocks inside the block, the loader will execute only those that
|
132
|
+
are relevant to a tool it wants. Hence:
|
133
|
+
|
134
|
+
cli.add_config_block do
|
135
|
+
tool "foo" do
|
136
|
+
def run
|
137
|
+
puts "foo called"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
tool "bar" do
|
142
|
+
def run
|
143
|
+
puts "bar called"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
If only `foo` is requested, the loader will execute the `tool "foo" do` block
|
149
|
+
to get that tool definition, but will not execute the `tool "bar" do` block.
|
150
|
+
|
151
|
+
We will discuss more about the features of the loader below in the section on
|
152
|
+
[defining functionality](#Defining_functionality).
|
153
|
+
|
154
|
+
#### Building context
|
155
|
+
|
156
|
+
Once a tool is defined, the CLI prepares it for execution by building a
|
157
|
+
{Toys::Context} object. This object is `self` during tool runtime, and it
|
158
|
+
includes:
|
159
|
+
|
160
|
+
* The tool's methods, including its `run` entrypoint method.
|
161
|
+
* Access to core tool functionality such as exit codes and logging.
|
162
|
+
* The results from parsing the command line arguments
|
163
|
+
* The runtime environment, including the tool's name, where the tool was
|
164
|
+
defined, detailed results from argumet parsing, and so forth.
|
165
|
+
|
166
|
+
Much of this information is stored in a data hash, whose keys are defined as
|
167
|
+
constants under {Toys::Context::Key}.
|
168
|
+
|
169
|
+
Argument parsing is directed by the {Toys::ArgParser} class. This class, for
|
170
|
+
the most part, replicates the semantics of the standard Ruby OptionParser
|
171
|
+
class, but it implements a few extra features and cleans up a few ambiguities.
|
172
|
+
|
173
|
+
#### Tool execution and error handling
|
174
|
+
|
175
|
+
The execution phase involves:
|
176
|
+
|
177
|
+
* Running the tool's initializers, if any, in order.
|
178
|
+
* Running the tool's middleware. Each middleware "wraps" the execution of
|
179
|
+
subsequent middleware and the final tool execution, and has the opportunity
|
180
|
+
to inject functionality before and after the main execution, or even to
|
181
|
+
forgo or replace the main functionality, similar to Rack middleware.
|
182
|
+
* Executing the tool itself by calling its `run` method.
|
183
|
+
|
184
|
+
During execution, exceptions are caught and reported along with the location in
|
185
|
+
the tool source where it was triggered. This logic is handled by the
|
186
|
+
{Toys::ContextualError} class.
|
187
|
+
|
188
|
+
The CLI can be configured with an error handler that responds to any exceptions
|
189
|
+
raised during execution. An error handler is simply a callable object (such as
|
190
|
+
a `Proc`) that takes an exception as an argument. The provided
|
191
|
+
{Toys::CLI::DefaultErrorHandler} class provides the default behavior of the
|
192
|
+
normal `toys` CLI, but you can provide any object that duck types the `call`
|
193
|
+
method.
|
194
|
+
|
195
|
+
#### Multiple runs
|
196
|
+
|
197
|
+
The {Toys::CLI} object can be reused to run multiple tools. This may save on
|
198
|
+
loading overhead, as the tools can be loaded just once and their definitions
|
199
|
+
reused for multiple executions. It can even perform multiple executions
|
200
|
+
concurrently in separate threads, assuming the tool implementations themselves
|
201
|
+
are thread-safe.
|
100
202
|
|
101
203
|
### Configuring the CLI
|
102
204
|
|
@@ -117,8 +219,9 @@ These features include:
|
|
117
219
|
|
118
220
|
Each of the actual parameters is covered in detail in the documentation for
|
119
221
|
{Toys::CLI#initialize}. The configuration of a CLI cannot be changed once the
|
120
|
-
CLI is constructed. If you need
|
121
|
-
{Toys::CLI#child}
|
222
|
+
CLI is constructed. If you need a CLI with modified configuration, use
|
223
|
+
{Toys::CLI#child}, which creates a _copy_ of the CLI with any modifications you
|
224
|
+
request.
|
122
225
|
|
123
226
|
## Defining functionality
|
124
227
|
|
@@ -151,3 +254,5 @@ CLI is constructed. If you need to a CLI with a modified configuration, use
|
|
151
254
|
### Tab completion
|
152
255
|
|
153
256
|
## Packaging your executable
|
257
|
+
|
258
|
+
## Overview of Toys-Core classes
|
data/lib/toys/acceptor.rb
CHANGED
@@ -83,7 +83,7 @@ module Toys
|
|
83
83
|
# When given a valid input, return an array in which the first element is
|
84
84
|
# the original input string, and the remaining elements (which may be
|
85
85
|
# empty) comprise any additional information that may be useful during
|
86
|
-
# conversion. If there is no additional information, you
|
86
|
+
# conversion. If there is no additional information, you can return the
|
87
87
|
# original input string by itself without wrapping in an array.
|
88
88
|
#
|
89
89
|
# When given an invalid input, return a falsy value such as `nil`.
|
@@ -95,8 +95,7 @@ module Toys
|
|
95
95
|
# as the only array element, indicating all inputs are valid. You can
|
96
96
|
# override this method to provide a different validation function.
|
97
97
|
#
|
98
|
-
# @param str [String
|
99
|
-
# value is optional and not provided.
|
98
|
+
# @param str [String] The input argument string.
|
100
99
|
# @return [String,Array,nil]
|
101
100
|
#
|
102
101
|
def match(str)
|
@@ -110,15 +109,14 @@ module Toys
|
|
110
109
|
# original input string and any other values returned from {#match}. It
|
111
110
|
# must return the final converted value to use.
|
112
111
|
#
|
113
|
-
# @param str [String
|
114
|
-
# value is optional and not provided.
|
112
|
+
# @param str [String] Original argument string.
|
115
113
|
# @param extra [Object...] Zero or more additional arguments comprising
|
116
114
|
# additional elements returned from the match function.
|
117
115
|
# @return [Object] The converted argument as it should be stored in the
|
118
116
|
# context data.
|
119
117
|
#
|
120
118
|
def convert(str, *extra) # rubocop:disable Lint/UnusedMethodArgument
|
121
|
-
str
|
119
|
+
str
|
122
120
|
end
|
123
121
|
|
124
122
|
##
|
@@ -183,7 +181,7 @@ module Toys
|
|
183
181
|
#
|
184
182
|
def match(str)
|
185
183
|
result = @function.call(str) rescue REJECT # rubocop:disable Style/RescueModifier
|
186
|
-
|
184
|
+
REJECT.equal?(result) ? nil : [str, result]
|
187
185
|
end
|
188
186
|
|
189
187
|
##
|
@@ -240,7 +238,7 @@ module Toys
|
|
240
238
|
# @private
|
241
239
|
#
|
242
240
|
def match(str)
|
243
|
-
|
241
|
+
@regex.match(str)
|
244
242
|
end
|
245
243
|
|
246
244
|
##
|
@@ -293,7 +291,7 @@ module Toys
|
|
293
291
|
# @private
|
294
292
|
#
|
295
293
|
def match(str)
|
296
|
-
|
294
|
+
@values.find { |s, _e| s == str }
|
297
295
|
end
|
298
296
|
|
299
297
|
##
|
@@ -418,9 +416,7 @@ module Toys
|
|
418
416
|
#
|
419
417
|
NUMERIC_CONVERTER =
|
420
418
|
proc do |s|
|
421
|
-
if s.
|
422
|
-
nil
|
423
|
-
elsif s.include?("/")
|
419
|
+
if s.include?("/")
|
424
420
|
Rational(s)
|
425
421
|
elsif s.include?(".") || (s.include?("e") && s !~ /\A-?0x/)
|
426
422
|
Float(s)
|
@@ -429,6 +425,41 @@ module Toys
|
|
429
425
|
end
|
430
426
|
end
|
431
427
|
|
428
|
+
##
|
429
|
+
# A set of strings that are considered true for boolean acceptors.
|
430
|
+
# Currently set to `["+", "true", "yes"]`.
|
431
|
+
# @return [Array<String>]
|
432
|
+
#
|
433
|
+
TRUE_STRINGS = ["+", "true", "yes"].freeze
|
434
|
+
|
435
|
+
##
|
436
|
+
# A set of strings that are considered false for boolean acceptors.
|
437
|
+
# Currently set to `["-", "false", "no", "nil"]`.
|
438
|
+
# @return [Array<String>]
|
439
|
+
#
|
440
|
+
FALSE_STRINGS = ["-", "false", "no", "nil"].freeze
|
441
|
+
|
442
|
+
##
|
443
|
+
# A converter proc that handles boolean strings. Recognizes {TRUE_STRINGS}
|
444
|
+
# and {FALSE_STRINGS}. Useful for Simple acceptors.
|
445
|
+
# @return [Proc]
|
446
|
+
#
|
447
|
+
BOOLEAN_CONVERTER =
|
448
|
+
proc do |s|
|
449
|
+
if s.empty?
|
450
|
+
REJECT
|
451
|
+
else
|
452
|
+
s = s.downcase
|
453
|
+
if TRUE_STRINGS.any? { |t| t.start_with?(s) }
|
454
|
+
true
|
455
|
+
elsif FALSE_STRINGS.any? { |f| f.start_with?(s) }
|
456
|
+
false
|
457
|
+
else
|
458
|
+
REJECT
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
432
463
|
class << self
|
433
464
|
##
|
434
465
|
# Lookup a standard acceptor name recognized by OptionParser.
|
@@ -521,7 +552,11 @@ module Toys
|
|
521
552
|
end
|
522
553
|
|
523
554
|
##
|
524
|
-
#
|
555
|
+
# Take the various ways to express an acceptor spec, and convert them to
|
556
|
+
# a canonical form expressed as a single object. This is called from the
|
557
|
+
# DSL to generate a spec object that can be stored.
|
558
|
+
#
|
559
|
+
# @private This interface is internal and subject to change without warning.
|
525
560
|
#
|
526
561
|
def scalarize_spec(spec, options, block)
|
527
562
|
spec ||= block
|
@@ -564,8 +599,8 @@ module Toys
|
|
564
599
|
::Float => build_float,
|
565
600
|
::Rational => build_rational,
|
566
601
|
::Numeric => build_numeric,
|
567
|
-
::TrueClass => build_boolean(::TrueClass
|
568
|
-
::FalseClass => build_boolean(::FalseClass
|
602
|
+
::TrueClass => build_boolean(::TrueClass),
|
603
|
+
::FalseClass => build_boolean(::FalseClass),
|
569
604
|
::Array => build_array,
|
570
605
|
::Regexp => build_regexp,
|
571
606
|
}
|
@@ -603,77 +638,48 @@ module Toys
|
|
603
638
|
Simple.new(NUMERIC_CONVERTER, type_desc: "number", well_known_spec: ::Numeric)
|
604
639
|
end
|
605
640
|
|
606
|
-
|
607
|
-
|
608
|
-
private_constant :TRUE_STRINGS, :FALSE_STRINGS
|
609
|
-
|
610
|
-
def build_boolean(spec, default)
|
611
|
-
Simple.new(type_desc: "boolean", well_known_spec: spec) do |s|
|
612
|
-
if s.nil?
|
613
|
-
default
|
614
|
-
elsif s.empty?
|
615
|
-
REJECT
|
616
|
-
else
|
617
|
-
s = s.downcase
|
618
|
-
if TRUE_STRINGS.any? { |t| t.start_with?(s) }
|
619
|
-
true
|
620
|
-
elsif FALSE_STRINGS.any? { |f| f.start_with?(s) }
|
621
|
-
false
|
622
|
-
else
|
623
|
-
REJECT
|
624
|
-
end
|
625
|
-
end
|
626
|
-
end
|
641
|
+
def build_boolean(spec)
|
642
|
+
Simple.new(BOOLEAN_CONVERTER, type_desc: "boolean", well_known_spec: spec)
|
627
643
|
end
|
628
644
|
|
629
645
|
def build_array
|
630
646
|
Simple.new(type_desc: "string array", well_known_spec: ::Array) do |s|
|
631
|
-
|
632
|
-
nil
|
633
|
-
else
|
634
|
-
s.split(",").collect { |elem| elem unless elem.empty? }
|
635
|
-
end
|
647
|
+
s.split(",").collect { |elem| elem unless elem.empty? }
|
636
648
|
end
|
637
649
|
end
|
638
650
|
|
639
651
|
def build_regexp
|
640
652
|
Simple.new(type_desc: "regular expression", well_known_spec: ::Regexp) do |s|
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
flags |= ::Regexp::IGNORECASE if opts.include?("i")
|
649
|
-
flags |= ::Regexp::MULTILINE if opts.include?("m")
|
650
|
-
flags |= ::Regexp::EXTENDED if opts.include?("x")
|
651
|
-
end
|
652
|
-
::Regexp.new(s, flags)
|
653
|
+
flags = 0
|
654
|
+
if (match = %r{\A/((?:\\.|[^\\])*)/([[:alpha:]]*)\z}.match(s))
|
655
|
+
s = match[1]
|
656
|
+
opts = match[2] || ""
|
657
|
+
flags |= ::Regexp::IGNORECASE if opts.include?("i")
|
658
|
+
flags |= ::Regexp::MULTILINE if opts.include?("m")
|
659
|
+
flags |= ::Regexp::EXTENDED if opts.include?("x")
|
653
660
|
end
|
661
|
+
::Regexp.new(s, flags)
|
654
662
|
end
|
655
663
|
end
|
656
664
|
|
657
665
|
def build_decimal_integer
|
658
666
|
Simple.new(type_desc: "decimal integer",
|
659
667
|
well_known_spec: ::OptionParser::DecimalInteger) do |s|
|
660
|
-
|
668
|
+
Integer(s, 10)
|
661
669
|
end
|
662
670
|
end
|
663
671
|
|
664
672
|
def build_octal_integer
|
665
673
|
Simple.new(type_desc: "octal integer",
|
666
674
|
well_known_spec: ::OptionParser::OctalInteger) do |s|
|
667
|
-
|
675
|
+
Integer(s, 8)
|
668
676
|
end
|
669
677
|
end
|
670
678
|
|
671
679
|
def build_decimal_numeric
|
672
680
|
Simple.new(type_desc: "decimal number",
|
673
681
|
well_known_spec: ::OptionParser::DecimalNumeric) do |s|
|
674
|
-
if s.
|
675
|
-
nil
|
676
|
-
elsif s.include?(".") || (s.include?("e") && s !~ /\A-?0x/)
|
682
|
+
if s.include?(".") || (s.include?("e") && s !~ /\A-?0x/)
|
677
683
|
Float(s)
|
678
684
|
else
|
679
685
|
Integer(s, 10)
|
data/lib/toys/arg_parser.rb
CHANGED
@@ -439,7 +439,7 @@ module Toys
|
|
439
439
|
return false unless @active_flag_def
|
440
440
|
result = @active_flag_def.value_type == :required || !arg.start_with?("-")
|
441
441
|
add_data(@active_flag_def.key, @active_flag_def.handler, @active_flag_def.acceptor,
|
442
|
-
result ? arg :
|
442
|
+
result ? arg : true, :flag, @active_flag_arg)
|
443
443
|
@seen_flag_keys << @active_flag_def.key
|
444
444
|
@active_flag_def = nil
|
445
445
|
@active_flag_arg = nil
|
@@ -481,7 +481,7 @@ module Toys
|
|
481
481
|
@active_flag_def = flag_def
|
482
482
|
@active_flag_arg = name
|
483
483
|
else
|
484
|
-
add_data(flag_def.key, flag_def.handler, flag_def.acceptor,
|
484
|
+
add_data(flag_def.key, flag_def.handler, flag_def.acceptor, true, :flag, name)
|
485
485
|
end
|
486
486
|
else
|
487
487
|
add_data(flag_def.key, flag_def.handler, flag_def.acceptor, following, :flag, name)
|
@@ -540,7 +540,7 @@ module Toys
|
|
540
540
|
end
|
541
541
|
|
542
542
|
def add_data(key, handler, accept, value, type_name, display_name)
|
543
|
-
if accept
|
543
|
+
if accept && value.is_a?(::String)
|
544
544
|
match = accept.match(value)
|
545
545
|
unless match
|
546
546
|
error_class = type_name == :flag ? FlagValueUnacceptableError : ArgValueUnacceptableError
|
@@ -562,7 +562,7 @@ module Toys
|
|
562
562
|
@errors << FlagValueMissingError.new(name: @active_flag_arg)
|
563
563
|
else
|
564
564
|
add_data(@active_flag_def.key, @active_flag_def.handler, @active_flag_def.acceptor,
|
565
|
-
|
565
|
+
true, :flag, @active_flag_arg)
|
566
566
|
end
|
567
567
|
end
|
568
568
|
end
|
data/lib/toys/cli.rb
CHANGED
@@ -226,9 +226,9 @@ module Toys
|
|
226
226
|
end
|
227
227
|
|
228
228
|
##
|
229
|
-
# Make a clone with the same settings but no
|
230
|
-
#
|
231
|
-
#
|
229
|
+
# Make a clone with the same settings but no config blocks and no paths in
|
230
|
+
# the loader. This is sometimes useful for calling another tool that has to
|
231
|
+
# be loaded from a different configuration.
|
232
232
|
#
|
233
233
|
# @param opts [keywords] Any configuration arguments that should be
|
234
234
|
# modified from the original. See {#initialize} for a list of
|
data/lib/toys/completion.rb
CHANGED
@@ -435,7 +435,11 @@ module Toys
|
|
435
435
|
end
|
436
436
|
|
437
437
|
##
|
438
|
-
#
|
438
|
+
# Take the various ways to express a completion spec, and convert them to a
|
439
|
+
# canonical form expressed as a single object. This is called from the DSL
|
440
|
+
# DSL to generate a spec object that can be stored.
|
441
|
+
#
|
442
|
+
# @private This interface is internal and subject to change without warning.
|
439
443
|
#
|
440
444
|
def self.scalarize_spec(spec, options, block)
|
441
445
|
spec ||= block
|
data/lib/toys/context.rb
CHANGED
@@ -302,7 +302,7 @@ module Toys
|
|
302
302
|
end
|
303
303
|
|
304
304
|
##
|
305
|
-
# Exit immediately with the given status code
|
305
|
+
# Exit immediately with the given status code.
|
306
306
|
#
|
307
307
|
# @param code [Integer] The status code, which should be 0 for no error,
|
308
308
|
# or nonzero for an error condition. Default is 0.
|
@@ -313,7 +313,8 @@ module Toys
|
|
313
313
|
end
|
314
314
|
|
315
315
|
##
|
316
|
-
# Exit immediately with the given status code
|
316
|
+
# Exit immediately with the given status code. This class method can be
|
317
|
+
# called if the instance method is or could be replaced by the tool.
|
317
318
|
#
|
318
319
|
# @param code [Integer] The status code, which should be 0 for no error,
|
319
320
|
# or nonzero for an error condition. Default is 0.
|
@@ -330,7 +331,7 @@ module Toys
|
|
330
331
|
#
|
331
332
|
# @param data [Hash]
|
332
333
|
#
|
333
|
-
# @private
|
334
|
+
# @private This interface is internal and subject to change without warning.
|
334
335
|
#
|
335
336
|
def initialize(data)
|
336
337
|
@__data = data
|
data/lib/toys/core.rb
CHANGED
data/lib/toys/errors.rb
CHANGED
@@ -45,7 +45,11 @@ module Toys
|
|
45
45
|
#
|
46
46
|
class ContextualError < ::StandardError
|
47
47
|
##
|
48
|
-
#
|
48
|
+
# Construct a ContextualError. This exception type is thrown from
|
49
|
+
# {ContextualError.capture} and {ContextualError.capture_path} and should
|
50
|
+
# not be constructed directly.
|
51
|
+
#
|
52
|
+
# @private This interface is internal and subject to change without warning.
|
49
53
|
#
|
50
54
|
def initialize(cause, banner,
|
51
55
|
config_path: nil, config_line: nil,
|
@@ -118,7 +122,11 @@ module Toys
|
|
118
122
|
|
119
123
|
class << self
|
120
124
|
##
|
121
|
-
#
|
125
|
+
# Execute the given block, and wrap any exceptions thrown with a
|
126
|
+
# ContextualError. This is intended for loading a config file from the
|
127
|
+
# given path, and wraps any Ruby parsing errors.
|
128
|
+
#
|
129
|
+
# @private This interface is internal and subject to change without warning.
|
122
130
|
#
|
123
131
|
def capture_path(banner, path, **opts)
|
124
132
|
yield
|
@@ -140,7 +148,10 @@ module Toys
|
|
140
148
|
end
|
141
149
|
|
142
150
|
##
|
143
|
-
#
|
151
|
+
# Execute the given block, and wrap any exceptions thrown with a
|
152
|
+
# ContextualError.
|
153
|
+
#
|
154
|
+
# @private This interface is internal and subject to change without warning.
|
144
155
|
#
|
145
156
|
def capture(banner, **opts)
|
146
157
|
yield
|