right_popen 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- #
1
+ #--
2
2
  # Copyright (c) 2009 RightScale Inc
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
@@ -19,7 +19,7 @@
19
19
  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
20
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
- #
22
+ #++
23
23
 
24
24
  # RightScale.popen3 allows running external processes aynchronously
25
25
  # while still capturing their standard and error outputs.
@@ -99,33 +99,35 @@ module RightScale
99
99
  # Forks process to run given command asynchronously, hooking all three
100
100
  # standard streams of the child process.
101
101
  #
102
- # Streams the command's stdout and stderr to the given handlers. Time-
103
- # ordering of bytes sent to stdout and stderr is not preserved.
104
- #
105
- # Calls given exit handler upon command process termination, passing in the
106
- # resulting Process::Status.
107
- #
108
- # All handlers must be methods exposed by the given target.
109
- #
110
- # === Parameters
111
- # cmd(String):: command to execute, including any arguments.
112
- # target(Object):: object defining handler methods to be called.
113
- # stdout_handler(String):: token for stdout handler method name.
114
- # stderr_handler(String):: token for stderr handler method name.
115
- # exit_handler(String):: token for exit handler method name.
116
- #
117
- # === Returns
118
- # true:: Always returns true
119
- def self.popen3(cmd, target, stdout_handler = nil, stderr_handler = nil, exit_handler = nil)
120
- raise "EventMachine reactor must be started" unless EM.reactor_running?
102
+ # See RightScale.popen3
103
+ def self.popen3_imp(options)
104
+ cmd = options[:command].dup
121
105
  GC.start # To garbage collect open file descriptors from passed executions
122
106
  EM.next_tick do
123
107
  saved_stderr = $stderr.dup
124
108
  r, w = Socket::pair(Socket::AF_LOCAL, Socket::SOCK_STREAM, 0)#IO::pipe
125
109
 
126
110
  $stderr.reopen w
127
- c = EM.attach(r, StdErrHandler, target, stderr_handler, r) if stderr_handler
128
- EM.popen(cmd, StdOutHandler, target, stdout_handler, exit_handler, c, r, w)
111
+ c = EM.attach(r, StdErrHandler, options[:target], options[:stderr_handler], r) if options[:stderr_handler]
112
+
113
+ # Setup environment for child process
114
+ envs = {}
115
+ options[:environment].each { |k, v| envs[k.to_s] = v } if options[:environment]
116
+ unless envs.empty?
117
+ old_envs = {}
118
+ ENV.each { |k, v| old_envs[k] = v if envs.include?(k) }
119
+ envs.each { |k, v| ENV[k] = v }
120
+ end
121
+
122
+ # Launch child process
123
+ EM.popen(cmd, StdOutHandler, options[:target], options[:stdout_handler], options[:exit_handler], c, r, w)
124
+
125
+ # Restore environment variables
126
+ unless envs.empty?
127
+ envs.each { |k, _| ENV[k] = nil }
128
+ old_envs.each { |k, v| ENV[k] = v }
129
+ end
130
+
129
131
  # Do not close 'w', strange things happen otherwise
130
132
  # (command protocol socket gets closed during decommission)
131
133
  $stderr.reopen saved_stderr
data/lib/right_popen.rb CHANGED
@@ -1,4 +1,4 @@
1
- #
1
+ #--
2
2
  # Copyright (c) 2009 RightScale Inc
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
@@ -19,7 +19,7 @@
19
19
  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
20
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
- #
22
+ #++
23
23
 
24
24
  # RightScale.popen3 allows running external processes aynchronously
25
25
  # while still capturing their standard and error outputs.
@@ -30,3 +30,36 @@ if RUBY_PLATFORM =~ /mswin/
30
30
  else
31
31
  require File.expand_path(File.join(File.dirname(__FILE__), 'linux', 'right_popen'))
32
32
  end
33
+
34
+ module RightScale
35
+
36
+ # Spawn process to run given command asynchronously, hooking all three
37
+ # standard streams of the child process.
38
+ #
39
+ # Streams the command's stdout and stderr to the given handlers. Time-
40
+ # ordering of bytes sent to stdout and stderr is not preserved.
41
+ #
42
+ # Calls given exit handler upon command process termination, passing in the
43
+ # resulting Process::Status.
44
+ #
45
+ # All handlers must be methods exposed by the given target.
46
+ #
47
+ # === Parameters
48
+ # options[:command](String):: Command to execute, including any arguments
49
+ # options[:environment](Hash):: Hash of environment variables values keyed by name
50
+ # options[:target](Object):: object defining handler methods to be called, optional (no handlers can be defined if not specified)
51
+ # options[:stdout_handler](String):: Stdout handler method name, optional
52
+ # options[:stderr_handler](String):: Stderr handler method name, optional
53
+ # options[:exit_handler](String):: Exit handler method name, optional
54
+ #
55
+ # === Returns
56
+ # true:: Always returns true
57
+ def self.popen3(options)
58
+ raise "EventMachine reactor must be started" unless EM.reactor_running?
59
+ raise "Missing command" unless options[:command]
60
+ raise "Missing target" unless options[:target] || !options[:stdout_handler] && !options[:stderr_handler] && !options[:exit_handler]
61
+ RightScale.popen3_imp(options)
62
+ true
63
+ end
64
+
65
+ end
data/right_popen.gemspec CHANGED
@@ -4,7 +4,7 @@ spec = Gem::Specification.new do |spec|
4
4
  is_windows = RUBY_PLATFORM =~ /mswin/
