lib/dolzenko/shell_out.rb in dolzenko-0.0.13 vs lib/dolzenko/shell_out.rb in dolzenko-0.0.14

- old
+ new

@@ -93,13 +93,32 @@ exitstatus end end def command(*args) - (args.last.is_a?(Hash) ? args[0..-2] : args).join(" ") + stripped_command = args.dup + stripped_command.pop if stripped_command[-1].is_a?(Hash) # remove options + stripped_command.shift if stripped_command[0].is_a?(Hash) # remove env + stripped_command.join(" ") end + def with_env(*args) + yield unless (env = args[0]).is_a?(Hash) + stored_env = {} + for name, value in env + stored_env[name] = ENV[name] + value == nil ? ENV.delete(name) : ENV[name] = value + end + begin + yield + ensure + for name, value in stored_env + ENV[name] = value + end + end + end + def getopt(opt, default, *args) if args.last.is_a?(Hash) if opt == :out && args.last[:out] == :return StringIO.new else @@ -113,60 +132,66 @@ module_function def shell_out_with_pty(*args) old_state = `stty -g` + return SUCCESS_EXIT_STATUS unless ShellOut::before(*args) - # stolen from ruby/ext/pty/script.rb - # disable echoing and enable raw (not having to press enter) - system "stty -echo raw lnext ^_" + begin + # stolen from ruby/ext/pty/script.rb + # disable echoing and enable raw (not having to press enter) + system "stty -echo raw lnext ^_" - in_stream = ShellOut.getopt(:in, STDIN, *args) - out_stream = ShellOut.getopt(:out, STDOUT, *args) + in_stream = ShellOut.getopt(:in, STDIN, *args) + out_stream = ShellOut.getopt(:out, STDOUT, *args) + writer = nil + ShellOut.with_env(*args) do + PTY.spawn(ShellOut.command(*args)) do |r_pty, w_pty, pid| + reader = Thread.current + writer = Thread.new do + while true + break if (ch = in_stream.getc).nil? + ch = ch.chr + if ch == ShellOut::CTRL_C_CODE + reader.raise Interrupt, "Interrupted by user" + else + w_pty.print ch + w_pty.flush + end + end + end + writer.abort_on_exception = true - PTY.spawn(ShellOut.command(*args)) do |r_pty, w_pty, pid| - reader = Thread.current - writer = Thread.new do - while true - break if (ch = in_stream.getc).nil? - ch = ch.chr - if ch == ShellOut::CTRL_C_CODE - reader.raise Interrupt, "Interrupted by user" - else - w_pty.print ch - w_pty.flush + loop do + c = begin + r_pty.sysread(512) + rescue Errno::EIO, EOFError + nil + end + break if c.nil? + + out_stream.print c + out_stream.flush end - end - end - writer.abort_on_exception = true - loop do - c = begin - r_pty.sysread(512) - rescue Errno::EIO, EOFError - nil + begin + # try to invoke waitpid() before the signal handler does it + return ShellOut::after(Process::waitpid2(pid)[1].exitstatus, out_stream, *args) + rescue Errno::ECHILD + # the signal handler managed to call waitpid() first; + # PTY::ChildExited will be delivered pretty soon, so just wait for it + sleep 1 + end end - break if c.nil? - - out_stream.print c - out_stream.flush end - - begin - # try to invoke waitpid() before the signal handler does it - return ShellOut::after(Process::waitpid2(pid)[1].exitstatus, out_stream, *args) - rescue Errno::ECHILD - # the signal handler managed to call waitpid() first; - # PTY::ChildExited will be delivered pretty soon, so just wait for it - sleep 1 - end + rescue PTY::ChildExited => e + return ShellOut::after(e.status.exitstatus, out_stream, *args) + ensure + writer && writer.kill + system "stty #{ old_state }" end - rescue PTY::ChildExited => e - return ShellOut::after(e.status.exitstatus, out_stream, *args) - ensure - system "stty #{ old_state }" end def shell_out_with_system(*args) return SUCCESS_EXIT_STATUS unless ShellOut::before(*args) @@ -247,9 +272,15 @@ fake_stdin.rewind STDOUT.supress do # TODO still mystery why "testing\n" gets written to the output ShellOut("ruby -e 'exit(123) if gets.chomp == \"testing\"'", :in => fake_stdin).should == 123 end + end + + it "alters command environment when first argument is a Hash" do + ShellOut({ "ENV_VAR" => "42" }, + "ruby -e 'puts ENV.inspect'", + :out => :return).should include('"ENV_VAR"=>"42"') end describe ":raise_exceptions option" do it "raises exception for non-zero exit codes" do lambda do \ No newline at end of file