necktie 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/bin/necktie +1 -1
  2. data/lib/necktie/rush.rb +11 -0
  3. data/lib/necktie/services.rb +31 -11
  4. data/lib/necktie.rb +10 -6
  5. data/necktie.gemspec +2 -2
  6. data/rush/README.rdoc +87 -0
  7. data/rush/Rakefile +60 -0
  8. data/rush/VERSION +1 -0
  9. data/rush/bin/rush +13 -0
  10. data/rush/bin/rushd +7 -0
  11. data/rush/lib/rush/access.rb +130 -0
  12. data/rush/lib/rush/array_ext.rb +19 -0
  13. data/rush/lib/rush/box.rb +112 -0
  14. data/rush/lib/rush/commands.rb +55 -0
  15. data/rush/lib/rush/config.rb +154 -0
  16. data/rush/lib/rush/dir.rb +160 -0
  17. data/rush/lib/rush/embeddable_shell.rb +26 -0
  18. data/rush/lib/rush/entry.rb +185 -0
  19. data/rush/lib/rush/exceptions.rb +31 -0
  20. data/rush/lib/rush/file.rb +85 -0
  21. data/rush/lib/rush/find_by.rb +39 -0
  22. data/rush/lib/rush/fixnum_ext.rb +18 -0
  23. data/rush/lib/rush/head_tail.rb +11 -0
  24. data/rush/lib/rush/local.rb +402 -0
  25. data/rush/lib/rush/process.rb +59 -0
  26. data/rush/lib/rush/process_set.rb +62 -0
  27. data/rush/lib/rush/remote.rb +156 -0
  28. data/rush/lib/rush/search_results.rb +58 -0
  29. data/rush/lib/rush/server.rb +117 -0
  30. data/rush/lib/rush/shell.rb +187 -0
  31. data/rush/lib/rush/ssh_tunnel.rb +122 -0
  32. data/rush/lib/rush/string_ext.rb +3 -0
  33. data/rush/lib/rush.rb +87 -0
  34. data/rush/rush.gemspec +113 -0
  35. data/rush/spec/access_spec.rb +134 -0
  36. data/rush/spec/array_ext_spec.rb +15 -0
  37. data/rush/spec/base.rb +24 -0
  38. data/rush/spec/box_spec.rb +64 -0
  39. data/rush/spec/commands_spec.rb +47 -0
  40. data/rush/spec/config_spec.rb +108 -0
  41. data/rush/spec/dir_spec.rb +164 -0
  42. data/rush/spec/embeddable_shell_spec.rb +17 -0
  43. data/rush/spec/entry_spec.rb +133 -0
  44. data/rush/spec/file_spec.rb +83 -0
  45. data/rush/spec/find_by_spec.rb +58 -0
  46. data/rush/spec/fixnum_ext_spec.rb +19 -0
  47. data/rush/spec/local_spec.rb +352 -0
  48. data/rush/spec/process_set_spec.rb +50 -0
  49. data/rush/spec/process_spec.rb +73 -0
  50. data/rush/spec/remote_spec.rb +140 -0
  51. data/rush/spec/rush_spec.rb +28 -0
  52. data/rush/spec/search_results_spec.rb +44 -0
  53. data/rush/spec/shell_spec.rb +23 -0
  54. data/rush/spec/ssh_tunnel_spec.rb +122 -0
  55. data/rush/spec/string_ext_spec.rb +23 -0
  56. metadata +53 -3
  57. data/lib/necktie/files.rb +0 -14
