rush2 0.7.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.
- checksums.yaml +7 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +86 -0
- data/README.rdoc +125 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/bin/rush +13 -0
- data/bin/rushd +7 -0
- data/lib/rush.rb +91 -0
- data/lib/rush/access.rb +121 -0
- data/lib/rush/array_ext.rb +19 -0
- data/lib/rush/box.rb +124 -0
- data/lib/rush/commands.rb +76 -0
- data/lib/rush/config.rb +147 -0
- data/lib/rush/dir.rb +166 -0
- data/lib/rush/embeddable_shell.rb +26 -0
- data/lib/rush/entry.rb +229 -0
- data/lib/rush/exceptions.rb +32 -0
- data/lib/rush/file.rb +94 -0
- data/lib/rush/find_by.rb +39 -0
- data/lib/rush/fixnum_ext.rb +18 -0
- data/lib/rush/head_tail.rb +11 -0
- data/lib/rush/local.rb +377 -0
- data/lib/rush/process.rb +59 -0
- data/lib/rush/process_set.rb +62 -0
- data/lib/rush/remote.rb +33 -0
- data/lib/rush/search_results.rb +71 -0
- data/lib/rush/shell.rb +111 -0
- data/lib/rush/shell/completion.rb +110 -0
- data/lib/rush/string_ext.rb +16 -0
- data/spec/access_spec.rb +134 -0
- data/spec/array_ext_spec.rb +15 -0
- data/spec/base.rb +22 -0
- data/spec/box_spec.rb +76 -0
- data/spec/commands_spec.rb +47 -0
- data/spec/config_spec.rb +108 -0
- data/spec/dir_spec.rb +163 -0
- data/spec/embeddable_shell_spec.rb +17 -0
- data/spec/entry_spec.rb +133 -0
- data/spec/file_spec.rb +83 -0
- data/spec/find_by_spec.rb +58 -0
- data/spec/fixnum_ext_spec.rb +19 -0
- data/spec/local_spec.rb +365 -0
- data/spec/process_set_spec.rb +50 -0
- data/spec/process_spec.rb +73 -0
- data/spec/remote_spec.rb +140 -0
- data/spec/rush_spec.rb +28 -0
- data/spec/search_results_spec.rb +44 -0
- data/spec/shell_spec.rb +35 -0
- data/spec/ssh_tunnel_spec.rb +122 -0
- data/spec/string_ext_spec.rb +23 -0
- metadata +209 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
# Rush mixes in Rush::Commands in order to allow operations on groups of
|
2
|
+
# Rush::Entry items. For example, dir['**/*.rb'] returns an array of files, so
|
3
|
+
# dir['**/*.rb'].destroy would destroy all the files specified.
|
4
|
+
#
|
5
|
+
# One cool tidbit: the array can contain entries anywhere, so you can create
|
6
|
+
# collections of entries from different servers and then operate across them:
|
7
|
+
#
|
8
|
+
# [ box1['/var/log/access.log'] + box2['/var/log/access.log'] ].search /#{url}/
|
9
|
+
class Array
|
10
|
+
include Rush::Commands
|
11
|
+
|
12
|
+
def entries
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
include Rush::FindBy
|
17
|
+
|
18
|
+
include Rush::HeadTail
|
19
|
+
end
|
data/lib/rush/box.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# A rush box is a single unix machine - a server, workstation, or VPS instance.
|
2
|
+
#
|
3
|
+
# Specify a box by hostname (default = 'localhost'). If the box is remote, the
|
4
|
+
# first action performed will attempt to open an ssh tunnel. Use square
|
5
|
+
# brackets to access the filesystem, or processes to access the process list.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# local = Rush::Box.new
|
10
|
+
# local['/etc/hosts'].contents
|
11
|
+
# local.processes
|
12
|
+
#
|
13
|
+
class Rush::Box
|
14
|
+
attr_reader :host, :local_path
|
15
|
+
|
16
|
+
# Instantiate a box. No action is taken to make a connection until you try
|
17
|
+
# to perform an action. If the box is remote, an ssh tunnel will be opened.
|
18
|
+
# Specify a username with the host if the remote ssh user is different from
|
19
|
+
# the local one (e.g. Rush::Box.new('user@host')).
|
20
|
+
def initialize(host='localhost', local_path = nil)
|
21
|
+
@host = host
|
22
|
+
@local_path = local_path
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s # :nodoc:
|
26
|
+
host
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect # :nodoc:
|
30
|
+
host
|
31
|
+
end
|
32
|
+
|
33
|
+
# Access / on the box.
|
34
|
+
def filesystem
|
35
|
+
if host == 'localhost'
|
36
|
+
Rush::Entry.factory('/', self)
|
37
|
+
else
|
38
|
+
connection.local_path
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Look up an entry on the filesystem, e.g. box['/path/to/some/file'].
|
43
|
+
# Returns a subclass of Rush::Entry - either Rush::Dir if you specifiy
|
44
|
+
# trailing slash, or Rush::File otherwise.
|
45
|
+
def [](key)
|
46
|
+
filesystem[key]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get the list of processes running on the box, not unlike "ps aux" in bash.
|
50
|
+
# Returns a Rush::ProcessSet.
|
51
|
+
def processes
|
52
|
+
Rush::ProcessSet.new(
|
53
|
+
connection.processes.map { |ps| Rush::Process.new(ps, self) }
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Guess if method missing then it's command for folder binded to that box.
|
58
|
+
#
|
59
|
+
def method_missing(meth, *args, &block)
|
60
|
+
filesystem.send(meth, *args, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Execute a command in the standard unix shell. Returns the contents of
|
64
|
+
# stdout if successful, or raises Rush::BashFailed with the output of stderr
|
65
|
+
# if the shell returned a non-zero value. Options:
|
66
|
+
#
|
67
|
+
# :user => unix username to become via sudo
|
68
|
+
# :env => hash of environment variables
|
69
|
+
# :background => run in the background (returns Rush::Process instead of stdout)
|
70
|
+
#
|
71
|
+
# Examples:
|
72
|
+
#
|
73
|
+
# box.bash '/etc/init.d/mysql restart', :user => 'root'
|
74
|
+
# box.bash 'rake db:migrate', :user => 'www', :env => { :RAILS_ENV => 'production' }
|
75
|
+
# box.bash 'mongrel_rails start', :background => true
|
76
|
+
# box.bash 'rake db:migrate', :user => 'www', :env => { :RAILS_ENV => 'production' }, :reset_environment => true
|
77
|
+
#
|
78
|
+
def bash(command, options = {})
|
79
|
+
cmd_with_env = command_with_environment(command, options[:env])
|
80
|
+
options[:reset_environment] ||= false
|
81
|
+
|
82
|
+
if options[:background]
|
83
|
+
pid = connection.bash(cmd_with_env, options[:user], true, options[:reset_environment])
|
84
|
+
processes.find_by_pid(pid)
|
85
|
+
else
|
86
|
+
connection.bash(cmd_with_env, options[:user], false, options[:reset_environment])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def command_with_environment(command, env) # :nodoc:
|
91
|
+
return command unless env
|
92
|
+
env.map do |key, value|
|
93
|
+
escaped = value.to_s.gsub('"', '\\"').gsub('`', '\\\`')
|
94
|
+
"export #{key}=\"#{escaped}\""
|
95
|
+
end.push(command).join("\n")
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns true if the box is responding to commands.
|
99
|
+
def alive?
|
100
|
+
connection.alive?
|
101
|
+
end
|
102
|
+
|
103
|
+
# This is called automatically the first time an action is invoked, but you
|
104
|
+
# may wish to call it manually ahead of time in order to have the tunnel
|
105
|
+
# already set up and running. You can also use this to pass a timeout option,
|
106
|
+
# either :timeout => (seconds) or :timeout => :infinite.
|
107
|
+
def establish_connection(options={})
|
108
|
+
connection.ensure_tunnel(options)
|
109
|
+
end
|
110
|
+
|
111
|
+
def connection # :nodoc:
|
112
|
+
@connection ||= make_connection
|
113
|
+
end
|
114
|
+
|
115
|
+
def make_connection # :nodoc:
|
116
|
+
host == 'localhost' ?
|
117
|
+
Rush::Connection::Local.new :
|
118
|
+
Rush::Connection::Remote.new(host, local_path)
|
119
|
+
end
|
120
|
+
|
121
|
+
def ==(other) # :nodoc:
|
122
|
+
host == other.host
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# The commands module contains operations against Rush::File entries, and is
|
2
|
+
# mixed in to Rush::Entry and Array. This means you can run these commands against a single
|
3
|
+
# file, a dir full of files, or an arbitrary list of files.
|
4
|
+
#
|
5
|
+
# Examples:
|
6
|
+
#
|
7
|
+
# box['/etc/hosts'].search /localhost/ # single file
|
8
|
+
# box['/etc/'].search /localhost/ # entire directory
|
9
|
+
# box['/etc/**/*.conf'].search /localhost/ # arbitrary list
|
10
|
+
module Rush::Commands
|
11
|
+
# The entries command must return an array of Rush::Entry items. This
|
12
|
+
# varies by class that it is mixed in to.
|
13
|
+
def entries
|
14
|
+
raise "must define me in class mixed in to for command use"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Search file contents for a regular expression. A Rush::SearchResults
|
18
|
+
# object is returned.
|
19
|
+
def search(pattern)
|
20
|
+
entries.inject(Rush::SearchResults.new(pattern)) do |results, entry|
|
21
|
+
if !entry.dir? and matches = entry.search(pattern)
|
22
|
+
results.add(entry, matches)
|
23
|
+
end
|
24
|
+
results
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Search and replace file contents.
|
29
|
+
def replace_contents!(pattern, with_text)
|
30
|
+
entries.each do |entry|
|
31
|
+
entry.replace_contents!(pattern, with_text) unless entry.dir?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Count the number of lines in the contained files.
|
36
|
+
def line_count
|
37
|
+
entries.inject(0) do |count, entry|
|
38
|
+
count + (entry.dir? ? 0 : entry.lines.size)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Invoke vi on one or more files - only works locally.
|
43
|
+
def vi(*args)
|
44
|
+
if self.class == Rush::Dir
|
45
|
+
system "cd #{full_path}; vim"
|
46
|
+
else
|
47
|
+
open_with('vim', *args)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
alias_method :vim, :vi
|
51
|
+
|
52
|
+
# Invoke TextMate on one or more files - only works locally.
|
53
|
+
def mate(*args)
|
54
|
+
open_with('mate', *args)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Open file with xdg-open.
|
58
|
+
# Usage:
|
59
|
+
# home.locate('mai_failz').open
|
60
|
+
def open(*args)
|
61
|
+
open_with('xdg-open', *args)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Open file with any application you like.
|
65
|
+
# Usage:
|
66
|
+
# home.locate('timetable').open_with :vim
|
67
|
+
def open_with(app, *args)
|
68
|
+
names = dir? ? '' : entries.map(&:to_s).join(' ')
|
69
|
+
system "cd #{dirname}; #{app.to_s} #{names} #{args.join(' ')}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def output_of(app, *args)
|
73
|
+
names = entries.map(&:to_s).join(' ')
|
74
|
+
`cd #{dirname}; #{app.to_s} #{names} #{args.join(' ')}`
|
75
|
+
end
|
76
|
+
end
|
data/lib/rush/config.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
# The config class accesses files in ~/.rush to load and save user preferences.
|
2
|
+
class Rush::Config
|
3
|
+
DefaultPort = 7770
|
4
|
+
|
5
|
+
attr_reader :dir
|
6
|
+
|
7
|
+
# By default, reads from the dir ~/.rush, but an optional parameter allows
|
8
|
+
# using another location.
|
9
|
+
def initialize(location=nil)
|
10
|
+
@dir = Rush::Dir.new(location || "#{ENV['HOME']}/.rush")
|
11
|
+
@dir.create
|
12
|
+
end
|
13
|
+
|
14
|
+
# History is a flat file of past commands in the interactive shell,
|
15
|
+
# equivalent to .bash_history.
|
16
|
+
def history_file
|
17
|
+
dir['history']
|
18
|
+
end
|
19
|
+
|
20
|
+
def save_history(array)
|
21
|
+
history_file.write(array.join("\n") + "\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_history
|
25
|
+
history_file.contents_or_blank.split("\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
# The environment file is executed when the interactive shell starts up.
|
29
|
+
# Put aliases and your own functions here; it is the equivalent of .bashrc
|
30
|
+
# or .profile.
|
31
|
+
#
|
32
|
+
# Example ~/.rush/env.rb:
|
33
|
+
#
|
34
|
+
# server = Rush::Box.new('www@my.server')
|
35
|
+
# myproj = home['projects/myproj/']
|
36
|
+
def env_file
|
37
|
+
dir['env.rb']
|
38
|
+
end
|
39
|
+
|
40
|
+
def load_env
|
41
|
+
env_file.contents_or_blank
|
42
|
+
end
|
43
|
+
|
44
|
+
# Commands are mixed in to Array and Rush::Entry, alongside the default
|
45
|
+
# commands from Rush::Commands. Any methods here should reference "entries"
|
46
|
+
# to get the list of entries to operate on.
|
47
|
+
#
|
48
|
+
# Example ~/.rush/commands.rb:
|
49
|
+
#
|
50
|
+
# def destroy_svn(*args)
|
51
|
+
# entries.select { |e| e.name == '.svn' }.destroy
|
52
|
+
# end
|
53
|
+
def commands_file
|
54
|
+
dir['commands.rb']
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_commands
|
58
|
+
commands_file.contents_or_blank
|
59
|
+
end
|
60
|
+
|
61
|
+
# Passwords contains a list of username:password combinations used for
|
62
|
+
# remote access via rushd. You can fill this in manually, or let the remote
|
63
|
+
# connection publish it automatically.
|
64
|
+
def passwords_file
|
65
|
+
dir['passwords']
|
66
|
+
end
|
67
|
+
|
68
|
+
def passwords
|
69
|
+
passwords_file.lines_or_empty.inject({}) do |result, line|
|
70
|
+
user, password = line.split(":", 2)
|
71
|
+
result.merge user => password
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Credentials is the client-side equivalent of passwords. It contains only
|
76
|
+
# one username:password combination that is transmitted to the server when
|
77
|
+
# connecting. This is also autogenerated if it does not exist.
|
78
|
+
def credentials_file
|
79
|
+
dir['credentials']
|
80
|
+
end
|
81
|
+
|
82
|
+
def credentials
|
83
|
+
credentials_file.lines.first.split(":", 2)
|
84
|
+
end
|
85
|
+
|
86
|
+
def save_credentials(user, password)
|
87
|
+
credentials_file.write("#{user}:#{password}\n")
|
88
|
+
end
|
89
|
+
|
90
|
+
def credentials_user
|
91
|
+
credentials[0]
|
92
|
+
end
|
93
|
+
|
94
|
+
def credentials_password
|
95
|
+
credentials[1]
|
96
|
+
end
|
97
|
+
|
98
|
+
def ensure_credentials_exist
|
99
|
+
generate_credentials if credentials_file.contents_or_blank == ""
|
100
|
+
end
|
101
|
+
|
102
|
+
def generate_credentials
|
103
|
+
save_credentials(generate_user, generate_password)
|
104
|
+
end
|
105
|
+
|
106
|
+
def generate_user
|
107
|
+
generate_secret(4, 8)
|
108
|
+
end
|
109
|
+
|
110
|
+
def generate_password
|
111
|
+
generate_secret(8, 15)
|
112
|
+
end
|
113
|
+
|
114
|
+
def generate_secret(min, max)
|
115
|
+
chars = self.secret_characters
|
116
|
+
len = rand(max - min + 1) + min
|
117
|
+
len.times.inject('') { |r| r += chars[rand(chars.length)] }
|
118
|
+
end
|
119
|
+
|
120
|
+
def secret_characters
|
121
|
+
[ ('a'..'z'), ('1'..'9') ].inject([]) do |chars, range|
|
122
|
+
chars += range.to_a
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# ~/.rush/tunnels contains a list of previously created ssh tunnels. The
|
127
|
+
# format is host:port, where port is the local port that the tunnel is
|
128
|
+
# listening on.
|
129
|
+
def tunnels_file
|
130
|
+
dir['tunnels']
|
131
|
+
end
|
132
|
+
|
133
|
+
def tunnels
|
134
|
+
tunnels_file.lines_or_empty.inject({}) do |hash, line|
|
135
|
+
host, port = line.split(':', 2)
|
136
|
+
hash.merge host => port.to_i
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def save_tunnels(hash)
|
141
|
+
string = ""
|
142
|
+
hash.each do |host, port|
|
143
|
+
string += "#{host}:#{port}\n"
|
144
|
+
end
|
145
|
+
tunnels_file.write string
|
146
|
+
end
|
147
|
+
end
|
data/lib/rush/dir.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
# A dir is a subclass of Rush::Entry that contains other entries. Also known
|
2
|
+
# as a directory or a folder.
|
3
|
+
#
|
4
|
+
# Dirs can be operated on with Rush::Commands the same as an array of files.
|
5
|
+
# They also offer a square bracket accessor which can use globbing to get a
|
6
|
+
# list of files.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# dir = box['/home/adam/']
|
11
|
+
# dir['**/*.rb'].line_count
|
12
|
+
#
|
13
|
+
# In the interactive shell, dir.ls is a useful command.
|
14
|
+
class Rush::Dir < Rush::Entry
|
15
|
+
def dir?
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
def full_path
|
20
|
+
"#{super}/"
|
21
|
+
end
|
22
|
+
alias_method :dirname, :full_path
|
23
|
+
|
24
|
+
# Entries contained within this dir - not recursive.
|
25
|
+
def contents
|
26
|
+
find_by_glob('*')
|
27
|
+
end
|
28
|
+
|
29
|
+
# Files contained in this dir only.
|
30
|
+
def files
|
31
|
+
contents.reject(&:dir?)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Other dirs contained in this dir only.
|
35
|
+
def dirs
|
36
|
+
contents.select(&:dir?)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Access subentries with square brackets, e.g. dir['subdir/file']
|
40
|
+
def [](key)
|
41
|
+
key = key.to_s
|
42
|
+
case
|
43
|
+
when key == '**' then files_flattened
|
44
|
+
when key.match(/\*/) then find_by_glob(key)
|
45
|
+
else find_by_name(key)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
# Slashes work as well, e.g. dir/'subdir/file'
|
49
|
+
alias_method :/, :[]
|
50
|
+
|
51
|
+
def locate(path)
|
52
|
+
located = bash("locate #{path}").split("\n").
|
53
|
+
map { |x| x.dir? ? Rush::Dir.new(x) : Rush::File.new(x) }
|
54
|
+
located.size == 1 ? located.first : located
|
55
|
+
end
|
56
|
+
|
57
|
+
def locate_dir(path)
|
58
|
+
located = bash("locate -r #{path}$").split("\n").
|
59
|
+
map { |x| Dir.new x }
|
60
|
+
located.size == 1 ? located.first : located
|
61
|
+
end
|
62
|
+
|
63
|
+
def find_by_name(name) # :nodoc:
|
64
|
+
Rush::Entry.factory("#{full_path}/#{name}", box)
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_by_glob(glob) # :nodoc:
|
68
|
+
connection.index(full_path, glob).map do |fname|
|
69
|
+
Rush::Entry.factory("#{full_path}/#{fname}", box)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# A list of all the recursively contained entries in flat form.
|
74
|
+
def entries_tree
|
75
|
+
find_by_glob('**/*')
|
76
|
+
end
|
77
|
+
|
78
|
+
# Recursively contained files.
|
79
|
+
def files_flattened
|
80
|
+
entries_tree.reject(&:dir?)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Recursively contained dirs.
|
84
|
+
def dirs_flattened
|
85
|
+
entries_tree.select(&:dir?)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Given a list of flat filenames, product a list of entries under this dir.
|
89
|
+
# Mostly for internal use.
|
90
|
+
def make_entries(filenames)
|
91
|
+
Array(filenames).
|
92
|
+
map { |fname| Rush::Entry.factory("#{full_path}/#{fname}") }
|
93
|
+
end
|
94
|
+
|
95
|
+
# Create a blank file within this dir.
|
96
|
+
def create_file(name)
|
97
|
+
file = self[name].create
|
98
|
+
file.write('')
|
99
|
+
file
|
100
|
+
end
|
101
|
+
|
102
|
+
# Create an empty subdir within this dir.
|
103
|
+
def create_dir(name)
|
104
|
+
name += '/' unless name[-1] == '/'
|
105
|
+
self[name].create
|
106
|
+
end
|
107
|
+
|
108
|
+
# Create an instantiated but not yet filesystem-created dir.
|
109
|
+
def create
|
110
|
+
connection.create_dir(full_path)
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
# Get the total disk usage of the dir and all its contents.
|
115
|
+
def size
|
116
|
+
connection.size(full_path)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Contained dirs that are not hidden.
|
120
|
+
def nonhidden_dirs
|
121
|
+
dirs.reject(&:hidden?)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Contained files that are not hidden.
|
125
|
+
def nonhidden_files
|
126
|
+
files.reject(&:hidden?)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Run a bash command starting in this directory. Options are the same as Rush::Box#bash.
|
130
|
+
def bash(command, options={})
|
131
|
+
box.bash "cd #{quoted_path} && #{command}", options
|
132
|
+
end
|
133
|
+
|
134
|
+
# Destroy all of the contents of the directory, leaving it fresh and clean.
|
135
|
+
def purge
|
136
|
+
connection.purge full_path
|
137
|
+
end
|
138
|
+
|
139
|
+
# Text output of dir listing, equivalent to the regular unix shell's ls command.
|
140
|
+
def ls
|
141
|
+
out = [ "#{self}" ]
|
142
|
+
nonhidden_dirs.each do |dir|
|
143
|
+
out << " #{dir.name}/"
|
144
|
+
end
|
145
|
+
nonhidden_files.each do |file|
|
146
|
+
out << " #{file.name}"
|
147
|
+
end
|
148
|
+
out.join("\n")
|
149
|
+
end
|
150
|
+
|
151
|
+
# Run rake within this dir.
|
152
|
+
def rake(*args)
|
153
|
+
bash "rake #{args.join(' ')}"
|
154
|
+
end
|
155
|
+
|
156
|
+
# Run git within this dir.
|
157
|
+
def git(*args)
|
158
|
+
bash "git #{args.join(' ')}"
|
159
|
+
end
|
160
|
+
|
161
|
+
include Rush::Commands
|
162
|
+
|
163
|
+
def entries
|
164
|
+
contents
|
165
|
+
end
|
166
|
+
end
|