terrapin 0.6.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +6 -0
- data/GOALS +8 -0
- data/Gemfile +7 -0
- data/LICENSE +26 -0
- data/NEWS.md +85 -0
- data/README.md +215 -0
- data/Rakefile +13 -0
- data/lib/terrapin.rb +12 -0
- data/lib/terrapin/command_line.rb +197 -0
- data/lib/terrapin/command_line/multi_pipe.rb +50 -0
- data/lib/terrapin/command_line/output.rb +12 -0
- data/lib/terrapin/command_line/runners.rb +7 -0
- data/lib/terrapin/command_line/runners/backticks_runner.rb +30 -0
- data/lib/terrapin/command_line/runners/fake_runner.rb +30 -0
- data/lib/terrapin/command_line/runners/popen_runner.rb +29 -0
- data/lib/terrapin/command_line/runners/posix_runner.rb +49 -0
- data/lib/terrapin/command_line/runners/process_runner.rb +41 -0
- data/lib/terrapin/exceptions.rb +8 -0
- data/lib/terrapin/os_detector.rb +27 -0
- data/lib/terrapin/version.rb +4 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/fake_logger.rb +18 -0
- data/spec/support/have_output.rb +11 -0
- data/spec/support/nonblocking_examples.rb +14 -0
- data/spec/support/stub_os.rb +25 -0
- data/spec/support/unsetting_exitstatus.rb +7 -0
- data/spec/support/with_exitstatus.rb +12 -0
- data/spec/terrapin/command_line/output_spec.rb +14 -0
- data/spec/terrapin/command_line/runners/backticks_runner_spec.rb +24 -0
- data/spec/terrapin/command_line/runners/fake_runner_spec.rb +22 -0
- data/spec/terrapin/command_line/runners/popen_runner_spec.rb +24 -0
- data/spec/terrapin/command_line/runners/posix_runner_spec.rb +40 -0
- data/spec/terrapin/command_line/runners/process_runner_spec.rb +40 -0
- data/spec/terrapin/command_line_spec.rb +195 -0
- data/spec/terrapin/errors_spec.rb +62 -0
- data/spec/terrapin/os_detector_spec.rb +23 -0
- data/spec/terrapin/runners_spec.rb +97 -0
- data/terrapin.gemspec +28 -0
- metadata +209 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
module Terrapin
|
2
|
+
class CommandLine
|
3
|
+
class MultiPipe
|
4
|
+
def initialize
|
5
|
+
@stdout_in, @stdout_out = IO.pipe
|
6
|
+
@stderr_in, @stderr_out = IO.pipe
|
7
|
+
end
|
8
|
+
|
9
|
+
def pipe_options
|
10
|
+
{ out: @stdout_out, err: @stderr_out }
|
11
|
+
end
|
12
|
+
|
13
|
+
def output
|
14
|
+
Output.new(@stdout_output, @stderr_output)
|
15
|
+
end
|
16
|
+
|
17
|
+
def read_and_then(&block)
|
18
|
+
close_write
|
19
|
+
read
|
20
|
+
block.call
|
21
|
+
close_read
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def close_write
|
27
|
+
@stdout_out.close
|
28
|
+
@stderr_out.close
|
29
|
+
end
|
30
|
+
|
31
|
+
def read
|
32
|
+
@stdout_output = read_stream(@stdout_in)
|
33
|
+
@stderr_output = read_stream(@stderr_in)
|
34
|
+
end
|
35
|
+
|
36
|
+
def close_read
|
37
|
+
@stdout_in.close
|
38
|
+
@stderr_in.close
|
39
|
+
end
|
40
|
+
|
41
|
+
def read_stream(io)
|
42
|
+
result = ""
|
43
|
+
while partial_result = io.read(8192)
|
44
|
+
result << partial_result
|
45
|
+
end
|
46
|
+
result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
require 'terrapin/command_line/runners/backticks_runner'
|
4
|
+
require 'terrapin/command_line/runners/process_runner'
|
5
|
+
require 'terrapin/command_line/runners/posix_runner'
|
6
|
+
require 'terrapin/command_line/runners/popen_runner'
|
7
|
+
require 'terrapin/command_line/runners/fake_runner'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
require 'climate_control'
|
4
|
+
|
5
|
+
module Terrapin
|
6
|
+
class CommandLine
|
7
|
+
class BackticksRunner
|
8
|
+
def self.supported?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def supported?
|
13
|
+
self.class.supported?
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(command, env = {}, options = {})
|
17
|
+
with_modified_environment(env) do
|
18
|
+
Output.new(`#{command}`)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def with_modified_environment(env, &block)
|
25
|
+
ClimateControl.modify(env, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
module Terrapin
|
4
|
+
class CommandLine
|
5
|
+
class FakeRunner
|
6
|
+
def self.supported?
|
7
|
+
false
|
8
|
+
end
|
9
|
+
|
10
|
+
def supported?
|
11
|
+
self.class.supported?
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :commands
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@commands = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(command, env = {}, options = {})
|
21
|
+
commands << [command, env]
|
22
|
+
Output.new("")
|
23
|
+
end
|
24
|
+
|
25
|
+
def ran?(predicate_command)
|
26
|
+
@commands.any?{|(command, _)| command =~ Regexp.new(predicate_command) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
module Terrapin
|
4
|
+
class CommandLine
|
5
|
+
class PopenRunner
|
6
|
+
def self.supported?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
|
10
|
+
def supported?
|
11
|
+
self.class.supported?
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(command, env = {}, options = {})
|
15
|
+
with_modified_environment(env) do
|
16
|
+
IO.popen(command, "r", options) do |pipe|
|
17
|
+
Output.new(pipe.read)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def with_modified_environment(env, &block)
|
25
|
+
ClimateControl.modify(env, &block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
module Terrapin
|
4
|
+
class CommandLine
|
5
|
+
class PosixRunner
|
6
|
+
def self.available?
|
7
|
+
return @available unless @available.nil?
|
8
|
+
|
9
|
+
@available = posix_spawn_gem_available?
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.supported?
|
13
|
+
available? && !OS.java?
|
14
|
+
end
|
15
|
+
|
16
|
+
def supported?
|
17
|
+
self.class.supported?
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(command, env = {}, options = {})
|
21
|
+
pipe = MultiPipe.new
|
22
|
+
pid = spawn(env, command, options.merge(pipe.pipe_options))
|
23
|
+
pipe.read_and_then do
|
24
|
+
waitpid(pid)
|
25
|
+
end
|
26
|
+
pipe.output
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def spawn(*args)
|
32
|
+
POSIX::Spawn.spawn(*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def waitpid(pid)
|
36
|
+
Process.waitpid(pid)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.posix_spawn_gem_available?
|
40
|
+
require 'posix/spawn'
|
41
|
+
true
|
42
|
+
rescue LoadError
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
private_class_method :posix_spawn_gem_available?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
module Terrapin
|
4
|
+
class CommandLine
|
5
|
+
class ProcessRunner
|
6
|
+
def self.available?
|
7
|
+
Process.respond_to?(:spawn)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.supported?
|
11
|
+
available? && !OS.java?
|
12
|
+
end
|
13
|
+
|
14
|
+
def supported?
|
15
|
+
self.class.supported?
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(command, env = {}, options = {})
|
19
|
+
pipe = MultiPipe.new
|
20
|
+
pid = spawn(env, command, options.merge(pipe.pipe_options))
|
21
|
+
pipe.read_and_then do
|
22
|
+
waitpid(pid)
|
23
|
+
end
|
24
|
+
pipe.output
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def spawn(*args)
|
30
|
+
Process.spawn(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
def waitpid(pid)
|
34
|
+
Process.waitpid(pid)
|
35
|
+
rescue Errno::ECHILD
|
36
|
+
# In JRuby, waiting on a finished pid raises.
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
module Terrapin
|
4
|
+
class OSDetector
|
5
|
+
def java?
|
6
|
+
arch =~ /java/
|
7
|
+
end
|
8
|
+
|
9
|
+
def unix?
|
10
|
+
RbConfig::CONFIG['host_os'] !~ /mswin|mingw/
|
11
|
+
end
|
12
|
+
|
13
|
+
def windows?
|
14
|
+
!unix?
|
15
|
+
end
|
16
|
+
|
17
|
+
def path_separator
|
18
|
+
File::PATH_SEPARATOR
|
19
|
+
end
|
20
|
+
|
21
|
+
def arch
|
22
|
+
RUBY_PLATFORM
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
OS = OSDetector.new
|
27
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'mocha/api'
|
3
|
+
require 'bourne'
|
4
|
+
require 'terrapin'
|
5
|
+
require 'timeout'
|
6
|
+
require 'tempfile'
|
7
|
+
require 'pry'
|
8
|
+
require 'thread'
|
9
|
+
require 'pathname'
|
10
|
+
|
11
|
+
begin; require 'active_support/logger'; rescue LoadError; end
|
12
|
+
begin; require 'active_support/buffered_logger'; rescue LoadError; end
|
13
|
+
|
14
|
+
Dir[File.dirname(__FILE__) + "/support/**.rb"].each{|support_file| require support_file }
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.mock_with :mocha
|
18
|
+
config.include WithExitstatus
|
19
|
+
config.include StubOS
|
20
|
+
config.include UnsettingExitstatus
|
21
|
+
end
|
22
|
+
|
23
|
+
def best_logger
|
24
|
+
if ActiveSupport.const_defined?("Logger")
|
25
|
+
ActiveSupport::Logger
|
26
|
+
elsif ActiveSupport.const_defined?("BufferedLogger")
|
27
|
+
ActiveSupport::BufferedLogger
|
28
|
+
else
|
29
|
+
Logger
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
shared_examples_for "a command that does not block" do
|
2
|
+
it 'does not block if the command outputs a lot of data' do
|
3
|
+
garbage_file = Tempfile.new("garbage")
|
4
|
+
10.times{ garbage_file.write("A" * 1024 * 1024) }
|
5
|
+
|
6
|
+
Timeout.timeout(5) do
|
7
|
+
output = subject.call("cat '#{garbage_file.path}'")
|
8
|
+
$?.exitstatus.should == 0
|
9
|
+
output.output.length.should == 10 * 1024 * 1024
|
10
|
+
end
|
11
|
+
|
12
|
+
garbage_file.close
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module StubOS
|
2
|
+
def on_windows!
|
3
|
+
stub_os('mswin')
|
4
|
+
Terrapin::OS.stubs(:path_separator).returns(";")
|
5
|
+
end
|
6
|
+
|
7
|
+
def on_unix!
|
8
|
+
stub_os('darwin11.0.0')
|
9
|
+
Terrapin::OS.stubs(:path_separator).returns(":")
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_mingw!
|
13
|
+
stub_os('mingw')
|
14
|
+
Terrapin::OS.stubs(:path_separator).returns(";")
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_java!
|
18
|
+
Terrapin::OS.stubs(:arch).returns("universal-java1.7")
|
19
|
+
end
|
20
|
+
|
21
|
+
def stub_os(host_string)
|
22
|
+
# http://blog.emptyway.com/2009/11/03/proper-way-to-detect-windows-platform-in-ruby/
|
23
|
+
RbConfig::CONFIG.stubs(:[]).with('host_os').returns(host_string)
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Terrapin::CommandLine::Output do
|
4
|
+
it 'holds an input and error stream' do
|
5
|
+
output = Terrapin::CommandLine::Output.new(:a, :b)
|
6
|
+
expect(output.output).to eq :a
|
7
|
+
expect(output.error_output).to eq :b
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'calls #to_s on the output when you call #to_s on it' do
|
11
|
+
output = Terrapin::CommandLine::Output.new(:a, :b)
|
12
|
+
expect(output.to_s).to eq 'a'
|
13
|
+
end
|
14
|
+
end
|