background_process 1.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.
- data/MIT-LICENSE +20 -0
 - data/README.textile +53 -0
 - data/lib/background_process/background_process.rb +97 -0
 - data/lib/background_process/io_helpers.rb +23 -0
 - data/lib/background_process.rb +2 -0
 - data/spec/background_process/background_process_spec.rb +114 -0
 - data/spec/background_process/io_helpers_spec.rb +30 -0
 - data/spec/spec_helper.rb +1 -0
 - metadata +73 -0
 
    
        data/MIT-LICENSE
    ADDED
    
    | 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Copyright (c) 2010 Tim Harper
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
      
 4 
     | 
    
         
            +
            a copy of this software and associated documentation files (the
         
     | 
| 
      
 5 
     | 
    
         
            +
            "Software"), to deal in the Software without restriction, including
         
     | 
| 
      
 6 
     | 
    
         
            +
            without limitation the rights to use, copy, modify, merge, publish,
         
     | 
| 
      
 7 
     | 
    
         
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         
     | 
| 
      
 8 
     | 
    
         
            +
            permit persons to whom the Software is furnished to do so, subject to
         
     | 
| 
      
 9 
     | 
    
         
            +
            the following conditions:
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be
         
     | 
| 
      
 12 
     | 
    
         
            +
            included in all copies or substantial portions of the Software.
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         
     | 
| 
      
 15 
     | 
    
         
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         
     | 
| 
      
 16 
     | 
    
         
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         
     | 
| 
      
 17 
     | 
    
         
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         
     | 
| 
      
 18 
     | 
    
         
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         
     | 
| 
      
 19 
     | 
    
         
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         
     | 
| 
      
 20 
     | 
    
         
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
    
        data/README.textile
    ADDED
    
    | 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            h1. Background Process
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            This is like popen4, but provides several convenience methods for interacting 
         
     | 
| 
      
 4 
     | 
    
         
            +
            with the process. It only works on UNIX and Ruby implementations that support 
         
     | 
| 
      
 5 
     | 
    
         
            +
            fork and native UNIX I/O streams.
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            <pre>
         
     | 
| 
      
 10 
     | 
    
         
            +
            process = BackgroundProcess.run("sh -c 'sleep 1; exit 1'")
         
     | 
| 
      
 11 
     | 
    
         
            +
            process.running? # => true
         
     | 
| 
      
 12 
     | 
    
         
            +
            process.wait # => #<Process::Status: pid=34774,exited(1)>
         
     | 
| 
      
 13 
     | 
    
         
            +
            process.running? #=> true
         
     | 
| 
      
 14 
     | 
    
         
            +
            process.exitstatus # => 1
         
     | 
| 
      
 15 
     | 
    
         
            +
            </pre>
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            <pre>
         
     | 
| 
      
 18 
     | 
    
         
            +
            process = BackgroundProcess.run("sh -c 'sleep 1; exit 1'")
         
     | 
| 
      
 19 
     | 
    
         
            +
            process.kill("KILL") # => true
         
     | 
| 
      
 20 
     | 
    
         
            +
            process.running? # => false
         
     | 
| 
      
 21 
     | 
    
         
            +
            process.exitstatus # => nil
         
     | 
| 
      
 22 
     | 
    
         
            +
            </pre>
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            <pre>
         
     | 
