class Session::AbstractSession
Attributes
debug[RW]
track_history[RW]
use_open3[RW]
use_spawn[RW]
debug[RW]
debug?[RW]
e[R]
errproc[RW]
history[R]
i[R]
o[R]
opts[R]
attributes
outproc[RW]
prog[R]
stderr[R]
stdin[R]
stdout[R]
threads[R]
track_history[R]
use_open3[R]
use_spawn[R]
Public Class Methods
default_prog()
click to toggle source
# File lib/session.rb, line 87 def default_prog return @default_prog if defined? @default_prog and @default_prog if defined? self::DEFAULT_PROG return @default_prog = self::DEFAULT_PROG else @default_prog = ENV["SESSION_#{ self }_PROG"] end nil end
default_prog=(prog)
click to toggle source
# File lib/session.rb, line 96 def default_prog= prog @default_prog = prog end
init()
click to toggle source
# File lib/session.rb, line 103 def init @track_history = nil @use_spawn = nil @use_open3 = nil @debug = nil end
new(*args) { |self| ... }
click to toggle source
instance methods
# File lib/session.rb, line 135 def initialize(*args) @opts = hashify(*args) @prog = getopt('prog', opts, getopt('program', opts, self.class::default_prog)) raise(ArgumentError, "no program specified") unless @prog @track_history = nil @track_history = Session::track_history unless Session::track_history.nil? @track_history = self.class::track_history unless self.class::track_history.nil? @track_history = getopt('history', opts) if hasopt('history', opts) @track_history = getopt('track_history', opts) if hasopt('track_history', opts) @use_spawn = nil @use_spawn = Session::use_spawn unless Session::use_spawn.nil? @use_spawn = self.class::use_spawn unless self.class::use_spawn.nil? @use_spawn = getopt('use_spawn', opts) if hasopt('use_spawn', opts) if defined? JRUBY_VERSION @use_open3 = true else @use_open3 = nil @use_open3 = Session::use_open3 unless Session::use_open3.nil? @use_open3 = self.class::use_open3 unless self.class::use_open3.nil? @use_open3 = getopt('use_open3', opts) if hasopt('use_open3', opts) end @debug = nil @debug = Session::debug unless Session::debug.nil? @debug = self.class::debug unless self.class::debug.nil? @debug = getopt('debug', opts) if hasopt('debug', opts) @history = nil @history = History::new if @track_history @outproc = nil @errproc = nil @stdin, @stdout, @stderr = if @use_spawn Spawn::spawn @prog elsif @use_open3 Open3::popen3 @prog else __popen3 @prog end @threads = [] clear if block_given? ret = nil begin ret = yield self ensure self.close! end return ret end return self end
Public Instance Methods
__fork(*a, &b)
click to toggle source
# File lib/session.rb, line 255 def __fork(*a, &b) verbose = $VERBOSE begin $VERBOSE = nil Kernel::fork(*a, &b) ensure $VERBOSE = verbose end end
__popen3(*cmd) { || ... }
click to toggle source
# File lib/session.rb, line 216 def __popen3(*cmd) pw = IO::pipe # pipe[0] for read, pipe[1] for write pr = IO::pipe pe = IO::pipe pid = __fork{ # child pw[1].close STDIN.reopen(pw[0]) pw[0].close pr[0].close STDOUT.reopen(pr[1]) pr[1].close pe[0].close STDERR.reopen(pe[1]) pe[1].close exec(*cmd) } Process::detach pid # avoid zombies pw[0].close pr[1].close pe[1].close pi = [pw[1], pr[0], pe[0]] pw[1].sync = true if defined? yield begin return yield(*pi) ensure pi.each{|p| p.close unless p.closed?} end end pi end
clear()
click to toggle source
abstract methods
# File lib/session.rb, line 266 def clear raise NotImplementedError end
Also aliased as: flush
close!()
click to toggle source
# File lib/session.rb, line 290 def close! [stdin, stdout, stderr].each{|pipe| pipe.close} stdin, stdout, stderr = nil, nil, nil true end
Also aliased as: close
execute(command, redirects = {}) { |nil, buf| ... }
click to toggle source
# File lib/session.rb, line 300 def execute(command, redirects = {}) $session_command = command if @debug raise(PipeError, command) unless ready? # clear buffers clear # setup redirects rerr = redirects[:e] || redirects[:err] || redirects[:stderr] || redirects['stderr'] || redirects['e'] || redirects['err'] || redirects[2] || redirects['2'] rout = redirects[:o] || redirects[:out] || redirects[:stdout] || redirects['stdout'] || redirects['o'] || redirects['out'] || redirects[1] || redirects['1'] # create cmd object and add to history cmd = Command::new command.to_s # store cmd if tracking history history << cmd if track_history # mutex for accessing shared data mutex = Mutex::new # io data for stderr and stdout err = { :io => stderr, :cmd => cmd.err, :name => 'stderr', :begin => false, :end => false, :begin_pat => cmd.begin_err_pat, :end_pat => cmd.end_err_pat, :redirect => rerr, :proc => errproc, :yield => lambda{|buf| yield(nil, buf)}, :mutex => mutex, } out = { :io => stdout, :cmd => cmd.out, :name => 'stdout', :begin => false, :end => false, :begin_pat => cmd.begin_out_pat, :end_pat => cmd.end_out_pat, :redirect => rout, :proc => outproc, :yield => lambda{|buf| yield(buf, nil)}, :mutex => mutex, } begin # send command in the background so we can begin processing output # immediately - thanks to tanaka akira for this suggestion threads << Thread::new { send_command cmd } # init main = Thread::current exceptions = [] # fire off reader threads [err, out].each do |iodat| threads << Thread::new(iodat, main) do |iodat, main| loop do main.raise(PipeError, command) unless ready? main.raise ExecutionError, iodat[:name] if iodat[:end] and not iodat[:begin] break if iodat[:end] or iodat[:io].eof? line = iodat[:io].gets buf = nil case line when iodat[:end_pat] iodat[:end] = true # handle the special case of non-newline terminated output if((m = %r/(.+)__CMD/o.match(line)) and (pre = m[1])) buf = pre end when iodat[:begin_pat] iodat[:begin] = true else next unless iodat[:begin] and not iodat[:end] # ignore chaff buf = line end if buf iodat[:mutex].synchronize do iodat[:cmd] << buf iodat[:redirect] << buf if iodat[:redirect] iodat[:proc].call buf if iodat[:proc] iodat[:yield].call buf if block_given? end end end true end end ensure # reap all threads - accumulating and rethrowing any exceptions begin while((t = threads.shift)) t.join raise ExecutionError, 'iodat thread failure' unless t.value end rescue => e exceptions << e retry unless threads.empty? ensure unless exceptions.empty? meta_message = '<' << exceptions.map{|e| "#{ e.message } - (#{ e.class })"}.join('|') << '>' meta_backtrace = exceptions.map{|e| e.backtrace}.flatten raise ExecutionError, meta_message, meta_backtrace end end end # this should only happen if eof was reached before end pat [err, out].each do |iodat| raise ExecutionError, iodat[:name] unless iodat[:begin] and iodat[:end] end # get the exit status get_status if respond_to? :get_status out = err = iodat = nil return [cmd.out, cmd.err] end
getopt(opt, hash, default = nil)
click to toggle source
# File lib/session.rb, line 198 def getopt opt, hash, default = nil key = opt return hash[key] if hash.has_key? key key = "#{ key }" return hash[key] if hash.has_key? key key = key.intern return hash[key] if hash.has_key? key return default end
hasopt(opt, hash)
click to toggle source
# File lib/session.rb, line 207 def hasopt opt, hash key = opt return key if hash.has_key? key key = "#{ key }" return key if hash.has_key? key key = key.intern return key if hash.has_key? key return false end
path()
click to toggle source
# File lib/session.rb, line 270 def path raise NotImplementedError end
path=()
click to toggle source
# File lib/session.rb, line 273 def path= raise NotImplementedError end
ready?()
click to toggle source
# File lib/session.rb, line 285 def ready? (stdin and stdout and stderr) and (IO === stdin and IO === stdout and IO === stderr) and (not (stdin.closed? or stdout.closed? or stderr.closed?)) end
send_command(cmd)
click to toggle source
# File lib/session.rb, line 276 def send_command cmd raise NotImplementedError end
track_history=(bool)
click to toggle source
concrete methods
# File lib/session.rb, line 281 def track_history= bool @history ||= History::new @track_history = bool end
Private Instance Methods
hashify(*a)
click to toggle source
# File lib/session.rb, line 296 def hashify(*a) a.inject({}){|o,h| o.update(h)} end