toys-core 0.4.3 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/toys/core_version.rb +1 -1
- data/lib/toys/standard_mixins/terminal.rb +9 -2
- data/lib/toys/utils/exec.rb +188 -42
- data/lib/toys/utils/terminal.rb +46 -18
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f312d702768f03fa72f49fb33b81b49736aa3095a863878a2eddb70750d27d8
|
4
|
+
data.tar.gz: a3ed6d02218c3e94559178021320603b5ef6d89f0e2d263fa183a583b68caf05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94ee591039f0191572b66274033dbb3245bdb210e1e93441910c6a7d1e6d5732f1afdd71ba09103a357c52dc8c59fe13b1538ddd53319e03aaea4502e266e452
|
7
|
+
data.tar.gz: 6cc7dfd5d6dc7a87c6e5cee9521d6374b1ec7b462119e7ce2700147d6a384df6d7e7d92a6498139f53dcc5f114cd47588d53e4fb2108757b2d575f46b26f3a4f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 0.4.4 / 2018-07-21
|
4
|
+
|
5
|
+
* FIXED: Utils::Exec wasn't closing streams after copying.
|
6
|
+
* IMPROVED: Utils::Exec::Controller can capture or redirect the remainder of a controlled stream.
|
7
|
+
* ADDED: Terminal#ask
|
8
|
+
|
3
9
|
### 0.4.3 / 2018-07-13
|
4
10
|
|
5
11
|
* IMPROVED: Utils::Exec methods can now spawn subprocesses in the background
|
data/lib/toys/core_version.rb
CHANGED
@@ -91,11 +91,18 @@ module Toys
|
|
91
91
|
terminal.write(str, *styles)
|
92
92
|
end
|
93
93
|
|
94
|
+
##
|
95
|
+
# @see Toys::Utils::Terminal#ask
|
96
|
+
#
|
97
|
+
def ask(prompt, *styles, default: nil, trailing_text: :default)
|
98
|
+
terminal.ask(prompt, *styles, default: default, trailing_text: trailing_text)
|
99
|
+
end
|
100
|
+
|
94
101
|
##
|
95
102
|
# @see Toys::Utils::Terminal#confirm
|
96
103
|
#
|
97
|
-
def confirm(prompt = "Proceed?", default: nil)
|
98
|
-
terminal.confirm(prompt, default: default)
|
104
|
+
def confirm(prompt = "Proceed?", *styles, default: nil)
|
105
|
+
terminal.confirm(prompt, *styles, default: default)
|
99
106
|
end
|
100
107
|
|
101
108
|
##
|
data/lib/toys/utils/exec.rb
CHANGED
@@ -437,15 +437,139 @@ module Toys
|
|
437
437
|
#
|
438
438
|
attr_reader :pid
|
439
439
|
|
440
|
+
##
|
441
|
+
# Captures the remaining data in the given stream.
|
442
|
+
# After calling this, do not read directly from the stream.
|
443
|
+
#
|
444
|
+
# @param [:out,:err] which Which stream to capture
|
445
|
+
#
|
446
|
+
def capture(which)
|
447
|
+
stream = stream_for(which)
|
448
|
+
@join_threads << ::Thread.new do
|
449
|
+
begin
|
450
|
+
@captures[which] = stream.read
|
451
|
+
ensure
|
452
|
+
stream.close
|
453
|
+
end
|
454
|
+
end
|
455
|
+
self
|
456
|
+
end
|
457
|
+
|
458
|
+
##
|
459
|
+
# Captures the remaining data in the stdandard output stream.
|
460
|
+
# After calling this, do not read directly from the stream.
|
461
|
+
#
|
462
|
+
def capture_out
|
463
|
+
capture(:out)
|
464
|
+
end
|
465
|
+
|
466
|
+
##
|
467
|
+
# Captures the remaining data in the stdandard error stream.
|
468
|
+
# After calling this, do not read directly from the stream.
|
469
|
+
#
|
470
|
+
def capture_err
|
471
|
+
capture(:err)
|
472
|
+
end
|
473
|
+
|
474
|
+
##
|
475
|
+
# Redirects the remainder of the given stream.
|
476
|
+
#
|
477
|
+
# You may specify the stream as an IO or IO-like object, or as a file
|
478
|
+
# specified by its path. If specifying a file, you may optionally
|
479
|
+
# provide the mode and permissions for the call to `File#open`. You can
|
480
|
+
# also specify the value `:null` to indicate the null file.
|
481
|
+
#
|
482
|
+
# After calling this, do not interact directly with the stream.
|
483
|
+
#
|
484
|
+
# @param [:in,:out,:err] which Which stream to redirect
|
485
|
+
# @param [IO,StringIO,String,:null] io Where to redirect the stream
|
486
|
+
# @param [Object...] io_args The mode and permissions for opening the
|
487
|
+
# file, if redirecting to/from a file.
|
488
|
+
#
|
489
|
+
def redirect(which, io, *io_args)
|
490
|
+
io = ::File::NULL if io == :null
|
491
|
+
if io.is_a?(::String)
|
492
|
+
io_args = which == :in ? ["r"] : ["w"] if io_args.empty?
|
493
|
+
io = ::File.open(io, *io_args)
|
494
|
+
end
|
495
|
+
stream = stream_for(which, allow_in: true)
|
496
|
+
@join_threads << ::Thread.new do
|
497
|
+
begin
|
498
|
+
if which == :in
|
499
|
+
::IO.copy_stream(io, stream)
|
500
|
+
else
|
501
|
+
::IO.copy_stream(stream, io)
|
502
|
+
end
|
503
|
+
ensure
|
504
|
+
stream.close
|
505
|
+
io.close
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
##
|
511
|
+
# Redirects the remainder of the standard input stream.
|
512
|
+
#
|
513
|
+
# You may specify the stream as an IO or IO-like object, or as a file
|
514
|
+
# specified by its path. If specifying a file, you may optionally
|
515
|
+
# provide the mode and permissions for the call to `File#open`. You can
|
516
|
+
# also specify the value `:null` to indicate the null file.
|
517
|
+
#
|
518
|
+
# After calling this, do not interact directly with the stream.
|
519
|
+
#
|
520
|
+
# @param [IO,StringIO,String,:null] io Where to redirect the stream
|
521
|
+
# @param [Object...] io_args The mode and permissions for opening the
|
522
|
+
# file, if redirecting from a file.
|
523
|
+
#
|
524
|
+
def redirect_in(io, *io_args)
|
525
|
+
redirect(:in, io, *io_args)
|
526
|
+
end
|
527
|
+
|
528
|
+
##
|
529
|
+
# Redirects the remainder of the standard output stream.
|
530
|
+
#
|
531
|
+
# You may specify the stream as an IO or IO-like object, or as a file
|
532
|
+
# specified by its path. If specifying a file, you may optionally
|
533
|
+
# provide the mode and permissions for the call to `File#open`. You can
|
534
|
+
# also specify the value `:null` to indicate the null file.
|
535
|
+
#
|
536
|
+
# After calling this, do not interact directly with the stream.
|
537
|
+
#
|
538
|
+
# @param [IO,StringIO,String,:null] io Where to redirect the stream
|
539
|
+
# @param [Object...] io_args The mode and permissions for opening the
|
540
|
+
# file, if redirecting to a file.
|
541
|
+
#
|
542
|
+
def redirect_out(io, *io_args)
|
543
|
+
redirect(:out, io, *io_args)
|
544
|
+
end
|
545
|
+
|
546
|
+
##
|
547
|
+
# Redirects the remainder of the standard error stream.
|
548
|
+
#
|
549
|
+
# You may specify the stream as an IO or IO-like object, or as a file
|
550
|
+
# specified by its path. If specifying a file, you may optionally
|
551
|
+
# provide the mode and permissions for the call to `File#open`.
|
552
|
+
#
|
553
|
+
# After calling this, do not interact directly with the stream.
|
554
|
+
#
|
555
|
+
# @param [IO,StringIO,String] io Where to redirect the stream
|
556
|
+
# @param [Object...] io_args The mode and permissions for opening the
|
557
|
+
# file, if redirecting to a file.
|
558
|
+
#
|
559
|
+
def redirect_err(io, *io_args)
|
560
|
+
redirect(:err, io, *io_args)
|
561
|
+
end
|
562
|
+
|
440
563
|
##
|
441
564
|
# Send the given signal to the process. The signal may be specified
|
442
565
|
# by name or number.
|
443
566
|
#
|
444
|
-
# @param [Integer,String]
|
567
|
+
# @param [Integer,String] sig The signal to send.
|
445
568
|
#
|
446
|
-
def kill(
|
447
|
-
::Process.kill(
|
569
|
+
def kill(sig)
|
570
|
+
::Process.kill(sig, pid)
|
448
571
|
end
|
572
|
+
alias signal kill
|
449
573
|
|
450
574
|
##
|
451
575
|
# Determine whether the subcommand is still executing
|
@@ -487,6 +611,29 @@ module Toys
|
|
487
611
|
@err.close if @err && !@err.closed?
|
488
612
|
self
|
489
613
|
end
|
614
|
+
|
615
|
+
private
|
616
|
+
|
617
|
+
def stream_for(which, allow_in: false)
|
618
|
+
stream = nil
|
619
|
+
case which
|
620
|
+
when :out
|
621
|
+
stream = @out
|
622
|
+
@out = nil
|
623
|
+
when :err
|
624
|
+
stream = @err
|
625
|
+
@err = nil
|
626
|
+
when :in
|
627
|
+
if allow_in
|
628
|
+
stream = @in
|
629
|
+
@in = nil
|
630
|
+
end
|
631
|
+
else
|
632
|
+
raise ::ArgumentError, "Unknown stream #{which}"
|
633
|
+
end
|
634
|
+
raise ::ArgumentError, "Stream #{which} not available" unless stream
|
635
|
+
stream
|
636
|
+
end
|
490
637
|
end
|
491
638
|
|
492
639
|
##
|
@@ -639,24 +786,12 @@ module Toys
|
|
639
786
|
|
640
787
|
def setup_streams_within_fork
|
641
788
|
@parent_streams.each(&:close)
|
642
|
-
setup_in_stream_within_fork(@spawn_opts[:in])
|
643
|
-
|
644
|
-
|
645
|
-
if out_stream == :close
|
646
|
-
$stdout.close
|
647
|
-
elsif out_stream
|
648
|
-
$stdout.reopen(out_stream)
|
649
|
-
$stdout.sync = true
|
650
|
-
end
|
651
|
-
if err_stream == :close
|
652
|
-
$stderr.close
|
653
|
-
elsif err_stream
|
654
|
-
$stderr.reopen(err_stream)
|
655
|
-
$stderr.sync = true
|
656
|
-
end
|
789
|
+
setup_in_stream_within_fork(@spawn_opts[:in], $stdin)
|
790
|
+
setup_out_stream_within_fork(@spawn_opts[:out], $stdout)
|
791
|
+
setup_out_stream_within_fork(@spawn_opts[:err], $stderr)
|
657
792
|
end
|
658
793
|
|
659
|
-
def setup_in_stream_within_fork(stream)
|
794
|
+
def setup_in_stream_within_fork(stream, stdstream)
|
660
795
|
in_stream =
|
661
796
|
case stream
|
662
797
|
when ::Integer
|
@@ -671,32 +806,43 @@ module Toys
|
|
671
806
|
stream if stream.respond_to?(:write)
|
672
807
|
end
|
673
808
|
if in_stream == :close
|
674
|
-
|
809
|
+
stdstream.close
|
675
810
|
elsif in_stream
|
676
|
-
|
811
|
+
stdstream.reopen(in_stream)
|
677
812
|
end
|
678
813
|
end
|
679
814
|
|
680
|
-
def
|
681
|
-
|
682
|
-
|
683
|
-
::
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
815
|
+
def setup_out_stream_within_fork(stream, stdstream)
|
816
|
+
out_stream =
|
817
|
+
case stream
|
818
|
+
when ::Integer
|
819
|
+
::IO.open(stream)
|
820
|
+
when ::Array
|
821
|
+
interpret_out_array_within_fork(stream)
|
822
|
+
when ::String
|
823
|
+
::File.open(stream, "w")
|
824
|
+
when :close
|
825
|
+
:close
|
691
826
|
else
|
692
|
-
|
827
|
+
stream if stream.respond_to?(:write)
|
828
|
+
end
|
829
|
+
if out_stream == :close
|
830
|
+
stdstream.close
|
831
|
+
elsif out_stream
|
832
|
+
stdstream.reopen(out_stream)
|
833
|
+
stdstream.sync = true
|
834
|
+
end
|
835
|
+
end
|
836
|
+
|
837
|
+
def interpret_out_array_within_fork(stream)
|
838
|
+
if stream.first == :child
|
839
|
+
if stream[1] == :err
|
840
|
+
$stderr
|
841
|
+
elsif stream[1] == :out
|
842
|
+
$stdout
|
693
843
|
end
|
694
|
-
when ::String
|
695
|
-
::File.open(stream, "w")
|
696
|
-
when :close
|
697
|
-
:close
|
698
844
|
else
|
699
|
-
|
845
|
+
::File.open(*stream)
|
700
846
|
end
|
701
847
|
end
|
702
848
|
|
@@ -870,26 +1016,26 @@ module Toys
|
|
870
1016
|
end
|
871
1017
|
end
|
872
1018
|
|
873
|
-
def copy_to_in_thread(io
|
1019
|
+
def copy_to_in_thread(io)
|
874
1020
|
stream = make_in_pipe
|
875
1021
|
@join_threads << ::Thread.new do
|
876
1022
|
begin
|
877
1023
|
::IO.copy_stream(io, stream)
|
878
1024
|
ensure
|
879
1025
|
stream.close
|
880
|
-
io.close
|
1026
|
+
io.close
|
881
1027
|
end
|
882
1028
|
end
|
883
1029
|
end
|
884
1030
|
|
885
|
-
def copy_from_out_thread(key, io
|
1031
|
+
def copy_from_out_thread(key, io)
|
886
1032
|
stream = make_out_pipe(key)
|
887
1033
|
@join_threads << ::Thread.new do
|
888
1034
|
begin
|
889
1035
|
::IO.copy_stream(stream, io)
|
890
1036
|
ensure
|
891
1037
|
stream.close
|
892
|
-
io.close
|
1038
|
+
io.close
|
893
1039
|
end
|
894
1040
|
end
|
895
1041
|
end
|
data/lib/toys/utils/terminal.rb
CHANGED
@@ -35,7 +35,7 @@ require "monitor"
|
|
35
35
|
begin
|
36
36
|
require "io/console"
|
37
37
|
rescue ::LoadError # rubocop:disable Lint/HandleExceptions
|
38
|
-
# TODO:
|
38
|
+
# TODO: alternate methods of getting terminal size
|
39
39
|
end
|
40
40
|
|
41
41
|
module Toys
|
@@ -210,33 +210,61 @@ module Toys
|
|
210
210
|
puts
|
211
211
|
end
|
212
212
|
|
213
|
+
##
|
214
|
+
# Ask a question and get a response.
|
215
|
+
#
|
216
|
+
# @param [String] prompt Required prompt string.
|
217
|
+
# @param [Symbol,String,Array<Integer>...] styles Styles to apply to the
|
218
|
+
# prompt.
|
219
|
+
# @param [String,nil] default Default value, or `nil` for no default.
|
220
|
+
# Uses `nil` if not specified.
|
221
|
+
# @param [:default,String,nil] trailing_text Trailing text appended to
|
222
|
+
# the prompt, `nil` for none, or `:default` to show the default.
|
223
|
+
# @return [String]
|
224
|
+
#
|
225
|
+
def ask(prompt, *styles, default: nil, trailing_text: :default)
|
226
|
+
if trailing_text == :default
|
227
|
+
trailing_text = default.nil? ? nil : "[#{default}]"
|
228
|
+
end
|
229
|
+
if trailing_text
|
230
|
+
ptext, pspaces, = prompt.partition(/\s+$/)
|
231
|
+
prompt = "#{ptext} #{trailing_text}#{pspaces}"
|
232
|
+
end
|
233
|
+
write(prompt, *styles)
|
234
|
+
resp = input.gets.to_s.chomp
|
235
|
+
resp.empty? ? default.to_s : resp
|
236
|
+
end
|
237
|
+
|
213
238
|
##
|
214
239
|
# Confirm with the user.
|
215
240
|
#
|
216
241
|
# @param [String] prompt Prompt string. Defaults to `"Proceed?"`.
|
242
|
+
# @param [Symbol,String,Array<Integer>...] styles Styles to apply to the
|
243
|
+
# prompt.
|
217
244
|
# @param [Boolean,nil] default Default value, or `nil` for no default.
|
218
245
|
# Uses `nil` if not specified.
|
219
246
|
# @return [Boolean]
|
220
247
|
#
|
221
|
-
def confirm(prompt = "Proceed?", default: nil)
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
true
|
229
|
-
when /^n/i
|
230
|
-
false
|
231
|
-
when nil
|
232
|
-
raise TerminalError, "Cannot confirm because the input stream is at eof." if default.nil?
|
233
|
-
default
|
234
|
-
else
|
235
|
-
if !resp.strip.empty? || default.nil?
|
236
|
-
confirm("Please answer \"y\" or \"n\"")
|
248
|
+
def confirm(prompt = "Proceed? ", *styles, default: nil)
|
249
|
+
default_val, trailing_text =
|
250
|
+
case default
|
251
|
+
when true
|
252
|
+
["y", "(Y/n)"]
|
253
|
+
when false
|
254
|
+
["n", "(y/N)"]
|
237
255
|
else
|
238
|
-
|
256
|
+
[nil, "(y/n)"]
|
239
257
|
end
|
258
|
+
resp = ask(prompt, *styles, default: default_val, trailing_text: trailing_text)
|
259
|
+
return true if resp =~ /^y/i
|
260
|
+
return false if resp =~ /^n/i
|
261
|
+
if resp.nil? && default.nil?
|
262
|
+
raise TerminalError, "Cannot confirm because the input stream is at eof."
|
263
|
+
end
|
264
|
+
if !resp.strip.empty? || default.nil?
|
265
|
+
confirm('Please answer "y" or "n"', default: default)
|
266
|
+
else
|
267
|
+
default
|
240
268
|
end
|
241
269
|
end
|
242
270
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: toys-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Azuma
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-07-
|
11
|
+
date: 2018-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|