rfs 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,51 @@
1
+ Sept. 3, 2005
2
+
3
+ Rename File Set v0.2 has been released with a few
4
+ more features. All bugs found so far have been
5
+ fixed, too.
6
+
7
+ What's new?
8
+
9
+ GUI version:
10
+ * when using the 'Toggle Roman Numerals' option
11
+ the search pattern " (roman) " will be replaced with
12
+ a robust regexp to capture roman numerals.
13
+ * the output pane will display results in multiple
14
+ columns if possible
15
+ * gui should now work from a gem installation
16
+ (requires fxruby 1.2.x. 1.4.x is yet untested)
17
+
18
+ Command-line version:
19
+ * same change for --roman as above.
20
+ * added --rm option to remove files matching
21
+ the search
22
+ * added --eval EXPR to allow you to do pretty
23
+ much whatever you like. Captures can be used
24
+ with %1..%n, but should be enclosed in quotes,
25
+ and local variables file, path, and md (match data)
26
+ are available, amoung other things. The idea
27
+ is to be able to do things like:
28
+ --eval "6 * '%2'.to_f"
29
+ * enhanced --count to allow anything as the starting
30
+ point, and increment using String#succ.
31
+ * changed the way --raw_output is displayed
32
+ * fixed up the --help output a lot
33
+
34
+ Cheers!
35
+ Darrick => innatesoftware.com
36
+
37
+ Sept. 6, 2005
38
+
39
+ * All of the remaining tests have now been implemented
40
+ and are passing.
41
+
42
+ * Fixed a bug in row or column output which occurred if
43
+ no items were wider than the 80 chars but only one-column
44
+ layout was possible. The bug would crash both interfaces.
45
+
46
+ Command-line version:
47
+ * Added --pattern to use Dir.glob type patterns
48
+ instead of path names
49
+ * Added --stdin to allow the program to capture
50
+ file names from a pipe. This does not work with
51
+ confirmations (STDIN is already in use).
@@ -4,17 +4,17 @@ require 'rake/gempackagetask'
4
4
  require 'rake/contrib/rubyforgepublisher'
5
5
 
6
6
  PKG_NAME = 'rfs'
7
- PKG_VERSION = '0.2'
7
+ PKG_VERSION = '0.3'
8
8
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
9
9
 
10
- RELEASE_NAME = "REL #{PKG_VERSION}"
10
+ RELEASE_NAME = "rfs #{PKG_VERSION}"
11
11
 
12
12
  RUBY_FORGE_PROJECT = "rfs"
13
13
  RUBY_FORGE_USER = "pangloss"
14
14
 
15
15
  task :default => :test
16
16
  Rake::TestTask.new { |t|
17
- t.pattern = 'tests/test_rename_functions.rb'
17
+ t.pattern = 'tests/all_tests.rb'
18
18
  }
19
19
 
20
20
  # Create compressed packages
@@ -50,7 +50,7 @@ spec = Gem::Specification.new do |s|
50
50
  ).delete_if { |item| item.include?( "\.svn" ) || item.include?("\pkg") }
51
51
  s.require_path = 'lib'
52
52
  s.has_rdoc = false
53
- s.test_files = ['./tests/test_rename_functions.rb', './lib/innate/test/all_tests.rb']
53
+ s.test_files = ['./tests/all_tests.rb', './lib/innate/test/all_tests.rb']
54
54
 
55
55
  s.bindir = 'bin'
56
56
  s.executables << 'rfs.rb'
data/bin/rfs.rb CHANGED
@@ -44,6 +44,7 @@ class CommandLineInterface
44
44
  quiet = false
45
45
  silent = false
46
46
  reverse = false
47
+ use_stdin = false
47
48
 
48
49
  # more defaults... make configurable in the future
49
50
  CommandLineInterface.confirm # confirm changes
@@ -152,6 +153,12 @@ class CommandLineInterface
152
153
  op.on('-T', '--title', 'Convert capture to title case.') {
153
154
  function = :rename_capitalize
154
155
  }
