rush 0.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -31,7 +31,7 @@ require 'rake/rdoctask'
31
31
  require 'fileutils'
32
32
  include FileUtils
33
33
 
34
- version = "0.3"
34
+ version = "0.4"
35
35
  name = "rush"
36
36
 
37
37
  spec = Gem::Specification.new do |s|
data/bin/rush CHANGED
@@ -3,5 +3,11 @@
3
3
  require File.dirname(__FILE__) + '/../lib/rush'
4
4
  require File.dirname(__FILE__) + '/../lib/rush/shell'
5
5
 
6
- Rush::Shell.new.run
6
+ shell = Rush::Shell.new
7
+
8
+ if ARGV.size > 0
9
+ shell.execute ARGV.join(' ')
10
+ else
11
+ shell.run
12
+ end
7
13
 
data/bin/rushd CHANGED
File without changes
@@ -19,6 +19,7 @@ require 'rush/string_ext'
19
19
  require 'rush/fixnum_ext'
20
20
  require 'rush/array_ext'
21
21
  require 'rush/process'
22
+ require 'rush/process_set'
22
23
  require 'rush/local'
23
24
  require 'rush/remote'
24
25
  require 'rush/ssh_tunnel'
@@ -41,26 +41,39 @@ class Rush::Box
41
41
  filesystem[key]
42
42
  end
43
43
 
44
- # Get the list of processes currently running on the box. Returns an array
45
- # of Rush::Process.
44
+ # Get the list of processes running on the box, not unlike "ps aux" in bash.
45
+ # Returns a Rush::ProcessSet.
46
46
  def processes
47
- connection.processes.map do |ps|
48
- Rush::Process.new(ps, self)
49
- end
47
+ Rush::ProcessSet.new(
48
+ connection.processes.map do |ps|
49
+ Rush::Process.new(ps, self)
50
+ end
51
+ )
50
52
  end
51
53
 
52
- # Execute a command in the standard unix shell. Options:
54
+ # Execute a command in the standard unix shell. Returns the contents of
55
+ # stdout if successful, or raises Rush::BashFailed with the output of stderr
56
+ # if the shell returned a non-zero value. Options:
53
57
  #
54
58
  # :user => unix username to become via sudo
55
59
  # :env => hash of environment variables
60
+ # :background => run in the background (returns Rush::Process instead of stdout)
56
61
  #
57
- # Example:
62
+ # Examples:
58
63
  #
59
64
  # box.bash '/etc/init.d/mysql restart', :user => 'root'
60
65
  # box.bash 'rake db:migrate', :user => 'www', :env => { :RAILS_ENV => 'production' }
66
+ # box.bash 'mongrel_rails start', :background => true
61
67
  #
62
68
  def bash(command, options={})
63
- connection.bash(command_with_environment(command, options[:env]), options[:user])
69
+ cmd_with_env = command_with_environment(command, options[:env])
70
+
71
+ if options[:background]
72
+ pid = connection.bash(cmd_with_env, options[:user], true)
73
+ processes.find_by_pid(pid)
74
+ else
75
+ connection.bash(cmd_with_env, options[:user], false)
76
+ end
64
77
  end
65
78
 
66
79
  def command_with_environment(command, env) # :nodoc:
@@ -1,12 +1,14 @@
1
1
  module Rush
2
2
  # Base class for all rush exceptions.
3
- class Exception < ::Exception; end
3
+ class Exception < ::RuntimeError; end
4
4
 
5
5
  # Client was not authorized by remote server; check credentials.
6
6
  class NotAuthorized < Exception; end
7
7
 
8
- # Failed to transmit to the remote server; check if the ssh tunnel is alive,
9
- # and rushd is listening on the other end.
8
+ # rushd is not running on the remote box.
9
+ class RushdNotRunning < Exception; end
10
+
11
+ # An unrecognized status code was returned by rushd.
10
12
  class FailedTransmit < Exception; end
11
13
 
12
14
  # The entry (file or dir) referenced does not exist. Message is the entry's full path.
@@ -133,7 +133,7 @@ class Rush::Connection::Local
133
133
  # Get the list of processes as an array of hashes.
134
134
  def processes
135
135
  if ::File.directory? "/proc"
136
- linux_processes
136
+ resolve_unix_uids(linux_processes)
137
137
  elsif ::File.directory? "C:/WINDOWS"
138
138
  windows_processes
139
139
  else
