Piloting processes through standard I/O in Ruby Jan 25 th  2012 Muriel Salvan Open Source Lead developer and architect X-Aeon Solutions http://x-aeon.com
The needs Run external  processes from Ruby Access their  stdin/out/err in real time  (follow their execution tightly) Work on both Unix and  Windows Work also with external  Ruby programs
Solutions tested IO::popen
Kernel::open
systemu
PTY::spawn
ChildProcess
Open3::popen3
ProcessPilot
Aruba  (used with  Cucumber )
External test program This is Line 1, before sleep 1
This is Line 2, after sleep 1
Enter string 1:  My String 1
String 1 entered: My String 1
This is Line 1 on STDERR, before sleep 1
This is Line 2 on STDERR, after sleep 1
Enter string 2 from STDERR:   My String 2
String 2 entered: My String 2
This is Line 3, before sleep 1
This is Line 4, after sleep 1 Output : Output on STDOUT only Output : Output on STDERR only Output : Output for interactive tests only Input : Input taken from STDIN sleep 1 sleep 1 sleep 1 Input from stdin Input from stdin
IO::popen IO . popen ( 'ruby test.rb' ,  'r+' )   do   | io | io . write ( "My String 1 \n My String 2 \n " ) io . close_write puts   "stdout: #{io.read}” end
IO::popen stdin must be closed before accessing stdout =>  No real time
stderr  support only for  Ruby > 1.9

