1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
#--
#
# Author:: Francis Cianfrocca (gmail: blackhedd)
# Homepage:: http://rubyeventmachine.com
# Date:: 13 Dec 07
#
# See EventMachine and EventMachine::Connection for documentation and
# usage examples.
#
#----------------------------------------------------------------------------
#
# Copyright (C) 2006-08 by Francis Cianfrocca. All Rights Reserved.
# Gmail: blackhedd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of either: 1) the GNU General Public License
# as published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version; or 2) Ruby's License.
#
# See the file COPYING for complete licensing information.
#
#---------------------------------------------------------------------------
#
#
module EventMachine
# EM::DeferrableChildProcess is a sugaring of a common use-case
# involving EM::popen.
# Call the #open method on EM::DeferrableChildProcess, passing
# a command-string. #open immediately returns an EM::Deferrable
# object. It also schedules the forking of a child process, which
# will execute the command passed to #open.
# When the forked child terminates, the Deferrable will be signalled
# and execute its callbacks, passing the data that the child process
# wrote to stdout.
#
class DeferrableChildProcess < EventMachine::Connection
include EventMachine::Deferrable
# @private
def initialize
super
@data = []
end
# Sugars a common use-case involving forked child processes.
# #open takes a String argument containing an shell command
# string (including arguments if desired). #open immediately
# returns an EventMachine::Deferrable object, without blocking.
#
# It also invokes EventMachine#popen to run the passed-in
# command in a forked child process.
#
# When the forked child terminates, the Deferrable that
# #open calls its callbacks, passing the data returned
# from the child process.
#
def self.open cmd
EventMachine.popen( cmd, DeferrableChildProcess )
end
# @private
def receive_data data
@data << data
end
# @private
def unbind
succeed( @data.join )
end
end
# @private
class SystemCmd < EventMachine::Connection
def initialize cb
@cb = cb
@output = []
end
def receive_data data
@output << data
end
def unbind
@cb.call @output.join(''), get_status if @cb
end
end
# EM::system is a simple wrapper for EM::popen. It is similar to Kernel::system, but requires a
# single string argument for the command and performs no shell expansion.
#
# The block or proc passed to EM::system is called with two arguments: the output generated by the command,
# and a Process::Status that contains information about the command's execution.
#
# EM.run{
# EM.system('ls'){ |output,status| puts output if status.exitstatus == 0 }
# }
#
# You can also supply an additional proc to send some data to the process:
#
# EM.run{
# EM.system('sh', proc{ |process|
# process.send_data("echo hello\n")
# process.send_data("exit\n")
# }, proc{ |out,status|
# puts(out)
# })
# }
#
# Like EventMachine.popen, EventMachine.system currently does not work on windows.
# It returns the pid of the spawned process.
def EventMachine::system cmd, *args, &cb
cb ||= args.pop if args.last.is_a? Proc
init = args.pop if args.last.is_a? Proc
# merge remaining arguments into the command
cmd = [cmd, *args] if args.any?
EM.get_subprocess_pid(EM.popen(cmd, SystemCmd, cb) do |c|
init[c] if init
end.signature)
end
end
|