@@ -154,6 +154,33 @@ class Rush::Connection::Local
154
154
  list
155
155
  end
156
156
 
157
+ def resolve_unix_uids(list)
158
+ @uid_map = {} # reset the cache between uid resolutions.
159
+ list.each do |process|
160
+ process[:user] = resolve_unix_uid_to_user(process[:uid])
161
+ end
162
+ list
163
+ end
164
+
165
+ # resolve uid to user
166
+ def resolve_unix_uid_to_user(uid)
167
+ require 'etc'
168
+
169
+ @uid_map ||= {}
170
+ uid = uid.to_i
171
+
172
+ return @uid_map[uid] if !@uid_map[uid].nil?
173
+
174
+ begin
175
+ record = Etc.getpwuid(uid)
176
+ rescue ArgumentError
177
+ return nil
178
+ end
179
+
180
+ @uid_map[uid] = record.name
181
+ @uid_map[uid]
182
+ end
183
+
157
184
  # Read a single file in /proc and store the parsed values in a hash suitable
158
185
  # for use in the Rush::Process#new.
159
186
  def read_proc_file(file)
@@ -256,15 +283,20 @@ class Rush::Connection::Local
256
283
  end
257
284
 
258
285
  ::Process.kill('KILL', pid) rescue nil
286
+
287
+ rescue Errno::ESRCH
288
+ # if it's dead, great - do nothing
259
289
  end
260
290
 
261
- def bash(command, user=nil)
291
+ def bash(command, user=nil, background=false)
292
+ return bash_background(command, user) if background
293
+
262
294
  require 'session'
263
295
 
264
296
  sh = Session::Bash.new
265
297
 
266
298
  if user and user != ""
267
- out, err = sh.execute "sudo -u #{user} bash", :stdin => command
299
+ out, err = sh.execute "cd /; sudo -H -u #{user} bash", :stdin => command
268
300
  else
269
301
  out, err = sh.execute command
270
302
  end
@@ -277,6 +309,27 @@ class Rush::Connection::Local
277
309
  out
278
310
  end
279
311
 
312
+ def bash_background(command, user)
313
+ inpipe, outpipe = IO.pipe
314
+
315
+ pid = fork do
316
+ outpipe.write command
317
+ outpipe.close
318
+ STDIN.reopen(inpipe)
319
+
320
+ if user and user != ''
321
+ exec "cd /; sudo -H -u #{user} bash"
322
+ else
323
+ exec "bash"
324
+ end
325
+ end
326
+ outpipe.close
327
+
328
+ Process::detach pid
329
+
330
+ pid
331
+ end
332
+
280
333
  ####################################
281
334
 
282
335
  # Raised when the action passed in by RushServer is not known.
@@ -304,7 +357,7 @@ class Rush::Connection::Local
304
357
  when 'processes' then YAML.dump(processes)
305
358
  when 'process_alive' then process_alive(params[:pid]) ? '1' : '0'
306
359
  when 'kill_process' then kill_process(params[:pid].to_i)
307
- when 'bash' then bash(params[:payload], params[:user])
360
+ when 'bash' then bash(params[:payload], params[:user], params[:background] == 'true')
308
361
  else
309
362
  raise UnknownAction
310
363
  end
@@ -1,6 +1,6 @@
1
1
  # An array of these objects is returned by Rush::Box#processes.
2
2
  class Rush::Process
3
- attr_reader :box, :pid, :uid, :parent_pid, :command, :cmdline, :mem, :cpu
3
+ attr_reader :box, :pid, :uid, :parent_pid, :command, :cmdline, :mem, :cpu, :user
4
4
 
5
5
  # params is a hash returned by the system-specific method of looking up the
6
6
  # process list.
@@ -9,6 +9,7 @@ class Rush::Process
9
9
 
10
10
  @pid = params[:pid].to_i
11
11
  @uid = params[:uid].to_i
12
+ @user = params[:user]
12
13
  @command = params[:command]
13
14
  @cmdline = params[:cmdline]
14
15
  @mem = params[:mem]
