right-popen 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 RightScale, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ 'Software'), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,120 @@
1
+ = RightPopen
2
+
3
+ == DESCRIPTION
4
+
5
+ === Synopsis
6
+
7
+ RightPopen allows running external processes aynchronously while still
8
+ capturing their standard and error outputs. It relies on EventMachine for most
9
+ of its internal mechanisms. The linux implementation is valid for any linux
10
+ platform but there is also a native implementation for Windows platforms.
11
+
12
+ Refer to the wiki (https://github.com/rightscale/right_popen/wikis) for up-to-date
13
+ documentation.
14
+
15
+ Also use the built-in issues tracker (https://github.com/rightscale/right_popen/issues)
16
+ to report issues.
17
+
18
+
19
+ == USAGE
20
+
21
+ === Simple Example
22
+
23
+ require 'rubygems'
24
+ require 'right_popen'
25
+ require 'eventmachine'
26
+
27
+ @stdout_text = ""
28
+ @stderr_text = ""
29
+ @exit_status = nil
30
+
31
+ def on_read_stdout(data)
32
+ @stdout_text << data
33
+ end
34
+
35
+ def on_read_stderr(data)
36
+ @stderr_text << data
37
+ end
38
+
39
+ def on_exit(status)
40
+ @exit_status = status
41
+ end
42
+
43
+ EM.run do
44
+ EM.next_tick do
45
+ cmd = "ruby -e \"puts 'some stdout text'; $stderr.puts 'some stderr text'\; exit 99\""
46
+ RightScale.popen3(cmd, self, :on_read_stdout, :on_read_stderr, :on_exit)
47
+ end
48
+ timer = EM::PeriodicTimer.new(0.1) do
49
+ if @exit_status
50
+ timer.cancel
51
+ EM.stop
52
+ end
53
+ end
54
+ end
55
+
56
+ puts "@stdout_text = #{@stdout_text}"
57
+ puts "@stderr_text = #{@stderr_text}"
58
+ puts "@exit_status.exitstatus = #{@exit_status.exitstatus}"
59
+
60
+
61
+ == INSTALLATION
62
+
63
+ RightPopen can be installed by entering the following at the command prompt:
64
+
65
+ gem install right-popen
66
+
67
+
68
+ == BUILDING
69
+
70
+ Install the following RubyGems required for building:
71
+ * rake
72
+
73
+ The Windows implementation relies on a native C module which must currently be
74
+ built using the MSVC 6.0 compiler due to a dependency on the standard libraries
75
+ FILE struct provided by the "msvcrt.dll".
76
+
77
+ The gem can be built on Linux or Windows platforms and will produce separate gem
78
+ files depending on current platform. Run the following command from the
79
+ directory containing the "Rakefile":
80
+
81
+ rake build_binary_gem
82
+
83
+
84
+ == TESTING
85
+
86
+ Install the following RubyGems required for testing:
87
+ * rspec
88
+
89
+ The build can be tested using the RSpec gem. Create a link to the installed
90
+ "spec" in your Ruby/bin directory (or ensure the bin directory is on the PATH
91
+ under Windows) and run the following command from the gem directory to execute
92
+ the RightPopen tests:
93
+
94
+ spec spec/right_popen_spec.rb
95
+
96
+
97
+ == LICENSE
98
+
99
+ <b>RightPopen</b>
100
+
101
+ Copyright:: Copyright (c) 2010 RightScale, Inc.
102
+
103
+ Permission is hereby granted, free of charge, to any person obtaining
104
+ a copy of this software and associated documentation files (the
105
+ 'Software'), to deal in the Software without restriction, including
106
+ without limitation the rights to use, copy, modify, merge, publish,
107
+ distribute, sublicense, and/or sell copies of the Software, and to
108
+ permit persons to whom the Software is furnished to do so, subject to
109
+ the following conditions:
110
+
111
+ The above copyright notice and this permission notice shall be
112
+ included in all copies or substantial portions of the Software.
113
+
114
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
115
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
116
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
117
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
118
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
119
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
120
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'fileutils'
3
+ require 'rake'
4
+ require 'rake/clean'
5
+ require 'rake/testtask'
6
+ require 'rbconfig'
7
+
8
+ include Config
9
+
10
+ desc "Clean any build files for right-popen"
11
+ task :clean do
12
+ if File.exists?('ext/Makefile')
13
+ Dir.chdir('ext') do
14
+ sh 'nmake distclean'
15
+ end
16
+ end
17
+ rm 'lib/win32/right_popen.so' if File.file?('lib/win32/right_popen.so')
18
+ end
19
+
20
+ desc "Build right-popen (but don't install it)"
21
+ task :build => [:clean] do
22
+ if RUBY_PLATFORM =~ /mswin/
23
+ Dir.chdir('ext') do
24
+ ruby 'extconf.rb'
25
+ sh 'nmake'
26
+ end
27
+ FileUtils::mkdir_p 'lib/win32'
28
+ mv 'ext/right_popen.so', 'lib/win32'
29
+ end
30
+ end
31
+
32
+ desc "Build a binary gem"
33
+ task :build_binary_gem => [:build] do
34
+ Dir["*.gem"].each { |gem| rm gem }
35
+ ruby 'right-popen.gemspec'
36
+ end
37
+
38
+ desc 'Install the right-popen library as a gem'
39
+ task :install_gem => [:build_binary_gem] do
40
+ file = Dir["*.gem"].first
41
+ sh "gem install #{file}"
42
+ end
43
+
44
+ desc 'Uninstalls and reinstalls the right-popen library as a gem'
45
+ task :reinstall_gem do
46
+ sh "gem uninstall right-popen"
47
+ sh "rake install_gem"
48
+ end
@@ -0,0 +1,139 @@
1
+ #
2
+ # Copyright (c) 2009 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ # RightScale.popen3 allows running external processes aynchronously
25
+ # while still capturing their standard and error outputs.
26
+ # It relies on EventMachine for most of its internal mechanisms.
27
+
28
+ require 'rubygems'
29
+ gem 'eventmachine', '=0.12.8' # has only been tested with 0.12.8
30
+ require 'eventmachine'
31
+
32
+ module RightScale
33
+
34
+ # Provides an eventmachine callback handler for the stdout stream.
35
+ module StdOutHandler
36
+
37
+ # === Parameters
38
+ # target(Object): Object defining handler methods to be called.
39
+ #
40
+ # stdout_handler(String): Token for stdout handler method name.
41
+ #
42
+ # exit_handler(String): Token for exit handler method name.
43
+ #
44
+ # stderr_eventable(Connector): EM object representing stderr handler.
45
+ #
46
+ # read_fd(IO): Standard output read file descriptor.
47
+ #
48
+ # write_fd(IO): Standard output write file descriptor.
49
+ def initialize(target, stdout_handler, exit_handler, stderr_eventable, read_fd, write_fd)
50
+ @target = target
51
+ @stdout_handler = stdout_handler
52
+ @exit_handler = exit_handler
53
+ @stderr_eventable = stderr_eventable
54
+ # Just so they don't get GCed before the process goes away
55
+ @read_fd = read_fd
56
+ @write_fd = write_fd
57
+ end
58
+
59
+ # Callback from EM to receive data.
60
+ def receive_data(data)
61
+ @target.method(@stdout_handler).call(data) if @stdout_handler
62
+ end
63
+
64
+ # Callback from EM to unbind.
65
+ def unbind
66
+ # We force the attached stderr handler to go away so that
67
+ # we don't end up with a broken pipe
68
+ @stderr_eventable.force_detach if @stderr_eventable
69
+ @target.method(@exit_handler).call(get_status) if @exit_handler
70
+ end
71
+ end
72
+
73
+ module StdErrHandler
74
+
75
+ # === Parameters
76
+ # target(Object): Object defining handler methods to be called.
77
+ #
78
+ # stderr_handler(String): Token for stderr handler method name.
79
+ def initialize(target, stderr_handler)
80
+ @target = target
81
+ @stderr_handler = stderr_handler
82
+ @unbound = false
83
+ end
84
+
85
+ # Callback from EM to receive data.
86
+ def receive_data(data)
87
+ @target.method(@stderr_handler).call(data)
88
+ end
89
+
90
+ # Callback from EM to unbind.
91
+ def unbind
92
+ @unbound = true
93
+ end
94
+
95
+ # Forces detachment of the stderr handler on EM's next tick.
96
+ def force_detach
97
+ # Use next tick to prevent issue in EM where descriptors list
98
+ # gets out-of-sync when calling detach in an unbind callback
99
+ EM.next_tick { detach unless @unbound }
100
+ end
101
+ end
102
+
103
+ # Forks process to run given command asynchronously, hooking all three
104
+ # standard streams of the child process.
105
+ #
106
+ # Streams the command's stdout and stderr to the given handlers. Time-
107
+ # ordering of bytes sent to stdout and stderr is not preserved.
108
+ #
109
+ # Calls given exit handler upon command process termination, passing in the
110
+ # resulting Process::Status.
111
+ #
112
+ # All handlers must be methods exposed by the given target.
113
+ #
114
+ # === Parameters
115
+ # cmd(String): command to execute, including any arguments.
116
+ #
117
+ # target(Object): object defining handler methods to be called.
118
+ #
119
+ # stdout_handler(String): token for stdout handler method name.
120
+ #
121
+ # stderr_handler(String): token for stderr handler method name.
122
+ #
123
+ # exit_handler(String): token for exit handler method name.
124
+ def self.popen3(cmd, target, stdout_handler = nil, stderr_handler = nil, exit_handler = nil)
125
+ raise "EventMachine reactor must be started" unless EM.reactor_running?
126
+ EM.next_tick do
127
+ saved_stderr = $stderr.dup
128
+ r, w = Socket::pair(Socket::AF_LOCAL, Socket::SOCK_STREAM, 0)#IO::pipe
129
+
130
+ $stderr.reopen w
131
+ c = EM.attach(r, StdErrHandler, target, stderr_handler) if stderr_handler
132
+ EM.popen(cmd, StdOutHandler, target, stdout_handler, exit_handler, c, r, w)
133
+ # Do not close 'w', strange things happen otherwise
134
+ # (command protocol socket gets closed during decommission)
135
+ $stderr.reopen saved_stderr
136
+ end
137
+ end
138
+
139
+ end
@@ -0,0 +1,57 @@
1
+ #
2
+ # Copyright (c) 2009 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ # RightScale.popen3 allows running external processes aynchronously
25
+ # while still capturing their standard and error outputs.
26
+ # It relies on EventMachine for most of its internal mechanisms.
27
+
28
+ if RUBY_PLATFORM =~ /mswin/
29
+ require File.expand_path(File.join(File.dirname(__FILE__), 'win32', 'right_popen'))
30
+ else
31
+ require File.expand_path(File.join(File.dirname(__FILE__), 'linux', 'right_popen'))
32
+ end
33
+
34
+ module RightScale
35
+
36
+ # Closes all open I/O objects in the object space, keeping any provided as
37
+ # arguments.
38
+ #
39
+ # === Parameters
40
+ # keep(Array):: array of I/O objects to keep.
41
+ def self.close_all_fd(keep = [])
42
+ keep = [keep] unless keep.is_a? Array
43
+ keep += [$stdin, $stdout, $stderr]
44
+ keep = keep.collect { |io| io.fileno }
45
+
46
+ ObjectSpace.each_object(IO) do |io|
47
+ unless io.closed? || keep.include?(io.fileno)
48
+ begin
49
+ io.close
50
+ rescue Exception
51
+ #do nothing
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,59 @@
1
+ require 'rubygems'
2
+
3
+ spec = Gem::Specification.new do |spec|
4
+ is_windows = RUBY_PLATFORM =~ /mswin/
5
+
6
+ spec.name = 'right-popen'
7
+ spec.version = '1.0.0'
8
+ spec.authors = ['Scott Messier', 'Raphael Simon']
9
+ spec.email = 'scott@rightscale.com'
10
+ spec.homepage = 'https://github.com/rightscale/right_popen'
11
+ if is_windows
12
+ spec.platform = 'x86-mswin32-60'
13
+ else
14
+ spec.platform = Gem::Platform::RUBY
15
+ end
16
+ spec.summary = 'Provides a platform-independent popen implementation'
17
+ spec.has_rdoc = true
18
+ spec.rdoc_options = ["--main", "README.rdoc", "--title", "RightPopen"]
19
+ spec.extra_rdoc_files = ["README.rdoc"]
20
+ spec.required_ruby_version = '>= 1.8.6'
21
+ spec.rubyforge_project = %q{right-popen}
22
+
23
+ spec.description = <<-EOF
24
+ RightPopen allows running external processes aynchronously while still
25
+ capturing their standard and error outputs. It relies on EventMachine for most
26
+ of its internal mechanisms. The linux implementation is valid for any linux
27
+ platform but there is also a native implementation for Windows platforms.
28
+ EOF
29
+
30
+ if is_windows
31
+ extension_dir = "ext,"
32
+ else
33
+ extension_dir = ""
34
+ end
35
+ candidates = Dir.glob("{#{extension_dir}lib,spec}/**/*") +
36
+ ["LICENSE", "README.rdoc", "Rakefile", "right-popen.gemspec"]
37
+ candidates = candidates.delete_if do |item|
38
+ item.include?("Makefile") || item.include?(".obj") || item.include?(".pdb") || item.include?(".def") || item.include?(".exp") || item.include?(".lib")
39
+ end
40
+ candidates = candidates.delete_if do |item|
41
+ if is_windows
42
+ item.include?("/linux/")
43
+ else
44
+ item.include?("/win32/")
45
+ end
46
+ end
47
+ spec.files = candidates.sort!
48
+
49
+ # Current implementation doesn't support > 0.12.8, but support any patched 0.12.8.x versions.
50
+ spec.add_runtime_dependency(%q<eventmachine>, [">= 0.12.8", "< 0.12.9"])
51
+ if is_windows
52
+ spec.add_runtime_dependency(%q<win32-process>, [">= 0.6.1"])
53
+ end
54
+ end
55
+
56
+ if $PROGRAM_NAME == __FILE__
57
+ Gem.manage_gems if Gem::RubyGemsVersion.to_f < 1.0
58
+ Gem::Builder.new(spec).build
59
+ end
@@ -0,0 +1,8 @@
1
+ count = ARGV[0] ? ARGV[0].to_i : 1
2
+
3
+ count.times do |i|
4
+ $stderr.puts "stderr #{i}" if 0 == i % 10
5
+ $stdout.puts "stdout #{i}"
6
+ end
7
+
8
+ exit 99
@@ -0,0 +1,2 @@
1
+ $stdout.puts ARGV[0]
2
+ $stderr.puts ARGV[1]
@@ -0,0 +1 @@
1
+ exit ARGV[0].to_i
@@ -0,0 +1,5 @@
1
+ count = ARGV[0] ? ARGV[0].to_i : 1
2
+
3
+ count.times do |i|
4
+ $stderr.puts "stderr #{i}"
5
+ end
@@ -0,0 +1,5 @@
1
+ count = ARGV[0] ? ARGV[0].to_i : 1
2
+
3
+ count.times do |i|
4
+ $stdout.puts "stdout #{i}"
5
+ end
@@ -0,0 +1,143 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+ require 'right_popen'
3
+
4
+ RUBY_CMD = 'ruby'
5
+ STANDARD_MESSAGE = 'Standard message'
6
+ ERROR_MESSAGE = 'Error message'
7
+ EXIT_STATUS = 146
8
+ LARGE_OUTPUT_COUNTER = 1000
9
+
10
+ describe 'RightScale::popen3' do
11
+
12
+ def on_read_stdout(data)
13
+ @stdoutput << data
14
+ end
15
+
16
+ def on_read_stderr(data)
17
+ @stderr << data
18
+ end
19
+
20
+ def on_exit(status)
21
+ @done = status
22
+ end
23
+
24
+ before(:each) do
25
+ @done = false
26
+ @stdoutput = ''
27
+ @stderr = ''
28
+ end
29
+
30
+ it 'should redirect output' do
31
+ EM.next_tick do
32
+ cmd = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_output.rb'))}\" \"#{STANDARD_MESSAGE}\" \"#{ERROR_MESSAGE}\""
33
+ RightScale.popen3(cmd, self, :on_read_stdout, :on_read_stderr, :on_exit)
34
+ end
35
+ EM.run do
36
+ timer = EM::PeriodicTimer.new(0.1) do
37
+ if @done
38
+ timer.cancel
39
+ @stdoutput.should == STANDARD_MESSAGE + "\n"
40
+ @stderr.should == ERROR_MESSAGE + "\n"
41
+ EM.stop
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ it 'should return the right status' do
48
+ EM.next_tick do
49
+ cmd = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_status.rb'))}\" #{EXIT_STATUS}"
50
+ RightScale.popen3(cmd, self, nil, nil, :on_exit)
51
+ end
52
+ EM.run do
53
+ timer = EM::PeriodicTimer.new(0.1) do
54
+ if @done
55
+ timer.cancel
56
+ @done.exitstatus.should == EXIT_STATUS
57
+ EM.stop
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ it 'should preserve the integrity of stdout when stderr is unavailable' do
64
+ # manually bump count up for more aggressive multi-processor testing, lessen
65
+ # for a quick smoke test
66
+ count = LARGE_OUTPUT_COUNTER
67
+ EM.next_tick do
68
+ cmd = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_stdout_only.rb'))}\" #{count}"
69
+ RightScale.popen3(cmd, self, :on_read_stdout, :on_read_stderr, :on_exit)
70
+ end
71
+ EM.run do
72
+ timer = EM::PeriodicTimer.new(0.1) do
73
+ if @done
74
+ timer.cancel
75
+ results = []
76
+ count.times do |i|
77
+ results << "stdout #{i}"
78
+ end
79
+ @stdoutput.should == results.join("\n") + "\n"
80
+ results = []
81
+ @stderr.should == ''
82
+ EM.stop
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ it 'should preserve the integrity of stderr when stdout is unavailable' do
89
+ # manually bump count up for more aggressive multi-processor testing, lessen
90
+ # for a quick smoke test
91
+ count = LARGE_OUTPUT_COUNTER
92
+ EM.next_tick do
93
+ cmd = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_stderr_only.rb'))}\" #{count}"
94
+ RightScale.popen3(cmd, self, :on_read_stdout, :on_read_stderr, :on_exit)
95
+ end
96
+ EM.run do
97
+ timer = EM::PeriodicTimer.new(0.1) do
98
+ if @done
99
+ timer.cancel
100
+ results = []
101
+ count.times do |i|
102
+ results << "stderr #{i}"
103
+ end
104
+ @stderr.should == results.join("\n") + "\n"
105
+ results = []
106
+ @stdoutput.should == ''
107
+ EM.stop
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ it 'should preserve the integrity of stdout and stderr despite interleaving' do
114
+ # manually bump count up for more aggressive multi-processor testing, lessen
115
+ # for a quick smoke test
116
+ count = LARGE_OUTPUT_COUNTER
117
+ EM.next_tick do
118
+ cmd = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_mixed_output.rb'))}\" #{count}"
119
+ RightScale.popen3(cmd, self, :on_read_stdout, :on_read_stderr, :on_exit)
120
+ end
121
+ EM.run do
122
+ timer = EM::PeriodicTimer.new(0.1) do
123
+ if @done
124
+ timer.cancel
125
+ results = []
126
+ count.times do |i|
127
+ results << "stdout #{i}"
128
+ end
129
+ @stdoutput.should == results.join("\n") + "\n"
130
+ results = []
131
+ count.times do |i|
132
+ (results << "stderr #{i}") if 0 == i % 10
133
+ end
134
+ @stderr.should == results.join("\n") + "\n"
135
+ results = []
136
+ @done.exitstatus.should == 99
137
+ EM.stop
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ end
@@ -0,0 +1,2 @@
1
+ require 'rubygems'
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: right-popen
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Scott Messier
8
+ - Raphael Simon
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2010-01-13 00:00:00 -08:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: eventmachine
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.12.8
25
+ - - <
26
+ - !ruby/object:Gem::Version
27
+ version: 0.12.9
28
+ version:
29
+ description: |
30
+ RightPopen allows running external processes aynchronously while still
31
+ capturing their standard and error outputs. It relies on EventMachine for most
32
+ of its internal mechanisms. The linux implementation is valid for any linux
33
+ platform but there is also a native implementation for Windows platforms.
34
+
35
+ email: scott@rightscale.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - README.rdoc
42
+ files:
43
+ - LICENSE
44
+ - README.rdoc
45
+ - Rakefile
46
+ - lib/linux/right_popen.rb
47
+ - lib/right_popen.rb
48
+ - right-popen.gemspec
49
+ - spec/produce_mixed_output.rb
50
+ - spec/produce_output.rb
51
+ - spec/produce_status.rb
52
+ - spec/produce_stderr_only.rb
53
+ - spec/produce_stdout_only.rb
54
+ - spec/right_popen_spec.rb
55
+ - spec/spec_helper.rb
56
+ has_rdoc: true
57
+ homepage: https://github.com/rightscale/right_popen
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --main
63
+ - README.rdoc
64
+ - --title
65
+ - RightPopen
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.8.6
73
+ version:
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
80
+ requirements: []
81
+
82
+ rubyforge_project: right-popen
83
+ rubygems_version: 1.3.2
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Provides a platform-independent popen implementation
87
+ test_files: []
88
+