rush 0.5 → 0.5.1

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.5"
34
+ version = "0.5.1"
35
35
  name = "rush"
36
36
 
37
37
  spec = Gem::Specification.new do |s|
@@ -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
@@ -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.full_path }.join(' ')
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.full_path }.join(' ')
52
+ names = entries.map { |f| f.quoted_path }.join(' ')
53
53
  system "mate #{names} #{args.join(' ')}"
54
54
  end
55
55
  end
@@ -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 #{full_path} && #{command}", options
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.
@@ -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)
@@ -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.
@@ -106,42 +106,81 @@ module Rush
106
106
  end
107
107
  end
108
108
 
109
- def path_parts(input) # :nodoc:
110
- input.match(/(\w+(?:\[[^\]]+\])*)\[(['"])([^\]]+)$/)
111
- $~.to_a.slice(1, 3).push($~.pre_match)
112
- rescue
113
- [ nil, nil, nil, nil ]
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
- # Try to do tab completion on dir square brackets accessors.
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
- possible_var, quote, partial_path, pre = path_parts(input)
127
- if possible_var
128
- original_var, fixed_path = possible_var, ''
129
- if /^(.+\/)([^\/]+)$/ === partial_path
130
- fixed_path, partial_path = $~.captures
131
- possible_var += "['#{fixed_path}']"
132
- end
133
- full_path = eval("#{possible_var}.full_path", @pure_binding) rescue nil
134
- box = eval("#{possible_var}.box", @pure_binding) rescue nil
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
@@ -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
- @box.should_receive(:bash).with('cmd', :opt1 => 1, :opt2 => 2)
156
- @box.bash('cmd', :opt1 => 1, :opt2 => 2)
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
@@ -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}"
@@ -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' ]
@@ -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: "0.5"
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: 2008-12-30 00:00:00 -08:00
12
+ date: 2009-03-24 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency