rush 0.5 → 0.5.1
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/Rakefile +1 -1
- data/lib/rush.rb +5 -0
- data/lib/rush/commands.rb +2 -2
- data/lib/rush/dir.rb +3 -1
- data/lib/rush/entry.rb +9 -2
- data/lib/rush/local.rb +3 -3
- data/lib/rush/shell.rb +62 -23
- data/spec/dir_spec.rb +7 -2
- data/spec/entry_spec.rb +4 -0
- data/spec/local_spec.rb +13 -0
- data/spec/shell_spec.rb +12 -1
- metadata +2 -2
data/Rakefile
CHANGED
data/lib/rush.rb
CHANGED
@@ -54,6 +54,11 @@ module Rush
|
|
54
54
|
def self.box
|
55
55
|
@@box = Rush::Box.new
|
56
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
|
57
62
|
end
|
58
63
|
|
59
64
|
module Rush::Connection; end
|
data/lib/rush/commands.rb
CHANGED
@@ -43,13 +43,13 @@ module Rush::Commands
|
|
43
43
|
|
44
44
|
# Invoke vi on one or more files - only works locally.
|
45
45
|
def vi(*args)
|
46
|
-
names = entries.map { |f| f.
|
46
|
+
names = entries.map { |f| f.quoted_path }.join(' ')
|
47
47
|
system "vim #{names} #{args.join(' ')}"
|
48
48
|
end
|
49
49
|
|
50
50
|
# Invoke TextMate on one or more files - only works locally.
|
51
51
|
def mate(*args)
|
52
|
-
names = entries.map { |f| f.
|
52
|
+
names = entries.map { |f| f.quoted_path }.join(' ')
|
53
53
|
system "mate #{names} #{args.join(' ')}"
|
54
54
|
end
|
55
55
|
end
|
data/lib/rush/dir.rb
CHANGED
@@ -46,6 +46,8 @@ class Rush::Dir < Rush::Entry
|
|
46
46
|
find_by_name(key)
|
47
47
|
end
|
48
48
|
end
|
49
|
+
# Slashes work as well, e.g. dir/'subdir/file'
|
50
|
+
alias_method :/, :[]
|
49
51
|
|
50
52
|
def find_by_name(name) # :nodoc:
|
51
53
|
Rush::Entry.factory("#{full_path}/#{name}", box)
|
@@ -120,7 +122,7 @@ class Rush::Dir < Rush::Entry
|
|
120
122
|
|
121
123
|
# Run a bash command starting in this directory. Options are the same as Rush::Box#bash.
|
122
124
|
def bash(command, options={})
|
123
|
-
box.bash "cd #{
|
125
|
+
box.bash "cd #{quoted_path} && #{command}", options
|
124
126
|
end
|
125
127
|
|
126
128
|
# Destroy all of the contents of the directory, leaving it fresh and clean.
|
data/lib/rush/entry.rb
CHANGED
@@ -18,6 +18,8 @@ class Rush::Entry
|
|
18
18
|
def self.factory(full_path, box=nil)
|
19
19
|
if full_path.tail(1) == '/'
|
20
20
|
Rush::Dir.new(full_path, box)
|
21
|
+
elsif File.directory?(full_path)
|
22
|
+
Rush::Dir.new(full_path, box)
|
21
23
|
else
|
22
24
|
Rush::File.new(full_path, box)
|
23
25
|
end
|
@@ -48,6 +50,10 @@ class Rush::Entry
|
|
48
50
|
"#{@path}/#{@name}"
|
49
51
|
end
|
50
52
|
|
53
|
+
def quoted_path
|
54
|
+
Rush.quote(full_path)
|
55
|
+
end
|
56
|
+
|
51
57
|
# Return true if the entry currently exists on the filesystem of the box.
|
52
58
|
def exists?
|
53
59
|
stat
|
@@ -82,13 +88,14 @@ class Rush::Entry
|
|
82
88
|
def rename(new_name)
|
83
89
|
connection.rename(@path, @name, new_name)
|
84
90
|
@name = new_name
|
91
|
+
self
|
85
92
|
end
|
86
93
|
|
87
94
|
# Rename an entry to another name within the same dir. The existing object
|
88
95
|
# will not be affected, but a new object representing the newly-created
|
89
96
|
# entry will be returned.
|
90
97
|
def duplicate(new_name)
|
91
|
-
raise NameCannotContainSlash if new_name.match(/\//)
|
98
|
+
raise Rush::NameCannotContainSlash if new_name.match(/\//)
|
92
99
|
new_full_path = "#{@path}/#{new_name}"
|
93
100
|
connection.copy(full_path, new_full_path)
|
94
101
|
self.class.new(new_full_path, box)
|
@@ -97,7 +104,7 @@ class Rush::Entry
|
|
97
104
|
# Copy the entry to another dir. Returns an object representing the new
|
98
105
|
# copy.
|
99
106
|
def copy_to(dir)
|
100
|
-
raise NotADir unless dir.class == Rush::Dir
|
107
|
+
raise Rush::NotADir unless dir.class == Rush::Dir
|
101
108
|
|
102
109
|
if box == dir.box
|
103
110
|
connection.copy(full_path, dir.full_path)
|
data/lib/rush/local.rb
CHANGED
@@ -83,12 +83,12 @@ class Rush::Connection::Local
|
|
83
83
|
# transmitted to another server for a copy or move. Note that archive
|
84
84
|
# operations have the dir name implicit in the archive.
|
85
85
|
def read_archive(full_path)
|
86
|
-
`cd #{::File.dirname(full_path)}; tar c #{::File.basename(full_path)}`
|
86
|
+
`cd #{Rush.quote(::File.dirname(full_path))}; tar c #{Rush.quote(::File.basename(full_path))}`
|
87
87
|
end
|
88
88
|
|
89
89
|
# Extract an in-memory archive to a dir.
|
90
90
|
def write_archive(archive, dir)
|
91
|
-
IO.popen("cd #{dir}; tar x", "w") do |p|
|
91
|
+
IO.popen("cd #{Rush::quote(dir)}; tar x", "w") do |p|
|
92
92
|
p.write archive
|
93
93
|
end
|
94
94
|
end
|
@@ -135,7 +135,7 @@ class Rush::Connection::Local
|
|
135
135
|
# Fetch the size of a dir, since a standard file stat does not include the
|
136
136
|
# size of the contents.
|
137
137
|
def size(full_path)
|
138
|
-
`du -sb #{full_path}`.match(/(\d+)/)[1].to_i
|
138
|
+
`du -sb #{Rush.quote(full_path)}`.match(/(\d+)/)[1].to_i
|
139
139
|
end
|
140
140
|
|
141
141
|
# Get the list of processes as an array of hashes.
|
data/lib/rush/shell.rb
CHANGED
@@ -106,42 +106,81 @@ module Rush
|
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
-
def path_parts(input)
|
110
|
-
input
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
114
132
|
end
|
115
133
|
|
116
|
-
|
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.
|
117
163
|
#
|
118
164
|
# Example:
|
119
165
|
#
|
120
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
|
121
168
|
#
|
122
169
|
# This isn't that cool yet, because it can't do multiple levels of subdirs.
|
123
170
|
# It does work remotely, though, which is pretty sweet.
|
124
171
|
def completion_proc
|
125
172
|
proc do |input|
|
126
|
-
|
127
|
-
if
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
if full_path and box
|
136
|
-
dir = Rush::Dir.new(full_path, box)
|
137
|
-
return dir.entries.select do |e|
|
138
|
-
e.name.match(/^#{Regexp.escape(partial_path)}/)
|
139
|
-
end.map do |e|
|
140
|
-
(pre || '') + original_var + '[' + quote + fixed_path + e.name + (e.dir? ? "/" : "")
|
141
|
-
end
|
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)
|
142
182
|
end
|
143
183
|
end
|
144
|
-
nil
|
145
184
|
end
|
146
185
|
end
|
147
186
|
end
|
data/spec/dir_spec.rb
CHANGED
@@ -151,9 +151,14 @@ describe Rush::Dir do
|
|
151
151
|
@dir.bash("cat file").should == "test\n"
|
152
152
|
end
|
153
153
|
|
154
|
+
it "can run bash within directories with spaces" do
|
155
|
+
@dir.create_dir('with space').create_file('file').write('test')
|
156
|
+
@dir["with space/"].bash("cat file").should == "test"
|
157
|
+
end
|
158
|
+
|
154
159
|
it "passes bash options (e.g., :user) through to the box bash command" do
|
155
|
-
@
|
156
|
-
@
|
160
|
+
@dir.should_receive(:bash).with('cmd', :opt1 => 1, :opt2 => 2)
|
161
|
+
@dir.bash('cmd', :opt1 => 1, :opt2 => 2)
|
157
162
|
end
|
158
163
|
|
159
164
|
end
|
data/spec/entry_spec.rb
CHANGED
@@ -56,6 +56,10 @@ describe Rush::Entry do
|
|
56
56
|
File.exists?("#{@sandbox_dir}/#{new_file}").should be_true
|
57
57
|
end
|
58
58
|
|
59
|
+
it "rename returns the renamed file" do
|
60
|
+
@entry.rename('file2').should == @entry.parent['file2']
|
61
|
+
end
|
62
|
+
|
59
63
|
it "can't rename itself if another file already exists with that name" do
|
60
64
|
new_file = "test3"
|
61
65
|
system "touch #{@sandbox_dir}/#{new_file}"
|
data/spec/local_spec.rb
CHANGED
@@ -194,6 +194,11 @@ describe Rush::Connection::Local do
|
|
194
194
|
@con.read_archive(@sandbox_dir).size.should > 50
|
195
195
|
end
|
196
196
|
|
197
|
+
it "read_archive works for paths with spaces" do
|
198
|
+
system "mkdir -p #{@sandbox_dir}/with\\ space; touch #{@sandbox_dir}/with\\ space/a"
|
199
|
+
@con.read_archive("#{@sandbox_dir}/with space").size.should > 50
|
200
|
+
end
|
201
|
+
|
197
202
|
it "write_archive to turn a byte stream into a dir" do
|
198
203
|
system "cd #{@sandbox_dir}; mkdir -p a; touch a/b; tar cf xfer.tar a; mkdir dst"
|
199
204
|
archive = File.read("#{@sandbox_dir}/xfer.tar")
|
@@ -202,6 +207,14 @@ describe Rush::Connection::Local do
|
|
202
207
|
File.exists?("#{@sandbox_dir}/dst/a/b").should be_true
|
203
208
|
end
|
204
209
|
|
210
|
+
it "write_archive works for paths with spaces" do
|
211
|
+
system "cd #{@sandbox_dir}; mkdir -p a; touch a/b; tar cf xfer.tar a; mkdir with\\ space"
|
212
|
+
archive = File.read("#{@sandbox_dir}/xfer.tar")
|
213
|
+
@con.write_archive(archive, "#{@sandbox_dir}/with space")
|
214
|
+
File.directory?("#{@sandbox_dir}/with space/a").should be_true
|
215
|
+
File.exists?("#{@sandbox_dir}/with space/a/b").should be_true
|
216
|
+
end
|
217
|
+
|
205
218
|
it "index fetches list of all files and dirs in a dir when pattern is empty" do
|
206
219
|
system "cd #{@sandbox_dir}; mkdir dir; touch file"
|
207
220
|
@con.index(@sandbox_dir, '').should == [ 'dir/', 'file' ]
|
data/spec/shell_spec.rb
CHANGED
@@ -7,6 +7,17 @@ describe Rush::Shell do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
it "matches open path commands for readline tab completion" do
|
10
|
-
@shell.path_parts("dir['app").should == [ "dir", "'", "app", "" ]
|
10
|
+
@shell.path_parts("dir['app").should == [ "dir", "[", "'", "app", "" ]
|
11
|
+
@shell.path_parts('dir/"app').should == [ "dir", "/", '"', "app", "" ]
|
12
|
+
end
|
13
|
+
|
14
|
+
it "matches open path commands on globals for readline tab completion" do
|
15
|
+
@shell.path_parts("$dir['app").should == [ "$dir", "[", "'", "app", "" ]
|
16
|
+
@shell.path_parts('$dir/"app').should == [ "$dir", "/", '"', "app", "" ]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "matches open path commands on instance vars for readline tab completion" do
|
20
|
+
@shell.path_parts("@dir['app").should == [ "@dir", "[", "'", "app", "" ]
|
21
|
+
@shell.path_parts('@dir/"app').should == [ "@dir", "/", '"', "app", "" ]
|
11
22
|
end
|
12
23
|
end
|
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:
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Wiggins
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-03-24 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|