cocaine 0.0.1
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.
- data/Rakefile +15 -0
- data/lib/cocaine.rb +5 -0
- data/lib/cocaine/command_line.rb +85 -0
- data/lib/cocaine/exceptions.rb +6 -0
- data/lib/cocaine/version.rb +3 -0
- data/spec/cocaine/command_line_spec.rb +132 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/with_exitstatus.rb +12 -0
- metadata +115 -0
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'rake'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
desc 'Default: Run specs.'
|
7
|
+
task :default => 'spec:unit'
|
8
|
+
|
9
|
+
namespace :spec do
|
10
|
+
desc "Run unit specs"
|
11
|
+
RSpec::Core::RakeTask.new('unit') do |t|
|
12
|
+
t.pattern = 'spec/cocaine/**/*_spec.rb'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
data/lib/cocaine.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
module Cocaine
|
2
|
+
class CommandLine
|
3
|
+
class << self
|
4
|
+
attr_accessor :path
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(binary, params = "", options = {})
|
8
|
+
@binary = binary.dup
|
9
|
+
@params = params.dup
|
10
|
+
@options = options.dup
|
11
|
+
@swallow_stderr = @options.has_key?(:swallow_stderr) ? @options.delete(:swallow_stderr) : Cocaine.options[:swallow_stderr]
|
12
|
+
@expected_outcodes = @options.delete(:expected_outcodes)
|
13
|
+
@expected_outcodes ||= [0]
|
14
|
+
end
|
15
|
+
|
16
|
+
def command
|
17
|
+
cmd = []
|
18
|
+
cmd << full_path(@binary)
|
19
|
+
cmd << interpolate(@params, @options)
|
20
|
+
cmd << bit_bucket if @swallow_stderr
|
21
|
+
cmd.join(" ")
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
begin
|
26
|
+
output = self.class.send(:'`', command)
|
27
|
+
rescue Errno::ENOENT
|
28
|
+
raise Cocaine::CommandNotFoundError
|
29
|
+
end
|
30
|
+
if $?.exitstatus == 127
|
31
|
+
raise Cocaine::CommandNotFoundError
|
32
|
+
end
|
33
|
+
unless @expected_outcodes.include?($?.exitstatus)
|
34
|
+
raise Cocaine::ExitStatusError, "Command '#{command}' returned #{$?.exitstatus}. Expected #{@expected_outcodes.join(", ")}"
|
35
|
+
end
|
36
|
+
output
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def full_path(binary)
|
42
|
+
[self.class.path, binary].compact.join("/")
|
43
|
+
end
|
44
|
+
|
45
|
+
def interpolate(pattern, vars)
|
46
|
+
# interpolates :variables and :{variables}
|
47
|
+
pattern.gsub(%r#:(?:\w+|\{\w+\})#) do |match|
|
48
|
+
key = match[1..-1]
|
49
|
+
key = key[1..-2] if key[0,1] == '{'
|
50
|
+
if invalid_variables.include?(key)
|
51
|
+
raise InterpolationError,
|
52
|
+
"Interpolation of #{key} isn't allowed."
|
53
|
+
end
|
54
|
+
interpolation(vars, key) || match
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def invalid_variables
|
59
|
+
%w(expected_outcodes swallow_stderr)
|
60
|
+
end
|
61
|
+
|
62
|
+
def interpolation(vars, key)
|
63
|
+
if vars.key?(key.to_sym)
|
64
|
+
shell_quote(vars[key.to_sym])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def shell_quote(string)
|
69
|
+
return "" if string.nil? or string.empty?
|
70
|
+
if self.class.unix?
|
71
|
+
string.split("'").map{|m| "'#{m}'" }.join("\\'")
|
72
|
+
else
|
73
|
+
%{"#{string}"}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def bit_bucket
|
78
|
+
self.class.unix? ? "2>/dev/null" : "2>NUL"
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.unix?
|
82
|
+
File.exist?("/dev/null")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cocaine::CommandLine do
|
4
|
+
before do
|
5
|
+
Cocaine::CommandLine.path = nil
|
6
|
+
File.stubs(:exist?).with("/dev/null").returns(true)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "takes a command and parameters and produce a shell command for bash" do
|
10
|
+
cmd = Cocaine::CommandLine.new("convert", "a.jpg b.png", :swallow_stderr => false)
|
11
|
+
cmd.command.should == "convert a.jpg b.png"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can set a default path and produce commands with that path" do
|
15
|
+
Cocaine::CommandLine.path = "/opt/bin"
|
16
|
+
cmd = Cocaine::CommandLine.new("convert", "a.jpg b.png", :swallow_stderr => false)
|
17
|
+
cmd.command.should == "/opt/bin/convert a.jpg b.png"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "can interpolate quoted variables into the parameters" do
|
21
|
+
cmd = Cocaine::CommandLine.new("convert",
|
22
|
+
":one :{two}",
|
23
|
+
:one => "a.jpg",
|
24
|
+
:two => "b.png",
|
25
|
+
:swallow_stderr => false)
|
26
|
+
cmd.command.should == "convert 'a.jpg' 'b.png'"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "quotes command line options differently if we're on windows" do
|
30
|
+
File.stubs(:exist?).with("/dev/null").returns(false)
|
31
|
+
cmd = Cocaine::CommandLine.new("convert",
|
32
|
+
":one :{two}",
|
33
|
+
:one => "a.jpg",
|
34
|
+
:two => "b.png",
|
35
|
+
:swallow_stderr => false)
|
36
|
+
cmd.command.should == 'convert "a.jpg" "b.png"'
|
37
|
+
end
|
38
|
+
|
39
|
+
it "can quote and interpolate dangerous variables" do
|
40
|
+
cmd = Cocaine::CommandLine.new("convert",
|
41
|
+
":one :two",
|
42
|
+
:one => "`rm -rf`.jpg",
|
43
|
+
:two => "ha'ha.png",
|
44
|
+
:swallow_stderr => false)
|
45
|
+
cmd.command.should == "convert '`rm -rf`.jpg' 'ha'\\''ha.png'"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "can quote and interpolate dangerous variables even on windows" do
|
49
|
+
File.stubs(:exist?).with("/dev/null").returns(false)
|
50
|
+
cmd = Cocaine::CommandLine.new("convert",
|
51
|
+
":one :two",
|
52
|
+
:one => "`rm -rf`.jpg",
|
53
|
+
:two => "ha'ha.png",
|
54
|
+
:swallow_stderr => false)
|
55
|
+
cmd.command.should == %{convert "`rm -rf`.jpg" "ha'ha.png"}
|
56
|
+
end
|
57
|
+
|
58
|
+
it "allows colons in parameters" do
|
59
|
+
cmd = Cocaine::CommandLine.new("convert", "'a.jpg' xc:black 'b.jpg'", :swallow_stderr => false)
|
60
|
+
cmd.command.should == "convert 'a.jpg' xc:black 'b.jpg'"
|
61
|
+
end
|
62
|
+
|
63
|
+
it "adds redirection to get rid of stderr in bash" do
|
64
|
+
File.stubs(:exist?).with("/dev/null").returns(true)
|
65
|
+
cmd = Cocaine::CommandLine.new("convert",
|
66
|
+
"a.jpg b.png",
|
67
|
+
:swallow_stderr => true)
|
68
|
+
|
69
|
+
cmd.command.should == "convert a.jpg b.png 2>/dev/null"
|
70
|
+
end
|
71
|
+
|
72
|
+
it "adds redirection to get rid of stderr in cmd.exe" do
|
73
|
+
File.stubs(:exist?).with("/dev/null").returns(false)
|
74
|
+
cmd = Cocaine::CommandLine.new("convert",
|
75
|
+
"a.jpg b.png",
|
76
|
+
:swallow_stderr => true)
|
77
|
+
|
78
|
+
cmd.command.should == "convert a.jpg b.png 2>NUL"
|
79
|
+
end
|
80
|
+
|
81
|
+
it "raises if trying to interpolate :swallow_stderr or :expected_outcodes" do
|
82
|
+
cmd = Cocaine::CommandLine.new("convert",
|
83
|
+
":swallow_stderr :expected_outcodes",
|
84
|
+
:swallow_stderr => false,
|
85
|
+
:expected_outcodes => [0, 1])
|
86
|
+
|
87
|
+
lambda do
|
88
|
+
cmd.command
|
89
|
+
end.should raise_error(Cocaine::CommandLineError)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "runs the #command it's given and return the output" do
|
93
|
+
cmd = Cocaine::CommandLine.new("convert", "a.jpg b.png", :swallow_stderr => false)
|
94
|
+
cmd.class.stubs(:"`").with("convert a.jpg b.png").returns(:correct_value)
|
95
|
+
with_exitstatus_returning(0) do
|
96
|
+
cmd.run.should == :correct_value
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it "raises a CommandLineError if the result code isn't expected" do
|
101
|
+
cmd = Cocaine::CommandLine.new("convert", "a.jpg b.png", :swallow_stderr => false)
|
102
|
+
cmd.class.stubs(:"`").with("convert a.jpg b.png").returns(:correct_value)
|
103
|
+
with_exitstatus_returning(1) do
|
104
|
+
lambda do
|
105
|
+
cmd.run
|
106
|
+
end.should raise_error(Cocaine::CommandLineError)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it "does not raise a CommandLineError if the result code is expected" do
|
111
|
+
cmd = Cocaine::CommandLine.new("convert",
|
112
|
+
"a.jpg b.png",
|
113
|
+
:expected_outcodes => [0, 1],
|
114
|
+
:swallow_stderr => false)
|
115
|
+
cmd.class.stubs(:"`").with("convert a.jpg b.png").returns(:correct_value)
|
116
|
+
with_exitstatus_returning(1) do
|
117
|
+
lambda do
|
118
|
+
cmd.run
|
119
|
+
end.should_not raise_error
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
it "detects that the system is unix or windows based on presence of /dev/null" do
|
124
|
+
File.stubs(:exist?).returns(true)
|
125
|
+
Cocaine::CommandLine.unix?.should == true
|
126
|
+
end
|
127
|
+
|
128
|
+
it "detects that the system is not unix or windows based on absence of /dev/null" do
|
129
|
+
File.stubs(:exist?).returns(false)
|
130
|
+
Cocaine::CommandLine.unix?.should_not == true
|
131
|
+
end
|
132
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cocaine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jon Yurek
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-05-18 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: bourne
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: mocha
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
description: A small library for doing (command) lines
|
64
|
+
email: jyurek@thoughtbot.com
|
65
|
+
executables: []
|
66
|
+
|
67
|
+
extensions: []
|
68
|
+
|
69
|
+
extra_rdoc_files: []
|
70
|
+
|
71
|
+
files:
|
72
|
+
- Rakefile
|
73
|
+
- lib/cocaine.rb
|
74
|
+
- lib/cocaine/command_line.rb
|
75
|
+
- lib/cocaine/exceptions.rb
|
76
|
+
- lib/cocaine/version.rb
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
- spec/cocaine/command_line_spec.rb
|
79
|
+
- spec/support/with_exitstatus.rb
|
80
|
+
has_rdoc: true
|
81
|
+
homepage: http://www.thoughtbot.com/projects/cocaine
|
82
|
+
licenses: []
|
83
|
+
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 3
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
version: "0"
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
hash: 3
|
104
|
+
segments:
|
105
|
+
- 0
|
106
|
+
version: "0"
|
107
|
+
requirements: []
|
108
|
+
|
109
|
+
rubyforge_project:
|
110
|
+
rubygems_version: 1.5.2
|
111
|
+
signing_key:
|
112
|
+
specification_version: 3
|
113
|
+
summary: A small library for doing (command) lines
|
114
|
+
test_files:
|
115
|
+
- spec/cocaine/command_line_spec.rb
|