toys-core 0.14.7 → 0.15.0
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/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)
|