awesome_spawn 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/awesome_spawn.rb +11 -106
- data/lib/awesome_spawn/command_line_builder.rb +107 -0
- data/lib/awesome_spawn/command_result.rb +8 -0
- data/lib/awesome_spawn/version.rb +1 -1
- data/spec/awesome_spawn_spec.rb +3 -89
- data/spec/command_line_builder_spec.rb +255 -0
- data/spec/command_result_spec.rb +13 -3
- data/spec/spec_helper.rb +9 -3
- metadata +5 -2
data/lib/awesome_spawn.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require "awesome_spawn/version"
|
2
|
+
require "awesome_spawn/command_line_builder"
|
2
3
|
require "awesome_spawn/command_result"
|
3
4
|
require "awesome_spawn/command_result_error"
|
4
5
|
require "awesome_spawn/no_such_file_error"
|
5
6
|
|
6
|
-
require "
|
7
|
+
require "open3"
|
7
8
|
|
8
9
|
module AwesomeSpawn
|
9
10
|
extend self
|
@@ -61,18 +62,14 @@ module AwesomeSpawn
|
|
61
62
|
params = options.delete(:params)
|
62
63
|
in_data = options.delete(:in_data)
|
63
64
|
|
64
|
-
output
|
65
|
-
|
66
|
-
status = nil
|
67
|
-
command_line = build_command_line(command, params)
|
65
|
+
output, error, status = "", "", nil
|
66
|
+
command_line = build_command_line(command, params)
|
68
67
|
|
69
68
|
begin
|
70
|
-
output, error = launch(command_line, in_data, options)
|
71
|
-
status = exitstatus
|
69
|
+
output, error, status = launch(command_line, in_data, options)
|
72
70
|
ensure
|
73
71
|
output ||= ""
|
74
72
|
error ||= ""
|
75
|
-
self.exitstatus = nil
|
76
73
|
end
|
77
74
|
rescue Errno::ENOENT => err
|
78
75
|
raise NoSuchFileError.new(err.message) if NoSuchFileError.detected?(err.message)
|
@@ -103,109 +100,17 @@ module AwesomeSpawn
|
|
103
100
|
command_result
|
104
101
|
end
|
105
102
|
|
106
|
-
#
|
107
|
-
#
|
108
|
-
# @param [String] command The command to run
|
109
|
-
# @param [Hash,Array] params Optional command line parameters. They can
|
110
|
-
# be passed as a Hash or associative Array. The values are sanitized to
|
111
|
-
# prevent command line injection. Keys as symbols are prefixed with `--`,
|
112
|
-
# and `_` is replaced with `-`.
|
113
|
-
#
|
114
|
-
# - `{:key => "value"}` generates `--key value`
|
115
|
-
# - `{"--key" => "value"}` generates `--key value`
|
116
|
-
# - `{:key= => "value"}` generates `--key=value`
|
117
|
-
# - `{"--key=" => "value"}` generates `--key=value`
|
118
|
-
# - `{:key_name => "value"}` generates `--key-name value`
|
119
|
-
# - `{:key => nil}` generates `--key`
|
120
|
-
# - `{"-f" => ["file1", "file2"]}` generates `-f file1 file2`
|
121
|
-
# - `{nil => ["file1", "file2"]}` generates `file1 file2`
|
122
|
-
#
|
123
|
-
# @return [String] The full command line
|
103
|
+
# (see CommandLineBuilder#build)
|
124
104
|
def build_command_line(command, params = nil)
|
125
|
-
|
126
|
-
"#{command} #{assemble_params(sanitize(params))}"
|
105
|
+
CommandLineBuilder.new.build(command, params)
|
127
106
|
end
|
128
107
|
|
129
108
|
private
|
130
109
|
|
131
|
-
def sanitize(params)
|
132
|
-
return [] if params.nil? || params.empty?
|
133
|
-
params.collect do |k, v|
|
134
|
-
[sanitize_key(k), sanitize_value(v)]
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def sanitize_key(key)
|
139
|
-
case key
|
140
|
-
when Symbol then "--#{key.to_s.tr("_", "-")}"
|
141
|
-
else key
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
def sanitize_value(value)
|
146
|
-
case value
|
147
|
-
when Array then value.collect { |i| i.to_s.shellescape }
|
148
|
-
when NilClass then value
|
149
|
-
else value.to_s.shellescape
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def assemble_params(sanitized_params)
|
154
|
-
sanitized_params.collect do |pair|
|
155
|
-
pair_joiner = pair.first.to_s.end_with?("=") ? "" : " "
|
156
|
-
pair.flatten.compact.join(pair_joiner)
|
157
|
-
end.join(" ")
|
158
|
-
end
|
159
|
-
|
160
|
-
# IO pipes have a maximum size of 64k before blocking,
|
161
|
-
# so we need to read and write synchronously.
|
162
|
-
# http://stackoverflow.com/questions/13829830/ruby-process-spawn-stdout-pipe-buffer-size-limit/13846146#13846146
|
163
|
-
THREAD_SYNC_KEY = "#{self.name}-exitstatus"
|
164
|
-
|
165
110
|
def launch(command, in_data, spawn_options = {})
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
spawn_options[:out] = out_w
|
171
|
-
spawn_options[:err] = err_w
|
172
|
-
spawn_options[:in] = in_r if in_data
|
173
|
-
|
174
|
-
pid = Kernel.spawn(command, spawn_options)
|
175
|
-
|
176
|
-
write_to_input(in_w, in_data) if in_data
|
177
|
-
wait_for_process(pid, out_w, err_w, in_r)
|
178
|
-
wait_for_pipes(out_r, err_r)
|
179
|
-
end
|
180
|
-
|
181
|
-
def write_to_input(in_w, in_data)
|
182
|
-
in_w.write(in_data)
|
183
|
-
in_w.close
|
184
|
-
end
|
185
|
-
|
186
|
-
def wait_for_process(pid, out_w, err_w, in_r)
|
187
|
-
self.exitstatus = :not_done
|
188
|
-
Thread.new(Thread.current) do |parent_thread|
|
189
|
-
_, status = Process.wait2(pid)
|
190
|
-
out_w.close
|
191
|
-
err_w.close
|
192
|
-
in_r.close if in_r
|
193
|
-
parent_thread[THREAD_SYNC_KEY] = status.exitstatus
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
def wait_for_pipes(out_r, err_r)
|
198
|
-
out = out_r.read
|
199
|
-
err = err_r.read
|
200
|
-
sleep(0.1) while exitstatus == :not_done
|
201
|
-
return out, err
|
202
|
-
end
|
203
|
-
|
204
|
-
def exitstatus
|
205
|
-
Thread.current[THREAD_SYNC_KEY]
|
206
|
-
end
|
207
|
-
|
208
|
-
def exitstatus=(value)
|
209
|
-
Thread.current[THREAD_SYNC_KEY] = value
|
111
|
+
spawn_options = spawn_options.merge(:stdin_data => in_data) if in_data
|
112
|
+
output, error, status = Open3.capture3(command, spawn_options)
|
113
|
+
status &&= status.exitstatus
|
114
|
+
return output, error, status
|
210
115
|
end
|
211
116
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require "shellwords"
|
2
|
+
|
3
|
+
module AwesomeSpawn
|
4
|
+
class CommandLineBuilder
|
5
|
+
# Build the full command line.
|
6
|
+
#
|
7
|
+
# @param [String] command The command to run
|
8
|
+
# @param [Hash,Array] params Optional command line parameters. They can
|
9
|
+
# be passed as a Hash or associative Array. The values are sanitized to
|
10
|
+
# prevent command line injection. Keys as Symbols are prefixed with `--`,
|
11
|
+
# and `_` is replaced with `-`.
|
12
|
+
#
|
13
|
+
# - `{:key => "value"}` generates `--key value`
|
14
|
+
# - `[[:key, "value"]]` generates `--key value`
|
15
|
+
# - `{:key= => "value"}` generates `--key=value`
|
16
|
+
# - `[[:key=, "value"]]` generates `--key=value` <br /><br />
|
17
|
+
#
|
18
|
+
# - `{"--key" => "value"}` generates `--key value`
|
19
|
+
# - `[["--key", "value"]]` generates `--key value`
|
20
|
+
# - `{"--key=" => "value"}` generates `--key=value`
|
21
|
+
# - `[["--key=", "value"]]` generates `--key=value` <br /><br />
|
22
|
+
#
|
23
|
+
# - `{:key_name => "value"}` generates `--key-name value`
|
24
|
+
# - `[[:key_name, "value"]]` generates `--key-name value`
|
25
|
+
# - `{:key_name= => "value"}` generates `--key-name=value`
|
26
|
+
# - `[[:key_name=, "value"]]` generates `--key-name=value` <br /><br />
|
27
|
+
#
|
28
|
+
# - `{"-f" => ["file1", "file2"]}` generates `-f file1 file2`
|
29
|
+
# - `[["-f", "file1", "file2"]]` generates `-f file1 file2` <br /><br />
|
30
|
+
#
|
31
|
+
# - `{:key => nil}` generates `--key`
|
32
|
+
# - `[[:key, nil]]` generates `--key`
|
33
|
+
# - `[[:key]]` generates `--key` <br /><br />
|
34
|
+
#
|
35
|
+
# - `{nil => ["file1", "file2"]}` generates `file1 file2`
|
36
|
+
# - `[[nil, ["file1", "file2"]]]` generates `file1 file2`
|
37
|
+
# - `[[nil, "file1", "file2"]]` generates `file1 file2`
|
38
|
+
# - `[["file1", "file2"]]` generates `file1 file2`
|
39
|
+
#
|
40
|
+
# @return [String] The full command line
|
41
|
+
def build(command, params = nil)
|
42
|
+
params = assemble_params(sanitize(params))
|
43
|
+
params.empty? ? command.to_s : "#{command} #{params}"
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def assemble_params(sanitized_params)
|
49
|
+
sanitized_params.collect do |group|
|
50
|
+
joiner = group.first.to_s.end_with?("=") ? "" : " "
|
51
|
+
group.compact.join(joiner)
|
52
|
+
end.join(" ")
|
53
|
+
end
|
54
|
+
|
55
|
+
def sanitize(params)
|
56
|
+
return [] if params.nil? || params.empty?
|
57
|
+
sanitize_associative_array(params.to_a)
|
58
|
+
end
|
59
|
+
|
60
|
+
def sanitize_associative_array(array)
|
61
|
+
array.collect { |a| sanitize_item(a) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def sanitize_item(item)
|
65
|
+
case item
|
66
|
+
when Array then sanitize_key_values(item[0], item[1..-1])
|
67
|
+
when Hash then sanitize_associative_array(item.to_a)
|
68
|
+
else sanitize_item([item])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def sanitize_key_values(key, values)
|
73
|
+
[sanitize_key(key), *sanitize_value(values)]
|
74
|
+
end
|
75
|
+
|
76
|
+
KEY_REGEX = /^((?:--?)?)(.+?)(=?)$/
|
77
|
+
|
78
|
+
def sanitize_key(key)
|
79
|
+
return key if key.nil?
|
80
|
+
key = convert_symbol_key(key) if key.kind_of?(Symbol)
|
81
|
+
|
82
|
+
case key
|
83
|
+
when String
|
84
|
+
return key if key.empty?
|
85
|
+
prefix, key, suffix = KEY_REGEX.match(key)[1..3]
|
86
|
+
"#{prefix}#{sanitize_value(key)}#{suffix}"
|
87
|
+
else
|
88
|
+
sanitize_value(key)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def convert_symbol_key(key)
|
93
|
+
"--#{key.to_s.tr("_", "-")}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def sanitize_value(value)
|
97
|
+
case value
|
98
|
+
when Enumerable
|
99
|
+
value.to_a.collect { |i| sanitize_value(i) }.compact
|
100
|
+
when NilClass
|
101
|
+
value
|
102
|
+
else
|
103
|
+
value.to_s.shellescape
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/spec/awesome_spawn_spec.rb
CHANGED
@@ -4,31 +4,12 @@ require 'pathname' # For Pathname specific specs
|
|
4
4
|
describe AwesomeSpawn do
|
5
5
|
subject { described_class }
|
6
6
|
|
7
|
-
let(:params) do
|
8
|
-
{
|
9
|
-
"--user" => "bob",
|
10
|
-
"--pass" => "P@$sw0^& |<>/-+*d%",
|
11
|
-
"--db" => nil,
|
12
|
-
"--desc=" => "Some Description",
|
13
|
-
:symkey => nil,
|
14
|
-
:symkey_dash => nil,
|
15
|
-
nil => ["pkg1", "some pkg"]
|
16
|
-
}
|
17
|
-
end
|
18
|
-
|
19
|
-
let (:modified_params) do
|
20
|
-
params.to_a + [123, 456].collect {|pool| ["--pool", pool]}
|
21
|
-
end
|
22
|
-
|
23
7
|
shared_examples_for "run" do
|
24
8
|
context "options" do
|
25
|
-
before do
|
26
|
-
subject.stub(:exitstatus => 0)
|
27
|
-
end
|
28
|
-
|
29
9
|
it ":params won't be modified" do
|
10
|
+
params = {:user => "bob"}
|
30
11
|
orig_params = params.dup
|
31
|
-
subject.stub(:launch)
|
12
|
+
subject.stub(:launch => ["", "", 0])
|
32
13
|
subject.send(run_method, "true", :params => params)
|
33
14
|
expect(orig_params).to eq(params)
|
34
15
|
end
|
@@ -49,7 +30,7 @@ describe AwesomeSpawn do
|
|
49
30
|
context "with real execution" do
|
50
31
|
before do
|
51
32
|
# Re-enable actual spawning just for these specs.
|
52
|
-
|
33
|
+
enable_spawning
|
53
34
|
end
|
54
35
|
|
55
36
|
it "command ok exit ok" do
|
@@ -132,71 +113,4 @@ describe AwesomeSpawn do
|
|
132
113
|
let(:run_method) {"run!"}
|
133
114
|
end
|
134
115
|
end
|
135
|
-
|
136
|
-
context ".build_command_line" do
|
137
|
-
it "sanitizes crazy params" do
|
138
|
-
cl = subject.build_command_line("true", modified_params)
|
139
|
-
expect(cl).to eq "true --user bob --pass P@\\$sw0\\^\\&\\ \\|\\<\\>/-\\+\\*d\\% --db --desc=Some\\ Description --symkey --symkey-dash pkg1 some\\ pkg --pool 123 --pool 456"
|
140
|
-
end
|
141
|
-
|
142
|
-
it "handles Symbol keys" do
|
143
|
-
cl = subject.build_command_line("true", :abc => "def")
|
144
|
-
expect(cl).to eq "true --abc def"
|
145
|
-
end
|
146
|
-
|
147
|
-
it "handles Symbol keys with tailing '='" do
|
148
|
-
cl = subject.build_command_line("true", :abc= => "def")
|
149
|
-
expect(cl).to eq "true --abc=def"
|
150
|
-
end
|
151
|
-
|
152
|
-
it "handles Symbol keys with underscore" do
|
153
|
-
cl = subject.build_command_line("true", :abc_def => "ghi")
|
154
|
-
expect(cl).to eq "true --abc-def ghi"
|
155
|
-
end
|
156
|
-
|
157
|
-
it "handles Symbol keys with underscore and tailing '='" do
|
158
|
-
cl = subject.build_command_line("true", :abc_def= => "ghi")
|
159
|
-
expect(cl).to eq "true --abc-def=ghi"
|
160
|
-
end
|
161
|
-
|
162
|
-
it "sanitizes Fixnum array param value" do
|
163
|
-
cl = subject.build_command_line("true", nil => [1])
|
164
|
-
expect(cl).to eq "true 1"
|
165
|
-
end
|
166
|
-
|
167
|
-
it "sanitizes Pathname param value" do
|
168
|
-
cl = subject.build_command_line("true", nil => [Pathname.new("/usr/bin/ruby")])
|
169
|
-
expect(cl).to eq "true /usr/bin/ruby"
|
170
|
-
end
|
171
|
-
|
172
|
-
it "sanitizes Pathname param key" do
|
173
|
-
cl = subject.build_command_line("true", Pathname.new("/usr/bin/ruby") => nil)
|
174
|
-
expect(cl).to eq "true /usr/bin/ruby"
|
175
|
-
end
|
176
|
-
|
177
|
-
it "with params as empty Hash" do
|
178
|
-
cl = subject.build_command_line("true", {})
|
179
|
-
expect(cl).to eq "true"
|
180
|
-
end
|
181
|
-
|
182
|
-
it "with params as nil" do
|
183
|
-
cl = subject.build_command_line("true", nil)
|
184
|
-
expect(cl).to eq "true"
|
185
|
-
end
|
186
|
-
|
187
|
-
it "without params" do
|
188
|
-
cl = subject.build_command_line("true")
|
189
|
-
expect(cl).to eq "true"
|
190
|
-
end
|
191
|
-
|
192
|
-
it "with Pathname command" do
|
193
|
-
cl = subject.build_command_line(Pathname.new("/usr/bin/ruby"))
|
194
|
-
expect(cl).to eq "/usr/bin/ruby"
|
195
|
-
end
|
196
|
-
|
197
|
-
it "with Pathname command and params" do
|
198
|
-
cl = subject.build_command_line(Pathname.new("/usr/bin/ruby"), "-v" => nil)
|
199
|
-
expect(cl).to eq "/usr/bin/ruby -v"
|
200
|
-
end
|
201
|
-
end
|
202
116
|
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AwesomeSpawn::CommandLineBuilder do
|
4
|
+
subject { described_class.new }
|
5
|
+
|
6
|
+
context "#build" do
|
7
|
+
def assert_params(params, expected_params)
|
8
|
+
expect(subject.build("true", params)).to eq "true #{expected_params}".strip
|
9
|
+
end
|
10
|
+
|
11
|
+
it "without params" do
|
12
|
+
expect(subject.build("true")).to eq "true"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "with nil" do
|
16
|
+
expect(subject.build("true", nil)).to eq "true"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "with Pathname command" do
|
20
|
+
actual = subject.build(Pathname.new("/usr/bin/ruby"))
|
21
|
+
expect(actual).to eq "/usr/bin/ruby"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "with Pathname command and params" do
|
25
|
+
actual = subject.build(Pathname.new("/usr/bin/ruby"), "-v" => nil)
|
26
|
+
expect(actual).to eq "/usr/bin/ruby -v"
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with Hash" do
|
30
|
+
it "that is empty" do
|
31
|
+
assert_params({}, "")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "with normal params" do
|
35
|
+
assert_params({"--user" => "bob"}, "--user bob")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "with key with tailing '='" do
|
39
|
+
assert_params({"--user=" => "bob"}, "--user=bob")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "with value requiring sanitization" do
|
43
|
+
assert_params({"--pass" => "P@$s w0rd%"}, "--pass P@\\$s\\ w0rd\\%")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "with key requiring sanitization" do
|
47
|
+
assert_params({"--h&x0r=" => "xxx"}, "--h\\&x0r=xxx")
|
48
|
+
end
|
49
|
+
|
50
|
+
it "with key as Symbol" do
|
51
|
+
assert_params({:abc => "def"}, "--abc def")
|
52
|
+
end
|
53
|
+
|
54
|
+
it "with key as Symbol with tailing '='" do
|
55
|
+
assert_params({:abc= => "def"}, "--abc=def")
|
56
|
+
end
|
57
|
+
|
58
|
+
it "with key as Symbol with underscore" do
|
59
|
+
assert_params({:abc_def => "ghi"}, "--abc-def ghi")
|
60
|
+
end
|
61
|
+
|
62
|
+
it "with key as Symbol with underscore and tailing '='" do
|
63
|
+
assert_params({:abc_def= => "ghi"}, "--abc-def=ghi")
|
64
|
+
end
|
65
|
+
|
66
|
+
it "with key as nil" do
|
67
|
+
assert_params({nil => "def"}, "def")
|
68
|
+
end
|
69
|
+
|
70
|
+
it "with value as nil" do
|
71
|
+
assert_params({"--abc" => nil}, "--abc")
|
72
|
+
end
|
73
|
+
|
74
|
+
it "with key and value nil" do
|
75
|
+
assert_params({nil => nil}, "")
|
76
|
+
end
|
77
|
+
|
78
|
+
it "with key of '--'" do
|
79
|
+
assert_params({"--" => nil}, "--")
|
80
|
+
end
|
81
|
+
|
82
|
+
it "with value as Symbol" do
|
83
|
+
assert_params({"--abc" => :def}, "--abc def")
|
84
|
+
end
|
85
|
+
|
86
|
+
it "with value as Array" do
|
87
|
+
assert_params({"--abc" => ["def", "ghi"]}, "--abc def ghi")
|
88
|
+
end
|
89
|
+
|
90
|
+
it "with value as Fixnum" do
|
91
|
+
assert_params({"--abc" => 1}, "--abc 1")
|
92
|
+
end
|
93
|
+
|
94
|
+
it "with value as Fixnum Array" do
|
95
|
+
assert_params({"--abc" => [1, 2]}, "--abc 1 2")
|
96
|
+
end
|
97
|
+
|
98
|
+
it "with value as Range" do
|
99
|
+
assert_params({"--abc" => (1..4)}, "--abc 1 2 3 4")
|
100
|
+
end
|
101
|
+
|
102
|
+
it "with value as Pathname" do
|
103
|
+
assert_params({"--abc" => Pathname.new("/usr/bin/ruby")}, "--abc /usr/bin/ruby")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "with associative Array" do
|
108
|
+
it "that is empty" do
|
109
|
+
assert_params([], "")
|
110
|
+
end
|
111
|
+
|
112
|
+
it "that is nested empty" do
|
113
|
+
assert_params([[]], "")
|
114
|
+
end
|
115
|
+
|
116
|
+
it "with normal params" do
|
117
|
+
assert_params([["--user", "bob"]], "--user bob")
|
118
|
+
end
|
119
|
+
|
120
|
+
it "with key with tailing '='" do
|
121
|
+
assert_params([["--user=", "bob"]], "--user=bob")
|
122
|
+
end
|
123
|
+
|
124
|
+
it "with value requiring sanitization" do
|
125
|
+
assert_params([["--pass", "P@$s w0rd%"]], "--pass P@\\$s\\ w0rd\\%")
|
126
|
+
end
|
127
|
+
|
128
|
+
it "with key requiring sanitization" do
|
129
|
+
assert_params([["--h&x0r=", "xxx"]], "--h\\&x0r=xxx")
|
130
|
+
end
|
131
|
+
|
132
|
+
it "with key as Symbol" do
|
133
|
+
assert_params([[:abc, "def"]], "--abc def")
|
134
|
+
end
|
135
|
+
|
136
|
+
it "with key as Symbol with tailing '='" do
|
137
|
+
assert_params([[:abc=, "def"]], "--abc=def")
|
138
|
+
end
|
139
|
+
|
140
|
+
it "with key as Symbol with underscore" do
|
141
|
+
assert_params([[:abc_def, "ghi"]], "--abc-def ghi")
|
142
|
+
end
|
143
|
+
|
144
|
+
it "with key as Symbol with underscore and tailing '='" do
|
145
|
+
assert_params([[:abc_def=, "ghi"]], "--abc-def=ghi")
|
146
|
+
end
|
147
|
+
|
148
|
+
it "with key as nil" do
|
149
|
+
assert_params([[nil, "def"]], "def")
|
150
|
+
end
|
151
|
+
|
152
|
+
it "with value as nil" do
|
153
|
+
assert_params([["--abc", nil]], "--abc")
|
154
|
+
end
|
155
|
+
|
156
|
+
it "with key and value nil" do
|
157
|
+
assert_params([[nil, nil]], "")
|
158
|
+
end
|
159
|
+
|
160
|
+
it "with key as nil and multiple values" do
|
161
|
+
assert_params([[nil, "def", "ghi"]], "def ghi")
|
162
|
+
end
|
163
|
+
|
164
|
+
it "with key of '--'" do
|
165
|
+
assert_params([["--", nil]], "--")
|
166
|
+
end
|
167
|
+
|
168
|
+
it "key alone" do
|
169
|
+
assert_params([["--abc"]], "--abc")
|
170
|
+
end
|
171
|
+
|
172
|
+
it "key as Symbol alone" do
|
173
|
+
assert_params([[:abc]], "--abc")
|
174
|
+
end
|
175
|
+
|
176
|
+
it "key as a bareword" do
|
177
|
+
assert_params(["--abc"], "--abc")
|
178
|
+
end
|
179
|
+
|
180
|
+
it "key as bareword Symbol" do
|
181
|
+
assert_params([:abc], "--abc")
|
182
|
+
end
|
183
|
+
|
184
|
+
it "value as a bareword" do
|
185
|
+
assert_params(["abc"], "abc")
|
186
|
+
end
|
187
|
+
|
188
|
+
it "with value as Symbol" do
|
189
|
+
assert_params([["--abc" => :def]], "--abc def")
|
190
|
+
end
|
191
|
+
|
192
|
+
it "with value as Array" do
|
193
|
+
assert_params([["--abc", ["def", "ghi"]]], "--abc def ghi")
|
194
|
+
end
|
195
|
+
|
196
|
+
it "with value as flattened Array" do
|
197
|
+
assert_params([["--abc", "def", "ghi"]], "--abc def ghi")
|
198
|
+
end
|
199
|
+
|
200
|
+
it "with value as Fixnum" do
|
201
|
+
assert_params([["--abc", 1]], "--abc 1")
|
202
|
+
end
|
203
|
+
|
204
|
+
it "with value as Fixnum Array" do
|
205
|
+
assert_params([["--abc", [1, 2]]], "--abc 1 2")
|
206
|
+
end
|
207
|
+
|
208
|
+
it "with value as Range" do
|
209
|
+
assert_params([["--abc", (1..4)]], "--abc 1 2 3 4")
|
210
|
+
end
|
211
|
+
|
212
|
+
it "with value as Pathname" do
|
213
|
+
assert_params([["--abc", Pathname.new("/usr/bin/ruby")]], "--abc /usr/bin/ruby")
|
214
|
+
end
|
215
|
+
|
216
|
+
it "with duplicate keys" do
|
217
|
+
assert_params([["--abc", 1], ["--abc", 2]], "--abc 1 --abc 2")
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context "with multiple params" do # real-world cases
|
222
|
+
let(:expected) { "log feature -E --oneline --grep abc" }
|
223
|
+
|
224
|
+
it "as full Hash" do
|
225
|
+
params = {"log" => nil, "feature" => nil, "-E" => nil, :oneline => nil, "--grep" => "abc"}
|
226
|
+
assert_params(params, expected)
|
227
|
+
end
|
228
|
+
|
229
|
+
it "as grouped Hash" do
|
230
|
+
params = {nil => ["log", "feature"], "-E" => nil, :oneline => nil, :grep => "abc"}
|
231
|
+
assert_params(params, expected)
|
232
|
+
end
|
233
|
+
|
234
|
+
it "as associative Array" do
|
235
|
+
params = [[nil, "log", "feature"], ["-E", nil], [:oneline, nil], [:grep, "abc"]]
|
236
|
+
assert_params(params, expected)
|
237
|
+
end
|
238
|
+
|
239
|
+
it "as associative Array without nil values" do
|
240
|
+
params = [["log", "feature"], ["-E"], [:oneline], [:grep, "abc"]]
|
241
|
+
assert_params(params, expected)
|
242
|
+
end
|
243
|
+
|
244
|
+
it "as mixed Array with barewords" do
|
245
|
+
params = ["log", "feature", "-E", :oneline, [:grep, "abc"]]
|
246
|
+
assert_params(params, expected)
|
247
|
+
end
|
248
|
+
|
249
|
+
it "as mixed Array with nested Hashes" do
|
250
|
+
params = ["log", "feature", "-E", :oneline, {:grep => "abc"}]
|
251
|
+
assert_params(params, expected)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
data/spec/command_result_spec.rb
CHANGED
@@ -5,9 +5,19 @@ describe AwesomeSpawn::CommandResult do
|
|
5
5
|
it "will not display sensitive information" do
|
6
6
|
str = described_class.new("aaa", "bbb", "ccc", 0).inspect
|
7
7
|
|
8
|
-
expect(str.include
|
9
|
-
expect(str.include
|
10
|
-
expect(str.include
|
8
|
+
expect(str).to_not include("aaa")
|
9
|
+
expect(str).to_not include("bbb")
|
10
|
+
expect(str).to_not include("ccc")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "will know if a command succeeded" do
|
14
|
+
expect(described_class.new("c", "o", "e", 0)).to be_a_success
|
15
|
+
expect(described_class.new("c", "o", "e", 0)).not_to be_a_failure
|
16
|
+
end
|
17
|
+
|
18
|
+
it "will know if a command failed" do
|
19
|
+
expect(described_class.new("c", "o", "e", 1)).to be_a_failure
|
20
|
+
expect(described_class.new("c", "o", "e", 1)).not_to be_a_success
|
11
21
|
end
|
12
22
|
end
|
13
23
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -15,9 +15,15 @@ RSpec.configure do |config|
|
|
15
15
|
# --seed 1234
|
16
16
|
config.order = 'random'
|
17
17
|
|
18
|
-
config.before
|
19
|
-
|
20
|
-
|
18
|
+
config.before { disable_spawning }
|
19
|
+
end
|
20
|
+
|
21
|
+
def disable_spawning
|
22
|
+
Open3.stub(:capture3).and_raise("Spawning is not permitted in specs. Please change your spec to use expectations/stubs.")
|
23
|
+
end
|
24
|
+
|
25
|
+
def enable_spawning
|
26
|
+
Open3.stub(:capture3).and_call_original
|
21
27
|
end
|
22
28
|
|
23
29
|
begin
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: awesome_spawn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2014-
|
15
|
+
date: 2014-07-09 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: bundler
|
@@ -90,6 +90,7 @@ extensions: []
|
|
90
90
|
extra_rdoc_files: []
|
91
91
|
files:
|
92
92
|
- lib/awesome_spawn.rb
|
93
|
+
- lib/awesome_spawn/command_line_builder.rb
|
93
94
|
- lib/awesome_spawn/command_result.rb
|
94
95
|
- lib/awesome_spawn/command_result_error.rb
|
95
96
|
- lib/awesome_spawn/no_such_file_error.rb
|
@@ -98,6 +99,7 @@ files:
|
|
98
99
|
- README.md
|
99
100
|
- LICENSE.txt
|
100
101
|
- spec/awesome_spawn_spec.rb
|
102
|
+
- spec/command_line_builder_spec.rb
|
101
103
|
- spec/command_result_spec.rb
|
102
104
|
- spec/spec_helper.rb
|
103
105
|
- .rspec
|
@@ -128,6 +130,7 @@ specification_version: 3
|
|
128
130
|
summary: AwesomeSpawn is a module that provides some useful features over Ruby's Kernel.spawn.
|
129
131
|
test_files:
|
130
132
|
- spec/awesome_spawn_spec.rb
|
133
|
+
- spec/command_line_builder_spec.rb
|
131
134
|
- spec/command_result_spec.rb
|
132
135
|
- spec/spec_helper.rb
|
133
136
|
- .rspec
|