terrapin 0.6.0.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|