process_executer 2.0.0 → 3.0.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 +23 -0
- data/README.md +156 -46
- data/lib/process_executer/destination_base.rb +83 -0
- data/lib/process_executer/destinations/child_redirection.rb +23 -0
- data/lib/process_executer/destinations/close.rb +23 -0
- data/lib/process_executer/destinations/file_descriptor.rb +36 -0
- data/lib/process_executer/destinations/file_path.rb +56 -0
- data/lib/process_executer/destinations/file_path_mode.rb +60 -0
- data/lib/process_executer/destinations/file_path_mode_perms.rb +61 -0
- data/lib/process_executer/destinations/io.rb +33 -0
- data/lib/process_executer/destinations/monitored_pipe.rb +39 -0
- data/lib/process_executer/destinations/stderr.rb +31 -0
- data/lib/process_executer/destinations/stdout.rb +31 -0
- data/lib/process_executer/destinations/tee.rb +60 -0
- data/lib/process_executer/destinations/writer.rb +33 -0
- data/lib/process_executer/destinations.rb +70 -0
- data/lib/process_executer/monitored_pipe.rb +40 -57
- data/lib/process_executer/options/base.rb +240 -0
- data/lib/process_executer/options/option_definition.rb +56 -0
- data/lib/process_executer/options/run_options.rb +48 -0
- data/lib/process_executer/options/spawn_and_wait_options.rb +39 -0
- data/lib/process_executer/options/spawn_options.rb +143 -0
- data/lib/process_executer/options.rb +7 -166
- data/lib/process_executer/result.rb +13 -23
- data/lib/process_executer/runner.rb +58 -50
- data/lib/process_executer/version.rb +1 -1
- data/lib/process_executer.rb +130 -21
- metadata +23 -4
@@ -0,0 +1,240 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'option_definition'
|
4
|
+
|
5
|
+
module ProcessExecuter
|
6
|
+
module Options
|
7
|
+
# Defines, validates, and holds a set of option values
|
8
|
+
#
|
9
|
+
# Options are defined by subclasses by overriding the `define_options` method.
|
10
|
+
#
|
11
|
+
# @example Define an options class with two options
|
12
|
+
# class MyOptions < ProcessExecuter::Options::Base
|
13
|
+
# def define_options
|
14
|
+
# # Call super to include options defined in the parent class
|
15
|
+
# [
|
16
|
+
# *super,
|
17
|
+
# ProcessExecuter::Options::OptionDefinition.new(:option1),
|
18
|
+
# ProcessExecuter::Options::OptionDefinition.new(:option2)
|
19
|
+
# ]
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
# options_hash = { options1: 'value1', option2: 'value2' }
|
23
|
+
# options = MyOptions.new(options_hash)
|
24
|
+
# options.option1 # => 'value1'
|
25
|
+
# options.option2 # => 'value2'
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
class Base
|
29
|
+
# Create a new Options object
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# options = ProcessExecuter::Options.new(out: $stdout, err: $stderr, timeout_after: 10)
|
33
|
+
#
|
34
|
+
# @param options [Hash] Process.spawn options plus additional options listed below.
|
35
|
+
#
|
36
|
+
# See [Process.spawn](https://ruby-doc.org/core/Process.html#method-c-spawn)
|
37
|
+
# for a list of valid options that can be passed to `Process.spawn`.
|
38
|
+
#
|
39
|
+
# @option options [Integer, Float, nil] :timeout_after
|
40
|
+
# Number of seconds to wait for the process to terminate. Any number
|
41
|
+
# may be used, including Floats to specify fractional seconds. A value of 0 or nil
|
42
|
+
# will allow the process to run indefinitely.
|
43
|
+
#
|
44
|
+
def initialize(**options)
|
45
|
+
@options = allowed_options.transform_values(&:default).merge(options)
|
46
|
+
@errors = []
|
47
|
+
assert_no_unknown_options
|
48
|
+
define_accessor_methods
|
49
|
+
validate_options
|
50
|
+
end
|
51
|
+
|
52
|
+
# All the allowed options as a hash whose keys are the option names
|
53
|
+
#
|
54
|
+
# The returned hash what is returned from `define_options` but with the
|
55
|
+
# option names as keys. The values are instances of `OptionDefinition`.
|
56
|
+
#
|
57
|
+
# The returned hash is frozen and cannot be modified.
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
# options.allowed_options # => { timeout_after: #<OptionDefinition>, ... }
|
61
|
+
#
|
62
|
+
# @return [Hash]
|
63
|
+
#
|
64
|
+
def allowed_options
|
65
|
+
@allowed_options ||=
|
66
|
+
define_options.each_with_object({}) do |option, hash|
|
67
|
+
hash[option.name] = option
|
68
|
+
end.freeze
|
69
|
+
end
|
70
|
+
|
71
|
+
# A string representation of the object that includes the options
|
72
|
+
# @example
|
73
|
+
# options = ProcessExecuter::Options.new(option1: 'value1', option2: 'value2')
|
74
|
+
# options.to_s # => #<ProcessExecuter::Options:0x00007f8f9b0b3d20 option1: "value1", option2: "value2">'
|
75
|
+
# @return [String]
|
76
|
+
def to_s
|
77
|
+
"#{super.to_s[0..-2]} #{inspect}>"
|
78
|
+
end
|
79
|
+
|
80
|
+
# A string representation of the options
|
81
|
+
# @example
|
82
|
+
# options = ProcessExecuter::Options.new(option1: 'value1', option2: 'value2')
|
83
|
+
# options.inspect # => '{:option1=>"value1", :option2=>"value2"}'
|
84
|
+
# @return [String]
|
85
|
+
def inspect
|
86
|
+
options.inspect
|
87
|
+
end
|
88
|
+
|
89
|
+
# A hash representation of the options
|
90
|
+
# @example
|
91
|
+
# options = ProcessExecuter::Options.new(option1: 'value1', option2: 'value2')
|
92
|
+
# options.to_h # => { option1: "value1", option2: "value2" }
|
93
|
+
# @return [Hash]
|
94
|
+
def to_h
|
95
|
+
@options.dup
|
96
|
+
end
|
97
|
+
|
98
|
+
# Iterate over each option with an object
|
99
|
+
# @example
|
100
|
+
# options = ProcessExecuter::Options.new(option1: 'value1', option2: 'value2')
|
101
|
+
# options.each_with_object({}) { |(option_key, option_value), obj| obj[option_key] = option_value }
|
102
|
+
# # => { option1: "value1", option2: "value2" }
|
103
|
+
# @yield [option_key, option_value, obj]
|
104
|
+
# @return [void]
|
105
|
+
def each_with_object(obj, &)
|
106
|
+
@options.each_with_object(obj, &)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Merge the given options into the current options
|
110
|
+
# @example
|
111
|
+
# options = ProcessExecuter::Options.new(option1: 'value1', option2: 'value2')
|
112
|
+
# options.merge!(option2: 'new_value2', option3: 'value3')
|
113
|
+
# options.option2 # => 'new_value2'
|
114
|
+
# options.option3 # => 'value3'
|
115
|
+
#
|
116
|
+
# @param other_options [Hash] the options to merge into the current options
|
117
|
+
# @return [void]
|
118
|
+
def merge!(**other_options)
|
119
|
+
@options.merge!(other_options)
|
120
|
+
end
|
121
|
+
|
122
|
+
# A shallow copy of self with options copied but not the values they reference
|
123
|
+
#
|
124
|
+
# If any keyword arguments are given, the copy will be created with the
|
125
|
+
# respective option values updated.
|
126
|
+
#
|
127
|
+
# @example
|
128
|
+
# options_hash = { option1: 'value1', option2: 'value2' }
|
129
|
+
# options = ProcessExecuter::MyOptions.new(options_hash)
|
130
|
+
# copy = options.with(option1: 'new_value1')
|
131
|
+
# copy.option1 # => 'new_value1'
|
132
|
+
# copy.option2 # => 'value2'
|
133
|
+
# options.option1 # => 'value1'
|
134
|
+
# options.option2 # => 'value2'
|
135
|
+
#
|
136
|
+
# @options_hash [Hash] the options to merge into the current options
|
137
|
+
# @return [self.class]
|
138
|
+
#
|
139
|
+
def with(**options_hash)
|
140
|
+
self.class.new(**@options, **options_hash)
|
141
|
+
end
|
142
|
+
|
143
|
+
# The list of validation errors
|
144
|
+
#
|
145
|
+
# Validators should add an error messages to this array.
|
146
|
+
#
|
147
|
+
# @example
|
148
|
+
# options = ProcessExecuter::Options::RunOptions.new(timeout_after: 'not_a_number', raise_errors: 'yes')
|
149
|
+
# #=> raises an Argument error with the following message:
|
150
|
+
# timeout_after must be nil or a non-negative real number but was "not_a_number"
|
151
|
+
# raise_errors must be true or false but was "yes""
|
152
|
+
# errors # => [
|
153
|
+
# "timeout_after must be nil or a non-negative real number but was \"not_a_number\"",
|
154
|
+
# "raise_errors must be true or false but was \"yes\""
|
155
|
+
# ]
|
156
|
+
#
|
157
|
+
# @return [Array<String>]
|
158
|
+
# @api private
|
159
|
+
attr_reader :errors
|
160
|
+
|
161
|
+
protected
|
162
|
+
|
163
|
+
# An array of OptionDefinition objects that define the allowed options
|
164
|
+
#
|
165
|
+
# Subclasses MUST override this method to define the allowed options.
|
166
|
+
#
|
167
|
+
# @return [Array<OptionDefinition>]
|
168
|
+
#
|
169
|
+
# @api private
|
170
|
+
#
|
171
|
+
def define_options
|
172
|
+
[].freeze
|
173
|
+
end
|
174
|
+
|
175
|
+
# Determine if the given option is a valid option
|
176
|
+
#
|
177
|
+
# May be overridden by subclasses to add additional validation.
|
178
|
+
#
|
179
|
+
# @param option [Symbol] the option to be tested
|
180
|
+
# @return [Boolean] true if the given option is a valid option
|
181
|
+
# @api private
|
182
|
+
def valid_option?(option)
|
183
|
+
allowed_options.keys.include?(option)
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
|
188
|
+
# @!attribute [r]
|
189
|
+
#
|
190
|
+
# A hash of all options keyed by the option name
|
191
|
+
#
|
192
|
+
# @return [Hash{Symbol => Object}]
|
193
|
+
#
|
194
|
+
# @api private
|
195
|
+
#
|
196
|
+
attr_reader :options
|
197
|
+
|
198
|
+
# Raise an argument error for invalid option values
|
199
|
+
# @return [void]
|
200
|
+
# @raise [ArgumentError] if any invalid option values are found
|
201
|
+
# @api private
|
202
|
+
def validate_options
|
203
|
+
options.each_key do |option_key|
|
204
|
+
validator = allowed_options[option_key]&.validator
|
205
|
+
instance_exec(&validator.to_proc) unless validator.nil?
|
206
|
+
end
|
207
|
+
|
208
|
+
raise ArgumentError, errors.join("\n") unless errors.empty?
|
209
|
+
end
|
210
|
+
|
211
|
+
# Define accessor methods for each option
|
212
|
+
# @return [void]
|
213
|
+
# @api private
|
214
|
+
def define_accessor_methods
|
215
|
+
allowed_options.each_key do |option|
|
216
|
+
define_singleton_method(option) do
|
217
|
+
@options[option]
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# Determine if the options hash contains any unknown options
|
223
|
+
# @return [void]
|
224
|
+
# @raise [ArgumentError] if the options hash contains any unknown options
|
225
|
+
# @api private
|
226
|
+
def assert_no_unknown_options
|
227
|
+
unknown_options = options.keys.reject { |key| valid_option?(key) }
|
228
|
+
|
229
|
+
return if unknown_options.empty?
|
230
|
+
|
231
|
+
# :nocov: SimpleCov on JRuby reports the last with the last argument line is not covered
|
232
|
+
raise(
|
233
|
+
ArgumentError,
|
234
|
+
"Unknown option#{unknown_options.count > 1 ? 's' : ''}: #{unknown_options.join(', ')}"
|
235
|
+
)
|
236
|
+
# :nocov:
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProcessExecuter
|
4
|
+
module Options
|
5
|
+
# Defines an option that can be used by an Options object
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
#
|
9
|
+
class OptionDefinition
|
10
|
+
# The name of the option
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# option = ProcessExecuter::Options::OptionDefinition.new(:timeout_after)
|
14
|
+
# option.name # => :timeout_after
|
15
|
+
#
|
16
|
+
# @return [Symbol]
|
17
|
+
#
|
18
|
+
attr_reader :name
|
19
|
+
|
20
|
+
# The default value of the option
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# option = ProcessExecuter::Options::OptionDefinition.new(:timeout_after, default: 10)
|
24
|
+
# option.default # => 10
|
25
|
+
#
|
26
|
+
# @return [Object]
|
27
|
+
#
|
28
|
+
attr_reader :default
|
29
|
+
|
30
|
+
# A method or proc that validates the option
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# option = ProcessExecuter::Options::OptionDefinition.new(
|
34
|
+
# :timeout_after, validator: method(:validate_timeout_after)
|
35
|
+
# )
|
36
|
+
# option.validator # => #<Method: ProcessExecuter#validate_timeout_after>
|
37
|
+
#
|
38
|
+
# @return [Method, Proc, nil]
|
39
|
+
#
|
40
|
+
attr_reader :validator
|
41
|
+
|
42
|
+
# Create a new option definition
|
43
|
+
#
|
44
|
+
# @example
|
45
|
+
# option = ProcessExecuter::Options::OptionDefinition.new(
|
46
|
+
# :timeout_after, default: 10, validator: -> { timeout_after.is_a?(Numeric) }
|
47
|
+
# )
|
48
|
+
#
|
49
|
+
def initialize(name, default: nil, validator: nil)
|
50
|
+
@name = name
|
51
|
+
@default = default
|
52
|
+
@validator = validator
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'spawn_and_wait_options'
|
4
|
+
require_relative 'option_definition'
|
5
|
+
|
6
|
+
module ProcessExecuter
|
7
|
+
module Options
|
8
|
+
# Define options for the `ProcessExecuter.run`
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
#
|
12
|
+
class RunOptions < SpawnAndWaitOptions
|
13
|
+
private
|
14
|
+
|
15
|
+
# :nocov: SimpleCov on JRuby reports the last with the last argument line is not covered
|
16
|
+
|
17
|
+
# The options allowed for objects of this class
|
18
|
+
# @return [Array<OptionDefinition>]
|
19
|
+
# @api private
|
20
|
+
def define_options
|
21
|
+
[
|
22
|
+
*super,
|
23
|
+
OptionDefinition.new(:raise_errors, default: true, validator: method(:validate_raise_errors)),
|
24
|
+
OptionDefinition.new(:logger, default: Logger.new(nil), validator: method(:validate_logger))
|
25
|
+
].freeze
|
26
|
+
end
|
27
|
+
# :nocov:
|
28
|
+
|
29
|
+
# Validate the raise_errors option value
|
30
|
+
# @return [String, nil] the error message if the value is not valid
|
31
|
+
# @api private
|
32
|
+
def validate_raise_errors
|
33
|
+
return if [true, false].include?(raise_errors)
|
34
|
+
|
35
|
+
errors << "raise_errors must be true or false but was #{raise_errors.inspect}"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Validate the logger option value
|
39
|
+
# @return [String, nil] the error message if the value is not valid
|
40
|
+
# @api private
|
41
|
+
def validate_logger
|
42
|
+
return if logger.respond_to?(:info) && logger.respond_to?(:debug)
|
43
|
+
|
44
|
+
errors << "logger must respond to #info and #debug but was #{logger.inspect}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'spawn_options'
|
4
|
+
require_relative 'option_definition'
|
5
|
+
|
6
|
+
module ProcessExecuter
|
7
|
+
module Options
|
8
|
+
# Define options for the `ProcessExecuter.spawn_and_wait`
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
#
|
12
|
+
class SpawnAndWaitOptions < SpawnOptions
|
13
|
+
private
|
14
|
+
|
15
|
+
# The options allowed for objects of this class
|
16
|
+
# @return [Array<OptionDefinition>]
|
17
|
+
# @api private
|
18
|
+
def define_options
|
19
|
+
# :nocov: SimpleCov on JRuby reports the last with the last argument line is not covered
|
20
|
+
[
|
21
|
+
*super,
|
22
|
+
OptionDefinition.new(:timeout_after, default: nil, validator: method(:validate_timeout_after))
|
23
|
+
].freeze
|
24
|
+
# :nocov:
|
25
|
+
end
|
26
|
+
|
27
|
+
# Raise an error unless timeout_after is nil or a non-negative real number
|
28
|
+
# @return [void]
|
29
|
+
# @raise [ArgumentError] if timeout_after is not a non-negative real number
|
30
|
+
# @api private
|
31
|
+
def validate_timeout_after
|
32
|
+
return if timeout_after.nil?
|
33
|
+
return if timeout_after.is_a?(Numeric) && timeout_after.real? && !timeout_after.negative?
|
34
|
+
|
35
|
+
errors << "timeout_after must be nil or a non-negative real number but was #{timeout_after.inspect}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
require_relative 'option_definition'
|
5
|
+
|
6
|
+
module ProcessExecuter
|
7
|
+
module Options
|
8
|
+
# Validate Process.spawn options and return Process.spawn options
|
9
|
+
#
|
10
|
+
# Allow subclasses to add additional options that are not passed to `Process.spawn`
|
11
|
+
#
|
12
|
+
# Valid options are those accepted by Process.spawn.
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
#
|
16
|
+
class SpawnOptions < Base
|
17
|
+
# :nocov: SimpleCov on JRuby reports hashes declared on multiple lines as not covered
|
18
|
+
SPAWN_OPTIONS = [
|
19
|
+
OptionDefinition.new(:unsetenv_others, default: :not_set),
|
20
|
+
OptionDefinition.new(:pgroup, default: :not_set),
|
21
|
+
OptionDefinition.new(:new_pgroup, default: :not_set),
|
22
|
+
OptionDefinition.new(:rlimit_resourcename, default: :not_set),
|
23
|
+
OptionDefinition.new(:umask, default: :not_set),
|
24
|
+
OptionDefinition.new(:close_others, default: :not_set),
|
25
|
+
OptionDefinition.new(:chdir, default: :not_set)
|
26
|
+
].freeze
|
27
|
+
# :nocov:
|
28
|
+
|
29
|
+
# Returns the options to be passed to Process.spawn
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# options = ProcessExecuter::Options.new(out: $stdout, err: $stderr, timeout_after: 10)
|
33
|
+
# options.spawn_options # => { out: $stdout, err: $stderr }
|
34
|
+
#
|
35
|
+
# @return [Hash]
|
36
|
+
#
|
37
|
+
def spawn_options
|
38
|
+
{}.tap do |spawn_options|
|
39
|
+
options.each do |option_key, value|
|
40
|
+
spawn_options[option_key] = value if include_spawn_option?(option_key, value)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Determine if the given option key indicates a redirection option
|
46
|
+
# @param option_key [Symbol, Integer, IO, Array] the option key to be tested
|
47
|
+
# @return [Boolean]
|
48
|
+
# @api private
|
49
|
+
def redirection?(option_key)
|
50
|
+
test = ->(key) { %i[in out err].include?(key) || key.is_a?(Integer) || (key.is_a?(IO) && !key.fileno.nil?) }
|
51
|
+
test.call(option_key) || (option_key.is_a?(Array) && option_key.all? { |key| test.call(key) })
|
52
|
+
end
|
53
|
+
|
54
|
+
# Does option_key indicate a standard redirection such as stdin, stdout, or stderr
|
55
|
+
# @param option_key [Symbol, Integer, IO, Array] the option key to be tested
|
56
|
+
# @param symbol [:in, :out, :err] the symbol to test for
|
57
|
+
# @param fileno [Integer] the file descriptor number to test for
|
58
|
+
# @return [Boolean]
|
59
|
+
# @api private
|
60
|
+
def std_redirection?(option_key, symbol, fileno)
|
61
|
+
test = ->(key) { key == symbol || key == fileno || (key.is_a?(IO) && key.fileno == fileno) }
|
62
|
+
test.call(option_key) || (option_key.is_a?(Array) && option_key.any? { |key| test.call(key) })
|
63
|
+
end
|
64
|
+
|
65
|
+
# Determine if the given option key indicates a redirection option for stdout
|
66
|
+
# @param option_key [Symbol, Integer, IO, Array] the option key to be tested
|
67
|
+
# @return [Boolean]
|
68
|
+
# @api private
|
69
|
+
def stdout_redirection?(option_key) = std_redirection?(option_key, :out, 1)
|
70
|
+
|
71
|
+
# Determine the option key that indicates a redirection option for stdout
|
72
|
+
# @return [Symbol, Integer, IO, Array, nil] nil if not found
|
73
|
+
# @api private
|
74
|
+
def stdout_redirection_key
|
75
|
+
options.keys.find { |option_key| option_key if stdout_redirection?(option_key) }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Determine the value of the redirection option for stdout
|
79
|
+
# @return [Object]
|
80
|
+
# @api private
|
81
|
+
def stdout_redirection_value
|
82
|
+
options[stdout_redirection_key]
|
83
|
+
end
|
84
|
+
|
85
|
+
# Determine if the given option key indicates a redirection option for stderr
|
86
|
+
# @param option_key [Symbol, Integer, IO, Array] the option key to be tested
|
87
|
+
# @return [Boolean]
|
88
|
+
# @api private
|
89
|
+
def stderr_redirection?(option_key) = std_redirection?(option_key, :err, 2)
|
90
|
+
|
91
|
+
# Determine the option key that indicates a redirection option for stderr
|
92
|
+
# @return [Symbol, Integer, IO, Array, nil] nil if not found
|
93
|
+
# @api private
|
94
|
+
def stderr_redirection_key
|
95
|
+
options.keys.find { |option_key| option_key if stderr_redirection?(option_key) }
|
96
|
+
end
|
97
|
+
|
98
|
+
# Determine the value of the redirection option for stderr
|
99
|
+
# @return [Object]
|
100
|
+
# @api private
|
101
|
+
def stderr_redirection_value
|
102
|
+
options[stderr_redirection_key]
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# Define the allowed options
|
108
|
+
#
|
109
|
+
# @example Adding new options in a subclass
|
110
|
+
# class MyOptions < SpawnOptions
|
111
|
+
# def define_options
|
112
|
+
# super.merge(timeout_after: { default: nil, validator: nil })
|
113
|
+
# end
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# @return [Hash<Symbol, Hash>]
|
117
|
+
#
|
118
|
+
# @api private
|
119
|
+
def define_options
|
120
|
+
[*super, *SPAWN_OPTIONS].freeze
|
121
|
+
end
|
122
|
+
|
123
|
+
# Determine if the given option should be passed to `Process.spawn`
|
124
|
+
# @param option_key [Symbol, Integer, IO] the option to be tested
|
125
|
+
# @param value [Object] the value of the option
|
126
|
+
# @return [Boolean] true if the given option should be passed to `Process.spawn`
|
127
|
+
# @api private
|
128
|
+
def include_spawn_option?(option_key, value)
|
129
|
+
return false if value == :not_set
|
130
|
+
|
131
|
+
redirection?(option_key) || SPAWN_OPTIONS.any? { |o| o.name == option_key }
|
132
|
+
end
|
133
|
+
|
134
|
+
# Spawn allows IO object and integers as options
|
135
|
+
# @param option_key [Symbol] the option to be tested
|
136
|
+
# @return [Boolean] true if the given option is a valid option
|
137
|
+
# @api private
|
138
|
+
def valid_option?(option_key)
|
139
|
+
super || redirection?(option_key)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|