toys-core 0.14.7 → 0.15.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 +28 -0
- data/LICENSE.md +1 -1
- data/README.md +10 -5
- data/docs/guide.md +534 -39
- data/lib/toys/cli.rb +76 -172
- data/lib/toys/compat.rb +15 -30
- data/lib/toys/context.rb +51 -0
- data/lib/toys/core.rb +1 -1
- data/lib/toys/dsl/flag.rb +40 -2
- data/lib/toys/dsl/flag_group.rb +15 -5
- data/lib/toys/dsl/internal.rb +23 -8
- data/lib/toys/dsl/positional_arg.rb +32 -1
- data/lib/toys/dsl/tool.rb +174 -68
- data/lib/toys/errors.rb +2 -2
- data/lib/toys/middleware.rb +3 -2
- data/lib/toys/settings.rb +1 -1
- data/lib/toys/standard_mixins/bundler.rb +16 -1
- data/lib/toys/standard_mixins/exec.rb +29 -8
- data/lib/toys/standard_mixins/gems.rb +17 -3
- data/lib/toys/standard_mixins/highline.rb +12 -12
- data/lib/toys/standard_mixins/terminal.rb +7 -7
- data/lib/toys/tool_definition.rb +153 -50
- data/lib/toys/utils/exec.rb +22 -1
- data/lib/toys/utils/gems.rb +3 -0
- data/lib/toys/utils/standard_ui.rb +261 -0
- data/lib/toys-core.rb +51 -3
- metadata +6 -5
@@ -10,11 +10,24 @@ module Toys
|
|
10
10
|
# tool DSL itself, so you can also ensure a gem is present when defining a
|
11
11
|
# tool.
|
12
12
|
#
|
13
|
-
#
|
14
|
-
#
|
13
|
+
# ### Usage
|
14
|
+
#
|
15
|
+
# Make these methods available to your tool by including this mixin in your
|
16
|
+
# tool:
|
15
17
|
#
|
16
18
|
# include :gems
|
17
19
|
#
|
20
|
+
# You can then call the mixin method {#gem} to ensure that a gem is
|
21
|
+
# installed and in the load path. For example:
|
22
|
+
#
|
23
|
+
# tool "my_tool" do
|
24
|
+
# include :gems
|
25
|
+
# gem "nokogiri", "~> 1.15"
|
26
|
+
# def run
|
27
|
+
# # Do stuff with Nokogiri
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
18
31
|
# If you pass additional options to the include directive, those are used
|
19
32
|
# to initialize settings for the gem install process. For example:
|
20
33
|
#
|
@@ -34,7 +47,8 @@ module Toys
|
|
34
47
|
end
|
35
48
|
|
36
49
|
##
|
37
|
-
# Activate the given gem.
|
50
|
+
# Activate the given gem. If it is not present, attempt to install it (or
|
51
|
+
# inform the user to update the bundle).
|
38
52
|
#
|
39
53
|
# @param name [String] Name of the gem
|
40
54
|
# @param requirements [String...] Version requirements
|
@@ -49,84 +49,84 @@ module Toys
|
|
49
49
|
# Calls [HighLine#agree](https://www.rubydoc.info/gems/highline/HighLine:agree)
|
50
50
|
#
|
51
51
|
def agree(*args, &block)
|
52
|
-
|
52
|
+
self[KEY].agree(*args, &block)
|
53
53
|
end
|
54
54
|
|
55
55
|
##
|
56
56
|
# Calls [HighLine#ask](https://www.rubydoc.info/gems/highline/HighLine:ask)
|
57
57
|
#
|
58
58
|
def ask(*args, &block)
|
59
|
-
|
59
|
+
self[KEY].ask(*args, &block)
|
60
60
|
end
|
61
61
|
|
62
62
|
##
|
63
63
|
# Calls [HighLine#choose](https://www.rubydoc.info/gems/highline/HighLine:choose)
|
64
64
|
#
|
65
65
|
def choose(*args, &block)
|
66
|
-
|
66
|
+
self[KEY].choose(*args, &block)
|
67
67
|
end
|
68
68
|
|
69
69
|
##
|
70
70
|
# Calls [HighLine#list](https://www.rubydoc.info/gems/highline/HighLine:list)
|
71
71
|
#
|
72
72
|
def list(*args, &block)
|
73
|
-
|
73
|
+
self[KEY].list(*args, &block)
|
74
74
|
end
|
75
75
|
|
76
76
|
##
|
77
77
|
# Calls [HighLine#say](https://www.rubydoc.info/gems/highline/HighLine:say)
|
78
78
|
#
|
79
79
|
def say(*args, &block)
|
80
|
-
|
80
|
+
self[KEY].say(*args, &block)
|
81
81
|
end
|
82
82
|
|
83
83
|
##
|
84
84
|
# Calls [HighLine#indent](https://www.rubydoc.info/gems/highline/HighLine:indent)
|
85
85
|
#
|
86
86
|
def indent(*args, &block)
|
87
|
-
|
87
|
+
self[KEY].indent(*args, &block)
|
88
88
|
end
|
89
89
|
|
90
90
|
##
|
91
91
|
# Calls [HighLine#newline](https://www.rubydoc.info/gems/highline/HighLine:newline)
|
92
92
|
#
|
93
93
|
def newline
|
94
|
-
|
94
|
+
self[KEY].newline
|
95
95
|
end
|
96
96
|
|
97
97
|
##
|
98
98
|
# Calls [HighLine#puts](https://www.rubydoc.info/gems/highline/HighLine:puts)
|
99
99
|
#
|
100
100
|
def puts(*args)
|
101
|
-
|
101
|
+
self[KEY].puts(*args)
|
102
102
|
end
|
103
103
|
|
104
104
|
##
|
105
105
|
# Calls [HighLine#color](https://www.rubydoc.info/gems/highline/HighLine:color)
|
106
106
|
#
|
107
107
|
def color(*args)
|
108
|
-
|
108
|
+
self[KEY].color(*args)
|
109
109
|
end
|
110
110
|
|
111
111
|
##
|
112
112
|
# Calls [HighLine#color_code](https://www.rubydoc.info/gems/highline/HighLine:color_code)
|
113
113
|
#
|
114
114
|
def color_code(*args)
|
115
|
-
|
115
|
+
self[KEY].color_code(*args)
|
116
116
|
end
|
117
117
|
|
118
118
|
##
|
119
119
|
# Calls [HighLine#uncolor](https://www.rubydoc.info/gems/highline/HighLine:uncolor)
|
120
120
|
#
|
121
121
|
def uncolor(*args)
|
122
|
-
|
122
|
+
self[KEY].uncolor(*args)
|
123
123
|
end
|
124
124
|
|
125
125
|
##
|
126
126
|
# Calls [HighLine#new_scope](https://www.rubydoc.info/gems/highline/HighLine:new_scope)
|
127
127
|
#
|
128
128
|
def new_scope
|
129
|
-
|
129
|
+
self[KEY].new_scope
|
130
130
|
end
|
131
131
|
|
132
132
|
on_initialize do |*args|
|
@@ -54,7 +54,7 @@ module Toys
|
|
54
54
|
# @return [self]
|
55
55
|
#
|
56
56
|
def puts(str = "", *styles)
|
57
|
-
|
57
|
+
self[KEY].puts(str, *styles)
|
58
58
|
self
|
59
59
|
end
|
60
60
|
alias say puts
|
@@ -70,7 +70,7 @@ module Toys
|
|
70
70
|
# @return [self]
|
71
71
|
#
|
72
72
|
def write(str = "", *styles)
|
73
|
-
|
73
|
+
self[KEY].write(str, *styles)
|
74
74
|
self
|
75
75
|
end
|
76
76
|
|
@@ -89,7 +89,7 @@ module Toys
|
|
89
89
|
# @return [String]
|
90
90
|
#
|
91
91
|
def ask(prompt, *styles, default: nil, trailing_text: :default)
|
92
|
-
|
92
|
+
self[KEY].ask(prompt, *styles, default: default, trailing_text: trailing_text)
|
93
93
|
end
|
94
94
|
|
95
95
|
##
|
@@ -105,7 +105,7 @@ module Toys
|
|
105
105
|
# @return [Boolean]
|
106
106
|
#
|
107
107
|
def confirm(prompt = "Proceed?", *styles, default: nil)
|
108
|
-
|
108
|
+
self[KEY].confirm(prompt, *styles, default: default)
|
109
109
|
end
|
110
110
|
|
111
111
|
##
|
@@ -130,9 +130,9 @@ module Toys
|
|
130
130
|
#
|
131
131
|
def spinner(leading_text: "", final_text: "",
|
132
132
|
frame_length: nil, frames: nil, style: nil, &block)
|
133
|
-
|
134
|
-
|
135
|
-
|
133
|
+
self[KEY].spinner(leading_text: leading_text, final_text: final_text,
|
134
|
+
frame_length: frame_length, frames: frames, style: style,
|
135
|
+
&block)
|
136
136
|
end
|
137
137
|
|
138
138
|
on_initialize do |**opts|
|
data/lib/toys/tool_definition.rb
CHANGED
@@ -18,12 +18,12 @@ module Toys
|
|
18
18
|
##
|
19
19
|
# Create a completion given configuration options.
|
20
20
|
#
|
21
|
-
# @param complete_subtools [
|
22
|
-
# @param include_hidden_subtools [
|
21
|
+
# @param complete_subtools [true,false] Whether to complete subtool names
|
22
|
+
# @param include_hidden_subtools [true,false] Whether to include hidden
|
23
23
|
# subtools (i.e. those beginning with an underscore)
|
24
|
-
# @param complete_args [
|
25
|
-
# @param complete_flags [
|
26
|
-
# @param complete_flag_values [
|
24
|
+
# @param complete_args [true,false] Whether to complete positional args
|
25
|
+
# @param complete_flags [true,false] Whether to complete flag names
|
26
|
+
# @param complete_flag_values [true,false] Whether to complete flag values
|
27
27
|
# @param delegation_target [Array<String>,nil] Delegation target, or
|
28
28
|
# `nil` if none.
|
29
29
|
#
|
@@ -41,7 +41,7 @@ module Toys
|
|
41
41
|
|
42
42
|
##
|
43
43
|
# Whether to complete subtool names
|
44
|
-
# @return [
|
44
|
+
# @return [true,false]
|
45
45
|
#
|
46
46
|
def complete_subtools?
|
47
47
|
@complete_subtools
|
@@ -49,7 +49,7 @@ module Toys
|
|
49
49
|
|
50
50
|
##
|
51
51
|
# Whether to include hidden subtools
|
52
|
-
# @return [
|
52
|
+
# @return [true,false]
|
53
53
|
#
|
54
54
|
def include_hidden_subtools?
|
55
55
|
@include_hidden_subtools
|
@@ -57,7 +57,7 @@ module Toys
|
|
57
57
|
|
58
58
|
##
|
59
59
|
# Whether to complete flags
|
60
|
-
# @return [
|
60
|
+
# @return [true,false]
|
61
61
|
#
|
62
62
|
def complete_flags?
|
63
63
|
@complete_flags
|
@@ -65,7 +65,7 @@ module Toys
|
|
65
65
|
|
66
66
|
##
|
67
67
|
# Whether to complete positional args
|
68
|
-
# @return [
|
68
|
+
# @return [true,false]
|
69
69
|
#
|
70
70
|
def complete_args?
|
71
71
|
@complete_args
|
@@ -73,7 +73,7 @@ module Toys
|
|
73
73
|
|
74
74
|
##
|
75
75
|
# Whether to complete flag values
|
76
|
-
# @return [
|
76
|
+
# @return [true,false]
|
77
77
|
#
|
78
78
|
def complete_flag_values?
|
79
79
|
@complete_flag_values
|
@@ -205,7 +205,7 @@ module Toys
|
|
205
205
|
#
|
206
206
|
# The following settings are supported:
|
207
207
|
#
|
208
|
-
# * `propagate_helper_methods` (
|
208
|
+
# * `propagate_helper_methods` (_boolean_) - Whether subtools should
|
209
209
|
# inherit methods defined by parent tools. Defaults to `false`.
|
210
210
|
#
|
211
211
|
class Settings < ::Toys::Settings
|
@@ -273,7 +273,8 @@ module Toys
|
|
273
273
|
@includes_modules = false
|
274
274
|
@custom_context_directory = nil
|
275
275
|
|
276
|
-
@
|
276
|
+
@run_handler = :run
|
277
|
+
@signal_handlers = {}
|
277
278
|
@usage_error_handler = nil
|
278
279
|
@delegate_target = nil
|
279
280
|
|
@@ -454,19 +455,33 @@ module Toys
|
|
454
455
|
attr_reader :completion
|
455
456
|
|
456
457
|
##
|
457
|
-
# The
|
458
|
+
# The run handler.
|
458
459
|
#
|
459
|
-
#
|
460
|
-
#
|
461
|
-
#
|
460
|
+
# This handler is called to run the tool. Normally it is a method name,
|
461
|
+
# represented by a symbol. (The default is `:run`.) It can be set to a
|
462
|
+
# different method name, or to a proc that will be called with `self` set
|
463
|
+
# to the tool context. Either way, it takes no arguments. The run handler
|
464
|
+
# can also be explicitly set to `nil` indicating a non-runnable tool;
|
465
|
+
# however, typically a tool is made non-runnable simply by leaving the run
|
466
|
+
# handler set to `:run` and not defining the method.
|
462
467
|
#
|
463
|
-
|
468
|
+
# @return [Proc] if the run handler is defined as a Proc
|
469
|
+
# @return [Symbol] if the run handler is defined as a method
|
470
|
+
# @return [nil] if the tool is explicitly made non-runnable
|
471
|
+
#
|
472
|
+
attr_reader :run_handler
|
464
473
|
|
465
474
|
##
|
466
475
|
# The usage error handler.
|
467
476
|
#
|
468
|
-
#
|
469
|
-
#
|
477
|
+
# This handler is called when at least one usage error is detected during
|
478
|
+
# argument parsing, and is called instead of the `run` method. It can be
|
479
|
+
# specified as a Proc, or a Symbol indicating a method to call. It
|
480
|
+
# optionally takes an array of {Toys::ArgParser::UsageError} as the sole
|
481
|
+
# argument.
|
482
|
+
#
|
483
|
+
# @return [Proc] if the usage error handler is defined as a Proc
|
484
|
+
# @return [Symbol] if the user error handler is defined as a method
|
470
485
|
# @return [nil] if there is no usage error handler
|
471
486
|
#
|
472
487
|
attr_reader :usage_error_handler
|
@@ -498,9 +513,37 @@ module Toys
|
|
498
513
|
full_name.join(" ")
|
499
514
|
end
|
500
515
|
|
516
|
+
##
|
517
|
+
# Return the signal handler for the given signal.
|
518
|
+
#
|
519
|
+
# This handler is called when the given signal is received, immediately
|
520
|
+
# taking over the execution as if it were the new run handler. The signal
|
521
|
+
# handler can be specified as a Proc, or a Symbol indicating a method to
|
522
|
+
# call. It optionally takes the `SignalException` as the sole argument.
|
523
|
+
#
|
524
|
+
# @param signal [Integer,String,Symbol] The signal number or name
|
525
|
+
# @return [Proc] if the signal handler is defined as a Proc
|
526
|
+
# @return [Symbol] if the signal handler is defined as a method
|
527
|
+
# @return [nil] if there is no handler for the given signal
|
528
|
+
#
|
529
|
+
def signal_handler(signal)
|
530
|
+
@signal_handlers[canonicalize_signal(signal)]
|
531
|
+
end
|
532
|
+
|
533
|
+
##
|
534
|
+
# Return the interrupt handler. This is equivalent to `signal_handler(2)`.
|
535
|
+
#
|
536
|
+
# @return [Proc] if the interrupt signal handler is defined as a Proc
|
537
|
+
# @return [Symbol] if the interrupt signal handler is defined as a method
|
538
|
+
# @return [nil] if there is no handler for the interrupt signals
|
539
|
+
#
|
540
|
+
def interrupt_handler
|
541
|
+
signal_handler(2)
|
542
|
+
end
|
543
|
+
|
501
544
|
##
|
502
545
|
# Returns true if this tool is a root tool.
|
503
|
-
# @return [
|
546
|
+
# @return [true,false]
|
504
547
|
#
|
505
548
|
def root?
|
506
549
|
full_name.empty?
|
@@ -508,23 +551,38 @@ module Toys
|
|
508
551
|
|
509
552
|
##
|
510
553
|
# Returns true if this tool is marked as runnable.
|
511
|
-
# @return [
|
554
|
+
# @return [true,false]
|
512
555
|
#
|
513
556
|
def runnable?
|
514
|
-
|
557
|
+
@run_handler.is_a?(::Symbol) &&
|
558
|
+
tool_class.public_instance_methods(false).include?(@run_handler) ||
|
559
|
+
@run_handler.is_a?(::Proc)
|
515
560
|
end
|
516
561
|
|
517
562
|
##
|
518
|
-
# Returns true if this tool handles interrupts.
|
519
|
-
#
|
563
|
+
# Returns true if this tool handles interrupts. This is equivalent to
|
564
|
+
# `handles_signal?(2)`.
|
565
|
+
#
|
566
|
+
# @return [true,false]
|
520
567
|
#
|
521
568
|
def handles_interrupts?
|
522
|
-
|
569
|
+
handles_signal?(2)
|
570
|
+
end
|
571
|
+
|
572
|
+
##
|
573
|
+
# Returns true if this tool handles the given signal.
|
574
|
+
#
|
575
|
+
# @param signal [Integer,String,Symbol] The signal number or name
|
576
|
+
# @return [true,false]
|
577
|
+
#
|
578
|
+
def handles_signal?(signal)
|
579
|
+
signal = canonicalize_signal(signal)
|
580
|
+
!@signal_handlers[signal].nil?
|
523
581
|
end
|
524
582
|
|
525
583
|
##
|
526
584
|
# Returns true if this tool handles usage errors.
|
527
|
-
# @return [
|
585
|
+
# @return [true,false]
|
528
586
|
#
|
529
587
|
def handles_usage_errors?
|
530
588
|
!usage_error_handler.nil?
|
@@ -532,7 +590,7 @@ module Toys
|
|
532
590
|
|
533
591
|
##
|
534
592
|
# Returns true if this tool has at least one included module.
|
535
|
-
# @return [
|
593
|
+
# @return [true,false]
|
536
594
|
#
|
537
595
|
def includes_modules?
|
538
596
|
@includes_modules
|
@@ -540,7 +598,7 @@ module Toys
|
|
540
598
|
|
541
599
|
##
|
542
600
|
# Returns true if there is a specific description set for this tool.
|
543
|
-
# @return [
|
601
|
+
# @return [true,false]
|
544
602
|
#
|
545
603
|
def includes_description?
|
546
604
|
!long_desc.empty? || !desc.empty?
|
@@ -549,7 +607,7 @@ module Toys
|
|
549
607
|
##
|
550
608
|
# Returns true if at least one flag or positional argument is defined
|
551
609
|
# for this tool.
|
552
|
-
# @return [
|
610
|
+
# @return [true,false]
|
553
611
|
#
|
554
612
|
def includes_arguments?
|
555
613
|
!default_data.empty? || !flags.empty? ||
|
@@ -559,7 +617,7 @@ module Toys
|
|
559
617
|
|
560
618
|
##
|
561
619
|
# Returns true if this tool has any definition information.
|
562
|
-
# @return [
|
620
|
+
# @return [true,false]
|
563
621
|
#
|
564
622
|
def includes_definition?
|
565
623
|
includes_arguments? || runnable? || argument_parsing_disabled? ||
|
@@ -568,7 +626,7 @@ module Toys
|
|
568
626
|
|
569
627
|
##
|
570
628
|
# Returns true if this tool's definition has been finished and is locked.
|
571
|
-
# @return [
|
629
|
+
# @return [true,false]
|
572
630
|
#
|
573
631
|
def definition_finished?
|
574
632
|
@definition_finished
|
@@ -576,7 +634,7 @@ module Toys
|
|
576
634
|
|
577
635
|
##
|
578
636
|
# Returns true if this tool has disabled argument parsing.
|
579
|
-
# @return [
|
637
|
+
# @return [true,false]
|
580
638
|
#
|
581
639
|
def argument_parsing_disabled?
|
582
640
|
@disable_argument_parsing
|
@@ -584,7 +642,7 @@ module Toys
|
|
584
642
|
|
585
643
|
##
|
586
644
|
# Returns true if this tool enforces flags before args.
|
587
|
-
# @return [
|
645
|
+
# @return [true,false]
|
588
646
|
#
|
589
647
|
def flags_before_args_enforced?
|
590
648
|
@enforce_flags_before_args
|
@@ -592,7 +650,7 @@ module Toys
|
|
592
650
|
|
593
651
|
##
|
594
652
|
# Returns true if this tool requires exact flag matches.
|
595
|
-
# @return [
|
653
|
+
# @return [true,false]
|
596
654
|
#
|
597
655
|
def exact_flag_match_required?
|
598
656
|
@require_exact_flag_match
|
@@ -867,7 +925,7 @@ module Toys
|
|
867
925
|
# Enforce that flags must come before args for this tool.
|
868
926
|
# You may disable enforcement by passoing `false` for the state.
|
869
927
|
#
|
870
|
-
# @param state [
|
928
|
+
# @param state [true,false]
|
871
929
|
# @return [self]
|
872
930
|
#
|
873
931
|
def enforce_flags_before_args(state = true)
|
@@ -885,7 +943,7 @@ module Toys
|
|
885
943
|
# Require that flags must match exactly. (If false, flags can match an
|
886
944
|
# unambiguous substring.)
|
887
945
|
#
|
888
|
-
# @param state [
|
946
|
+
# @param state [true,false]
|
889
947
|
# @return [self]
|
890
948
|
#
|
891
949
|
def require_exact_flag_match(state = true)
|
@@ -919,10 +977,10 @@ module Toys
|
|
919
977
|
# formats. Defaults to the empty array.
|
920
978
|
# @param name [String,Symbol,nil] The name of the group, or nil for no
|
921
979
|
# name.
|
922
|
-
# @param report_collisions [
|
980
|
+
# @param report_collisions [true,false] If `true`, raise an exception if a
|
923
981
|
# the given name is already taken. If `false`, ignore. Default is
|
924
982
|
# `true`.
|
925
|
-
# @param prepend [
|
983
|
+
# @param prepend [true,false] If `true`, prepend rather than append the
|
926
984
|
# group to the list. Default is `false`.
|
927
985
|
# @return [self]
|
928
986
|
#
|
@@ -977,7 +1035,7 @@ module Toys
|
|
977
1035
|
# @param complete_values [Object] A specifier for shell tab completion
|
978
1036
|
# for flag values associated with this flag. Pass any spec
|
979
1037
|
# recognized by {Toys::Completion.create}.
|
980
|
-
# @param report_collisions [
|
1038
|
+
# @param report_collisions [true,false] Raise an exception if a flag is
|
981
1039
|
# requested that is already in use or marked as disabled. Default is
|
982
1040
|
# true.
|
983
1041
|
# @param group [Toys::FlagGroup,String,Symbol,nil] Group for
|
@@ -1147,33 +1205,65 @@ module Toys
|
|
1147
1205
|
end
|
1148
1206
|
|
1149
1207
|
##
|
1150
|
-
# Set the run handler
|
1208
|
+
# Set the run handler.
|
1209
|
+
#
|
1210
|
+
# This handler is called to run the tool. Normally it is a method name,
|
1211
|
+
# represented by a symbol. (The default is `:run`.) It can be set to a
|
1212
|
+
# different method name, or to a proc that will be called with `self` set
|
1213
|
+
# to the tool context. Either way, it takes no arguments. The run handler
|
1214
|
+
# can also be explicitly set to `nil` indicating a non-runnable tool;
|
1215
|
+
# however, typically a tool is made non-runnable simply by leaving the run
|
1216
|
+
# handler set to `:run` and not defining the method.
|
1151
1217
|
#
|
1152
|
-
# @param
|
1218
|
+
# @param handler [Proc,Symbol,nil] the run handler
|
1153
1219
|
#
|
1154
|
-
def run_handler=(
|
1220
|
+
def run_handler=(handler)
|
1155
1221
|
check_definition_state(is_method: true)
|
1156
|
-
|
1157
|
-
|
1222
|
+
if !handler.is_a?(::Proc) && !handler.is_a?(::Symbol) && !handler.nil?
|
1223
|
+
raise ToolDefinitionError, "Run handler must be a proc or symbol"
|
1158
1224
|
end
|
1225
|
+
@run_handler = handler
|
1159
1226
|
end
|
1160
1227
|
|
1161
1228
|
##
|
1162
|
-
# Set the interrupt handler.
|
1229
|
+
# Set the interrupt handler. This is equivalent to calling
|
1230
|
+
# {#set_signal_handler} for the `SIGINT` signal.
|
1163
1231
|
#
|
1164
|
-
# @param handler [Proc,Symbol] The interrupt handler
|
1232
|
+
# @param handler [Proc,Symbol] The interrupt signal handler
|
1165
1233
|
#
|
1166
1234
|
def interrupt_handler=(handler)
|
1235
|
+
set_signal_handler(2, handler)
|
1236
|
+
end
|
1237
|
+
|
1238
|
+
##
|
1239
|
+
# Set the handler for the given signal.
|
1240
|
+
#
|
1241
|
+
# This handler is called when the given signal is received, immediately
|
1242
|
+
# taking over the execution as if it were the new `run` method. The signal
|
1243
|
+
# handler can be specified as a Proc, or a Symbol indicating a method to
|
1244
|
+
# call. It optionally takes the `SignalException` as the sole argument.
|
1245
|
+
#
|
1246
|
+
# @param signal [Integer,String,Symbol] The signal number or name
|
1247
|
+
# @param handler [Proc,Symbol] The signal handler
|
1248
|
+
#
|
1249
|
+
def set_signal_handler(signal, handler)
|
1167
1250
|
check_definition_state(is_method: true)
|
1168
1251
|
if !handler.is_a?(::Proc) && !handler.is_a?(::Symbol) && !handler.nil?
|
1169
|
-
raise ToolDefinitionError, "
|
1252
|
+
raise ToolDefinitionError, "Signal handler must be a proc or symbol"
|
1170
1253
|
end
|
1171
|
-
|
1254
|
+
signal = canonicalize_signal(signal)
|
1255
|
+
@signal_handlers[signal] = handler
|
1172
1256
|
end
|
1173
1257
|
|
1174
1258
|
##
|
1175
1259
|
# Set the usage error handler.
|
1176
1260
|
#
|
1261
|
+
# This handler is called when at least one usage error is detected during
|
1262
|
+
# argument parsing, and is called instead of the `run` method. It can be
|
1263
|
+
# specified as a Proc, or a Symbol indicating a method to call. It
|
1264
|
+
# optionally takes an array of {Toys::ArgParser::UsageError} as the sole
|
1265
|
+
# argument.
|
1266
|
+
#
|
1177
1267
|
# @param handler [Proc,Symbol] The usage error handler
|
1178
1268
|
#
|
1179
1269
|
def usage_error_handler=(handler)
|
@@ -1374,14 +1464,14 @@ module Toys
|
|
1374
1464
|
name = walk_context[::Toys::Context::Key::TOOL_NAME]
|
1375
1465
|
path << name.join(" ").inspect
|
1376
1466
|
if name == target
|
1377
|
-
raise "Delegation loop: #{path.join(' <- ')}"
|
1467
|
+
raise ToolDefinitionError, "Delegation loop: #{path.join(' <- ')}"
|
1378
1468
|
end
|
1379
1469
|
walk_context = walk_context[::Toys::Context::Key::DELEGATED_FROM]
|
1380
1470
|
end
|
1381
1471
|
cli = self[::Toys::Context::Key::CLI]
|
1382
1472
|
cli.loader.load_for_prefix(target)
|
1383
1473
|
unless cli.loader.tool_defined?(target)
|
1384
|
-
raise "Delegate target not found: \"#{target.join(' ')}\""
|
1474
|
+
raise ToolDefinitionError, "Delegate target not found: \"#{target.join(' ')}\""
|
1385
1475
|
end
|
1386
1476
|
exit(cli.run(target + self[::Toys::Context::Key::ARGS], delegated_from: self))
|
1387
1477
|
end
|
@@ -1400,5 +1490,18 @@ module Toys
|
|
1400
1490
|
raise ToolDefinitionError, "Unknown completion: #{name.inspect}" if completion.nil?
|
1401
1491
|
completion
|
1402
1492
|
end
|
1493
|
+
|
1494
|
+
def canonicalize_signal(signal)
|
1495
|
+
case signal
|
1496
|
+
when ::String, ::Symbol
|
1497
|
+
sigstr = signal.to_s
|
1498
|
+
sigstr = sigstr[3..-1] if sigstr.start_with?("SIG")
|
1499
|
+
signo = ::Signal.list[sigstr]
|
1500
|
+
return signo if signo
|
1501
|
+
when ::Integer
|
1502
|
+
return signal if ::Signal.signame(signal)
|
1503
|
+
end
|
1504
|
+
raise ::ArgumentError, "Unknown signal: #{signal}"
|
1505
|
+
end
|
1403
1506
|
end
|
1404
1507
|
end
|
data/lib/toys/utils/exec.rb
CHANGED
@@ -16,6 +16,27 @@ module Toys
|
|
16
16
|
# This class is not loaded by default. Before using it directly, you should
|
17
17
|
# `require "toys/utils/exec"`
|
18
18
|
#
|
19
|
+
# ### The exec service
|
20
|
+
#
|
21
|
+
# The main entrypoint class is this one, {Toys::Utils::Exec}. It's a
|
22
|
+
# "service" object that provides functionality, primarily methods that
|
23
|
+
# spawn processes. Create it like any object:
|
24
|
+
#
|
25
|
+
# require "toys/utils/exec"
|
26
|
+
# exec_service = Toys::Utils::Exec.new
|
27
|
+
#
|
28
|
+
# There are two "primitive" functions: {#exec} and {#exec_proc}. The {#exec}
|
29
|
+
# method spawns an operating system process specified by an executable and
|
30
|
+
# a set of arguments. The {#exec_proc} method takes a `Proc` and forks a
|
31
|
+
# Ruby process. Both of these can be heavily configured with stream
|
32
|
+
# handling, result handling, and numerous other options described below.
|
33
|
+
# The class also provides convenience methods for common cases such as
|
34
|
+
# spawning a Ruby process, spawning a shell script, or capturing output.
|
35
|
+
#
|
36
|
+
# The exec service class also stores default configuration that it applies
|
37
|
+
# to processes it spawns. You can set these defaults when constructing the
|
38
|
+
# service class, or at any time by calling {#configure_defaults}.
|
39
|
+
#
|
19
40
|
# ### Controlling processes
|
20
41
|
#
|
21
42
|
# A process can be started in the *foreground* or the *background*. If you
|
@@ -24,7 +45,7 @@ module Toys
|
|
24
45
|
# If you start a background process, its streams will be redirected to null
|
25
46
|
# by default, and control will be returned to you immediately.
|
26
47
|
#
|
27
|
-
#
|
48
|
+
# While a process is running, you can control it using a
|
28
49
|
# {Toys::Utils::Exec::Controller} object. Use a controller to interact with
|
29
50
|
# the process's input and output streams, send it signals, or wait for it
|
30
51
|
# to complete.
|
data/lib/toys/utils/gems.rb
CHANGED
@@ -304,6 +304,9 @@ module Toys
|
|
304
304
|
end
|
305
305
|
|
306
306
|
def setup_bundle(gemfile_path, groups: nil, retries: nil)
|
307
|
+
# Lock the bundler version, preventing bundler's SelfManager from
|
308
|
+
# installing a different bundler and taking over the process.
|
309
|
+
::ENV["BUNDLER_VERSION"] = ::Bundler::VERSION
|
307
310
|
check_gemfile_compatibility(gemfile_path)
|
308
311
|
groups = Array(groups)
|
309
312
|
modified_gemfile_path = create_modified_gemfile(gemfile_path)
|