awesome_spawn 1.1.1 → 1.2.0
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/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
|