Goal: I am writing a workflow command-line program in ruby that sequentially executes other programs on the UNIX shell, some of which require the user to enter input.
Problem: Although I can successfully handle the stdout and stderr thanks to this helpful blog post by Nick Charlton, I am however stuck on capturing user input and passing it into the sub-processes via the command line. The code is as follows:
Method
module CMD
  def run(cmd, &block)
    Open3.popen3(cmd) do |stdin, stdout, stderr, thread|
      Thread.new do # STDOUT
        until (line = stdout.gets).nil? do
          yield nil, line, nil, thread if block_given?
        end
      end
      Thread.new do # STDERR 
        until (line = stderr.gets).nil? do
          yield nil, nil, line, thread if block_given?
        end
      end
      Thread.new do # STDIN
        # ????? How to handle
      end
      thread.join
    end
  end
end 
Calling the method
This example calls the shell command units which prompts the user to enter a unit of measurement and then prompts for a unit to convert to. This is how it would look in the shell
> units
586 units, 56 prefixes        # stdout
You have: 1 litre             # user input
You want: gallons             # user input
* 0.26417205                  # stdout
/ 3.7854118                   # stdout
When I run this from my program I expect to be able to interact with it in exactly the same way.
unix_cmd = 'units'
run unix_cmd do | stdin, stdout, stderr, thread|
  puts "stdout #{stdout.strip}" if stdout
  puts "stderr #{stderr.strip}" if stderr
  # I'm unsure how I would allow the user to
  # interact with STDIN here?
end
Note: Calling the run method this way allows the user to be able to parse the output, control process flow and add custom logging.
From what I've gathered about STDIN, the snippet below is as close as I've come in understanding how to handle STDIN, there are clearly some gaps in my knowledge because I'm still unsure how to integrate this into my run method above and pass the input into the child process.
# STDIN: Constant declared in ruby
# stdin: Parameter declared in Open3.popen3
Thread.new do 
    # Read each line from the console
    STDIN.each_line do |line|
       puts "STDIN: #{line}" # print captured input 
       stdin.write line      # write input into stdin
       stdin.sync            # sync the input into the sub process
       break if line == "\n"
    end
end
Summary: I wish to understand how to handle user input from the command-line via the Open3.popen3 method so that I can allow users to enter data into various sequence of sub-commands called from my program.
 
     
    