156
+ op.on('--upcase', 'Convert capture to upper case.') {
157
+ function = :rename_upcase
158
+ }
159
+ op.on('--downcase', 'Convert capture to lower case.') {
160
+ function = :rename_downcase
161
+ }
155
162
  op.on('-u', '--up', 'Move matching files up to parent directory.') {
156
163
  function = :rename_move_up
157
164
  }
@@ -171,6 +178,11 @@ class CommandLineInterface
171
178
  op.on('-I', '--ignorecase', 'Make all REGEXP options case-insensitive.') {
172
179
  insensitive = true
173
180
  }
181
+ op.on('-P', '--pattern', 'Treat trailing [path] arguments as',
182
+ 'file search patterns rather than as',
183
+ 'folder names. ie. **/xy?/*.mp3') {
184
+ file_provider = Provider::File::Glob
185
+ }
174
186
  op.on('-R', '--recursive',
175
187
  'Recursively descend the directory',
176
188
  'tree.') {
@@ -179,7 +191,9 @@ class CommandLineInterface
179
191
  op.on('--reverse', 'Operate on files in reverse order.') {
180
192
  reverse = true
181
193
  }
182
-
194
+ op.on('--stdin', 'Read filenames from stdin.') {
195
+ use_stdin = true
196
+ }
183
197
 
184
198
  op.separator "Output Options:"
185
199
  op.on('--sort', 'Sort output') { @@results.sorted = true }
@@ -197,9 +211,9 @@ class CommandLineInterface
197
211
  op.on('-x', '--sideways', 'Display in rows instead of in columns.') {
198
212
  @@results.sideways = true
199
213
  }
200
- op.on('-X', '--raw_output', 'Don\'t sort and keep everything on separate',
201
- 'lines.') {
202
- @@results.lineways = true
214
+ op.on('-X', '--raw_output', 'Alternate output mode. Output immediately',
215
+ 'without sorting, line by line.') {
216
+ @@results = RawResults.new $<
203
217
  }
204
218
 
205
219
  paths = op.parse ARGV
@@ -224,6 +238,8 @@ class CommandLineInterface
224
238
  puts "DON'T confirm changes."
225
239
  end
226
240
  end
241
+
242
+ puts "function: #{function}" if options[:verbose]
227
243
 
228
244
  PrepareRegexp.each(options) do |k, v|
229
245
  t = v.create(insensitive, print_matches, true) do |mode, output|
@@ -232,6 +248,21 @@ class CommandLineInterface
232
248
  t.k = k if print_matches
233
249
  t
234
250
  end
251
+
252
+ if use_stdin
253
+ if options[:confirm] and options[:action] == :commit
254
+ puts 'You may not use confirmations when using stdin to read file data. ' +
255
+ 'Please specify --noconfirm'
256
+ exit
257
+ file_provider = Provider::File::BufferedStdIn
258
+ else
259
+ file_provider = Provider::File::StdIn
260
+ end
261
+ end
262
+
263
+ # todo: ask: is it possible to read data from a pipe and then
264
+ # close that pipe and open stdin on a tty? I want to be able
265
+ # to ask the user for confirmations.
235
266
 
236
267
  options[:file_provider] = file_provider.new(paths)
237
268
  options[:file_provider].reverse if reverse
@@ -102,6 +102,7 @@ protected
102
102
  best_col_widths = col_widths
103
103
  end
104
104
  end
105
+ return self, 1, [width] unless best_col_count
105
106
  return yield(self.clone, best_col_count, '').first, best_col_count, best_col_widths
106
107
  end
107
108
 
@@ -1,3 +1,5 @@
1
+ $: << File.split(__FILE__).first + '/..'
2
+ $: << File.split(__FILE__).first
1
3
  require 'testarray'
2
4
  require 'testfile'
3
5
  require 'testfilelines'
@@ -50,6 +50,18 @@ module Provider
50
50
  end
51
51
  end
52
52
  end
53
+
54
+
55
+ class Glob < Base
56
+ def each
57
+ @folders.each do |pattern|
58
+ Dir.glob(pattern).send(@iterator) do |fn|
59
+ path, file = ::File.split fn
60
+ yield path, file unless file =~ /^\.+$/
61
+ end
62
+ end
63
+ end
64
+ end
53
65
 
54
66
 
55
67
  class Recursive < Base
@@ -65,7 +77,7 @@ module Provider
65
77
  dir.collect.send(@iterator) do |file|
66
78
  j = ::File.join(folder, file)
67
79
  unless file =~ /^\.+$/
68
- if ::File.directory? j
80
+ if ::File.directory? j
69
81
  recursive_each j, &block
70
82
  end
71
83
  yield folder, file
@@ -92,61 +104,39 @@ module Provider
92
104
  end
93
105
 
94
106
 
95
- ##I am decomissioning this code because
96
- ##it did not result in any performance
97
- ##improvement. Upon profiling the program
98
- ##it turns out (to my surprise) that
99
- ##retrieving file listings was not a
100
- ##bottleneck at all.
101
- #class Cache < Base
102
- #def initialize(file_provider)
103
- #@fp = file_provider
104
- #reset_cache
105
- #end
106
-
107
- #def reset_cache
108
- #@fg = Generator.new(@fp)
109
- #@cache = []
110
- #@cache_complete = false
111
- #end
112
-
113
- #def each
114
- #@cache.each do |a|
115
- #yield a.first, a.last
116
- #end
117
- #unless @cache_complete
118
- #while @fg.next?
119
- #begin
120
- #Thread.critical = true
121
- #path, file = @fg.next
122
- #@cache << [path, file]
123
- #@cache_complete = true unless @fg.next?
124
- #ensure
125
- #Thread.critical = false
126
- #end
127
- #yield path, file
128
- #end
129
- #end
130
- #end
131
- #end
132
-
107
+ class BufferedStdIn < Base
108
+ def initialize(*ignored)
109
+ super nil
110
+ end
111
+
112
+ def each
113
+ file = []
114
+ until STDIN.eof?
115
+ file << STDIN.readline.rstrip
116
+ end
117
+ file.each do |line|
118
+ next if line.empty?
119
+ path, file = ::File.split(line)
120
+ yield path, file
121
+ end
122
+ end
123
+ end
124
+
133
125
 
134
- #class EnumerableFoldersCache < Cache
135
- #def initialize(file_provider)
136
- #@folders = []
137
- #super
138
- #end
139
-
140
- #def each(&b)
141
- #f = @fp.folders.collect
142
- #unless f == @folders
143
- #reset_cache
144
- #@folders = f
145
- #end
146
- #super
147
- #end
148
- #end
126
+ class StdIn < Base
127
+ def initialize(*ignored)
128
+ super nil
129
+ end
130
+
131
+ def each
132
+ until STDIN.eof?
133
+ line = STDIN.readline.rstrip
134
+ next if line.empty?
135
+ path, file = ::File.split(line)
136
+ yield path, file
137
+ end
138
+ end
139
+ end
149
140
  end
150
141
  end
151
142
 
152
-
@@ -150,6 +150,18 @@ protected
150
150
  subst(file, md, eval(s).to_s)
151
151
  end
152
152
  end
153
+
154
+ def rename_upcase
155
+ rename_each_match do |file, md|
156
+ subst(file, md, active_capture(md).upcase)
157
+ end
158
+ end
159
+
160
+ def rename_downcase
161
+ rename_each_match do |file, md|
162
+ subst(file, md, active_capture(md).downcase)
163
+ end
164
+ end
153
165
  end
154
166
 
155
167
 
@@ -42,14 +42,8 @@ protected
42
42
  end
43
43
 
44
44
  def actual_rename(o, n)
45
- len = File.expand_path(n).length
46
- raise NameLengthError.new(len) if len >= 255
47
45
  File.mkdirs File.split(n).first
48
- if File.directory? n
49
- raise DirectoryExistsError.new("Can't overwrite an existing directory.")
50
- else
51
- File.rename(o, n)
52
- end
46
+ File.rename(o, n)
53
47
  end
54
48
 
55
49
  def actual_delete(fn)
@@ -103,12 +97,18 @@ protected
103
97
  unless oldfn == newfn or (full_path and old_full == newfn)
104
98
  t = newfn
105
99
  fn = dest_file path, newfn, full_path
106
- if !@options[:force] and already_exists?(path, oldfn, newfn, full_path)
100
+ if (!@options[:force]) and already_exists?(path, oldfn, newfn, full_path)
107
101
  result :fileerror, 'File already exists', path, oldfn
108
102
  elsif @options[:action] == :commit
109
103
  if confirm path, oldfn, newfn, full_path
110
104
  begin
111
- actual_rename old_full, fn
105
+ len = File.expand_path(fn).length
106
+ raise NameLengthError.new(len) if len >= 255
107
+ if File.directory? fn and old_full.downcase != fn.downcase
108
+ raise DirectoryExistsError.new("Can't overwrite an existing directory.")
109
+ else
110
+ actual_rename old_full, fn
111
+ end
112
112
  rescue => e
113
113
  result :fileerror, "#{e.class}: #{e.message}", path, oldfn
114
114
  else
@@ -1,14 +1,16 @@
1
- class CollectResults
2
- attr_accessor :verbose, :quiet, :silent, :sideways, :lineways, :sorted, :list
1
+ class BaseResults
2
+ attr_accessor :verbose, :quiet, :silent, :sideways, :sorted, :list
3
3
  attr_accessor :out
4
4
  attr_reader :results
5
+ end
6
+
7
+ class CollectResults < BaseResults
5
8
 
6
9
  def initialize(out_io, width)
7
10
  @verbose = false
8
11
  @quiet = false
9
12
  @silent = false
10
13
  @sideways = false
11
- @lineways = false
12
14
  @sorted = false
13
15
  @list = false
14
16
 
@@ -53,31 +55,15 @@ class CollectResults
53
55
  when :fileerror: errors += v.length
54
56
  end
55
57
  s = ''
56
- unless @lineways
57
- s += "\n#{File.expand_path(k)}"
58
- s += ": #{mode.to_s.upcase} - #{v.length} files" unless @quiet or @list
59
- @out.puts s
60
- end
58
+ s += "\n#{File.expand_path(k)}"
59
+ s += ": #{mode.to_s.upcase} - #{v.length} files" unless @quiet or @list
60
+ @out.puts s
61
61
  v = v.sort if @sorted
62
- if @lineways
63
- v.each do |file|
64
- if @list
65
- out.puts File.join(k, file)
66
- else
67
- @out.puts format('%s: %s', mode.to_s.upcase, File.join(k, file))
68
- end
69
- end
70
- else
71
- @out.puts v.send(m, @width)
72
- end
62
+ @out.puts v.send(m, @width)
73
63
  end
74
64
  else
75
65
  @out.puts "\n#{mode.to_s.upcase}"
76
- if @lineways
77
- @out.puts value.join("\n")
78
- else
79
- @out.puts value.send(m, @width)
80
- end
66
+ @out.puts value.send(m, @width)
81
67
  end
82
68
  end
83
69
  unless @quiet
@@ -90,4 +76,23 @@ class CollectResults
90
76
  end
91
77
  end
92
78
 
79
+ class RawResults < BaseResults
80
+ def initialize(out_io)
81
+ @out = out_io
82
+ end
83
+
84
+ def output(mode, text, path = nil, file = nil)
85
+ return if @silent
86
+ if @quiet or @list
87
+ puts text
88
+ else
89
+ puts "#{mode}: #{File.join(path, file)} => #{text}"
90
+ end
91
+ end
92
+
93
+ def display
94
+ puts '-- done' unless @quiet or @silent
95
+ end
96
+ end
97
+
93
98
 
@@ -8,7 +8,6 @@ $:.unshift '../lib'
8
8
  require 'renamer'
9
9
  require 'cursor/indexed'
10
10
 
11
-
12
11
  class RenamerMock < Renamer
13
12
  attr_reader :renames
14
13
 
@@ -106,6 +105,12 @@ class BaseTest < Test::Unit::TestCase
106
105
  def setup
107
106
  new_files( %w{ dir/. dir/.. dir/file1 dir/file2 dir/file3 } )
108
107
  @r = RenamerMock.new
108
+ @orig_dir = Dir.getwd
109
+ Dir.chdir File.join(File.split(__FILE__).first, '/../../tests')
110
+ end
111
+
112
+ def teardown
113
+ Dir.chdir @orig_dir
109
114
  end
110
115
 
111
116
  def new_files(files)