toys-core 0.13.1 → 0.14.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
- 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
|