Class: Amberletters::Process
- Inherits:
-
Object
- Object
- Amberletters::Process
- Extended by:
- Forwardable
- Defined in:
- lib/amberletters.rb
Constant Summary collapse
- END_MARKER =
'__Amberletters_PROCESS_ENDED__'
- DEFAULT_LOG_LEVEL =
::Logger::WARN
- DEFAULT_TIMEOUT =
1.0
- RUBY_EXT =
Shamelessly stolen from Rake
((RbConfig::CONFIG['ruby_install_name'] =~ /\.(com|cmd|exe|bat|rb|sh)$/) ? "" : RbConfig::CONFIG['EXEEXT'])
- RUBY =
File.join( RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'] + RUBY_EXT). sub(/.*\s.*/m, '"\&"')
Instance Attribute Summary collapse
-
#blocker ⇒ Object
The Trigger currently being waited for, if any.
-
#command ⇒ Object
readonly
Command to run in a subshell.
-
#cwd ⇒ Object
readonly
Working directory for the command.
-
#input ⇒ Object
readonly
Returns the value of attribute input.
-
#input_buffer ⇒ Object
readonly
Input waiting to be written to process.
-
#output ⇒ Object
readonly
Returns the value of attribute output.
-
#output_buffer ⇒ Object
readonly
Output ready to be read from process.
-
#pid ⇒ Object
readonly
Returns the value of attribute pid.
-
#status ⇒ Object
readonly
:not_started -> :running -> :ended -> :exited.
-
#timeout ⇒ Object
Returns the value of attribute timeout.
Instance Method Summary collapse
- #add_blocking_trigger(event, *args, &block) ⇒ Object
- #add_nonblocking_trigger(event, *args, &block) ⇒ Object
- #add_trigger(event, *args, &block) ⇒ Object
- #alive? ⇒ Boolean
- #blocked? ⇒ Boolean
-
#ended? ⇒ Boolean
Have we seen the end marker yet?.
- #exited? ⇒ Boolean
- #flush_output_buffer! ⇒ Object
-
#initialize(*args) ⇒ Process
constructor
A new instance of Process.
- #kill!(signal = "TERM") ⇒ Object
- #not_started? ⇒ Boolean
- #on(event, *args, &block) ⇒ Object
- #prepend_trigger(event, *args, &block) ⇒ Object
- #remove_trigger(t) ⇒ Object
- #running? ⇒ Boolean
- #start! ⇒ Object
- #time ⇒ Object
- #to_s ⇒ Object
- #wait_for(event, *args, &block) ⇒ Object
Constructor Details
#initialize(*args) ⇒ Process
Returns a new instance of Process.
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/amberletters.rb', line 246 def initialize(*args) = if args.last.is_a?(Hash) then args.pop else {} end @command = args @triggers = [] @blocker = nil @input_buffer = StringIO.new @output_buffer = StringScanner.new("") @env = .fetch(:env) {{}} @cwd = .fetch(:cwd) {Dir.pwd} @logger = .fetch(:logger) { l = ::Logger.new($stdout) l.level = DEFAULT_LOG_LEVEL l } @state = :not_started @shell = .fetch(:shell) { '/bin/sh' } @transcript = .fetch(:transcript) { t = Object.new def t.<<(*) # NOOP end t } @history = TranscriptHistoryBuffer.new(@transcript) @timeout = .fetch(:timeout) { DEFAULT_TIMEOUT } ObjectSpace.define_finalizer(self) do kill! end end |
Instance Attribute Details
#blocker ⇒ Object
The Trigger currently being waited for, if any
232 233 234 |
# File 'lib/amberletters.rb', line 232 def blocker @blocker end |
#command ⇒ Object (readonly)
Command to run in a subshell
231 232 233 |
# File 'lib/amberletters.rb', line 231 def command @command end |
#cwd ⇒ Object (readonly)
Working directory for the command
236 237 238 |
# File 'lib/amberletters.rb', line 236 def cwd @cwd end |
#input ⇒ Object (readonly)
Returns the value of attribute input.
237 238 239 |
# File 'lib/amberletters.rb', line 237 def input @input end |
#input_buffer ⇒ Object (readonly)
Input waiting to be written to process
233 234 235 |
# File 'lib/amberletters.rb', line 233 def input_buffer @input_buffer end |
#output ⇒ Object (readonly)
Returns the value of attribute output.
238 239 240 |
# File 'lib/amberletters.rb', line 238 def output @output end |
#output_buffer ⇒ Object (readonly)
Output ready to be read from process
234 235 236 |
# File 'lib/amberletters.rb', line 234 def output_buffer @output_buffer end |
#pid ⇒ Object (readonly)
Returns the value of attribute pid.
239 240 241 |
# File 'lib/amberletters.rb', line 239 def pid @pid end |
#status ⇒ Object (readonly)
:not_started -> :running -> :ended -> :exited
235 236 237 |
# File 'lib/amberletters.rb', line 235 def status @status end |
#timeout ⇒ Object
Returns the value of attribute timeout.
240 241 242 |
# File 'lib/amberletters.rb', line 240 def timeout @timeout end |
Instance Method Details
#add_blocking_trigger(event, *args, &block) ⇒ Object
319 320 321 322 323 324 325 326 |
# File 'lib/amberletters.rb', line 319 def add_blocking_trigger(event, *args, &block) t = add_trigger(event, *args, &block) t.time_to_live = 1 @logger.debug "waiting for #{t}" self.blocker = t catchup_trigger!(t) t end |
#add_nonblocking_trigger(event, *args, &block) ⇒ Object
292 293 294 295 296 |
# File 'lib/amberletters.rb', line 292 def add_nonblocking_trigger(event, *args, &block) t = add_trigger(event, *args, &block) catchup_trigger!(t) t end |
#add_trigger(event, *args, &block) ⇒ Object
298 299 300 301 302 303 |
# File 'lib/amberletters.rb', line 298 def add_trigger(event, *args, &block) t = build_trigger(event, *args, &block) triggers << t @logger.debug "added trigger on #{t}" t end |
#alive? ⇒ Boolean
357 358 359 360 361 362 |
# File 'lib/amberletters.rb', line 357 def alive? ::Process.kill(0, @pid) true rescue Errno::ESRCH, Errno::ENOENT false end |
#blocked? ⇒ Boolean
364 365 366 |
# File 'lib/amberletters.rb', line 364 def blocked? @blocker end |
#ended? ⇒ Boolean
Have we seen the end marker yet?
381 382 383 |
# File 'lib/amberletters.rb', line 381 def ended? @state == :ended end |
#exited? ⇒ Boolean
376 377 378 |
# File 'lib/amberletters.rb', line 376 def exited? @state == :exited end |
#flush_output_buffer! ⇒ Object
345 346 347 348 |
# File 'lib/amberletters.rb', line 345 def flush_output_buffer! @logger.debug "flushing output buffer" @output_buffer.terminate end |
#kill!(signal = "TERM") ⇒ Object
350 351 352 353 354 355 |
# File 'lib/amberletters.rb', line 350 def kill!(signal="TERM") handle_child_exit do @logger.info "Killing process #{@pid}" ::Process.kill(signal, @pid) end end |
#not_started? ⇒ Boolean
372 373 374 |
# File 'lib/amberletters.rb', line 372 def not_started? @state == :not_started end |
#on(event, *args, &block) ⇒ Object
277 278 279 |
# File 'lib/amberletters.rb', line 277 def on(event, *args, &block) add_nonblocking_trigger(event, *args, &block) end |
#prepend_trigger(event, *args, &block) ⇒ Object
311 312 313 314 315 316 |
# File 'lib/amberletters.rb', line 311 def prepend_trigger(event, *args, &block) t = build_trigger(event, *args, &block) triggers.unshift(t) @logger.debug "prepended trigger on #{t}" t end |
#remove_trigger(t) ⇒ Object
305 306 307 308 309 |
# File 'lib/amberletters.rb', line 305 def remove_trigger(t) triggers.delete(t) @logger.debug "removed trigger on #{t}" t end |
#running? ⇒ Boolean
368 369 370 |
# File 'lib/amberletters.rb', line 368 def running? @state == :running end |
#start! ⇒ Object
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
# File 'lib/amberletters.rb', line 328 def start! raise StateError, "Already started!" unless not_started? @logger.debug "installing end marker handler for #{END_MARKER}" prepend_trigger(:output, /#{END_MARKER}/, :exclusive => false, :time_to_live => 1) do |process, data| handle_end_marker end handle_child_exit do cmd = wrapped_command @logger.debug "executing #{cmd.join(' ')}" merge_environment(@env) do @output, @input, @pid = PTY.spawn(*cmd) end @state = :running @logger.debug "spawned pid #{@pid}; in: #{@input.inspect}; out: #{@output.inspect}" end end |
#time ⇒ Object
385 386 387 |
# File 'lib/amberletters.rb', line 385 def time Time.now end |
#to_s ⇒ Object
389 390 391 |
# File 'lib/amberletters.rb', line 389 def to_s "Process<pid: #{pid}; in: #{input.inspect}; out: #{output.inspect}>" end |
#wait_for(event, *args, &block) ⇒ Object
281 282 283 284 285 286 287 288 289 290 |
# File 'lib/amberletters.rb', line 281 def wait_for(event, *args, &block) raise "Already waiting for #{blocker}" if blocker t = add_blocking_trigger(event, *args, &block) @logger.debug "Entering wait cycle for #{event}" process_events rescue unblock! triggers.delete(t) raise end |