awesome_spawn 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of awesome_spawn might be problematic. Click here for more details.

data/README.md CHANGED
@@ -11,6 +11,7 @@ AwesomeSpawn is a module that provides some useful features over Ruby's Kernel.s
11
11
  Some additional features include...
12
12
 
13
13
  - Parameter passing as a Hash or associative Array sanitizing them to prevent command line injection.
14
+ - Ability to pass the contents of stdin as a String.
14
15
  - Results returned as an object giving access to the output stream, error stream, and exit status.
15
16
  - Optionally raising an exception when exit status is not 0.
16
17
 
data/lib/awesome_spawn.rb CHANGED
@@ -31,26 +31,35 @@ module AwesomeSpawn
31
31
  # result.exit_status # => 1
32
32
  #
33
33
  # @example With parameters sanitized
34
- # result = AwesomeSpawn.run('echo', :params => {"--out" => "; rm /some/file"})
34
+ # result = AwesomeSpawn.run('echo', :params => {:out => "; rm /some/file"})
35
35
  # # => #<AwesomeSpawn::CommandResult:0x007ff64baa6650 @exit_status=0>
36
36
  # result.command_line
37
37
  # # => "echo --out \\;\\ rm\\ /some/file"
38
38
  #
39
+ # @example With data to be passed on stdin
40
+ # result = AwesomeSpawn.run('cat', :in_data => "line1\nline2")
41
+ # => #<AwesomeSpawn::CommandResult:0x007fff05b0ab10 @exit_status=0>
42
+ # result.output
43
+ # => "line1\nline2"
44
+ #
39
45
  # @param [String] command The command to run
40
- # @param [Hash] options The options for running the command
46
+ # @param [Hash] options The options for running the command. Also accepts any
47
+ # option that can be passed to Kernel.spawn, except `:out` and `:err`.
41
48
  # @option options [Hash,Array] :params The command line parameters. See
42
- # {#build_command_line} for how to specify params. Alternate key
43
- # `:parameters`.
44
- # @option options [String] :chdir see the `:chdir` parameter for Kernel.spawn
49
+ # {#build_command_line} for how to specify params.
50
+ # @option options [String] :in_data Data to be passed on stdin. If this option
51
+ # is specified you cannot specify `:in`.
45
52
  #
46
53
  # @raise [NoSuchFileError] if the `command` is not found
47
54
  # @return [CommandResult] the output stream, error stream, and exit status
48
55
  # @see http://ruby-doc.org/core/Kernel.html#method-i-spawn Kernel.spawn
49
56
  def run(command, options = {})
50
- params = options[:params] || options[:parameters]
51
-
52
- launch_params = {}
53
- launch_params[:chdir] = options[:chdir] if options[:chdir]
57
+ raise ArgumentError, "options cannot contain :out" if options.include?(:out)
58
+ raise ArgumentError, "options cannot contain :err" if options.include?(:err)
59
+ raise ArgumentError, "options cannot contain :in if :in_data is specified" if options.include?(:in) && options.include?(:in_data)
60
+ options = options.dup
61
+ params = options.delete(:params)
62
+ in_data = options.delete(:in_data)
54
63
 
55
64
  output = ""
56
65
  error = ""
@@ -58,7 +67,7 @@ module AwesomeSpawn
58
67
  command_line = build_command_line(command, params)
59
68
 
60
69
  begin
61
- output, error = launch(command_line, launch_params)
70
+ output, error = launch(command_line, in_data, options)
62
71
  status = exitstatus
63
72
  ensure
64
73
  output ||= ""
@@ -99,11 +108,15 @@ module AwesomeSpawn
99
108
  # @param [String] command The command to run
100
109
  # @param [Hash,Array] params Optional command line parameters. They can
101
110
  # be passed as a Hash or associative Array. The values are sanitized to
102
- # prevent command line injection.
111
+ # prevent command line injection. Keys as symbols are prefixed with `--`,
112
+ # and `_` is replaced with `-`.
103
113
  #
114
+ # - `{:key => "value"}` generates `--key value`
104
115
  # - `{"--key" => "value"}` generates `--key value`
116
+ # - `{:key= => "value"}` generates `--key=value`
105
117
  # - `{"--key=" => "value"}` generates `--key=value`
106
- # - `{"--key" => nil}` generates `--key`
118
+ # - `{:key_name => "value"}` generates `--key-name value`
119
+ # - `{:key => nil}` generates `--key`
107
120
  # - `{"-f" => ["file1", "file2"]}` generates `-f file1 file2`
108
121
  # - `{nil => ["file1", "file2"]}` generates `file1 file2`
109
122
  #
@@ -118,12 +131,22 @@ module AwesomeSpawn
118
131
  def sanitize(params)
119
132
  return [] if params.nil? || params.empty?
120
133
  params.collect do |k, v|
