toys-core 0.4.3 → 0.4.4
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 +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
|