11# frozen_string_literal: true 
22
3+ require  'logger' 
4+ require  'timeout' 
5+ 
36require  'process_executer/errors' 
47require  'process_executer/monitored_pipe' 
58require  'process_executer/options' 
9+ require  'process_executer/option_definition' 
610require  'process_executer/result' 
711require  'process_executer/runner' 
812
9- require  'logger' 
10- require  'timeout' 
11- 
1213# The `ProcessExecuter` module provides methods to execute subprocess commands 
1314# with enhanced features such as output capture, timeout handling, and custom 
1415# environment variables. 
1516# 
1617# Methods: 
18+ # 
1719# * {run}: Executes a command and returns the result which includes the process 
1820#   status and output 
1921# * {spawn_and_wait}: a thin wrapper around `Process.spawn` that blocks until the 
2022#   command finishes 
2123# 
2224# Features: 
25+ # 
2326# * Supports executing commands via a shell or directly. 
2427# * Captures stdout and stderr to buffers, files, or custom objects. 
2528# * Optionally enforces timeouts and terminates long-running commands. 
2629# * Provides detailed status information, including the command that was run, the 
2730#   options that were given, and success, failure, or timeout states. 
2831# 
2932# @api public 
30- # 
3133module  ProcessExecuter 
3234  # Run a command in a subprocess, wait for it to finish, then return the result 
3335  # 
@@ -70,7 +72,20 @@ module ProcessExecuter
7072  # @return [ProcessExecuter::Result] The result of the completed subprocess 
7173  # 
7274  def  self . spawn_and_wait ( *command ,  **options_hash ) 
73-     options  =  ProcessExecuter ::Options . new ( **options_hash ) 
75+     options  =  ProcessExecuter . spawn_and_wait_options ( options_hash ) 
76+     spawn_and_wait_with_options ( command ,  options ) 
77+   end 
78+ 
79+   # Run a command in a subprocess, wait for it to finish, then return the result 
80+   # 
81+   # @see ProcessExecuter.spawn_and_wait for full documentation 
82+   # 
83+   # @param command [Array<String>] The command to run 
84+   # @param options [ProcessExecuter::SpawnAndWaitOptions] The options to use when running the command 
85+   # 
86+   # @return [ProcessExecuter::Result] The result of the completed subprocess 
87+   # @api private 
88+   def  self . spawn_and_wait_with_options ( command ,  options ) 
7489    pid  =  Process . spawn ( *command ,  **options . spawn_options ) 
7590    wait_for_process ( pid ,  command ,  options ) 
7691  end 
@@ -252,7 +267,6 @@ def self.spawn_and_wait(*command, **options_hash)
252267  #   Otherwise, the command is run bypassing the shell. When bypassing the shell, shell expansions 
253268  #   and redirections are not supported. 
254269  # 
255-   # @param logger [Logger] The logger to use 
256270  # @param options_hash [Hash] Additional options 
257271  # @option options_hash [Numeric] :timeout_after The maximum seconds to wait for the 
258272  #   command to complete 
@@ -276,6 +290,7 @@ def self.spawn_and_wait(*command, **options_hash)
276290  # @option options_hash [Integer] :umask (nil) Set the umask (see File.umask) 
277291  # @option options_hash [Boolean] :close_others (false) If true, close non-standard file descriptors 
278292  # @option options_hash [String] :chdir (nil) The directory to run the command in 
293+   # @option options_hash [Logger] :logger The logger to use 
279294  # 
280295  # @raise [ProcessExecuter::FailedError] if the command returned a non-zero exit status 
281296  # @raise [ProcessExecuter::SignaledError] if the command exited because of an unhandled signal 
@@ -284,8 +299,23 @@ def self.spawn_and_wait(*command, **options_hash)
284299  # 
285300  # @return [ProcessExecuter::Result] The result of the completed subprocess 
286301  # 
287-   def  self . run ( *command ,  logger : Logger . new ( nil ) ,  **options_hash ) 
288-     ProcessExecuter ::Runner . new ( logger ) . call ( *command ,  **options_hash ) 
302+   def  self . run ( *command ,  **options_hash ) 
303+     options  =  ProcessExecuter . run_options ( options_hash ) 
304+     run_with_options ( command ,  options ) 
305+   end 
306+ 
307+   # Run a command with the given options 
308+   # 
309+   # @see ProcessExecuter.run for full documentation 
310+   # 
311+   # @param command [Array<String>] The command to run 
312+   # @param options [ProcessExecuter::RunOptions] The options to use when running the command 
313+   # 
314+   # @return [ProcessExecuter::Result] The result of the completed subprocess 
315+   # 
316+   # @api private 
317+   def  self . run_with_options ( command ,  options ) 
318+     ProcessExecuter ::Runner . new . call ( command ,  options ) 
289319  end 
290320
291321  # Wait for process to terminate 
@@ -328,4 +358,88 @@ def self.run(*command, logger: Logger.new(nil), **options_hash)
328358
329359    [ process_status ,  timed_out ] 
330360  end 
361+ 
362+   # Convert a hash to a SpawnOptions object 
363+   # 
364+   # @example 
365+   #   options_hash = { out: $stdout } 
366+   #   options = ProcessExecuter.spawn_options(options_hash) # => 
367+   #     #<ProcessExecuter::SpawnOptions:0x00007f8f9b0b3d20 out: $stdout> 
368+   #   ProcessExecuter.spawn_options(options) # => 
369+   #     #<ProcessExecuter::SpawnOptions:0x00007f8f9b0b3d20 out: $stdout> 
370+   # 
371+   # @param obj [Hash, SpawnOptions] the object to be converted 
372+   # 
373+   # @return [SpawnOptions] 
374+   # 
375+   # @raise [ArgumentError] if obj is not a Hash or SpawnOptions 
376+   # 
377+   # @api public 
378+   # 
379+   def  self . spawn_options ( obj ) 
380+     case  obj 
381+     when  ProcessExecuter ::SpawnOptions 
382+       obj 
383+     when  Hash 
384+       ProcessExecuter ::SpawnOptions . new ( **obj ) 
385+     else 
386+       raise  ArgumentError ,  "Expected a Hash or ProcessExecuter::SpawnOptions but got a #{ obj . class }  " 
387+     end 
388+   end 
389+ 
390+   # Convert a hash to a SpawnAndWaitOptions object 
391+   # 
392+   # @example 
393+   #   options_hash = { out: $stdout } 
394+   #   options = ProcessExecuter.spawn_and_wait_options(options_hash) # => 
395+   #     #<ProcessExecuter::SpawnAndWaitOptions:0x00007f8f9b0b3d20 out: $stdout> 
396+   #   ProcessExecuter.spawn_and_wait_options(options) # => 
397+   #     #<ProcessExecuter::SpawnAndWaitOptions:0x00007f8f9b0b3d20 out: $stdout> 
398+   # 
399+   # @param obj [Hash, SpawnAndWaitOptions] the object to be converted 
400+   # 
401+   # @return [SpawnAndWaitOptions] 
402+   # 
403+   # @raise [ArgumentError] if obj is not a Hash or SpawnOptions 
404+   # 
405+   # @api public 
406+   # 
407+   def  self . spawn_and_wait_options ( obj ) 
408+     case  obj 
409+     when  ProcessExecuter ::SpawnAndWaitOptions 
410+       obj 
411+     when  Hash 
412+       ProcessExecuter ::SpawnAndWaitOptions . new ( **obj ) 
413+     else 
414+       raise  ArgumentError ,  "Expected a Hash or ProcessExecuter::SpawnAndWaitOptions but got a #{ obj . class }  " 
415+     end 
416+   end 
417+ 
418+   # Convert a hash to a RunOptions object 
419+   # 
420+   # @example 
421+   #   options_hash = { out: $stdout } 
422+   #   options = ProcessExecuter.run_options(options_hash) # => 
423+   #     #<ProcessExecuter::RunOptions:0x00007f8f9b0b3d20 out: $stdout> 
424+   #   ProcessExecuter.run_options(options) # => 
425+   #     #<ProcessExecuter::RunOptions:0x00007f8f9b0b3d20 out: $stdout> 
426+   # 
427+   # @param obj [Hash, RunOptions] the object to be converted 
428+   # 
429+   # @return [RunOptions] 
430+   # 
431+   # @raise [ArgumentError] if obj is not a Hash or SpawnOptions 
432+   # 
433+   # @api public 
434+   # 
435+   def  self . run_options ( obj ) 
436+     case  obj 
437+     when  ProcessExecuter ::RunOptions 
438+       obj 
439+     when  Hash 
440+       ProcessExecuter ::RunOptions . new ( **obj ) 
441+     else 
442+       raise  ArgumentError ,  "Expected a Hash or ProcessExecuter::RunOptions but got a #{ obj . class }  " 
443+     end 
444+   end 
331445end 
0 commit comments