121
- v = case v
122
- when Array; v.collect {|i| i.to_s.shellescape}
123
- when NilClass; v
124
- else v.to_s.shellescape
125
- end
126
- [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
127
150
  end
128
151
  end
129
152
 
@@ -139,20 +162,34 @@ module AwesomeSpawn
139
162
  # http://stackoverflow.com/questions/13829830/ruby-process-spawn-stdout-pipe-buffer-size-limit/13846146#13846146
140
163
  THREAD_SYNC_KEY = "#{self.name}-exitstatus"
141
164
 
142
- def launch(command, spawn_options = {})
165
+ def launch(command, in_data, spawn_options = {})
143
166
  out_r, out_w = IO.pipe
144
167
  err_r, err_w = IO.pipe
145
- pid = Kernel.spawn(command, {:err => err_w, :out => out_w}.merge(spawn_options))
146
- wait_for_process(pid, out_w, err_w)
168
+ in_r, in_w = IO.pipe if in_data
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)
147
178
  wait_for_pipes(out_r, err_r)
148
179
  end
149
180
 
150
- def wait_for_process(pid, out_w, err_w)
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)
151
187
  self.exitstatus = :not_done
152
188
  Thread.new(Thread.current) do |parent_thread|
153
189
  _, status = Process.wait2(pid)
154
190
  out_w.close
155
191
  err_w.close
192
+ in_r.close if in_r
156
193
  parent_thread[THREAD_SYNC_KEY] = status.exitstatus
157
194
  end
158
195
  end
@@ -1,3 +1,3 @@
1
1
  module AwesomeSpawn
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -6,11 +6,13 @@ describe AwesomeSpawn do
6
6
 
7
7
  let(:params) do
8
8
  {
9
- "--user" => "bob",
10
- "--pass" => "P@$sw0^& |<>/-+*d%",
11
- "--db" => nil,
12
- "--desc=" => "Some Description",
13
- nil => ["pkg1", "some pkg"]
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"]
14
16
  }
15
17
  end
16
18
 
@@ -19,21 +21,28 @@ describe AwesomeSpawn do
19
21
  end
20
22
 
21
23
  shared_examples_for "run" do
22
- context "parameters" do
24
+ context "options" do
23
25
  before do
24
26
  subject.stub(:exitstatus => 0)
25
27
  end
26
28
 
27
- it "won't modify caller params" do
29
+ it ":params won't be modified" do
28
30
  orig_params = params.dup
29
31
  subject.stub(:launch)
30
32
  subject.send(run_method, "true", :params => params)
31
33
  expect(orig_params).to eq(params)
32
34
  end
33
35
 
34
- it "supports spawn's chdir option" do
35
- subject.should_receive(:launch).once.with("true", {:chdir => ".."})
36
- subject.send(run_method, "true", :chdir => "..")
36
+ it ":in_data cannot be passed with :in" do
37
+ expect { subject.send(run_method, "true", :in_data => "XXXXX", :in => "/dev/null") } .to raise_error(ArgumentError)
38
+ end
39
+
40
+ it ":out is not supported" do
41
+ expect { subject.send(run_method, "true", :out => "/dev/null") }.to raise_error(ArgumentError)
42
+ end
43
+
44
+ it ":err is not supported" do
45
+ expect { subject.send(run_method, "true", :err => "/dev/null") }.to raise_error(ArgumentError)
37
46
  end
38
47
  end
39
48
 
@@ -66,6 +75,20 @@ describe AwesomeSpawn do
66
75
  expect { subject.send(run_method, "XXXXX --user=bob") }.to raise_error(Errno::ENOENT, "No such file or directory - XXXXX")
67
76
  end
68
77
 
78
+ context "with option" do
79
+ it ":chdir" do
80
+ result = subject.send(run_method, "pwd", :chdir => "..")
81
+ expect(result.exit_status).to eq(0)
82
+ expect(result.output.chomp).to eq(File.expand_path("..", Dir.pwd))
83
+ end
84
+
85
+ it ":in_data" do
86
+ result = subject.send(run_method, "cat", :in_data => "line1\nline2")
87
+ expect(result.exit_status).to eq(0)
88
+ expect(result.output).to eq("line1\nline2")
89
+ end
90
+ end
91
+
69
92
  context "#exit_status" do
70
93
  it "command ok exit ok" do
71
94
  expect(subject.send(run_method, "true").exit_status).to eq(0)
@@ -113,7 +136,27 @@ describe AwesomeSpawn do
113
136
  context ".build_command_line" do
114
137
  it "sanitizes crazy params" do
115
138
  cl = subject.build_command_line("true", modified_params)
116
- expect(cl).to eq "true --user bob --pass P@\\$sw0\\^\\&\\ \\|\\<\\>/-\\+\\*d\\% --db --desc=Some\\ Description pkg1 some\\ pkg --pool 123 --pool 456"
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"
117
160
  end
118
161
 
119
162
  it "sanitizes Fixnum array param value" do
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.0.0
4
+ version: 1.1.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-01-04 00:00:00.000000000 Z
15
+ date: 2014-02-03 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: bundler
@@ -151,18 +151,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
151
151
  - - ! '>='
152
152
  - !ruby/object:Gem::Version
153
153
  version: '0'
154
- segments:
155
- - 0
156
- hash: -1223152174146401530
157
154
  required_rubygems_version: !ruby/object:Gem::Requirement
158
155
  none: false
159
156
  requirements:
160
157
  - - ! '>='
161
158
  - !ruby/object:Gem::Version
162
159
  version: '0'
163
- segments:
164
- - 0
165
- hash: -1223152174146401530
166
160
  requirements: []
167
161
  rubyforge_project:
168
162
  rubygems_version: 1.8.23