toys-core 0.12.2 → 0.13.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
- data/CHANGELOG.md +22 -0
- data/LICENSE.md +1 -1
- data/README.md +4 -1
- data/docs/guide.md +1 -1
- data/lib/toys/acceptor.rb +10 -1
- data/lib/toys/arg_parser.rb +1 -0
- data/lib/toys/cli.rb +127 -107
- data/lib/toys/compat.rb +54 -3
- data/lib/toys/completion.rb +15 -5
- data/lib/toys/context.rb +22 -20
- data/lib/toys/core.rb +6 -2
- data/lib/toys/dsl/base.rb +2 -0
- data/lib/toys/dsl/flag.rb +23 -17
- data/lib/toys/dsl/flag_group.rb +11 -7
- data/lib/toys/dsl/positional_arg.rb +23 -13
- data/lib/toys/dsl/tool.rb +10 -6
- data/lib/toys/errors.rb +63 -8
- data/lib/toys/flag.rb +660 -651
- data/lib/toys/flag_group.rb +19 -6
- data/lib/toys/input_file.rb +9 -3
- data/lib/toys/loader.rb +129 -115
- data/lib/toys/middleware.rb +45 -21
- data/lib/toys/mixin.rb +8 -6
- data/lib/toys/positional_arg.rb +18 -17
- data/lib/toys/settings.rb +81 -67
- data/lib/toys/source_info.rb +33 -24
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +2 -0
- data/lib/toys/standard_middleware/apply_config.rb +1 -0
- data/lib/toys/standard_middleware/handle_usage_errors.rb +1 -0
- data/lib/toys/standard_middleware/set_default_descriptions.rb +1 -0
- data/lib/toys/standard_middleware/show_help.rb +2 -0
- data/lib/toys/standard_middleware/show_root_version.rb +2 -0
- data/lib/toys/standard_mixins/bundler.rb +22 -14
- data/lib/toys/standard_mixins/exec.rb +31 -20
- data/lib/toys/standard_mixins/fileutils.rb +3 -1
- data/lib/toys/standard_mixins/gems.rb +21 -17
- data/lib/toys/standard_mixins/git_cache.rb +5 -7
- data/lib/toys/standard_mixins/highline.rb +8 -8
- data/lib/toys/standard_mixins/terminal.rb +5 -5
- data/lib/toys/standard_mixins/xdg.rb +5 -5
- data/lib/toys/template.rb +9 -7
- data/lib/toys/tool_definition.rb +209 -202
- data/lib/toys/utils/completion_engine.rb +7 -2
- data/lib/toys/utils/exec.rb +158 -127
- data/lib/toys/utils/gems.rb +81 -57
- data/lib/toys/utils/git_cache.rb +674 -45
- data/lib/toys/utils/help_text.rb +27 -3
- data/lib/toys/utils/terminal.rb +10 -2
- data/lib/toys/wrappable_string.rb +9 -2
- data/lib/toys-core.rb +14 -5
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68a552244c25e03216c7bf958a7ccd2a35bb7fb3795a02e77c4a839b7720e258
|
4
|
+
data.tar.gz: 58513ce7d5d60646a1b9e51235d04df470ee5d9317a9a72048a8e95872227da0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 299c193de07d0802046029c7b305a1e4cecac715eee32d259a917b6ecc2505975ba10a2aca90b510036076d9f32b58849a02381ae61837336eee5361cd2126ea
|
7
|
+
data.tar.gz: b42a4d81db4a71a136deafd4a62f0e7087325097a1c3e7473a5b7edda699b08ee1c8d81377765a70d8a18fd0a2693874781418f9953ca52f92cced670bec1421
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,27 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### v0.13.0 / 2022-02-08
|
4
|
+
|
5
|
+
Toys-Core 0.13.0 is a major release with significant improvements to the git cache, along with compatibility improvements and bug fixes.
|
6
|
+
|
7
|
+
New functionality:
|
8
|
+
|
9
|
+
* The `load_git` directive and the underlying `Toys::Utils::GitCache` class now support updating from git based on cache age.
|
10
|
+
* The `Toys::Utils::GitCache` class supports copying git content into a provided directory, querying repo information, and deleting cache data.
|
11
|
+
* The `Toys::Utils::GitCache` class makes files read-only, to help prevent clients from interfering with one another.
|
12
|
+
* The `:terminal` mixin and the underlying `Toys::Utils::Terminal` class now honor the `NO_COLOR` environment variable.
|
13
|
+
* Added `Toys::CLI#load_tool`, which is useful for testing tools.
|
14
|
+
|
15
|
+
Fixes and compatibility updates:
|
16
|
+
|
17
|
+
* Bundler install/updates are now spawned in subprocesses for compatibility with bundler 2.3. The bundler integration also now requires bundler 2.2 or later.
|
18
|
+
* The `exec_tool` and `exec_proc` methods in the `:exec` mixin now log their execution in the same way as other exec functions.
|
19
|
+
* Minor compatibility fixes to provide partial support for TruffleRuby.
|
20
|
+
|
21
|
+
Other notes:
|
22
|
+
|
23
|
+
* The internal GitCache representation has changed significantly to support additional features and improve robustness and performance. This will force existing caches to update, but should not break existing usage.
|
24
|
+
|
3
25
|
### v0.12.2 / 2021-08-30
|
4
26
|
|
5
27
|
* FIXED: Tool context inspect string is no longer overwhelmingly long
|
data/LICENSE.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# License
|
2
2
|
|
3
|
-
Copyright 2019-
|
3
|
+
Copyright 2019-2022 Daniel Azuma and the Toys contributors
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -39,6 +39,9 @@ Toys-Core requires Ruby 2.4 or later.
|
|
39
39
|
Most parts of Toys-Core work on JRuby. However, JRuby is not recommended
|
40
40
|
because of JVM boot latency, lack of support for Kernel#fork, and other issues.
|
41
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
|
+
|
42
45
|
### Create a new executable
|
43
46
|
|
44
47
|
We'll start by creating an executable Ruby script. Using your favorite text
|
@@ -338,7 +341,7 @@ templates, and middleware, in the
|
|
338
341
|
|
339
342
|
## License
|
340
343
|
|
341
|
-
Copyright 2019-
|
344
|
+
Copyright 2019-2022 Daniel Azuma and the Toys contributors
|
342
345
|
|
343
346
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
344
347
|
of this software and associated documentation files (the "Software"), to deal
|
data/docs/guide.md
CHANGED
@@ -31,7 +31,7 @@ sophisticated command line tools.
|
|
31
31
|
|
32
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
|
-
framework provides common facilities such as
|
34
|
+
framework provides common facilities such as argument parsing and online help,
|
35
35
|
while your executable chooses and configures those facilities, and implements
|
36
36
|
the actual behavior.
|
37
37
|
|
data/lib/toys/acceptor.rb
CHANGED
@@ -178,6 +178,7 @@ module Toys
|
|
178
178
|
|
179
179
|
##
|
180
180
|
# Overrides {Toys::Acceptor::Base#match} to use the given function.
|
181
|
+
#
|
181
182
|
# @private
|
182
183
|
#
|
183
184
|
def match(str)
|
@@ -188,6 +189,7 @@ module Toys
|
|
188
189
|
##
|
189
190
|
# Overrides {Toys::Acceptor::Base#convert} to use the given function's
|
190
191
|
# result.
|
192
|
+
#
|
191
193
|
# @private
|
192
194
|
#
|
193
195
|
def convert(_str, result)
|
@@ -234,6 +236,7 @@ module Toys
|
|
234
236
|
|
235
237
|
##
|
236
238
|
# Overrides {Toys::Acceptor::Base#match} to use the given regex.
|
239
|
+
#
|
237
240
|
# @private
|
238
241
|
#
|
239
242
|
def match(str)
|
@@ -242,6 +245,7 @@ module Toys
|
|
242
245
|
|
243
246
|
##
|
244
247
|
# Overrides {Toys::Acceptor::Base#convert} to use the given converter.
|
248
|
+
#
|
245
249
|
# @private
|
246
250
|
#
|
247
251
|
def convert(str, *extra)
|
@@ -285,6 +289,7 @@ module Toys
|
|
285
289
|
|
286
290
|
##
|
287
291
|
# Overrides {Toys::Acceptor::Base#match} to find the value.
|
292
|
+
#
|
288
293
|
# @private
|
289
294
|
#
|
290
295
|
def match(str)
|
@@ -294,6 +299,7 @@ module Toys
|
|
294
299
|
##
|
295
300
|
# Overrides {Toys::Acceptor::Base#convert} to return the actual enum
|
296
301
|
# element.
|
302
|
+
#
|
297
303
|
# @private
|
298
304
|
#
|
299
305
|
def convert(_str, elem)
|
@@ -303,6 +309,7 @@ module Toys
|
|
303
309
|
##
|
304
310
|
# Overrides {Toys::Acceptor::Base#suggestions} to return close matches
|
305
311
|
# from the enum.
|
312
|
+
#
|
306
313
|
# @private
|
307
314
|
#
|
308
315
|
def suggestions(str)
|
@@ -513,7 +520,9 @@ module Toys
|
|
513
520
|
internal_create(spec, options, block)
|
514
521
|
end
|
515
522
|
|
516
|
-
##
|
523
|
+
##
|
524
|
+
# @private
|
525
|
+
#
|
517
526
|
def scalarize_spec(spec, options, block)
|
518
527
|
spec ||= block
|
519
528
|
if options.empty?
|
data/lib/toys/arg_parser.rb
CHANGED
@@ -417,6 +417,7 @@ module Toys
|
|
417
417
|
|
418
418
|
REMAINING_HANDLER = ->(val, prev) { prev.is_a?(::Array) ? prev << val : [val] }
|
419
419
|
ARG_HANDLER = ->(val, _prev) { val }
|
420
|
+
private_constant :REMAINING_HANDLER, :ARG_HANDLER
|
420
421
|
|
421
422
|
def initial_data(cli, tool, default_data)
|
422
423
|
data = {
|
data/lib/toys/cli.rb
CHANGED
@@ -175,26 +175,24 @@ module Toys
|
|
175
175
|
# Optional. If not provided, lib directories are disabled.
|
176
176
|
# Note: the standard toys executable sets this to `".lib"`.
|
177
177
|
#
|
178
|
-
def initialize( # rubocop:disable Metrics/MethodLength
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
completion: nil
|
197
|
-
)
|
178
|
+
def initialize(executable_name: nil, # rubocop:disable Metrics/MethodLength
|
179
|
+
middleware_stack: nil,
|
180
|
+
extra_delimiters: "",
|
181
|
+
config_dir_name: nil,
|
182
|
+
config_file_name: nil,
|
183
|
+
index_file_name: nil,
|
184
|
+
preload_file_name: nil,
|
185
|
+
preload_dir_name: nil,
|
186
|
+
data_dir_name: nil,
|
187
|
+
lib_dir_name: nil,
|
188
|
+
mixin_lookup: nil,
|
189
|
+
middleware_lookup: nil,
|
190
|
+
template_lookup: nil,
|
191
|
+
logger_factory: nil,
|
192
|
+
logger: nil,
|
193
|
+
base_level: nil,
|
194
|
+
error_handler: nil,
|
195
|
+
completion: nil)
|
198
196
|
@executable_name = executable_name || ::File.basename($PROGRAM_NAME)
|
199
197
|
@middleware_stack = middleware_stack || CLI.default_middleware_stack
|
200
198
|
@mixin_lookup = mixin_lookup || CLI.default_mixin_lookup
|
@@ -446,6 +444,9 @@ module Toys
|
|
446
444
|
# run and what arguments to pass to it. You may pass either a single
|
447
445
|
# array of strings, or a series of string arguments.
|
448
446
|
# @param verbosity [Integer] Initial verbosity. Default is 0.
|
447
|
+
# @param delegated_from [Toys::Context] The context from which this
|
448
|
+
# execution is delegated. Optional. Should be set only if this is a
|
449
|
+
# delegated execution.
|
449
450
|
#
|
450
451
|
# @return [Integer] The resulting process status code (i.e. 0 for success).
|
451
452
|
#
|
@@ -457,101 +458,32 @@ module Toys
|
|
457
458
|
"Error during tool execution!", tool.source_info&.source_path,
|
458
459
|
tool_name: tool.full_name, tool_args: remaining
|
459
460
|
) do
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
run_tool(tool, remaining, default_data)
|
461
|
+
context = build_context(tool, remaining,
|
462
|
+
verbosity: verbosity,
|
463
|
+
delegated_from: delegated_from)
|
464
|
+
execute_tool(tool, context, &:run)
|
465
465
|
end
|
466
466
|
rescue ContextualError, ::Interrupt => e
|
467
467
|
@error_handler.call(e).to_i
|
468
468
|
end
|
469
469
|
|
470
|
-
private
|
471
|
-
|
472
470
|
##
|
473
|
-
#
|
474
|
-
#
|
471
|
+
# Prepare a tool to be run, but just execute the given block rather than
|
472
|
+
# performing a full run of the tool. This is intended for testing tools.
|
473
|
+
# Unlike {#run}, this does not catch errors and perform error handling.
|
475
474
|
#
|
476
|
-
# @param
|
477
|
-
#
|
478
|
-
#
|
479
|
-
# @
|
475
|
+
# @param args [String...] Command line arguments specifying which tool to
|
476
|
+
# run and what arguments to pass to it. You may pass either a single
|
477
|
+
# array of strings, or a series of string arguments.
|
478
|
+
# @yieldparam context [Toys::Context] Yields the tool context.
|
480
479
|
#
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
context
|
487
|
-
|
488
|
-
tool.run_initializers(context)
|
489
|
-
|
490
|
-
cur_logger = context[Context::Key::LOGGER]
|
491
|
-
if cur_logger
|
492
|
-
original_level = cur_logger.level
|
493
|
-
cur_logger.level = (base_level || original_level) - context[Context::Key::VERBOSITY].to_i
|
494
|
-
end
|
495
|
-
begin
|
496
|
-
execute_tool_in_context(context, tool)
|
497
|
-
ensure
|
498
|
-
cur_logger.level = original_level if cur_logger
|
499
|
-
end
|
500
|
-
end
|
501
|
-
|
502
|
-
def execute_tool_in_context(context, tool)
|
503
|
-
executor = proc do
|
504
|
-
begin
|
505
|
-
if !context[Context::Key::USAGE_ERRORS].empty?
|
506
|
-
handle_usage_errors(context, tool)
|
507
|
-
elsif !tool.runnable?
|
508
|
-
raise NotRunnableError, "No implementation for tool #{tool.display_name.inspect}"
|
509
|
-
else
|
510
|
-
context.run
|
511
|
-
end
|
512
|
-
rescue ::Interrupt => e
|
513
|
-
raise e unless tool.handles_interrupts?
|
514
|
-
handle_interrupt(context, tool.interrupt_handler, e)
|
515
|
-
end
|
516
|
-
end
|
517
|
-
tool.built_middleware.reverse_each do |middleware|
|
518
|
-
executor = make_executor(middleware, context, executor)
|
519
|
-
end
|
520
|
-
catch(:result) do
|
521
|
-
executor.call
|
522
|
-
0
|
523
|
-
end
|
524
|
-
end
|
525
|
-
|
526
|
-
def handle_usage_errors(context, tool)
|
527
|
-
usage_errors = context[Context::Key::USAGE_ERRORS]
|
528
|
-
handler = tool.usage_error_handler
|
529
|
-
raise ArgParsingError, usage_errors if handler.nil?
|
530
|
-
handler = context.method(handler).to_proc if handler.is_a?(::Symbol)
|
531
|
-
if handler.arity.zero?
|
532
|
-
context.instance_exec(&handler)
|
533
|
-
else
|
534
|
-
context.instance_exec(usage_errors, &handler)
|
535
|
-
end
|
536
|
-
end
|
537
|
-
|
538
|
-
def handle_interrupt(context, handler, exception)
|
539
|
-
handler = context.method(handler).to_proc if handler.is_a?(::Symbol)
|
540
|
-
if handler.arity.zero?
|
541
|
-
context.instance_exec(&handler)
|
542
|
-
else
|
543
|
-
context.instance_exec(exception, &handler)
|
544
|
-
end
|
545
|
-
rescue ::Interrupt => e
|
546
|
-
raise e if e.equal?(exception)
|
547
|
-
handle_interrupt(context, handler, e)
|
548
|
-
end
|
549
|
-
|
550
|
-
def make_executor(middleware, context, next_executor)
|
551
|
-
if middleware.respond_to?(:run)
|
552
|
-
proc { middleware.run(context, &next_executor) }
|
553
|
-
else
|
554
|
-
next_executor
|
480
|
+
# @return [Object] The value returned from the block.
|
481
|
+
#
|
482
|
+
def load_tool(*args)
|
483
|
+
tool, remaining = @loader.lookup(args.flatten)
|
484
|
+
context = build_context(tool, remaining)
|
485
|
+
execute_tool(tool, context) do |ctx|
|
486
|
+
ctx.exit(yield ctx)
|
555
487
|
end
|
556
488
|
end
|
557
489
|
|
@@ -757,5 +689,93 @@ module Toys
|
|
757
689
|
"#{styled_header} #{msg}\n"
|
758
690
|
end
|
759
691
|
end
|
692
|
+
|
693
|
+
private
|
694
|
+
|
695
|
+
def build_context(tool, args, verbosity: 0, delegated_from: nil)
|
696
|
+
default_data = {
|
697
|
+
Context::Key::VERBOSITY => verbosity,
|
698
|
+
Context::Key::DELEGATED_FROM => delegated_from,
|
699
|
+
}
|
700
|
+
arg_parser = ArgParser.new(self, tool,
|
701
|
+
default_data: default_data,
|
702
|
+
require_exact_flag_match: tool.exact_flag_match_required?)
|
703
|
+
arg_parser.parse(args).finish
|
704
|
+
tool.tool_class.new(arg_parser.data)
|
705
|
+
end
|
706
|
+
|
707
|
+
def execute_tool(tool, context)
|
708
|
+
tool.source_info&.apply_lib_paths
|
709
|
+
tool.run_initializers(context)
|
710
|
+
cur_logger = context[Context::Key::LOGGER]
|
711
|
+
if cur_logger
|
712
|
+
original_level = cur_logger.level
|
713
|
+
cur_logger.level = (base_level || original_level) - context[Context::Key::VERBOSITY].to_i
|
714
|
+
end
|
715
|
+
begin
|
716
|
+
executor = build_executor(tool, context) do
|
717
|
+
yield context
|
718
|
+
end
|
719
|
+
catch(:result) do
|
720
|
+
executor.call
|
721
|
+
0
|
722
|
+
end
|
723
|
+
ensure
|
724
|
+
cur_logger.level = original_level if cur_logger
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
def build_executor(tool, context)
|
729
|
+
executor = proc do
|
730
|
+
begin
|
731
|
+
if !context[Context::Key::USAGE_ERRORS].empty?
|
732
|
+
handle_usage_errors(context, tool)
|
733
|
+
elsif !tool.runnable?
|
734
|
+
raise NotRunnableError, "No implementation for tool #{tool.display_name.inspect}"
|
735
|
+
else
|
736
|
+
yield
|
737
|
+
end
|
738
|
+
rescue ::Interrupt => e
|
739
|
+
raise e unless tool.handles_interrupts?
|
740
|
+
handle_interrupt(context, tool.interrupt_handler, e)
|
741
|
+
end
|
742
|
+
end
|
743
|
+
tool.built_middleware.reverse_each do |middleware|
|
744
|
+
executor = make_executor(middleware, context, executor)
|
745
|
+
end
|
746
|
+
executor
|
747
|
+
end
|
748
|
+
|
749
|
+
def handle_usage_errors(context, tool)
|
750
|
+
usage_errors = context[Context::Key::USAGE_ERRORS]
|
751
|
+
handler = tool.usage_error_handler
|
752
|
+
raise ArgParsingError, usage_errors if handler.nil?
|
753
|
+
handler = context.method(handler).to_proc if handler.is_a?(::Symbol)
|
754
|
+
if handler.arity.zero?
|
755
|
+
context.instance_exec(&handler)
|
756
|
+
else
|
757
|
+
context.instance_exec(usage_errors, &handler)
|
758
|
+
end
|
759
|
+
end
|
760
|
+
|
761
|
+
def handle_interrupt(context, handler, exception)
|
762
|
+
handler = context.method(handler).to_proc if handler.is_a?(::Symbol)
|
763
|
+
if handler.arity.zero?
|
764
|
+
context.instance_exec(&handler)
|
765
|
+
else
|
766
|
+
context.instance_exec(exception, &handler)
|
767
|
+
end
|
768
|
+
rescue ::Interrupt => e
|
769
|
+
raise e if e.equal?(exception)
|
770
|
+
handle_interrupt(context, handler, e)
|
771
|
+
end
|
772
|
+
|
773
|
+
def make_executor(middleware, context, next_executor)
|
774
|
+
if middleware.respond_to?(:run)
|
775
|
+
proc { middleware.run(context, &next_executor) }
|
776
|
+
else
|
777
|
+
next_executor
|
778
|
+
end
|
779
|
+
end
|
760
780
|
end
|
761
781
|
end
|
data/lib/toys/compat.rb
CHANGED
@@ -5,28 +5,44 @@ require "rbconfig"
|
|
5
5
|
module Toys
|
6
6
|
##
|
7
7
|
# Compatibility wrappers for older Ruby versions.
|
8
|
+
#
|
8
9
|
# @private
|
9
10
|
#
|
10
11
|
module Compat
|
11
12
|
parts = ::RUBY_VERSION.split(".")
|
12
13
|
ruby_version = parts[0].to_i * 10000 + parts[1].to_i * 100 + parts[2].to_i
|
13
14
|
|
15
|
+
##
|
14
16
|
# @private
|
17
|
+
#
|
15
18
|
def self.jruby?
|
16
|
-
::
|
19
|
+
::RUBY_ENGINE == "jruby"
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# @private
|
24
|
+
#
|
25
|
+
def self.truffleruby?
|
26
|
+
::RUBY_ENGINE == "truffleruby"
|
17
27
|
end
|
18
28
|
|
29
|
+
##
|
19
30
|
# @private
|
31
|
+
#
|
20
32
|
def self.windows?
|
21
33
|
::RbConfig::CONFIG["host_os"] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
22
34
|
end
|
23
35
|
|
36
|
+
##
|
24
37
|
# @private
|
38
|
+
#
|
25
39
|
def self.allow_fork?
|
26
|
-
!jruby? && !windows?
|
40
|
+
!jruby? && !truffleruby? && !windows?
|
27
41
|
end
|
28
42
|
|
43
|
+
##
|
29
44
|
# @private
|
45
|
+
#
|
30
46
|
def self.supports_suggestions?
|
31
47
|
unless defined?(@supports_suggestions)
|
32
48
|
begin
|
@@ -44,7 +60,9 @@ module Toys
|
|
44
60
|
@supports_suggestions
|
45
61
|
end
|
46
62
|
|
63
|
+
##
|
47
64
|
# @private
|
65
|
+
#
|
48
66
|
def self.suggestions(word, list)
|
49
67
|
if supports_suggestions?
|
50
68
|
::DidYouMean::SpellChecker.new(dictionary: list).correct(word)
|
@@ -55,26 +73,53 @@ module Toys
|
|
55
73
|
|
56
74
|
# The :base argument to Dir.glob requires Ruby 2.5 or later.
|
57
75
|
if ruby_version >= 20500
|
76
|
+
##
|
58
77
|
# @private
|
78
|
+
#
|
59
79
|
def self.glob_in_dir(glob, dir)
|
60
80
|
::Dir.glob(glob, base: dir)
|
61
81
|
end
|
62
82
|
else
|
83
|
+
##
|
63
84
|
# @private
|
85
|
+
#
|
64
86
|
def self.glob_in_dir(glob, dir)
|
65
87
|
::Dir.chdir(dir) { ::Dir.glob(glob) }
|
66
88
|
end
|
67
89
|
end
|
68
90
|
|
91
|
+
# Dir.children requires Ruby 2.5 or later.
|
92
|
+
if ruby_version >= 20500
|
93
|
+
##
|
94
|
+
# @private
|
95
|
+
#
|
96
|
+
def self.dir_children(dir)
|
97
|
+
::Dir.children(dir)
|
98
|
+
end
|
99
|
+
else
|
100
|
+
##
|
101
|
+
# @private
|
102
|
+
#
|
103
|
+
def self.dir_children(dir)
|
104
|
+
::Dir.entries(dir) - [".", ".."]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
69
108
|
# Due to a bug in Ruby < 2.7, passing an empty **kwargs splat to
|
70
109
|
# initialize will fail if there are no formal keyword args.
|
71
|
-
|
110
|
+
# This also hits TruffleRuby
|
111
|
+
# (see https://github.com/oracle/truffleruby/issues/2567)
|
112
|
+
if ruby_version >= 20700 && !truffleruby?
|
113
|
+
##
|
72
114
|
# @private
|
115
|
+
#
|
73
116
|
def self.instantiate(klass, args, kwargs, block)
|
74
117
|
klass.new(*args, **kwargs, &block)
|
75
118
|
end
|
76
119
|
else
|
120
|
+
##
|
77
121
|
# @private
|
122
|
+
#
|
78
123
|
def self.instantiate(klass, args, kwargs, block)
|
79
124
|
formals = klass.instance_method(:initialize).parameters
|
80
125
|
if kwargs.empty? && formals.all? { |arg| arg.first != :key && arg.first != :keyrest }
|
@@ -88,17 +133,23 @@ module Toys
|
|
88
133
|
# File.absolute_path? requires Ruby 2.7 or later. For earlier Rubies, use
|
89
134
|
# an ad-hoc mechanism.
|
90
135
|
if ruby_version >= 20700
|
136
|
+
##
|
91
137
|
# @private
|
138
|
+
#
|
92
139
|
def self.absolute_path?(path)
|
93
140
|
::File.absolute_path?(path)
|
94
141
|
end
|
95
142
|
elsif ::Dir.getwd =~ /^[a-zA-Z]:/
|
143
|
+
##
|
96
144
|
# @private
|
145
|
+
#
|
97
146
|
def self.absolute_path?(path)
|
98
147
|
/^[a-zA-Z]:/.match?(path)
|
99
148
|
end
|
100
149
|
else
|
150
|
+
##
|
101
151
|
# @private
|
152
|
+
#
|
102
153
|
def self.absolute_path?(path)
|
103
154
|
path.start_with?("/")
|
104
155
|
end
|
data/lib/toys/completion.rb
CHANGED
@@ -116,7 +116,9 @@ module Toys
|
|
116
116
|
@arg_parser ||= ArgParser.new(@cli, @tool).parse(@args)
|
117
117
|
end
|
118
118
|
|
119
|
-
##
|
119
|
+
##
|
120
|
+
# @private
|
121
|
+
#
|
120
122
|
def inspect
|
121
123
|
"<Toys::Completion::Context previous=#{previous_words.inspect}" \
|
122
124
|
" prefix=#{fragment_prefix.inspect} fragment=#{fragment.inspect}>"
|
@@ -175,17 +177,23 @@ module Toys
|
|
175
177
|
!@partial
|
176
178
|
end
|
177
179
|
|
178
|
-
##
|
180
|
+
##
|
181
|
+
# @private
|
182
|
+
#
|
179
183
|
def eql?(other)
|
180
184
|
other.is_a?(Candidate) && other.string.eql?(string) && other.partial? == @partial
|
181
185
|
end
|
182
186
|
|
183
|
-
##
|
187
|
+
##
|
188
|
+
# @private
|
189
|
+
#
|
184
190
|
def <=>(other)
|
185
191
|
string <=> other.string
|
186
192
|
end
|
187
193
|
|
188
|
-
##
|
194
|
+
##
|
195
|
+
# @private
|
196
|
+
#
|
189
197
|
def hash
|
190
198
|
string.hash ^ (partial? ? 1 : 0)
|
191
199
|
end
|
@@ -426,7 +434,9 @@ module Toys
|
|
426
434
|
end
|
427
435
|
end
|
428
436
|
|
429
|
-
##
|
437
|
+
##
|
438
|
+
# @private
|
439
|
+
#
|
430
440
|
def self.scalarize_spec(spec, options, block)
|
431
441
|
spec ||= block
|
432
442
|
if options.empty?
|
data/lib/toys/context.rb
CHANGED
@@ -133,26 +133,6 @@ module Toys
|
|
133
133
|
VERBOSITY = ::Object.new.freeze
|
134
134
|
end
|
135
135
|
|
136
|
-
##
|
137
|
-
# Create a Context object. Applications generally will not need to create
|
138
|
-
# these objects directly; they are created by the tool when it is preparing
|
139
|
-
# for execution.
|
140
|
-
#
|
141
|
-
# @private
|
142
|
-
#
|
143
|
-
# @param data [Hash]
|
144
|
-
#
|
145
|
-
def initialize(data)
|
146
|
-
@__data = data
|
147
|
-
end
|
148
|
-
|
149
|
-
# @private
|
150
|
-
def inspect
|
151
|
-
name = Array(@__data[Key::TOOL_NAME]).join(" ")
|
152
|
-
id = object_id.to_s(16)
|
153
|
-
"#<Toys::Context id=0x#{id} #{name}>"
|
154
|
-
end
|
155
|
-
|
156
136
|
##
|
157
137
|
# The raw arguments passed to the tool, as an array of strings.
|
158
138
|
# This does not include the tool name itself.
|
@@ -342,5 +322,27 @@ module Toys
|
|
342
322
|
def self.exit(code = 0)
|
343
323
|
throw :result, code
|
344
324
|
end
|
325
|
+
|
326
|
+
##
|
327
|
+
# Create a Context object. Applications generally will not need to create
|
328
|
+
# these objects directly; they are created by the tool when it is preparing
|
329
|
+
# for execution.
|
330
|
+
#
|
331
|
+
# @param data [Hash]
|
332
|
+
#
|
333
|
+
# @private
|
334
|
+
#
|
335
|
+
def initialize(data)
|
336
|
+
@__data = data
|
337
|
+
end
|
338
|
+
|
339
|
+
##
|
340
|
+
# @private
|
341
|
+
#
|
342
|
+
def inspect
|
343
|
+
name = Array(@__data[Key::TOOL_NAME]).join(" ")
|
344
|
+
id = object_id.to_s(16)
|
345
|
+
"#<Toys::Context id=0x#{id} #{name}>"
|
346
|
+
end
|
345
347
|
end
|
346
348
|
end
|
data/lib/toys/core.rb
CHANGED