| 
      
 25 
     | 
    
         
            +
            process = BackgroundProcess.run("sh -c '
         
     | 
| 
      
 26 
     | 
    
         
            +
              echo Service Starting
         
     | 
| 
      
 27 
     | 
    
         
            +
              sleep 2
         
     | 
| 
      
 28 
     | 
    
         
            +
              echo Service Started 1>&2
         
     | 
| 
      
 29 
     | 
    
         
            +
            '")
         
     | 
| 
      
 30 
     | 
    
         
            +
            if process.detect(:stderr, 10) { |line| line.include?("Service Started") }
         
     | 
| 
      
 31 
     | 
    
         
            +
              puts "Service was started!"
         
     | 
| 
      
 32 
     | 
    
         
            +
            else
         
     | 
| 
      
 33 
     | 
    
         
            +
              puts "Service failed to start!"
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
      
 35 
     | 
    
         
            +
            process.kill
         
     | 
| 
      
 36 
     | 
    
         
            +
            </pre>
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            h2. Common signal names
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            | Name   | id |
         
     | 
| 
      
 41 
     | 
    
         
            +
            | HUP    | 1  |
         
     | 
| 
      
 42 
     | 
    
         
            +
            | INT    | 2  |
         
     | 
| 
      
 43 
     | 
    
         
            +
            | QUIT   | 3  |
         
     | 
| 
      
 44 
     | 
    
         
            +
            | ABRT   | 6  |
         
     | 
| 
      
 45 
     | 
    
         
            +
            | KILL   | 9  |
         
     | 
| 
      
 46 
     | 
    
         
            +
            | ALRM   | 14 |
         
     | 
| 
      
 47 
     | 
    
         
            +
            | TERM   | 15 |
         
     | 
| 
      
 48 
     | 
    
         
            +
            | USR1   | 30 |
         
     | 
| 
      
 49 
     | 
    
         
            +
            | USR2   | 31 |
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
            h2. Author
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
            Tim Harper, on behalf of Lead Media Partners
         
     | 
| 
         @@ -0,0 +1,97 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class BackgroundProcess
         
     | 
| 
      
 2 
     | 
    
         
            +
              attr_reader :stdin, :stdout, :stderr, :pid
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              # Initialize a BackgroundProcess task. Don't do this.  Use BackgroundProcess.run instead
         
     | 
| 
      
 5 
     | 
    
         
            +
              def initialize(pid, stdin, stdout, stderr)
         
     | 
| 
      
 6 
     | 
    
         
            +
                @pid, @stdin, @stdout, @stderr = pid, stdin, stdout, stderr
         
     | 
| 
      
 7 
     | 
    
         
            +
                ObjectSpace.define_finalizer(self) { kill }
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            	# Run a BackgroundProcess
         
     | 
| 
      
 11 
     | 
    
         
            +
              def self.run(command, &block)
         
     | 
| 
      
 12 
     | 
    
         
            +
                command = sanitize_params(command) if command.is_a?(Array)
         
     | 
| 
      
 13 
     | 
    
         
            +
                child_stdin, parent_stdin = IO::pipe
         
     | 
| 
      
 14 
     | 
    
         
            +
                parent_stdout, child_stdout = IO::pipe
         
     | 
| 
      
 15 
     | 
    
         
            +
                parent_stderr, child_stderr = IO::pipe
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                pid = Kernel.fork do
         
     | 
| 
      
 18 
     | 
    
         
            +
                  [parent_stdin, parent_stdout, parent_stderr].each { |io| io.close }
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  STDIN.reopen(child_stdin)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  STDOUT.reopen(child_stdout)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  STDERR.reopen(child_stderr)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  [child_stdin, child_stdout, child_stderr].each { |io| io.close }
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  exec command
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                [child_stdin, child_stdout, child_stderr].each { |io| io.close }
         
     | 
| 
      
 30 
     | 
    
         
            +
                parent_stdin.sync = true
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                new(pid, parent_stdin, parent_stdout, parent_stderr)
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              # send a signal to the process. If the processes and running, do nothing.
         
     | 
| 
      
 36 
     | 
    
         
            +
              # Valid signals are those in Signal.list. Default is "TERM"
         
     | 
| 
      
 37 
     | 
    
         
            +
              def kill(signal = 'TERM')
         
     | 
| 
      
 38 
     | 
    
         
            +
                if running?
         
     | 
| 
      
 39 
     | 
    
         
            +
                  Process.kill(Signal.list[signal], @pid)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  true
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              # Sends the interrupt signal to the process. The equivalent of pressing control-C in it.
         
     | 
| 
      
 45 
     | 
    
         
            +
              def interrupt
         
     | 
| 
      
 46 
     | 
    
         
            +
                kill('INT')
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
              # asks the operating system is the process still exists.
         
     | 
| 
      
 50 
     | 
    
         
            +
              def running?
         
     | 
| 
      
 51 
     | 
    
         
            +
                return false unless @pid
         
     | 
| 
      
 52 
     | 
    
         
            +
                Process.getpgid(@pid)
         
     | 
| 
      
 53 
     | 
    
         
            +
                true
         
     | 
| 
      
 54 
     | 
    
         
            +
              rescue Errno::ESRCH
         
     | 
| 
      
 55 
     | 
    
         
            +
                false
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
              # waits for the process to finish. Freeze the process so it can rest in peace.
         
     | 
| 
      
 59 
     | 
    
         
            +
              # You should call this on every background job you create to avoid a flood of
         
     | 
| 
      
 60 
     | 
    
         
            +
              # zombie processes. (Processes won't go away until they are waited on)
         
     | 
| 
      
 61 
     | 
    
         
            +
              def wait(timeout = nil)
         
     | 
| 
      
 62 
     | 
    
         
            +
                @exit_status ||= Timeout.timeout(timeout) do
         
     | 
| 
      
 63 
     | 
    
         
            +
                  Process.wait(@pid)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  $?
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
              rescue Timeout::Error
         
     | 
| 
      
 67 
     | 
    
         
            +
                nil
         
     | 
| 
      
 68 
     | 
    
         
            +
              end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
              # Waits for the process to terminate, and then returns the exit status
         
     | 
| 
      
 71 
     | 
    
         
            +
              def exitstatus
         
     | 
| 
      
 72 
     | 
    
         
            +
                wait && wait.exitstatus
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
              # Calls block each time a line is available in the specified output buffer(s) and returns the first non-false value
         
     | 
| 
      
 76 
     | 
    
         
            +
              # By default, both stdout and stderr are monitored.
         
     | 
| 
      
 77 
     | 
    
         
            +
              #
         
     | 
| 
      
 78 
     | 
    
         
            +
              # Args:
         
     | 
| 
      
 79 
     | 
    
         
            +
              # * which: which streams to monitor. valid values are :stdout, :stderr, or :both.
         
     | 
| 
      
 80 
     | 
    
         
            +
              # * timeout: Total time in seconds to run detect for. If result not found within this time, abort and return nil. Pass nil for no timeout.
         
     | 
| 
      
 81 
     | 
    
         
            +
              # * &block: the block to call.  If block takes two arguments, it will pass both the stream that received the input (an instance of IO, not the symbol), and the line read from the buffer.
         
     | 
| 
      
 82 
     | 
    
         
            +
              def detect(which = :both, timeout = nil, &block)
         
     | 
| 
      
 83 
     | 
    
         
            +
                streams = case which
         
     | 
| 
      
 84 
     | 
    
         
            +
                          when :stdout then [stdout]
         
     | 
| 
      
 85 
     | 
    
         
            +
                          when :stderr then [stderr]
         
     | 
| 
      
 86 
     | 
    
         
            +
                          when :both   then [stdout, stderr]
         
     | 
| 
      
 87 
     | 
    
         
            +
                          else raise(ArgumentError, "invalid stream specification: #{which}")
         
     | 
| 
      
 88 
     | 
    
         
            +
                          end
         
     | 
| 
      
 89 
     | 
    
         
            +
                BackgroundProcess::IOHelpers.detect(streams, timeout, &block)
         
     | 
| 
      
 90 
     | 
    
         
            +
              end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
              protected
         
     | 
| 
      
 93 
     | 
    
         
            +
              # It's protected. What do you care? :P
         
     | 
| 
      
 94 
     | 
    
         
            +
              def self.sanitize_params(params)
         
     | 
| 
      
 95 
     | 
    
         
            +
                params.map { |p| p.gsub(' ', '\ ') }.join(" ")
         
     | 
| 
      
 96 
     | 
    
         
            +
              end
         
     | 
| 
      
 97 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module BackgroundProcess::IOHelpers
         
     | 
| 
      
 2 
     | 
    
         
            +
              extend self
         
     | 
| 
      
 3 
     | 
    
         
            +
              def detect(streams = [], timeout = nil, &block)
         
     | 
| 
      
 4 
     | 
    
         
            +
                begin
         
     | 
| 
      
 5 
     | 
    
         
            +
                  Timeout::timeout(timeout) do
         
     | 
| 
      
 6 
     | 
    
         
            +
                    # Something that should be interrupted if it takes too much time...
         
     | 
| 
      
 7 
     | 
    
         
            +
                    while true
         
     | 
| 
      
 8 
     | 
    
         
            +
                      available_streams, _, _ = Kernel.select(streams, nil, nil, 1)
         
     | 
| 
      
 9 
     | 
    
         
            +
                      available_streams.each do |s|
         
     | 
| 
      
 10 
     | 
    
         
            +
                        content = s.gets
         
     | 
| 
      
 11 
     | 
    
         
            +
                        if result = (block.arity == 1 ? yield(content) : yield(s, content))
         
     | 
| 
      
 12 
     | 
    
         
            +
                          return result
         
     | 
| 
      
 13 
     | 
    
         
            +
                        end
         
     | 
| 
      
 14 
     | 
    
         
            +
                      end if available_streams
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  true
         
     | 
| 
      
 18 
     | 
    
         
            +
                rescue Timeout::Error
         
     | 
| 
      
 19 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,114 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe BackgroundProcess do
         
     | 
| 
      
 4 
     | 
    
         
            +
              describe ".run" do
         
     | 
| 
      
 5 
     | 
    
         
            +
                it "runs a command and returns a background process with properly attached IO pipes" do
         
     | 
| 
      
 6 
     | 
    
         
            +
                  process = BackgroundProcess.run("printf 'hi'")
         
     | 
| 
      
 7 
     | 
    
         
            +
                  process.stdout.readline.should == "hi"
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  process = BackgroundProcess.run("printf 'hi' 1>&2")
         
     | 
| 
      
 10 
     | 
    
         
            +
                  process.stderr.readline.should == "hi"
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  process = BackgroundProcess.run(%(sh -c 'read hi; printf "$hi"'))
         
     | 
| 
      
 13 
     | 
    
         
            +
                  process.stdin.puts "hello, world"
         
     | 
| 
      
 14 
     | 
    
         
            +
                  process.stdin.flush
         
     | 
| 
      
 15 
     | 
    
         
            +
                  process.stdout.readline.should == "hello, world"
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                it "accepts an array, and properly handles spaces" do
         
     | 
| 
      
 19 
     | 
    
         
            +
                  process = BackgroundProcess.run(['sh', '-c', 'printf hi'])
         
     | 
| 
      
 20 
     | 
    
         
            +
                  process.stdout.readline.should == "hi"
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              describe "#running?" do
         
     | 
| 
      
 25 
     | 
    
         
            +
                it "reports when the process is running" do
         
     | 
| 
      
 26 
     | 
    
         
            +
                  process = BackgroundProcess.run("sleep 0.1")
         
     | 
| 
      
 27 
     | 
    
         
            +
                  process.should be_running
         
     | 
| 
      
 28 
     | 
    
         
            +
                  process.wait
         
     | 
| 
      
 29 
     | 
    
         
            +
                  sleep 0.1
         
     | 
| 
      
 30 
     | 
    
         
            +
                  process.should_not be_running
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              describe "#kill" do
         
     | 
| 
      
 35 
     | 
    
         
            +
                it "kills a process" do
         
     | 
| 
      
 36 
     | 
    
         
            +
                  started_at = Time.now
         
     | 
| 
      
 37 
     | 
    
         
            +
                  process = BackgroundProcess.run("sleep 4")
         
     | 
| 
      
 38 
     | 
    
         
            +
                  sleep(0.1)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  process.kill("KILL")
         
     | 
| 
      
 40 
     | 
    
         
            +
                  process.wait
         
     | 
| 
      
 41 
     | 
    
         
            +
                  (Time.now - started_at).should < 4.0
         
     | 
| 
      
 42 
     | 
    
         
            +
                  process.should_not be_running
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              describe "#exitstatus" do
         
     | 
| 
      
 47 
     | 
    
         
            +
                it "returns the exit status of a process after it exits." do
         
     | 
| 
      
 48 
     | 
    
         
            +
                  process = BackgroundProcess.run("bash -c 'sleep 1; exit 1'")
         
     | 
| 
      
 49 
     | 
    
         
            +
                  process.exitstatus.should == 1
         
     | 
| 
      
 50 
     | 
    
         
            +
                  process.exitstatus.should == 1
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              describe "#wait" do
         
     | 
| 
      
 55 
     | 
    
         
            +
                it "waits for a process with timeout" do
         
     | 
| 
      
 56 
     | 
    
         
            +
                  process = BackgroundProcess.run("sleep 3")
         
     | 
| 
      
 57 
     | 
    
         
            +
                  started_waiting = Time.now
         
     | 
| 
      
 58 
     | 
    
         
            +
                  process.wait(0.5).should be_false
         
     | 
| 
      
 59 
     | 
    
         
            +
                  (Time.now - started_waiting).should be_close(0.5, 0.1)
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
              describe "#detect" do
         
     | 
| 
      
 64 
     | 
    
         
            +
                it "calls the provided block for every line outputted, and returns the first non-false value" do
         
     | 
| 
      
 65 
     | 
    
         
            +
                  process = BackgroundProcess.run("bash -c 'a=0; while sleep 0.1; do a=$(($a + 1)); echo $a; done'")
         
     | 
| 
      
 66 
     | 
    
         
            +
                  result = process.detect do |line|
         
     | 
| 
      
 67 
     | 
    
         
            +
                    "golden" if line.strip == "3"
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
                  result.should == "golden"
         
     | 
| 
      
 70 
     | 
    
         
            +
                  process.kill
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                it "yields the stream if two parameters are provided on the block" do
         
     | 
| 
      
 74 
     | 
    
         
            +
                  process = BackgroundProcess.run("bash -c 'a=0; while sleep 0.1; do a=$(($a + 1)); echo $a 1>&2; done'")
         
     | 
| 
      
 75 
     | 
    
         
            +
                  result = process.detect(:both, 1) do |stream, line|
         
     | 
| 
      
 76 
     | 
    
         
            +
                    "golden" if stream == process.stderr && line.strip == "3"
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                  result.should == "golden"
         
     | 
| 
      
 79 
     | 
    
         
            +
                  process.kill
         
     | 
| 
      
 80 
     | 
    
         
            +
                end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                it "aborts if the provided timeout is reached" do
         
     | 
| 
      
 83 
     | 
    
         
            +
                  process = BackgroundProcess.run("sleep 2")
         
     | 
| 
      
 84 
     | 
    
         
            +
                  result = process.detect(:both, 0.1) do |stream, line|
         
     | 
| 
      
 85 
     | 
    
         
            +
                    true
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
                  result.should be_nil
         
     | 
| 
      
 88 
     | 
    
         
            +
                  process.kill
         
     | 
| 
      
 89 
     | 
    
         
            +
                end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                it "monitors the specified stream" do
         
     | 
| 
      
 92 
     | 
    
         
            +
                  process = BackgroundProcess.run("bash -c 'a=0; while sleep 0.1; do a=$(($a + 1)); echo $a; echo $a 1>&2; done'")
         
     | 
| 
      
 93 
     | 
    
         
            +
                  output = []
         
     | 
| 
      
 94 
     | 
    
         
            +
                  process.detect(:stdout) do |line|
         
     | 
| 
      
 95 
     | 
    
         
            +
                    output << line.to_i
         
     | 
| 
      
 96 
     | 
    
         
            +
                    true if line.to_i == 3
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                  process.detect(:stderr) do |line|
         
     | 
| 
      
 100 
     | 
    
         
            +
                    output << line.to_i
         
     | 
| 
      
 101 
     | 
    
         
            +
                    true if line.to_i == 3
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                  output.should == [1, 2, 3, 1, 2, 3]
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                it "never yields if nothing occurs on specified streams" do
         
     | 
| 
      
 108 
     | 
    
         
            +
                  process = BackgroundProcess.run("bash -c 'a=0; while sleep 0.1; do a=$(($a + 1)); echo $a; done'")
         
     | 
| 
      
 109 
     | 
    
         
            +
                  process.detect(:stderr, 1) do |line|
         
     | 
| 
      
 110 
     | 
    
         
            +
                    raise(Spec::Expectations::ExpectationNotMetError, "expected to not yield the block")
         
     | 
| 
      
 111 
     | 
    
         
            +
                  end
         
     | 
| 
      
 112 
     | 
    
         
            +
                end
         
     | 
| 
      
 113 
     | 
    
         
            +
              end
         
     | 
| 
      
 114 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,30 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe BackgroundProcess::IOHelpers do
         
     | 
| 
      
 4 
     | 
    
         
            +
              describe "#detect" do
         
     | 
| 
      
 5 
     | 
    
         
            +
                it "iterates over the lines from each stream as they are available, and returns when a non-false value is reached" do
         
     | 
| 
      
 6 
     | 
    
         
            +
                  process = BackgroundProcess.run("bash -c 'a=0; while sleep 0.1; do a=$(($a + 1)); echo $a; echo $a 1>&2; done'")
         
     | 
| 
      
 7 
     | 
    
         
            +
                  outputs = {:out => [], :err => []}
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  result = BackgroundProcess::IOHelpers.detect([process.stderr, process.stdout], 1) do |io, line|
         
     | 
| 
      
 10 
     | 
    
         
            +
                    outputs[io == process.stderr ? :err : :out] << line.to_i
         
     | 
| 
      
 11 
     | 
    
         
            +
                    true if outputs.values.map {|o| o.length }.uniq == [5]
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
                  outputs[:out].should == (1..5).to_a
         
     | 
| 
      
 14 
     | 
    
         
            +
                  outputs[:err].should == (1..5).to_a
         
     | 
| 
      
 15 
     | 
    
         
            +
                  result.should be_true
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                it "gives up after the timeout is reached" do
         
     | 
| 
      
 19 
     | 
    
         
            +
                  process = BackgroundProcess.run("bash -c 'sleep 2; echo done'")
         
     | 
| 
      
 20 
     | 
    
         
            +
                  started_at = Time.now
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  result = BackgroundProcess::IOHelpers.detect([process.stderr, process.stdout], 0.5) do |io, line|
         
     | 
| 
      
 23 
     | 
    
         
            +
                    true if line.strip == "done"
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  result.should be_nil
         
     | 
| 
      
 26 
     | 
    
         
            +
                  (Time.now - started_at).should < 0.6
         
     | 
| 
      
 27 
     | 
    
         
            +
                  process.kill
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/spec_helper.rb
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.dirname(__FILE__) + "/../lib/background_process"
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,73 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification 
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: background_process
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version 
         
     | 
| 
      
 4 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 5 
     | 
    
         
            +
              segments: 
         
     | 
| 
      
 6 
     | 
    
         
            +
              - 1
         
     | 
| 
      
 7 
     | 
    
         
            +
              - 0
         
     | 
| 
      
 8 
     | 
    
         
            +
              version: "1.0"
         
     | 
| 
      
 9 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 10 
     | 
    
         
            +
            authors: 
         
     | 
| 
      
 11 
     | 
    
         
            +
            - Tim Harper
         
     | 
| 
      
 12 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 13 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 14 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            date: 2010-04-21 00:00:00 -06:00
         
     | 
| 
      
 17 
     | 
    
         
            +
            default_executable: 
         
     | 
| 
      
 18 
     | 
    
         
            +
            dependencies: []
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            description: A library for spawning and interacting with UNIX processes
         
     | 
| 
      
 21 
     | 
    
         
            +
            email: 
         
     | 
| 
      
 22 
     | 
    
         
            +
            - timcharper+bp@gmail.com
         
     | 
| 
      
 23 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            extra_rdoc_files: 
         
     | 
| 
      
 28 
     | 
    
         
            +
            - MIT-LICENSE
         
     | 
| 
      
 29 
     | 
    
         
            +
            - README.textile
         
     | 
| 
      
 30 
     | 
    
         
            +
            files: 
         
     | 
| 
      
 31 
     | 
    
         
            +
            - README.textile
         
     | 
| 
      
 32 
     | 
    
         
            +
            - MIT-LICENSE
         
     | 
| 
      
 33 
     | 
    
         
            +
            - lib/background_process/background_process.rb
         
     | 
| 
      
 34 
     | 
    
         
            +
            - lib/background_process/io_helpers.rb
         
     | 
| 
      
 35 
     | 
    
         
            +
            - lib/background_process.rb
         
     | 
| 
      
 36 
     | 
    
         
            +
            - spec/background_process/background_process_spec.rb
         
     | 
| 
      
 37 
     | 
    
         
            +
            - spec/background_process/io_helpers_spec.rb
         
     | 
| 
      
 38 
     | 
    
         
            +
            - spec/spec_helper.rb
         
     | 
| 
      
 39 
     | 
    
         
            +
            has_rdoc: true
         
     | 
| 
      
 40 
     | 
    
         
            +
            homepage: http://github.com/timcharper/background_process
         
     | 
| 
      
 41 
     | 
    
         
            +
            licenses: []
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 44 
     | 
    
         
            +
            rdoc_options: 
         
     | 
| 
      
 45 
     | 
    
         
            +
            - --main
         
     | 
| 
      
 46 
     | 
    
         
            +
            - README.textile
         
     | 
| 
      
 47 
     | 
    
         
            +
            require_paths: 
         
     | 
| 
      
 48 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 49 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 50 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 51 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 52 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 53 
     | 
    
         
            +
                  segments: 
         
     | 
| 
      
 54 
     | 
    
         
            +
                  - 0
         
     | 
| 
      
 55 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 56 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 57 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 58 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 59 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 60 
     | 
    
         
            +
                  segments: 
         
     | 
| 
      
 61 
     | 
    
         
            +
                  - 0
         
     | 
| 
      
 62 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 63 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            rubyforge_project: background_process
         
     | 
| 
      
 66 
     | 
    
         
            +
            rubygems_version: 1.3.6
         
     | 
| 
      
 67 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 68 
     | 
    
         
            +
            specification_version: 3
         
     | 
| 
      
 69 
     | 
    
         
            +
            summary: background_process
         
     | 
| 
      
 70 
     | 
    
         
            +
            test_files: 
         
     | 
| 
      
 71 
     | 
    
         
            +
            - spec/background_process/background_process_spec.rb
         
     | 
| 
      
 72 
     | 
    
         
            +
            - spec/background_process/io_helpers_spec.rb
         
     | 
| 
      
 73 
     | 
    
         
            +
            - spec/spec_helper.rb
         
     |