@@ -0,0 +1,58 @@
1
+ # An instance of this class is returned by Rush::Commands#search. It contains
2
+ # both the list of entries which matched the search, as well as the raw line
3
+ # matches. These methods get equivalent functionality to "grep -l" and "grep -h".
4
+ #
5
+ # SearchResults mixes in Rush::Commands so that you can chain multiple searches
6
+ # or do file operations on the resulting entries.
7
+ #
8
+ # Examples:
9
+ #
10
+ # myproj['**/*.rb'].search(/class/).entries.size
11
+ # myproj['**/*.rb'].search(/class/).lines.size
12
+ # myproj['**/*.rb'].search(/class/).copy_to other_dir
13
+ class Rush::SearchResults
14
+ attr_reader :entries, :lines, :entries_with_lines, :pattern
15
+
16
+ # Make a blank container. Track the pattern so that we can colorize the
17
+ # output to show what was matched.
18
+ def initialize(pattern)
19
+ # Duplication of data, but this lets us return everything in the exact
20
+ # order it was received.
21
+ @pattern = pattern
22
+ @entries = []
23
+ @entries_with_lines = {}
24
+ @lines = []
25
+ end
26
+
27
+ # Add a Rush::Entry and the array of string matches.
28
+ def add(entry, lines)
29
+ # this assumes that entry is unique
30
+ @entries << entry
31
+ @entries_with_lines[entry] = lines
32
+ @lines += lines
33
+ end
34
+
35
+ include Rush::Commands
36
+
37
+ def each(&block)
38
+ @entries.each(&block)
39
+ end
40
+
41
+ include Enumerable
42
+
43
+ def colorize(line)
44
+ lowlight + line.gsub(/(#{pattern.source})/, "#{hilight}\\1#{lowlight}") + normal
45
+ end
46
+
47
+ def hilight
48
+ "\e[34;1m"
49
+ end
50
+
51
+ def lowlight
52
+ "\e[37;2m"
53
+ end
54
+
55
+ def normal
56
+ "\e[0m"
57
+ end
58
+ end
@@ -0,0 +1,117 @@
1
+ require 'rubygems'
2
+ require 'mongrel'
3
+ require 'base64'
4
+
5
+ # Mongrel handler that translates the incoming HTTP request into a
6
+ # Rush::Connection::Local call. The results are sent back across the wire to
7
+ # be decoded by Rush::Connection::Remote on the other side.
8
+ class RushHandler < Mongrel::HttpHandler
9
+ def process(request, response)
10
+ params = {}
11
+ request.params['QUERY_STRING'].split("?").last.split("&").each do |tuple|
12
+ key, value = tuple.split("=")
13
+ params[key.to_sym] = value
14
+ end
15
+
16
+ unless authorize(request.params['HTTP_AUTHORIZATION'])
17
+ response.start(401) do |head, out|
18
+ end
19
+ else
20
+ payload = request.body.read
21
+
22
+ without_action = params
23
+ without_action.delete(params[:action])
24
+
25
+ msg = sprintf "%-20s", params[:action]
26
+ msg += without_action.inspect
27
+ msg += " + #{payload.size} bytes of payload" if payload.size > 0
28
+ log msg
29
+
30
+ params[:payload] = payload
31
+
32
+ begin
33
+ result = box.connection.receive(params)
34
+
35
+ response.start(200) do |head, out|
36
+ out.write result
37
+ end
38
+ rescue Rush::Exception => e
39
+ response.start(400) do |head, out|
40
+ out.write "#{e.class}\n#{e.message}\n"
41
+ end
42
+ end
43
+ end
44
+ rescue Exception => e
45
+ log e.full_display
46
+ end
47
+
48
+ def authorize(auth)
49
+ unless m = auth.match(/^Basic (.+)$/)
50
+ log "Request with no authorization data"
51
+ return false
52
+ end
53
+
54
+ decoded = Base64.decode64(m[1])
55
+ user, password = decoded.split(':', 2)
56
+
57
+ if user.nil? or user.length == 0 or password.nil? or password.length == 0
58
+ log "Malformed user or password"
59
+ return false
60
+ end
61
+
62
+ if password == config.passwords[user]
63
+ return true
64
+ else
65
+ log "Access denied to #{user}"
66
+ return false
67
+ end
68
+ end
69
+
70
+ def box
71
+ @box ||= Rush::Box.new('localhost')
72
+ end
73
+
74
+ def config
75
+ @config ||= Rush::Config.new
76
+ end
77
+
78
+ def log(msg)
79
+ File.open('rushd.log', 'a') do |f|
80
+ f.puts "#{Time.now.strftime('%Y-%m-%d %H:%I:%S')} :: #{msg}"
81
+ end
82
+ end
83
+ end
84
+
85
+ # A container class to run the Mongrel server for rushd.
86
+ class RushServer
87
+ def run
88
+ host = "127.0.0.1"
89
+ port = Rush::Config::DefaultPort
90
+
91
+ rushd = RushHandler.new
92
+ rushd.log "rushd listening on #{host}:#{port}"
93
+
94
+ h = Mongrel::HttpServer.new(host, port)
95
+ h.register("/", rushd)
96
+ h.run.join
97
+ end
98
+ end
99
+
100
+ class Exception
101
+ def full_display
102
+ out = []
103
+ out << "Exception #{self.class} => #{self}"
104
+ out << "Backtrace:"
105
+ out << self.filtered_backtrace.collect do |t|
106
+ " #{t}"
107
+ end
108
+ out << ""
109
+ out.join("\n")
110
+ end
111
+
112
+ def filtered_backtrace
113
+ backtrace.reject do |bt|
114
+ bt.match(/^\/usr\//)
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,187 @@
1
+ require 'readline'
2
+
3
+ # Rush::Shell is used to create an interactive shell. It is invoked by the rush binary.
4
+ module Rush
5
+ class Shell
6
+ attr_accessor :suppress_output
7
+ # Set up the user's environment, including a pure binding into which
8
+ # env.rb and commands.rb are mixed.
9
+ def initialize
10
+ root = Rush::Dir.new('/')
11
+ home = Rush::Dir.new(ENV['HOME']) if ENV['HOME']
12
+ pwd = Rush::Dir.new(ENV['PWD']) if ENV['PWD']
13
+
14
+ @config = Rush::Config.new
15
+
16
+ @config.load_history.each do |item|
17
+ Readline::HISTORY.push(item)
18
+ end
19
+
20
+ Readline.basic_word_break_characters = ""
21
+ Readline.completion_append_character = nil
22
+ Readline.completion_proc = completion_proc
23
+
24
+ @box = Rush::Box.new
25
+ @pure_binding = @box.instance_eval "binding"
26
+ $last_res = nil
27
+
28
+ eval @config.load_env, @pure_binding
29
+
30
+ commands = @config.load_commands
31
+ Rush::Dir.class_eval commands
32
+ Array.class_eval commands
33
+ end
34
+
35
+ # Run a single command.
36
+ def execute(cmd)
37
+ res = eval(cmd, @pure_binding)
38
+ $last_res = res
39
+ eval("_ = $last_res", @pure_binding)
40
+ print_result res
41
+ rescue Rush::Exception => e
42
+ puts "Exception #{e.class} -> #{e.message}"
43
+ rescue ::Exception => e
44
+ puts "Exception #{e.class} -> #{e.message}"
45
+ e.backtrace.each do |t|
46
+ puts " #{::File.expand_path(t)}"
47
+ end
48
+ end
49
+
50
+ # Run the interactive shell using readline.
51
+ def run
52
+ loop do
53
+ cmd = Readline.readline('rush> ')
54
+
55
+ finish if cmd.nil? or cmd == 'exit'
56
+ next if cmd == ""
57
+ Readline::HISTORY.push(cmd)
58
+
59
+ execute(cmd)
60
+ end
61
+ end
62
+
63
+ # Save history to ~/.rush/history when the shell exists.
64
+ def finish
65
+ @config.save_history(Readline::HISTORY.to_a)
66
+ puts
67
+ exit
68
+ end
69
+
70
+ # Nice printing of different return types, particularly Rush::SearchResults.
71
+ def print_result(res)
72
+ return if self.suppress_output
73
+ if res.kind_of? String
74
+ puts res
75
+ elsif res.kind_of? Rush::SearchResults
76
+ widest = res.entries.map { |k| k.full_path.length }.max
77
+ res.entries_with_lines.each do |entry, lines|
78
+ print entry.full_path
79
+ print ' ' * (widest - entry.full_path.length + 2)
80
+ print "=> "
81
+ print res.colorize(lines.first.strip.head(30))
82
+ print "..." if lines.first.strip.length > 30
83
+ if lines.size > 1
84
+ print " (plus #{lines.size - 1} more matches)"
85
+ end
86
+ print "\n"
87
+ end
88
+ puts "#{res.entries.size} matching files with #{res.lines.size} matching lines"
89
+ elsif res.respond_to? :each
90
+ counts = {}
91
+ res.each do |item|
92
+ puts item
93
+ counts[item.class] ||= 0
94
+ counts[item.class] += 1
95
+ end
96
+ if counts == {}
97
+ puts "=> (empty set)"
98
+ else
99
+ count_s = counts.map do |klass, count|
100
+ "#{count} x #{klass}"
101
+ end.join(', ')
102
+ puts "=> #{count_s}"
103
+ end
104
+ else
105
+ puts "=> #{res.inspect}"
106
+ end
107
+ end
108
+
109
+ def path_parts(input) # :nodoc:
110
+ case input
111
+ when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)([\[\/])(['"])([^\3]*)$/
112
+ $~.to_a.slice(1, 4).push($~.pre_match)
113
+ when /((?:@{1,2}|\$|)\w+(?:\[[^\]]+\])*)(\.)(\w*)$/
114
+ $~.to_a.slice(1, 3).push($~.pre_match)
115
+ when /((?:@{1,2}|\$|)\w+)$/
116
+ $~.to_a.slice(1, 1).push(nil).push($~.pre_match)
117
+ else
118
+ [ nil, nil, nil ]
119
+ end
120
+ end
121
+
122
+ def complete_method(receiver, dot, partial_name, pre)
123
+ path = eval("#{receiver}.full_path", @pure_binding) rescue nil
124
+ box = eval("#{receiver}.box", @pure_binding) rescue nil
125
+ if path and box
126
+ (box[path].methods - Object.methods).select do |e|
127
+ e.match(/^#{Regexp.escape(partial_name)}/)
128
+ end.map do |e|
129
+ (pre || '') + receiver + dot + e
130
+ end
131
+ end
132
+ end
133
+
134
+ def complete_path(possible_var, accessor, quote, partial_path, pre) # :nodoc:
135
+ original_var, fixed_path = possible_var, ''
136
+ if /^(.+\/)([^\/]*)$/ === partial_path
137
+ fixed_path, partial_path = $~.captures
138
+ possible_var += "['#{fixed_path}']"
139
+ end
140
+ full_path = eval("#{possible_var}.full_path", @pure_binding) rescue nil
141
+ box = eval("#{possible_var}.box", @pure_binding) rescue nil
142
+ if full_path and box
143
+ Rush::Dir.new(full_path, box).entries.select do |e|
144
+ e.name.match(/^#{Regexp.escape(partial_path)}/)
145
+ end.map do |e|
146
+ (pre || '') + original_var + accessor + quote + fixed_path + e.name + (e.dir? ? "/" : "")
147
+ end
148
+ end
149
+ end
150
+
151
+ def complete_variable(partial_name, pre)
152
+ lvars = eval('local_variables', @pure_binding)
153
+ gvars = eval('global_variables', @pure_binding)
154
+ ivars = eval('instance_variables', @pure_binding)
155
+ (lvars + gvars + ivars).select do |e|
156
+ e.match(/^#{Regexp.escape(partial_name)}/)
157
+ end.map do |e|
158
+ (pre || '') + e
159
+ end
160
+ end
161
+
162
+ # Try to do tab completion on dir square brackets and slash accessors.
163
+ #
164
+ # Example:
165
+ #
166
+ # dir['subd # presing tab here will produce dir['subdir/ if subdir exists
167
+ # dir/'subd # presing tab here will produce dir/'subdir/ if subdir exists
168
+ #
169
+ # This isn't that cool yet, because it can't do multiple levels of subdirs.
170
+ # It does work remotely, though, which is pretty sweet.
171
+ def completion_proc
172
+ proc do |input|
173
+ receiver, accessor, *rest = path_parts(input)
174
+ if receiver
175
+ case accessor
176
+ when /^[\[\/]$/
177
+ complete_path(receiver, accessor, *rest)
178
+ when /^\.$/
179
+ complete_method(receiver, accessor, *rest)
180
+ when nil
181
+ complete_variable(receiver, *rest)
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,122 @@
1
+ # Internal class for managing an ssh tunnel, across which relatively insecure
2
+ # HTTP commands can be sent by Rush::Connection::Remote.
3
+ class Rush::SshTunnel
4
+ def initialize(real_host)
5
+ @real_host = real_host
6
+ end
7
+
8
+ def host
9
+ 'localhost'
10
+ end
11
+
12
+ def port
13
+ @port
14
+ end
15
+
16
+ def ensure_tunnel(options={})
17
+ return if @port and tunnel_alive?
18
+
19
+ @port = config.tunnels[@real_host]
20
+
21
+ if !@port or !tunnel_alive?
22
+ setup_everything(options)
23
+ end
24
+ end
25
+
26
+ def setup_everything(options={})
27
+ display "Connecting to #{@real_host}..."
28
+ push_credentials
29
+ launch_rushd
30
+ establish_tunnel(options)
31
+ end
32
+
33
+ def push_credentials
34
+ display "Pushing credentials"
35
+ config.ensure_credentials_exist
36
+ ssh_append_to_credentials(config.credentials_file.contents.strip)
37
+ end
38
+
39
+ def ssh_append_to_credentials(string)
40
+ # the following horror is exactly why rush is needed
41
+ passwords_file = "~/.rush/passwords"
42
+ string = "'#{string}'"
43
+ ssh "M=`grep #{string} #{passwords_file} 2>/dev/null | wc -l`; if [ $M = 0 ]; then mkdir -p .rush; chmod 700 .rush; echo #{string} >> #{passwords_file}; chmod 600 #{passwords_file}; fi"
44
+ end
45
+
46
+ def launch_rushd
47
+ display "Launching rushd"
48
+ ssh("if [ `ps aux | grep rushd | grep -v grep | wc -l` -ge 1 ]; then exit; fi; rushd > /dev/null 2>&1 &")
49
+ end
50
+
51
+ def establish_tunnel(options={})
52
+ display "Establishing ssh tunnel"
53
+ @port = next_available_port
54
+
55
+ make_ssh_tunnel(options)
56
+
57
+ tunnels = config.tunnels
58
+ tunnels[@real_host] = @port
59
+ config.save_tunnels tunnels
60
+
61
+ sleep 0.5
62
+ end
63
+
64
+ def tunnel_options
65
+ {
66
+ :local_port => @port,
67
+ :remote_port => Rush::Config::DefaultPort,
68
+ :ssh_host => @real_host,
69
+ }
70
+ end
71
+
72
+ def tunnel_alive?
73
+ `#{tunnel_count_command}`.to_i > 0
74
+ end
75
+
76
+ def tunnel_count_command
77
+ "ps x | grep '#{ssh_tunnel_command_without_stall}' | grep -v grep | wc -l"
78
+ end
79
+
80
+ class SshFailed < Exception; end
81
+ class NoPortSelectedYet < Exception; end
82
+
83
+ def ssh(command)
84
+ raise SshFailed unless system("ssh #{@real_host} '#{command}'")
85
+ end
86
+
87
+ def make_ssh_tunnel(options={})
88
+ raise SshFailed unless system(ssh_tunnel_command(options))
89
+ end
90
+
91
+ def ssh_tunnel_command_without_stall
92
+ options = tunnel_options
93
+ raise NoPortSelectedYet unless options[:local_port]
94
+ "ssh -f -L #{options[:local_port]}:127.0.0.1:#{options[:remote_port]} #{options[:ssh_host]}"
95
+ end
96
+
97
+ def ssh_stall_command(options={})
98
+ if options[:timeout] == :infinite
99
+ "while [ 1 ]; do sleep 1000; done"
100
+ elsif options[:timeout].to_i > 10
101
+ "sleep #{options[:timeout].to_i}"
102
+ else
103
+ "sleep 9000"
104
+ end
105
+ end
106
+
107
+ def ssh_tunnel_command(options={})
108
+ ssh_tunnel_command_without_stall + ' "' + ssh_stall_command(options) + '"'
109
+ end
110
+
111
+ def next_available_port
112
+ (config.tunnels.values.max || Rush::Config::DefaultPort) + 1
113
+ end
114
+
115
+ def config
116
+ @config ||= Rush::Config.new
117
+ end
118
+
119
+ def display(msg)
120
+ puts msg
121
+ end
122
+ end
@@ -0,0 +1,3 @@
1
+ class String
2
+ include Rush::HeadTail
3
+ end
data/rush/lib/rush.rb ADDED
@@ -0,0 +1,87 @@
1
+ require 'rubygems'
2
+
3
+ # The top-level Rush module has some convenience methods for accessing the
4
+ # local box.
5
+ module Rush
6
+ # Access the root filesystem of the local box. Example:
7
+ #
8
+ # Rush['/etc/hosts'].contents
9
+ #
10
+ def self.[](key)
11
+ box[key]
12
+ end
13
+
14
+ # Create a dir object from the path of a provided file. Example:
15
+ #
16
+ # Rush.dir(__FILE__).files
17
+ #
18
+ def self.dir(filename)
19
+ box[::File.expand_path(::File.dirname(filename)) + '/']
20
+ end
21
+
22
+ # Create a dir object based on the shell's current working directory at the
23
+ # time the program was run. Example:
24
+ #
25
+ # Rush.launch_dir.files
26
+ #
27
+ def self.launch_dir
28
+ box[::Dir.pwd + '/']
29
+ end
30
+
31
+ # Run a bash command in the root of the local machine. Equivalent to
32
+ # Rush::Box.new.bash.
33
+ def self.bash(command, options={})
34
+ box.bash(command, options)
35
+ end
36
+
37
+ # Pull the process list for the local machine. Example:
38
+ #
39
+ # Rush.processes.filter(:cmdline => /ruby/)
40
+ #
41
+ def self.processes
42
+ box.processes
43
+ end
44
+
45
+ # Get the process object for this program's PID. Example:
46
+ #
47
+ # puts "I'm using #{Rush.my_process.mem} blocks of memory"
48
+ #
49
+ def self.my_process
50
+ box.processes.filter(:pid => ::Process.pid).first
51
+ end
52
+
53
+ # Create a box object for localhost.
54
+ def self.box
55
+ @@box = Rush::Box.new
56
+ end
57
+
58
+ # Quote a path for use in backticks, say.
59
+ def self.quote(path)
60
+ path.gsub(/(?=[^a-zA-Z0-9_.\/\-\x7F-\xFF\n])/n, '\\').gsub(/\n/, "'\n'").sub(/^$/, "''")
61
+ end
62
+ end
63
+
64
+ module Rush::Connection; end
65
+
66
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
67
+
68
+ require 'rush/exceptions'
69
+ require 'rush/config'
70
+ require 'rush/commands'
71
+ require 'rush/access'
72
+ require 'rush/entry'
73
+ require 'rush/file'
74
+ require 'rush/dir'
75
+ require 'rush/search_results'
76
+ require 'rush/head_tail'
77
+ require 'rush/find_by'
78
+ require 'rush/string_ext'
79
+ require 'rush/fixnum_ext'
80
+ require 'rush/array_ext'
81
+ require 'rush/process'
82
+ require 'rush/process_set'
83
+ require 'rush/local'
84
+ require 'rush/remote'
85
+ require 'rush/ssh_tunnel'
86
+ require 'rush/box'
87
+ require 'rush/embeddable_shell'
data/rush/rush.gemspec ADDED
@@ -0,0 +1,113 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rush}
8
+ s.version = "0.6.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Adam Wiggins"]
12
+ s.date = %q{2009-08-17}
13
+ s.description = %q{A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client.}
14
+ s.email = %q{adam@heroku.com}
15
+ s.executables = ["rush", "rushd"]
16
+ s.extra_rdoc_files = [
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "bin/rush",
24
+ "bin/rushd",
25
+ "lib/rush.rb",
26
+ "lib/rush/access.rb",
27
+ "lib/rush/array_ext.rb",
28
+ "lib/rush/box.rb",
29
+ "lib/rush/commands.rb",
30
+ "lib/rush/config.rb",
31
+ "lib/rush/dir.rb",
32
+ "lib/rush/embeddable_shell.rb",
33
+ "lib/rush/entry.rb",
34
+ "lib/rush/exceptions.rb",
35
+ "lib/rush/file.rb",
36
+ "lib/rush/find_by.rb",
37
+ "lib/rush/fixnum_ext.rb",
38
+ "lib/rush/head_tail.rb",
39
+ "lib/rush/local.rb",
40
+ "lib/rush/process.rb",
41
+ "lib/rush/process_set.rb",
42
+ "lib/rush/remote.rb",
43
+ "lib/rush/search_results.rb",
44
+ "lib/rush/server.rb",
45
+ "lib/rush/shell.rb",
46
+ "lib/rush/ssh_tunnel.rb",
47
+ "lib/rush/string_ext.rb",
48
+ "spec/access_spec.rb",
49
+ "spec/array_ext_spec.rb",
50
+ "spec/base.rb",
51
+ "spec/box_spec.rb",
52
+ "spec/commands_spec.rb",
53
+ "spec/config_spec.rb",
54
+ "spec/dir_spec.rb",
55
+ "spec/embeddable_shell_spec.rb",
56
+ "spec/entry_spec.rb",
57
+ "spec/file_spec.rb",
58
+ "spec/find_by_spec.rb",
59
+ "spec/fixnum_ext_spec.rb",
60
+ "spec/local_spec.rb",
61
+ "spec/process_set_spec.rb",
62
+ "spec/process_spec.rb",
63
+ "spec/remote_spec.rb",
64
+ "spec/rush_spec.rb",
65
+ "spec/search_results_spec.rb",
66
+ "spec/shell_spec.rb",
67
+ "spec/ssh_tunnel_spec.rb",
68
+ "spec/string_ext_spec.rb"
69
+ ]
70
+ s.has_rdoc = true
71
+ s.homepage = %q{http://rush.heroku.com/}
72
+ s.rdoc_options = ["--charset=UTF-8"]
73
+ s.require_paths = ["lib"]
74
+ s.rubyforge_project = %q{ruby-shell}
75
+ s.rubygems_version = %q{1.3.1}
76
+ s.summary = %q{A Ruby replacement for bash+ssh.}
77
+ s.test_files = [
78
+ "spec/access_spec.rb",
79
+ "spec/array_ext_spec.rb",
80
+ "spec/base.rb",
81
+ "spec/box_spec.rb",
82
+ "spec/commands_spec.rb",
83
+ "spec/config_spec.rb",
84
+ "spec/dir_spec.rb",
85
+ "spec/embeddable_shell_spec.rb",
86
+ "spec/entry_spec.rb",
87
+ "spec/file_spec.rb",
88
+ "spec/find_by_spec.rb",
89
+ "spec/fixnum_ext_spec.rb",
90
+ "spec/local_spec.rb",
91
+ "spec/process_set_spec.rb",
92
+ "spec/process_spec.rb",
93
+ "spec/remote_spec.rb",
94
+ "spec/rush_spec.rb",
95
+ "spec/search_results_spec.rb",
96
+ "spec/shell_spec.rb",
97
+ "spec/ssh_tunnel_spec.rb",
98
+ "spec/string_ext_spec.rb"
99
+ ]
100
+
101
+ if s.respond_to? :specification_version then
102
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
103
+ s.specification_version = 2
104
+
105
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
106
+ s.add_runtime_dependency(%q<session>, [">= 0"])
107
+ else
108
+ s.add_dependency(%q<session>, [">= 0"])
109
+ end
110
+ else
111
+ s.add_dependency(%q<session>, [">= 0"])
112
+ end
113
+ end