@@ -0,0 +1,62 @@
1
+ # A container for processes that behaves like an array, and adds process-specific operations
2
+ # on the entire set, like kill.
3
+ #
4
+ # Example:
5
+ #
6
+ # processes.filter(:cmdline => /mongrel_rails/).kill
7
+ #
8
+ class Rush::ProcessSet
9
+ attr_reader :processes
10
+
11
+ def initialize(processes)
12
+ @processes = processes
13
+ end
14
+
15
+ # Filter by any field that the process responds to. Specify an exact value,
16
+ # or a regular expression. All conditions are put together as a boolean
17
+ # AND, so these two statements are equivalent:
18
+ #
19
+ # processes.filter(:uid => 501).filter(:cmdline => /ruby/)
20
+ # processes.filter(:uid => 501, :cmdline => /ruby/)
21
+ #
22
+ def filter(conditions)
23
+ Rush::ProcessSet.new(
24
+ processes.select do |p|
25
+ conditions.all? do |key, value|
26
+ value.class == Regexp ?
27
+ value.match(p.send(key)) :
28
+ p.send(key) == value
29
+ end
30
+ end
31
+ )
32
+ end
33
+
34
+ # Kill all processes in the set.
35
+ def kill
36
+ processes.each { |p| p.kill }
37
+ end
38
+
39
+ # Check status of all processes in the set, returns an array of booleans.
40
+ def alive?
41
+ processes.map { |p| p.alive? }
42
+ end
43
+
44
+ include Enumerable
45
+
46
+ def each
47
+ processes.each { |p| yield p }
48
+ end
49
+
50
+ def ==(other)
51
+ if other.class == self.class
52
+ other.processes == processes
53
+ else
54
+ to_a == other
55
+ end
56
+ end
57
+
58
+ # All other messages (like size or first) are passed through to the array.
59
+ def method_missing(meth, *args)
60
+ processes.send(meth, *args)
61
+ end
62
+ end
@@ -63,7 +63,7 @@ class Rush::Connection::Remote
63
63
  end
64
64
 
65
65
  def size(full_path)
66
- transmit(:action => 'size', :full_path => full_path)
66
+ transmit(:action => 'size', :full_path => full_path).to_i
67
67
  end
68
68
 
69
69
  def processes
@@ -78,8 +78,8 @@ class Rush::Connection::Remote
78
78
  transmit(:action => 'kill_process', :pid => pid)
79
79
  end
80
80
 
81
- def bash(command, user)
82
- transmit(:action => 'bash', :payload => command, :user => user)
81
+ def bash(command, user, background)
82
+ transmit(:action => 'bash', :payload => command, :user => user, :background => background)
83
83
  end
84
84
 
85
85
  # Given a hash of parameters (converted by the method call on the connection
@@ -104,6 +104,8 @@ class Rush::Connection::Remote
104
104
  res = http.request(req, payload)
105
105
  process_result(res.code, res.body)
106
106
  end
107
+ rescue EOFError
108
+ raise Rush::RushdNotRunning
107
109
  end
108
110
 
109
111
  # Take the http result of a transmit and raise an error, or return the body
@@ -31,6 +31,21 @@ module Rush
31
31
  Array.class_eval commands
32
32
  end
33
33
 
34
+ # Run a single command.
35
+ def execute(cmd)
36
+ res = eval(cmd, @pure_binding)
37
+ $last_res = res
38
+ eval("_ = $last_res", @pure_binding)
39
+ print_result res
40
+ rescue Rush::Exception => e
41
+ puts "Exception #{e.class} -> #{e.message}"
42
+ rescue ::Exception => e
43
+ puts "Exception #{e.class} -> #{e.message}"
44
+ e.backtrace.each do |t|
45
+ puts " #{::File.expand_path(t)}"
46
+ end
47
+ end
48
+
34
49
  # Run the interactive shell using readline.
35
50
  def run
36
51
  loop do
@@ -40,19 +55,7 @@ module Rush
40
55
  next if cmd == ""
41
56
  Readline::HISTORY.push(cmd)
42
57
 
43
- begin
44
- res = eval(cmd, @pure_binding)
45
- $last_res = res
46
- eval("_ = $last_res", @pure_binding)
47
- print_result res
48
- rescue Rush::Exception => e
49
- puts "Exception #{e.class} -> #{e.message}"
50
- rescue ::Exception => e
51
- puts "Exception #{e.class} -> #{e.message}"
52
- e.backtrace.each do |t|
53
- puts " #{::File.expand_path(t)}"
54
- end
55
- end
58
+ execute(cmd)
56
59
  end
57
60
  end
58
61
 
@@ -67,10 +70,6 @@ module Rush
67
70
  def print_result(res)
68
71
  if res.kind_of? String
