toys-core 0.11.5 → 0.12.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 +31 -0
- data/README.md +1 -1
- data/lib/toys-core.rb +4 -1
- data/lib/toys/acceptor.rb +3 -3
- data/lib/toys/arg_parser.rb +6 -7
- data/lib/toys/cli.rb +44 -14
- data/lib/toys/compat.rb +19 -22
- data/lib/toys/completion.rb +3 -1
- data/lib/toys/context.rb +2 -2
- data/lib/toys/core.rb +1 -1
- data/lib/toys/dsl/base.rb +85 -0
- data/lib/toys/dsl/flag.rb +3 -3
- data/lib/toys/dsl/flag_group.rb +7 -7
- data/lib/toys/dsl/internal.rb +206 -0
- data/lib/toys/dsl/positional_arg.rb +3 -3
- data/lib/toys/dsl/tool.rb +174 -216
- data/lib/toys/errors.rb +1 -0
- data/lib/toys/flag.rb +15 -18
- data/lib/toys/flag_group.rb +5 -4
- data/lib/toys/input_file.rb +4 -4
- data/lib/toys/loader.rb +189 -50
- data/lib/toys/middleware.rb +1 -1
- data/lib/toys/mixin.rb +2 -2
- data/lib/toys/positional_arg.rb +3 -3
- data/lib/toys/settings.rb +900 -0
- data/lib/toys/source_info.rb +121 -18
- data/lib/toys/standard_middleware/apply_config.rb +5 -4
- data/lib/toys/standard_middleware/set_default_descriptions.rb +18 -18
- data/lib/toys/standard_middleware/show_help.rb +17 -5
- data/lib/toys/standard_mixins/exec.rb +12 -14
- data/lib/toys/standard_mixins/git_cache.rb +48 -0
- data/lib/toys/standard_mixins/xdg.rb +56 -0
- data/lib/toys/template.rb +2 -2
- data/lib/toys/{tool.rb → tool_definition.rb} +100 -41
- data/lib/toys/utils/exec.rb +4 -5
- data/lib/toys/utils/gems.rb +8 -7
- data/lib/toys/utils/git_cache.rb +184 -0
- data/lib/toys/utils/help_text.rb +90 -34
- data/lib/toys/utils/terminal.rb +1 -1
- data/lib/toys/utils/xdg.rb +293 -0
- metadata +14 -7
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module Toys
|
6
|
+
module StandardMixins
|
7
|
+
##
|
8
|
+
# A mixin that provides a git cache.
|
9
|
+
#
|
10
|
+
# This mixin provides an instance of {Toys::Utils::GitCache}, providing
|
11
|
+
# cached access to files from a remote git repo.
|
12
|
+
#
|
13
|
+
# Example usage:
|
14
|
+
#
|
15
|
+
# include :git_cache
|
16
|
+
#
|
17
|
+
# def run
|
18
|
+
# # Pull and cache the HEAD commit from the Toys repo.
|
19
|
+
# dir = git_cache.find("https://github.com/dazuma/toys.git")
|
20
|
+
# # Display the contents of the readme file.
|
21
|
+
# puts File.read(File.join(dir, "README.md"))
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
module GitCache
|
25
|
+
include Mixin
|
26
|
+
|
27
|
+
##
|
28
|
+
# Context key for the GitCache object.
|
29
|
+
# @return [Object]
|
30
|
+
#
|
31
|
+
KEY = ::Object.new.freeze
|
32
|
+
|
33
|
+
on_initialize do
|
34
|
+
require "toys/utils/git_cache"
|
35
|
+
self[KEY] = Utils::GitCache.new
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Access the builtin GitCache.
|
40
|
+
#
|
41
|
+
# @return [Toys::Utils::GitCache]
|
42
|
+
#
|
43
|
+
def git_cache
|
44
|
+
self[KEY]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module Toys
|
6
|
+
module StandardMixins
|
7
|
+
##
|
8
|
+
# A mixin that provides tools for working with the XDG Base Directory
|
9
|
+
# Specification.
|
10
|
+
#
|
11
|
+
# This mixin provides an instance of {Toys::Utils::XDG}, which includes
|
12
|
+
# utility methods that locate base directories and search paths for
|
13
|
+
# application state, configuration, caches, and other data, according to
|
14
|
+
# the [XDG Base Directory Spec version
|
15
|
+
# 0.8](https://specifications.freedesktop.org/basedir-spec/0.8/).
|
16
|
+
#
|
17
|
+
# Example usage:
|
18
|
+
#
|
19
|
+
# include :xdg
|
20
|
+
#
|
21
|
+
# def run
|
22
|
+
# # Get config file paths, in order from most to least inportant
|
23
|
+
# config_files = xdg.lookup_config("my-config.toml")
|
24
|
+
# config_files.each { |path| read_my_config(path) }
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
module XDG
|
28
|
+
include Mixin
|
29
|
+
|
30
|
+
##
|
31
|
+
# Context key for the XDG object.
|
32
|
+
# @return [Object]
|
33
|
+
#
|
34
|
+
KEY = ::Object.new.freeze
|
35
|
+
|
36
|
+
on_initialize do
|
37
|
+
require "toys/utils/xdg"
|
38
|
+
self[KEY] = Utils::XDG.new
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Access XDG utility methods.
|
43
|
+
#
|
44
|
+
# @return [Toys::Utils::XDG]
|
45
|
+
#
|
46
|
+
def xdg
|
47
|
+
self[KEY]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# An alternate name for the {XDG} module
|
53
|
+
#
|
54
|
+
Xdg = XDG
|
55
|
+
end
|
56
|
+
end
|
data/lib/toys/template.rb
CHANGED
@@ -10,7 +10,7 @@ module Toys
|
|
10
10
|
# Templates will often support configuration; for example the minitest
|
11
11
|
# template lets you configure the paths to the test files.
|
12
12
|
#
|
13
|
-
#
|
13
|
+
# ### Usage
|
14
14
|
#
|
15
15
|
# To create a template, define a class and include this module.
|
16
16
|
# The class defines the "configuration" of the template. If your template
|
@@ -25,7 +25,7 @@ module Toys
|
|
25
25
|
# this block are "inserted" into the user's configuration. The template
|
26
26
|
# object is passed to the block so you have access to the template options.
|
27
27
|
#
|
28
|
-
#
|
28
|
+
# ### Example
|
29
29
|
#
|
30
30
|
# This is a simple template that generates a "hello" tool. The tool simply
|
31
31
|
# prints a `"Hello, #{name}!"` greeting. The name is set as a template
|
@@ -4,22 +4,25 @@ require "set"
|
|
4
4
|
|
5
5
|
module Toys
|
6
6
|
##
|
7
|
-
# A
|
7
|
+
# A ToolDefinition describes a single command that can be invoked using Toys.
|
8
8
|
# It has a name, a series of one or more words that you use to identify
|
9
9
|
# the tool on the command line. It also has a set of formal flags and
|
10
10
|
# command line arguments supported, and a block that gets run when the
|
11
11
|
# tool is executed.
|
12
12
|
#
|
13
|
-
class
|
13
|
+
class ToolDefinition
|
14
14
|
##
|
15
15
|
# Create a new tool.
|
16
16
|
# Should be created only from the DSL via the Loader.
|
17
17
|
# @private
|
18
18
|
#
|
19
|
-
def initialize(
|
19
|
+
def initialize(parent, full_name, priority, source_root, middleware_stack, middleware_lookup,
|
20
|
+
tool_class = nil)
|
20
21
|
@parent = parent
|
22
|
+
@settings = Settings.new(parent: parent&.settings)
|
21
23
|
@full_name = full_name.dup.freeze
|
22
24
|
@priority = priority
|
25
|
+
@source_root = source_root
|
23
26
|
@built_middleware = middleware_stack.build(middleware_lookup)
|
24
27
|
@subtool_middleware_stack = middleware_stack.dup
|
25
28
|
|
@@ -28,7 +31,9 @@ module Toys
|
|
28
31
|
@templates = {}
|
29
32
|
@completions = {}
|
30
33
|
|
31
|
-
|
34
|
+
@precreated_class = tool_class
|
35
|
+
|
36
|
+
reset_definition
|
32
37
|
end
|
33
38
|
|
34
39
|
##
|
@@ -37,8 +42,8 @@ module Toys
|
|
37
42
|
# Should be called only from the DSL.
|
38
43
|
# @private
|
39
44
|
#
|
40
|
-
def reset_definition
|
41
|
-
@tool_class =
|
45
|
+
def reset_definition
|
46
|
+
@tool_class = @precreated_class || create_class
|
42
47
|
|
43
48
|
@source_info = nil
|
44
49
|
@definition_finished = false
|
@@ -72,6 +77,13 @@ module Toys
|
|
72
77
|
@completion = DefaultCompletion.new
|
73
78
|
end
|
74
79
|
|
80
|
+
##
|
81
|
+
# Settings for this tool
|
82
|
+
#
|
83
|
+
# @return [Toys::Tool::Settings]
|
84
|
+
#
|
85
|
+
attr_reader :settings
|
86
|
+
|
75
87
|
##
|
76
88
|
# The name of the tool as an array of strings.
|
77
89
|
# This array may not be modified.
|
@@ -87,6 +99,13 @@ module Toys
|
|
87
99
|
#
|
88
100
|
attr_reader :priority
|
89
101
|
|
102
|
+
##
|
103
|
+
# The root source info defining this tool, or nil if there is no source.
|
104
|
+
#
|
105
|
+
# @return [Toys::SourceInfo,nil]
|
106
|
+
#
|
107
|
+
attr_reader :source_root
|
108
|
+
|
90
109
|
##
|
91
110
|
# The tool class.
|
92
111
|
#
|
@@ -217,14 +236,14 @@ module Toys
|
|
217
236
|
#
|
218
237
|
# When reading, this may return an instance of one of the subclasses of
|
219
238
|
# {Toys::Completion::Base}, or a Proc that duck-types it. Generally, this
|
220
|
-
# defaults to a {Toys::
|
221
|
-
# algorithm that finds appropriate completions from flags,
|
222
|
-
# arguments, and subtools.
|
239
|
+
# defaults to a {Toys::ToolDefinition::DefaultCompletion}, providing a
|
240
|
+
# standard algorithm that finds appropriate completions from flags,
|
241
|
+
# positional arguments, and subtools.
|
223
242
|
#
|
224
243
|
# When setting, you may pass any of the following:
|
225
244
|
# * `nil` or `:default` which sets the value to a default instance.
|
226
|
-
# * A Hash of options to pass to the
|
227
|
-
# constructor.
|
245
|
+
# * A Hash of options to pass to the
|
246
|
+
# {Toys::ToolDefinition::DefaultCompletion} constructor.
|
228
247
|
# * Any other form recognized by {Toys::Completion.create}.
|
229
248
|
#
|
230
249
|
# @return [Toys::Completion::Base,Proc]
|
@@ -408,7 +427,7 @@ module Toys
|
|
408
427
|
# Get the named acceptor from this tool or its ancestors.
|
409
428
|
#
|
410
429
|
# @param name [String] The acceptor name.
|
411
|
-
# @return [
|
430
|
+
# @return [Toys::Acceptor::Base] The acceptor.
|
412
431
|
# @return [nil] if no acceptor of the given name is found.
|
413
432
|
#
|
414
433
|
def lookup_acceptor(name)
|
@@ -441,7 +460,7 @@ module Toys
|
|
441
460
|
# Get the named completion from this tool or its ancestors.
|
442
461
|
#
|
443
462
|
# @param name [String] The completion name
|
444
|
-
# @return [
|
463
|
+
# @return [Toys::Completion::Base,Proc] The completion proc.
|
445
464
|
# @return [nil] if no completion of the given name is found.
|
446
465
|
#
|
447
466
|
def lookup_completion(name)
|
@@ -451,11 +470,31 @@ module Toys
|
|
451
470
|
##
|
452
471
|
# Include the given mixin in the tool class.
|
453
472
|
#
|
454
|
-
#
|
473
|
+
# The mixin must be given as a module. You can use {#lookup_mixin} or
|
474
|
+
# {Loader#resolve_standard_mixin} to resolve named mixins.
|
475
|
+
#
|
476
|
+
# @param mod [Module] The mixin module
|
455
477
|
# @return [self]
|
456
478
|
#
|
457
|
-
def include_mixin(
|
458
|
-
|
479
|
+
def include_mixin(mod, *args, **kwargs)
|
480
|
+
check_definition_state
|
481
|
+
if tool_class.included_modules.include?(mod)
|
482
|
+
raise ToolDefinitionError, "Mixin already included: #{mod.name}"
|
483
|
+
end
|
484
|
+
@includes_modules = true
|
485
|
+
if tool_class.respond_to?(:super_include)
|
486
|
+
tool_class.super_include(mod)
|
487
|
+
else
|
488
|
+
tool_class.include(mod)
|
489
|
+
end
|
490
|
+
if mod.respond_to?(:initializer)
|
491
|
+
callback = mod.initializer
|
492
|
+
add_initializer(callback, *args, **kwargs) if callback
|
493
|
+
end
|
494
|
+
if mod.respond_to?(:inclusion)
|
495
|
+
callback = mod.inclusion
|
496
|
+
tool_class.class_exec(*args, **kwargs, &callback) if callback
|
497
|
+
end
|
459
498
|
self
|
460
499
|
end
|
461
500
|
|
@@ -565,7 +604,7 @@ module Toys
|
|
565
604
|
# completion.
|
566
605
|
#
|
567
606
|
# @param name [String] The name of the completion.
|
568
|
-
# @param completion [Proc,
|
607
|
+
# @param completion [Proc,Toys::Completion::Base,Object] The completion to
|
569
608
|
# add. You can provide either a completion object, or a spec understood
|
570
609
|
# by {Toys::Completion.create}.
|
571
610
|
# @param options [Hash] Additional options to pass to the completion.
|
@@ -669,11 +708,12 @@ module Toys
|
|
669
708
|
#
|
670
709
|
# @param type [Symbol] The type of group. Default is `:optional`.
|
671
710
|
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
672
|
-
# description for the group. See {Toys::
|
673
|
-
# of allowed formats. Defaults to `"Flags"`.
|
711
|
+
# description for the group. See {Toys::ToolDefinition#desc} for a
|
712
|
+
# description of allowed formats. Defaults to `"Flags"`.
|
674
713
|
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
675
|
-
# Long description for the flag group. See
|
676
|
-
# a description of allowed
|
714
|
+
# Long description for the flag group. See
|
715
|
+
# {Toys::ToolDefinition#long_desc} for a description of allowed
|
716
|
+
# formats. Defaults to the empty array.
|
677
717
|
# @param name [String,Symbol,nil] The name of the group, or nil for no
|
678
718
|
# name.
|
679
719
|
# @param report_collisions [Boolean] If `true`, raise an exception if a
|
@@ -741,11 +781,11 @@ module Toys
|
|
741
781
|
# this flag. You may provide a group name, a FlagGroup object, or
|
742
782
|
# `nil` which denotes the default group.
|
743
783
|
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
744
|
-
# description for the flag. See {Toys::
|
745
|
-
# allowed formats. Defaults to the empty string.
|
784
|
+
# description for the flag. See {Toys::ToolDefinition#desc} for a
|
785
|
+
# description of allowed formats. Defaults to the empty string.
|
746
786
|
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
747
|
-
# Long description for the flag. See {Toys::
|
748
|
-
# description of allowed formats. Defaults to the empty array.
|
787
|
+
# Long description for the flag. See {Toys::ToolDefinition#long_desc}
|
788
|
+
# for a description of allowed formats. Defaults to the empty array.
|
749
789
|
# @param display_name [String] A display name for this flag, used in help
|
750
790
|
# text and error messages.
|
751
791
|
# @return [self]
|
@@ -808,11 +848,11 @@ module Toys
|
|
808
848
|
# @param display_name [String] A name to use for display (in help text and
|
809
849
|
# error reports). Defaults to the key in upper case.
|
810
850
|
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
811
|
-
# description for the arg. See {Toys::
|
812
|
-
# allowed formats. Defaults to the empty string.
|
851
|
+
# description for the arg. See {Toys::ToolDefinition#desc} for a
|
852
|
+
# description of allowed formats. Defaults to the empty string.
|
813
853
|
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
814
|
-
# Long description for the arg. See {Toys::
|
815
|
-
# description of allowed formats. Defaults to the empty array.
|
854
|
+
# Long description for the arg. See {Toys::ToolDefinition#long_desc}
|
855
|
+
# for a description of allowed formats. Defaults to the empty array.
|
816
856
|
# @return [self]
|
817
857
|
#
|
818
858
|
def add_required_arg(key, accept: nil, complete: nil, display_name: nil,
|
@@ -846,11 +886,11 @@ module Toys
|
|
846
886
|
# @param display_name [String] A name to use for display (in help text and
|
847
887
|
# error reports). Defaults to the key in upper case.
|
848
888
|
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
849
|
-
# description for the arg. See {Toys::
|
850
|
-
# allowed formats. Defaults to the empty string.
|
889
|
+
# description for the arg. See {Toys::ToolDefinition#desc} for a
|
890
|
+
# description of allowed formats. Defaults to the empty string.
|
851
891
|
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
852
|
-
# Long description for the arg. See {Toys::
|
853
|
-
# description of allowed formats. Defaults to the empty array.
|
892
|
+
# Long description for the arg. See {Toys::ToolDefinition#long_desc}
|
893
|
+
# for a description of allowed formats. Defaults to the empty array.
|
854
894
|
# @return [self]
|
855
895
|
#
|
856
896
|
def add_optional_arg(key, default: nil, accept: nil, complete: nil,
|
@@ -884,11 +924,11 @@ module Toys
|
|
884
924
|
# @param display_name [String] A name to use for display (in help text and
|
885
925
|
# error reports). Defaults to the key in upper case.
|
886
926
|
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
887
|
-
# description for the arg. See {Toys::
|
888
|
-
# allowed formats. Defaults to the empty string.
|
927
|
+
# description for the arg. See {Toys::ToolDefinition#desc} for a
|
928
|
+
# description of allowed formats. Defaults to the empty string.
|
889
929
|
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
890
|
-
# Long description for the arg. See {Toys::
|
891
|
-
# description of allowed formats. Defaults to the empty array.
|
930
|
+
# Long description for the arg. See {Toys::ToolDefinition#long_desc}
|
931
|
+
# for a description of allowed formats. Defaults to the empty array.
|
892
932
|
# @return [self]
|
893
933
|
#
|
894
934
|
def set_remaining_args(key, default: [], accept: nil, complete: nil,
|
@@ -910,7 +950,9 @@ module Toys
|
|
910
950
|
#
|
911
951
|
def run_handler=(proc)
|
912
952
|
check_definition_state(is_method: true)
|
913
|
-
|
953
|
+
tool_class.class_eval do
|
954
|
+
define_method(:run, &proc)
|
955
|
+
end
|
914
956
|
end
|
915
957
|
|
916
958
|
##
|
@@ -966,7 +1008,7 @@ module Toys
|
|
966
1008
|
end
|
967
1009
|
|
968
1010
|
##
|
969
|
-
# Set the completion strategy for this
|
1011
|
+
# Set the completion strategy for this ToolDefinition.
|
970
1012
|
#
|
971
1013
|
# See {#completion} for details.
|
972
1014
|
#
|
@@ -1056,7 +1098,7 @@ module Toys
|
|
1056
1098
|
def finish_definition(loader)
|
1057
1099
|
unless @definition_finished
|
1058
1100
|
ContextualError.capture("Error installing tool middleware!", tool_name: full_name) do
|
1059
|
-
config_proc = proc {}
|
1101
|
+
config_proc = proc { nil }
|
1060
1102
|
@built_middleware.reverse_each do |middleware|
|
1061
1103
|
config_proc = make_config_proc(middleware, loader, config_proc)
|
1062
1104
|
end
|
@@ -1120,6 +1162,7 @@ module Toys
|
|
1120
1162
|
def initialize(complete_subtools: true, include_hidden_subtools: false,
|
1121
1163
|
complete_args: true, complete_flags: true, complete_flag_values: true,
|
1122
1164
|
delegation_target: nil)
|
1165
|
+
super()
|
1123
1166
|
@complete_subtools = complete_subtools
|
1124
1167
|
@include_hidden_subtools = include_hidden_subtools
|
1125
1168
|
@complete_flags = complete_flags
|
@@ -1206,7 +1249,7 @@ module Toys
|
|
1206
1249
|
return unless arg_parser.flags_allowed?
|
1207
1250
|
active_flag_def = arg_parser.active_flag_def
|
1208
1251
|
return if active_flag_def && active_flag_def.value_type == :required
|
1209
|
-
match = /\A(--\w[
|
1252
|
+
match = /\A(--\w[?\w-]*)=(.*)\z/.match(context.fragment_prefix)
|
1210
1253
|
return unless match
|
1211
1254
|
flag_value_context = context.with(fragment_prefix: match[2])
|
1212
1255
|
flag_def = flag_value_context.tool.resolve_flag(match[1]).unique_flag
|
@@ -1287,8 +1330,24 @@ module Toys
|
|
1287
1330
|
end
|
1288
1331
|
end
|
1289
1332
|
|
1333
|
+
##
|
1334
|
+
# Tool-based settings class.
|
1335
|
+
#
|
1336
|
+
# The following settings are supported:
|
1337
|
+
#
|
1338
|
+
# * `propagate_helper_methods` (_Boolean_) - Whether subtools should
|
1339
|
+
# inherit methods defined by parent tools. Defaults to `false`.
|
1340
|
+
#
|
1341
|
+
class Settings < ::Toys::Settings
|
1342
|
+
settings_attr :propagate_helper_methods, default: false
|
1343
|
+
end
|
1344
|
+
|
1290
1345
|
private
|
1291
1346
|
|
1347
|
+
def create_class
|
1348
|
+
::Class.new(@parent&.settings&.propagate_helper_methods ? @parent.tool_class : ::Toys::Context)
|
1349
|
+
end
|
1350
|
+
|
1292
1351
|
def make_config_proc(middleware, loader, next_config)
|
1293
1352
|
if middleware.respond_to?(:config)
|
1294
1353
|
proc { middleware.config(self, loader, &next_config) }
|
data/lib/toys/utils/exec.rb
CHANGED
@@ -16,8 +16,6 @@ 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
|
-
# ## Features
|
20
|
-
#
|
21
19
|
# ### Controlling processes
|
22
20
|
#
|
23
21
|
# A process can be started in the *foreground* or the *background*. If you
|
@@ -138,7 +136,7 @@ module Toys
|
|
138
136
|
# end
|
139
137
|
# exec_service.exec(["git", "init"], result_callback: my_callback)
|
140
138
|
#
|
141
|
-
#
|
139
|
+
# ### Configuration options
|
142
140
|
#
|
143
141
|
# A variety of options can be used to control subprocesses. These can be
|
144
142
|
# provided to any method that starts a subprocess. Youc an also set
|
@@ -1093,9 +1091,10 @@ module Toys
|
|
1093
1091
|
|
1094
1092
|
def interpret_out_array_within_fork(stream)
|
1095
1093
|
if stream.first == :child
|
1096
|
-
|
1094
|
+
case stream[1]
|
1095
|
+
when :err
|
1097
1096
|
$stderr
|
1098
|
-
|
1097
|
+
when :out
|
1099
1098
|
$stdout
|
1100
1099
|
end
|
1101
1100
|
else
|