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 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