69
72
  puts res
70
- elsif res.kind_of? Array
71
- res.each do |item|
72
- puts item
73
- end
74
73
  elsif res.kind_of? Rush::SearchResults
75
74
  widest = res.entries.map { |k| k.full_path.length }.max
76
75
  res.entries_with_lines.each do |entry, lines|
@@ -85,6 +84,21 @@ module Rush
85
84
  print "\n"
86
85
  end
87
86
  puts "#{res.entries.size} matching files with #{res.lines.size} matching lines"
87
+ elsif res.respond_to? :each
88
+ counts = {}
89
+ res.each do |item|
90
+ puts item
91
+ counts[item.class] ||= 0
92
+ counts[item.class] += 1
93
+ end
94
+ if counts == {}
95
+ puts "=> (empty set)"
96
+ else
97
+ count_s = counts.map do |klass, count|
98
+ "#{count} x #{klass}"
99
+ end.join(', ')
100
+ puts "=> #{count_s}"
101
+ end
88
102
  else
89
103
  puts "=> #{res.inspect}"
90
104
  end
@@ -22,15 +22,21 @@ describe Rush::Box do
22
22
  end
23
23
 
24
24
  it "executes bash commands" do
25
- @box.connection.should_receive(:bash).with('cmd', nil).and_return('output')
25
+ @box.connection.should_receive(:bash).with('cmd', nil, false).and_return('output')
26
26
  @box.bash('cmd').should == 'output'
27
27
  end
28
28
 
29
29
  it "executes bash commands with an optional user" do
30
- @box.connection.should_receive(:bash).with('cmd', 'user')
30
+ @box.connection.should_receive(:bash).with('cmd', 'user', false)
31
31
  @box.bash('cmd', :user => 'user')
32
32
  end
33
33
 
34
+ it "executes bash commands in the background, returning a Rush::Process" do
35
+ @box.connection.should_receive(:bash).with('cmd', nil, true).and_return(123)
36
+ @box.stub!(:processes).and_return([ mock('ps', :pid => 123) ])
37
+ @box.bash('cmd', :background => true).pid.should == 123
38
+ end
39
+
34
40
  it "builds a script of environment variables to prefix the bash command" do
35
41
  @box.command_with_environment('cmd', { :a => 'b' }).should == "export a='b'\ncmd"
36
42
  end
@@ -152,8 +152,8 @@ describe Rush::Dir do
152
152
  end
153
153
 
154
154
  it "passes bash options (e.g., :user) through to the box bash command" do
155
- @box.should_receive(:bash).with('cmd', 'options')
156
- @box.bash('cmd', 'options')
155
+ @box.should_receive(:bash).with('cmd', :opt1 => 1, :opt2 => 2)
156
+ @box.bash('cmd', :opt1 => 1, :opt2 => 2)
157
157
  end
158
158
 
159
159
  end
@@ -95,9 +95,14 @@ describe Rush::Connection::Local do
95
95
  @con.receive(:action => 'kill_process', :pid => '123')
96
96
  end
97
97
 
98
- it "receive -> bash" do
99
- @con.should_receive(:bash).with('cmd', 'user').and_return('output')
100
- @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user').should == 'output'
98
+ it "receive -> bash (foreground)" do
99
+ @con.should_receive(:bash).with('cmd', 'user', false).and_return('output')
100
+ @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'false').should == 'output'
101
+ end
102
+
103
+ it "receive -> bash (background)" do
104
+ @con.should_receive(:bash).with('cmd', 'user', true).and_return('output')
105
+ @con.receive(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'true').should == 'output'
101
106
  end
102
107
 
103
108
  it "receive -> unknown action exception" do
@@ -263,6 +268,11 @@ EOPS
263
268
  @con.kill_process(123)
264
269
  end
265
270
 
271
+ it "does not raise an error if the process is already dead" do
272
+ ::Process.should_receive(:kill).and_raise(Errno::ESRCH)
273
+ lambda { @con.kill_process(123) }.should_not raise_error
274
+ end
275
+
266
276
  it "executes a bash command, returning stdout when successful" do
267
277
  @con.bash("echo test").should == "test\n"
268
278
  end
@@ -272,7 +282,11 @@ EOPS
272
282
  end
273
283
 
274
284
  it "executes a bash command as another user using sudo" do
275
- @con.bash("echo test2", ENV['user']).should == "test2\n"
285
+ @con.bash("echo test2", ENV['USER']).should == "test2\n"
286
+ end
287
+
288
+ it "executes a bash command in the background, returning the pid" do
289
+ @con.bash("true", nil, true).should > 0
276
290
  end
277
291
 
278
292
  it "ensure_tunnel to match with remote connection" do
@@ -282,4 +296,18 @@ EOPS
282
296
  it "always returns true on alive?" do
283
297
  @con.should be_alive
284
298
  end
299
+
300
+ it "resolves a unix uid to a user" do
301
+ @con.resolve_unix_uid_to_user(0).should == "root"
302
+ @con.resolve_unix_uid_to_user('0').should == "root"
303
+ end
304
+
305
+ it "returns nil if the unix uid does not exist" do
306
+ @con.resolve_unix_uid_to_user(9999).should be_nil
307
+ end
308
+
309
+ it "iterates through a process list and resolves the unix uid for each" do
310
+ list = [ { :uid => 0, :command => 'pureftpd' }, { :uid => 9999, :command => 'defunk' } ]
311
+ @con.resolve_unix_uids(list).should == [ { :uid => 0, :user => 'root', :command => 'pureftpd' }, { :uid => 9999, :command => 'defunk', :user => nil } ]
312
+ end
285
313
  end
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ describe Rush::ProcessSet do
4
+ before do
5
+ @process = mock('process')
6
+ @set = Rush::ProcessSet.new([ @process ])
7
+ end
8
+
9
+ it "is Enumerable" do
10
+ @set.select { |s| s == @process }.should == [ @process ]
11
+ end
12
+
13
+ it "defines size" do
14
+ @set.size.should == 1
15
+ end
16
+
17
+ it "defines first" do
18
+ @set.first.should == @process
19
+ end
20
+
21
+ it "is equal to sets with the same contents" do
22
+ @set.should == Rush::ProcessSet.new([ @process ])
23
+ end
24
+
25
+ it "is equal to arrays with the same contents" do
26
+ @set.should == [ @process ]
27
+ end
28
+
29
+ it "kills all processes in the set" do
30
+ @process.should_receive(:kill)
31
+ @set.kill
32
+ end
33
+
34
+ it "checks the alive? state of all processes in the set" do
35
+ @process.should_receive(:alive?).and_return(true)
36
+ @set.alive?.should == [ true ]
37
+ end
38
+
39
+ it "filters the set from a conditions hash and returns the filtered set" do
40
+ @process.stub!(:pid).and_return(123)
41
+ @set.filter(:pid => 123).first.should == @process
42
+ @set.filter(:pid => 456).size.should == 0
43
+ end
44
+
45
+ it "filters with regexps if provided in the conditions" do
46
+ @process.stub!(:command).and_return('foobaz')
47
+ @set.filter(:command => /baz/).first.should == @process
48
+ @set.filter(:command => /blerg/).size.should == 0
49
+ end
50
+ end
@@ -34,7 +34,7 @@ describe Rush::Process do
34
34
  end
35
35
 
36
36
  it "knows the executed binary" do
37
- @process.command.should == "ruby"
37
+ @process.command.should match(/^ruby/)
38
38
  end
39
39
 
40
40
  it "knows the command line" do
@@ -73,8 +73,8 @@ describe Rush::Connection::Local do
73
73
  end
74
74
 
75
75
  it "transmits size" do
76
- @con.should_receive(:transmit).with(:action => 'size', :full_path => 'full_path').and_return("")
77
- @con.size('full_path')
76
+ @con.should_receive(:transmit).with(:action => 'size', :full_path => 'full_path').and_return("123")
77
+ @con.size('full_path').should == 123
78
78
  end
79
79
 
80
80
  it "transmits processes" do
@@ -93,8 +93,8 @@ describe Rush::Connection::Local do
93
93
  end
94
94
 
95
95
  it "transmits bash" do
96
- @con.should_receive(:transmit).with(:action => 'bash', :payload => 'cmd', :user => 'user').and_return('output')
97
- @con.bash('cmd', 'user').should == 'output'
96
+ @con.should_receive(:transmit).with(:action => 'bash', :payload => 'cmd', :user => 'user', :background => 'bg').and_return('output')
97
+ @con.bash('cmd', 'user', 'bg').should == 'output'
98
98
  end
99
99
 