Piloting processes through std IO at the Ruby Drink-up of Sophia, January 2012

  • 1.
    Piloting processes throughstandard I/O in Ruby Jan 25 th 2012 Muriel Salvan Open Source Lead developer and architect X-Aeon Solutions http://x-aeon.com
  • 2.
    The needs Runexternal processes from Ruby Access their stdin/out/err in real time (follow their execution tightly) Work on both Unix and Windows Work also with external Ruby programs
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
    Aruba (usedwith Cucumber )
  • 11.
    External test programThis is Line 1, before sleep 1
  • 12.
    This is Line2, after sleep 1
  • 13.
    Enter string 1: My String 1
  • 14.
    String 1 entered:My String 1
  • 15.
    This is Line1 on STDERR, before sleep 1
  • 16.
    This is Line2 on STDERR, after sleep 1
  • 17.
    Enter string 2from STDERR: My String 2
  • 18.
    String 2 entered:My String 2
  • 19.
    This is Line3, before sleep 1
  • 20.
    This is Line4, after sleep 1 Output : Output on STDOUT only Output : Output on STDERR only Output : Output for interactive tests only Input : Input taken from STDIN sleep 1 sleep 1 sleep 1 Input from stdin Input from stdin
  • 21.
    IO::popen IO .popen ( 'ruby test.rb' , 'r+' ) do | io | io . write ( "My String 1 \n My String 2 \n " ) io . close_write puts "stdout: #{io.read}” end
  • 22.
    IO::popen stdin mustbe closed before accessing stdout => No real time
  • 23.
    stderr supportonly for Ruby > 1.9
  • 24.
    stderr supportdoes not work on Windows
  • 25.
    Does not workwith Ruby programs that don't explicitly turn off stdout internal cache
  • 26.
    Kernel::open Kernel ::open ( '|ruby test.rb', 'r+' ) do | io | puts io . gets # stdout Line 1 puts io . gets # stdout Line 2 puts io . read ( 31 ) # stdout Prompt sleep 1 io . write ( "My String 1 \n " ) puts io . gets # stdout String 1 sleep 1 io . write ( "My String 2 \n " ) puts io . gets # stdout Line 3 puts io . gets # stdout Line 4 end
  • 27.
    Kernel::open stdin and stdout can be written and read in real time No support for stderr
  • 28.
    Does not workwith Ruby programs that don't explicitly turn off stdout internal cache
  • 29.
    systemu require 'systemu' status, stdout, stderr = systemu ( 'ruby test.rb' , 0 => "My String 1 \n My String 2 \n " ) puts "stdout: #{stdout}" puts "stderr: #{stderr}"
  • 30.
    systemu Handle stderr Does not work on Windows
  • 31.
    stdin must begiven to the process at startup time => No real time
  • 32.
    stdout/err can onlybe accessed when process has exited => No real time
  • 33.
    PTY::spawn require 'pty' PTY ::spawn ( 'ruby test.rb' ) do | stdout, stdin, pid | puts stdout. gets # stdout Line 1 puts stdout. gets # stdout Line 2 puts stdout. read ( 31 ) # stdout Prompt sleep 1 stdin. write ( "My String 1 \n " ) puts stdout. gets # Extra for input echoed puts stdout. gets # stdout String 1 puts stdout. gets # Should be on stderr puts stdout. gets # Should be on stderr puts stdout. read ( 43 ) # Should be on stderr sleep 1 stdin. write ( "My String 2 \n " ) puts stdout. gets # Extra for input echoed puts stdout. gets # Should be on stderr puts stdout. gets # stdout Line 3 puts stdout. gets # stdout Line 4 end
  • 34.
    PTY::spawn stdin and stdout can be written and read in real time Does not exist on Windows
  • 35.
    Mix stdout andstderr in the same IO => No stderr support
  • 36.
    Echoes all inputfrom stdin to stdout
  • 37.
    ChildProcess require 'childprocess' require 'tempfile' lProcess = ChildProcess. build ( 'ruby' , 'test.rb' ) lProcess. duplex = true Tempfile . open ( 'stdout' ) do | oStdOUT | lProcess. io . stdout = oStdOUT Tempfile . open ( 'stderr' ) do | oStdERR | lProcess. io . stderr = oStdERR lProcess. start stdout = File . open ( oStdOUT. path , 'r' ) stderr = File . open ( oStdERR. path , 'r' ) stdin = lProcess. io . stdin waitfor ( stdout, 'This is Line 2' ) # Need active wait sleep 1 stdin. write ( "My String 1 \n " ) waitfor ( stderr, 'This is Line 2' ) # Need active wait sleep 1 stdin. write ( "My String 2 \n " ) waitfor ( stdout, 'This is Line 4' ) # Need active wait end end
  • 38.
    ChildProcess All stdin/out/err can be written/read in real time Need stdout/err redirections to files on Windows
  • 39.
    Need activeblocking read on stdout and stderr to wait for next line
  • 40.
    Very unstableon native Windows
  • 41.
    Does not workwith Ruby programs that don't explicitly turn off stdout internal cache
  • 42.
    Open3::popen3 require 'open3' Open3 ::popen3 ( 'ruby test.rb' ) do | stdin, stdout, stderr, wait_thr | puts stdout. gets # stdout Line 1 puts stdout. gets # stdout Line 2 puts stdout. read ( 31 ) # stdout Prompt sleep 1 stdin. write ( "My String 1 \n " ) puts stdout. gets # stdout String 1 puts stderr. gets # stderr Line 1 puts stderr. gets # stderr Line 2 puts stderr. read ( 43 ) # stderr Prompt sleep 1 stdin. write ( "My String 2 \n " ) puts stderr. gets # stderr String 2 puts stdout. gets # stdout Line 3 puts stdout. gets # stdout Line 4 end
  • 43.
    Open3::popen3 All stdin/out/err can be written/read in real time
  • 44.
    Works on any OS
  • 45.
    No need forfiles redirection Does not work with Ruby programs that don't explicitly turn off stdout internal cache
  • 46.
    ProcessPilot require 'processpilot/processpilot' ProcessPilot ::pilot ( 'ruby' , 'test.rb' ) do | stdin, stdout, stderr, childproc | puts stdout. gets # stdout Line 1 puts stdout. gets # stdout Line 2 puts stdout. read ( 31 ) # stdout Prompt sleep 1 stdin. write ( "My String 1 \n " ) puts stdout. gets # stdout String 1 puts stderr. gets # stderr Line 1 puts stderr. gets # stderr Line 2 puts stderr. read ( 43 ) # stderr Prompt sleep 1 stdin. write ( "My String 2 \n " ) puts stderr. gets # stderr String 2 puts stdout. gets # stdout Line 3 puts stdout. gets # stdout Line 4 end
  • 47.
    ProcessPilot All stdin/out/err can be written/read in real time
  • 48.
    Works on any OS
  • 49.
    Works with all Ruby programs , even if they don't explicitly turn off stdout internal cache
  • 50.
  • 51.
    ProcessPilot Time outs:stdout. gets ( :TimeOutSecs => 5 ) Deactivate Ruby cache: ProcessPilot ::pilot ( 'test.rb', :ForceRubyProcessSync => true ) do | stdin, stdout, stderr, childproc | # … end
  • 52.
    Aruba When I run `ruby test.rb` interactively And I type "My String 1" And I type "My String 2" Then the stdout should contain : """ This is Line 1, before sleep 1 This is Line 2, after sleep 1 Enter string 1: String 1 entered: My String 1 This is Line 3, before sleep 1 This is Line 4, after sleep 1 """ And the stderr should contain : " "" This is Line 1 on STDERR, before sleep 1 This is Line 2 on STDERR, after sleep 1 Enter string 2 from STDERR: String 2 entered: My String 2 """
  • 53.
    Aruba Handle stderr correctly
  • 54.
    Works on any OS
  • 55.
    Works with all Ruby programs , even if they don't explicitly turn off stdout internal cache All stdin has to be entered at the beginning of the scenario: No real time
  • 56.
    No easy wayto test for lines sequence between stdout and stderr flows
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
    Aruba (usedwith Cucumber ) This presentation is available under CC-BY license by Muriel Salvan
  • 65.