5
5
 
6
6
  spec.name = 'right_popen'
7
- spec.version = '1.0.4'
7
+ spec.version = '1.0.5'
8
8
  spec.authors = ['Scott Messier', 'Raphael Simon']
9
9
  spec.email = 'scott@rightscale.com'
10
10
  spec.homepage = 'https://github.com/rightscale/right_popen'
data/spec/print_env.rb ADDED
@@ -0,0 +1 @@
1
+ ENV.each { |k, v| $stdout.puts "#{k}=#{v}\n" }
@@ -1,5 +1,4 @@
1
1
  require File.join(File.dirname(__FILE__), 'spec_helper')
2
- require 'right_popen'
3
2
 
4
3
  RUBY_CMD = 'ruby'
5
4
  STANDARD_MESSAGE = 'Standard message'
@@ -29,18 +28,23 @@ describe 'RightScale::popen3' do
29
28
 
30
29
  attr_reader :output_text, :error_text, :status
31
30
 
32
- def do_right_popen(command)
31
+ def do_right_popen(command, env=nil)
33
32
  @output_text = ''
34
33
  @error_text = ''
35
34
  @status = nil
36
- RightScale.popen3(command, self, :on_read_stdout, :on_read_stderr, :on_exit)
35
+ RightScale.popen3(:command => command,
36
+ :target => self,
37
+ :environment => env,
38
+ :stdout_handler => :on_read_stdout,
39
+ :stderr_handler => :on_read_stderr,
40
+ :exit_handler => :on_exit)
37
41
  end
38
42
 
39
- def run_right_popen(command, count = 1)
43
+ def run_right_popen(command, env=nil, count = 1)
40
44
  puts "#{count}>" if count > 1
41
45
  last_iteration = 0
42
46
  EM.next_tick do
43
- do_right_popen(command)
47
+ do_right_popen(command, env)
44
48
  end
45
49
  EM.run do
46
50
  timer = EM::PeriodicTimer.new(0.05) do
@@ -54,7 +58,7 @@ describe 'RightScale::popen3' do
54
58
  print '+'
55
59
  STDOUT.flush
56
60
  end
57
- do_right_popen(command)
61
+ do_right_popen(command, env)
58
62
  end
59
63
  else
60
64
  puts "<" if count > 1
@@ -162,11 +166,35 @@ describe 'RightScale::popen3' do
162
166
  end
163
167
  runner.error_text.should == results
164
168
  end
169
+
170
+ it 'should setup environment variables' do
171
+ command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
172
+ runner = RightPopenSpec::Runner.new
173
+ runner.run_right_popen(command)
174
+ runner.status.exitstatus.should == 0
175
+ runner.output_text.should_not include('_test_')
176
+ runner.run_right_popen(command, :__test__ => '42')
177
+ runner.status.exitstatus.should == 0
178
+ runner.output_text.should match(/^__test__=42$/)
179
+ end
180
+
181
+ it 'should restore environment variables' do
182
+ ENV['__test__'] = '41'
183
+ old_envs = {}
184
+ ENV.each { |k, v| old_envs[k] = v }
185
+ command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
186
+ runner = RightPopenSpec::Runner.new
187
+ runner.run_right_popen(command, :__test__ => '42')
188
+ runner.status.exitstatus.should == 0
189
+ runner.output_text.should match(/^__test__=42$/)
190
+ ENV.each { |k, v| old_envs[k].should == v }
191
+ old_envs.each { |k, v| ENV[k].should == v }
192
+ end
165
193
 
166
194
  it 'should run repeatedly without leaking resources' do
167
195
  command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_output.rb'))}\" \"#{STANDARD_MESSAGE}\" \"#{ERROR_MESSAGE}\""
168
196
  runner = RightPopenSpec::Runner.new
169
- runner.run_right_popen(command, REPEAT_TEST_COUNTER)
197
+ runner.run_right_popen(command, nil, REPEAT_TEST_COUNTER)
170
198
  runner.status.exitstatus.should == 0
171
199
  runner.output_text.should == STANDARD_MESSAGE + "\n"
172
200
  runner.error_text.should == ERROR_MESSAGE + "\n"
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,4 @@
1
1
  require 'rubygems'
2
- $:.push File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'spec'
3
+ require 'eventmachine'
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'right_popen')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: right_popen
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Messier
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2010-02-18 00:00:00 -08:00
13
+ date: 2010-02-19 00:00:00 -08:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -43,6 +43,7 @@ files:
43
43
  - lib/linux/right_popen.rb
44
44
  - lib/right_popen.rb
45
45
  - right_popen.gemspec
46
+ - spec/print_env.rb
46
47
  - spec/produce_mixed_output.rb
47
48
  - spec/produce_output.rb
48
49
  - spec/produce_status.rb