100
100
  it "an http result code of 401 raises NotAuthorized" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rush
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.3"
4
+ version: "0.4"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Wiggins
@@ -9,11 +9,12 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-03-26 00:00:00 -07:00
12
+ date: 2008-07-14 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: mongrel
17
+ type: :runtime
17
18
  version_requirement:
18
19
  version_requirements: !ruby/object:Gem::Requirement
19
20
  requirements:
@@ -23,6 +24,7 @@ dependencies:
23
24
  version:
24
25
  - !ruby/object:Gem::Dependency
25
26
  name: rspec
27
+ type: :runtime
26
28
  version_requirement:
27
29
  version_requirements: !ruby/object:Gem::Requirement
28
30
  requirements:
@@ -32,6 +34,7 @@ dependencies:
32
34
  version:
33
35
  - !ruby/object:Gem::Dependency
34
36
  name: session
37
+ type: :runtime
35
38
  version_requirement:
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
@@ -52,46 +55,48 @@ files:
52
55
  - Rakefile
53
56
  - bin/rush
54
57
  - bin/rushd
58
+ - lib/rush.rb
55
59
  - lib/rush
56
- - lib/rush/access.rb
57
- - lib/rush/array_ext.rb
58
- - lib/rush/box.rb
59
- - lib/rush/commands.rb
60
60
  - lib/rush/config.rb
61
- - lib/rush/dir.rb
61
+ - lib/rush/head_tail.rb
62
+ - lib/rush/search_results.rb
62
63
  - lib/rush/entry.rb
63
- - lib/rush/exceptions.rb
64
- - lib/rush/file.rb
65
64
  - lib/rush/find_by.rb
66
- - lib/rush/fixnum_ext.rb
67
- - lib/rush/head_tail.rb
65
+ - lib/rush/file.rb
68
66
  - lib/rush/local.rb
69
- - lib/rush/process.rb
70
- - lib/rush/remote.rb
71
- - lib/rush/search_results.rb
72
- - lib/rush/server.rb
73
- - lib/rush/shell.rb
74
67
  - lib/rush/ssh_tunnel.rb
68
+ - lib/rush/shell.rb
69
+ - lib/rush/server.rb
70
+ - lib/rush/process_set.rb
71
+ - lib/rush/array_ext.rb
72
+ - lib/rush/remote.rb
73
+ - lib/rush/fixnum_ext.rb
74
+ - lib/rush/commands.rb
75
+ - lib/rush/process.rb
76
+ - lib/rush/dir.rb
75
77
  - lib/rush/string_ext.rb
76
- - lib/rush.rb
78
+ - lib/rush/box.rb
79
+ - lib/rush/exceptions.rb
80
+ - lib/rush/access.rb
81
+ - spec/remote_spec.rb
82
+ - spec/base.rb
83
+ - spec/ssh_tunnel_spec.rb
84
+ - spec/local_spec.rb
77
85
  - spec/access_spec.rb
86
+ - spec/file_spec.rb
87
+ - spec/string_ext_spec.rb
78
88
  - spec/array_ext_spec.rb
79
- - spec/base.rb
80
- - spec/box_spec.rb
81
89
  - spec/commands_spec.rb
82
90
  - spec/config_spec.rb
83
- - spec/dir_spec.rb
84
- - spec/entry_spec.rb
85
- - spec/file_spec.rb
86
- - spec/find_by_spec.rb
87
91
  - spec/fixnum_ext_spec.rb
88
- - spec/local_spec.rb
89
- - spec/process_spec.rb
90
- - spec/remote_spec.rb
91
92
  - spec/search_results_spec.rb
93
+ - spec/process_spec.rb
92
94
  - spec/shell_spec.rb
93
- - spec/ssh_tunnel_spec.rb
94
- - spec/string_ext_spec.rb
95
+ - spec/dir_spec.rb
96
+ - spec/entry_spec.rb
97
+ - spec/find_by_spec.rb
98
+ - spec/box_spec.rb
99
+ - spec/process_set_spec.rb
95
100
  has_rdoc: true
96
101
  homepage: http://rush.heroku.com/
97
102
  post_install_message:
@@ -114,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
119
  requirements: []
115
120
 
116
121
  rubyforge_project: ruby-shell
117
- rubygems_version: 1.0.1
122
+ rubygems_version: 1.2.0
118
123
  signing_key:
119
124
  specification_version: 2
120
125
  summary: A Ruby replacement for bash+ssh.