rfs 0.2 → 0.3

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.
@@ -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)