rfs 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/MIT-LICENSE +7 -0
  2. data/Rakefile.rb +193 -0
  3. data/bin/RenameFileSet.rbw +14 -0
  4. data/bin/rfs.rb +214 -0
  5. data/bin/rfsd.rb +8 -0
  6. data/description.txt +24 -0
  7. data/lib/RenameFileSet.bak.rb +372 -0
  8. data/lib/errors.rb +15 -0
  9. data/lib/filters.rb +68 -0
  10. data/lib/gui.rb +216 -0
  11. data/lib/innate/array.rb +104 -0
  12. data/lib/innate/coverage.rb +315 -0
  13. data/lib/innate/debug.rb +12 -0
  14. data/lib/innate/debugger.rb +944 -0
  15. data/lib/innate/file.rb +5 -0
  16. data/lib/innate/filelines.rb +148 -0
  17. data/lib/innate/kernel.rb +41 -0
  18. data/lib/innate/metaid.rb +30 -0
  19. data/lib/innate/mkdirs.rb +12 -0
  20. data/lib/innate/regexp.rb +80 -0
  21. data/lib/innate/reload.rb +11 -0
  22. data/lib/innate/roman.rb +72 -0
  23. data/lib/innate/scriptlines.rb +60 -0
  24. data/lib/innate/string.rb +56 -0
  25. data/lib/innate/test/all_tests.rb +11 -0
  26. data/lib/innate/test/files/mkdirs_dummy.file +1 -0
  27. data/lib/innate/test/files/reloadtarget.rb +5 -0
  28. data/lib/innate/test/files/reloadtarget1.rb +5 -0
  29. data/lib/innate/test/files/reloadtarget2.rb +5 -0
  30. data/lib/innate/test/test_coverage.rb +13 -0
  31. data/lib/innate/test/testarray.rb +98 -0
  32. data/lib/innate/test/testfile.rb +15 -0
  33. data/lib/innate/test/testfilelines.rb +106 -0
  34. data/lib/innate/test/testkernel.rb +30 -0
  35. data/lib/innate/test/testmkdirs.rb +19 -0
  36. data/lib/innate/test/testregexp.rb +86 -0
  37. data/lib/innate/test/testreload.rb +61 -0
  38. data/lib/innate/test/testroman.rb +54 -0
  39. data/lib/innate/test/testscriptlines.rb +39 -0
  40. data/lib/innate/test/teststring.rb +52 -0
  41. data/lib/innate/test/testtitlecase.rb +20 -0
  42. data/lib/innate/titlecase.rb +64 -0
  43. data/lib/innate/tracerequire.rb +22 -0
  44. data/lib/namesource.rb +53 -0
  45. data/lib/options.rb +64 -0
  46. data/lib/providers.rb +93 -0
  47. data/lib/regexp.rb +56 -0
  48. data/lib/rename_functions.rb +142 -0
  49. data/lib/renamer.rb +210 -0
  50. data/lib/results.rb +83 -0
  51. data/lib/test/test_helper.rb +147 -0
  52. data/tests/dir/file1 +0 -0
  53. data/tests/dir/file2 +0 -0
  54. data/tests/dir/file3 +0 -0
  55. data/tests/test_helper.rb +147 -0
  56. data/tests/test_rename_functions.rb +605 -0
  57. metadata +105 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2005 Darrick Wiebe
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile.rb ADDED
@@ -0,0 +1,193 @@
1
+ require 'rake/testtask'
2
+ require 'rake/rdoctask'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/contrib/rubyforgepublisher'
5
+
6
+ PKG_NAME = 'rfs'
7
+ PKG_VERSION = '0.1'
8
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
9
+
10
+ RELEASE_NAME = "REL #{PKG_VERSION}"
11
+
12
+ RUBY_FORGE_PROJECT = "rfs"
13
+ RUBY_FORGE_USER = "pangloss"
14
+
15
+ task :default => :test
16
+ Rake::TestTask.new { |t|
17
+ t.pattern = 'tests/test_rename_functions.rb'
18
+ }
19
+
20
+ # Create compressed packages
21
+ dist_dirs = [ "lib", "tests"]
22
+
23
+ # Genereate the RDoc documentation
24
+
25
+ #Rake::RDocTask.new { |rdoc|
26
+ #rdoc.rdoc_dir = 'doc'
27
+ #rdoc.title = "Rename File Set"
28
+ #rdoc.options << '--line-numbers --inline-source --main README'
29
+ #rdoc.template = "#{ENV['template']}.rb" if ENV['template']
30
+ #rdoc.rdoc_files.include('README', 'CHANGELOG', 'MIT-LICENSE', 'DESCRIPTION')
31
+ #rdoc.rdoc_files.include('lib/*.rb')
32
+ #}
33
+
34
+ spec = Gem::Specification.new do |s|
35
+ s.name = PKG_NAME
36
+ s.version = PKG_VERSION
37
+ s.summary = "A utility that allows you to use regular expressions to manage large sets of files or folders."
38
+ s.description = <<-EOD
39
+ Rename File Set is a powerful way to manage large or small
40
+ sets of files such as mp3 collections etc. It uses the full
41
+ power of regular expressions in at least a dozen useful
42
+ operations. Fox GUI or command-line.
43
+ EOD
44
+
45
+ s.files = (Dir.glob( "./**/*" ) #+
46
+ #Dir.glob( "./**/innate/**/*")
47
+ ).delete_if { |item| item.include?( "\.svn" ) || item.include?("\pkg") }
48
+ s.require_path = 'lib'
49
+ s.has_rdoc = false
50
+ # s.test_files = ['./tests/test_rename_functions.rb', './lib/innate/test/all_tests.rb']
51
+
52
+ s.bindir = 'bin'
53
+ s.executables << 'rfs.rb'
54
+ s.executables << 'rfsd.rb'
55
+ s.executables << 'RenameFileSet.rbw'
56
+
57
+ s.author = "Darrick Wiebe"
58
+ s.email = "darrick@innatesoftware.com"
59
+ s.homepage = "http://rfs.rubyforge.org"
60
+ s.rubyforge_project = "rfs"
61
+ end
62
+
63
+ Rake::GemPackageTask.new(spec) do |p|
64
+ p.gem_spec = spec
65
+ p.need_tar = true
66
+ # p.need_zip = true
67
+ end
68
+
69
+ desc "Publish the beta gem"
70
+ task :pgem => [:package] do
71
+ Rake::SshFilePublisher.new("pangloss@rfs.rubyforge.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
72
+ #`ssh davidhh@wrath.rubyonrails.com './gemupdate.sh'`
73
+ end
74
+
75
+ desc "Publish the API documentation"
76
+ task :pdoc => [:rdoc] do
77
+ Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.com", "public_html/as", "doc").upload
78
+ end
79
+
80
+ desc "Publish the release files to RubyForge."
81
+ task :release => [:package] do
82
+ files = ["gem", "tgz", "zip"].map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" }
83
+
84
+ if RUBY_FORGE_PROJECT then
85
+ require 'net/http'
86
+ require 'open-uri'
87
+
88
+ project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
89
+ project_data = open(project_uri) { |data| data.read }
90
+ group_id = project_data[/[?&]group_id=(\d+)/, 1]
91
+ raise "Couldn't get group id" unless group_id
92
+
93
+ # This echos password to shell which is a bit sucky
94
+ if ENV["RUBY_FORGE_PASSWORD"]
95
+ password = ENV["RUBY_FORGE_PASSWORD"]
96
+ else
97
+ print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
98
+ password = STDIN.gets.chomp
99
+ end
100
+
101
+ login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
102
+ data = [
103
+ "login=1",
104
+ "form_loginname=#{RUBY_FORGE_USER}",
105
+ "form_pw=#{password}"
106
+ ].join("&")
107
+ http.post("/account/login.php", data)
108
+ end
109
+
110
+ cookie = login_response["set-cookie"]
111
+ raise "Login failed" unless cookie
112
+ headers = { "Cookie" => cookie }
113
+
114
+ release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
115
+ release_data = open(release_uri, headers) { |data| data.read }
116
+ package_id = release_data[/[?&]package_id=(\d+)/, 1]
117
+ raise "Couldn't get package id" unless package_id
118
+
119
+ first_file = true
120
+ release_id = ""
121
+
122
+ files.each do |filename|
123
+ basename = File.basename(filename)
124
+ file_ext = File.extname(filename)
125
+ file_data = File.open(filename, "rb") { |file| file.read }
126
+
127
+ puts "Releasing #{basename}..."
128
+
129
+ release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
130
+ release_date = Time.now.strftime("%Y-%m-%d %H:%M")
131
+ type_map = {
132
+ ".zip" => "3000",
133
+ ".tgz" => "3110",
134
+ ".gz" => "3110",
135
+ ".gem" => "1400"
136
+ }; type_map.default = "9999"
137
+ type = type_map[file_ext]
138
+ boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
139
+
140
+ query_hash = if first_file then
141
+ {
142
+ "group_id" => group_id,
143
+ "package_id" => package_id,
144
+ "release_name" => RELEASE_NAME,
145
+ "release_date" => release_date,
146
+ "type_id" => type,
147
+ "processor_id" => "8000", # Any
148
+ "release_notes" => "",
149
+ "release_changes" => "",
150
+ "preformatted" => "1",
151
+ "submit" => "1"
152
+ }
153
+ else
154
+ {
155
+ "group_id" => group_id,
156
+ "release_id" => release_id,
157
+ "package_id" => package_id,
158
+ "step2" => "1",
159
+ "type_id" => type,
160
+ "processor_id" => "8000", # Any
161
+ "submit" => "Add This File"
162
+ }
163
+ end
164
+
165
+ query = "?" + query_hash.map do |(name, value)|
166
+ [name, URI.encode(value)].join("=")
167
+ end.join("&")
168
+
169
+ data = [
170
+ "--" + boundary,
171
+ "Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
172
+ "Content-Type: application/octet-stream",
173
+ "Content-Transfer-Encoding: binary",
174
+ "", file_data, ""
175
+ ].join("\x0D\x0A")
176
+
177
+ release_headers = headers.merge(
178
+ "Content-Type" => "multipart/form-data; boundary=#{boundary}"
179
+ )
180
+
181
+ target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
182
+ http.post(target + query, data, release_headers)
183
+ end
184
+
185
+ if first_file then
186
+ release_id = release_response.body[/release_id=(\d+)/, 1]
187
+ raise("Couldn't get release id") unless release_id
188
+ end
189
+
190
+ first_file = false
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,14 @@
1
+ $:.unshift File.join(File.split(__FILE__).first, '../lib')
2
+ require 'gui'
3
+ require 'renamer'
4
+
5
+
6
+ if $0 == __FILE__
7
+ $>.sync = true
8
+ theApp = FXApp.new
9
+ theMainWindow = RenameWindow.new theApp
10
+ theMainWindow.renamer = Renamer.new
11
+ theApp.create
12
+ theMainWindow.show
13
+ theApp.run
14
+ end
data/bin/rfs.rb ADDED
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/ruby
2
+ $:.unshift File.expand_path(File.join(File.split(__FILE__).first, '../lib'))
3
+ require 'renamer'
4
+ require 'optparse'
5
+ require 'results'
6
+
7
+ class CommandLineInterface
8
+ def self.confirm
9
+ @@options[:confirm] = proc do |path, oldfn, newfn, full_name, exists, default|
10
+ r = %w{Yes All No Cancel}
11
+ begin
12
+ print "\"#{File.join path, oldfn}\" =>#{exists ? ' existing file' : ''} \"#{newfn}\"? [#{r.join '/'}] " + (default ? "(#{default}) " : '')
13
+ s = (i = STDIN.gets).prefix_to_i?(*r)
14
+ end until s or (i == "\n" and default)
15
+ s ? s.downcase.to_sym : default
16
+ end
17
+ end
18
+
19
+ def self.noconfirm
20
+ @@options[:confirm] = nil
21
+ end
22
+
23
+ def self.output(*a)
24
+ @@results.output(*a)
25
+ end
26
+
27
+ def self.start
28
+ op = OptionParser.new
29
+ file_provider = Provider::File::NonRecursive
30
+ @@options = options = {}
31
+ @@results = CollectResults.new $>
32
+ renamer = Renamer.new
33
+
34
+ # basic default settings.
35
+ function = nil
36
+ options[:filter] = Filter.new(/^\.+$/) # remove . & .. dirs
37
+ options[:verbose] = false
38
+ insensitive = false
39
+ print_matches = false
40
+ quiet = false
41
+ silent = false
42
+ reverse = false
43
+
44
+ # more defaults... make configurable in the future
45
+ CommandLineInterface.confirm # confirm changes
46
+ options[:action] = :preview # dry-run mode
47
+
48
+
49
+ op.on_head('--noconfirm', 'Don\'t confirm.') {
50
+ CommandLineInterface.noconfirm
51
+ }
52
+ op.on_head('--confirm', 'Confirm before rename') {
53
+ CommandLineInterface.confirm
54
+ }
55
+ op.on_head('-t', '--test', 'Test run only, don\'t acually rename anything.') {
56
+ options[:action] = :preview
57
+ }
58
+ op.on_head('-c', '--commit', 'Commit changes and actually rename the files.') {
59
+ options[:action] = :commit
60
+ }
61
+ op.on_head('-l', '--list', 'List original file names in folders to operate on only.') {
62
+ function = :rename_replace
63
+ options[:action] = :list
64
+ @@results.list = true
65
+ }
66
+
67
+ op.on('-a', '--add NUMBER', Integer, 'Add to capture.') {|options[:add]|
68
+ function = :rename_add
69
+ }
70
+ op.on('-C', '--count', 'Replace capture with count.') {
71
+ function = :rename_count
72
+ }
73
+ op.on('--capture NUM', Integer, 'Replace the text in a specific capture.',
74
+ 'Default: 1 (or whole match if there are no captures)') {|n|
75
+ options[:capture_num] = n
76
+ }
77
+ op.on('--file [FILE]', String,
78
+ 'Use files.txt or FILE as source for replace strings.') {|file|
79
+ options[:source] = NameFileSource.new(file || 'files.txt')
80
+ function = :rename_name_source
81
+ options[:filter] = Filter.add(options[:filter], Filter.new(/^#{file || 'files.txt'}$/))
82
+ if file
83
+ options[:filter] = Filter.add(options[:filter], Filter.new(/^#{file}$/, nil, true))
84
+ end
85
+ }
86
+ op.on('-f', '--filter REGEXP', String,
87
+ 'Remove matching files from the search.') {|f|
88
+ options[:filter] = Filter.add(options[:filter], Filter.new(PrepareRegexp.new(f)))
89
+ }
90
+ op.on('-F', '--keep REGEXP', String,
91
+ 'Include only matching files in the search.') {|f|
92
+ options[:filter] = Filter.add(options[:filter], FilterNonMatches.new(PrepareRegexp.new(f)))
93
+ }
94
+ op.on('--force', 'Force rename even if a file will be replaced.') {
95
+ options[:force] = true
96
+ }
97
+ op.on('-i', '--fill REGEXP', String, 'Copy -s match from previous files and fill downwards, replacing the match for this pattern.') {|f|
98
+ options[:fill] = PrepareRegexp.new f
99
+ function = :rename_fill
100
+ }
101
+ op.on('-I', '--ignorecase', 'Make all REGEXP options case-insensitive.') {
102
+ insensitive = true
103
+ }
104
+ op.on('-n', '--roman', 'Toggle match to or from roman numerals.') {
105
+ function = :rename_roman
106
+ }
107
+ op.on('-m', '--mark [PATTERN]', 'Mark files if the captured pattern changes.',
108
+ 'The capture in PATTERN will be replaced with --mark_text.',
109
+ 'Default: /()$/') {|p|
110
+ function = :rename_mark_change
111
+ options[:replace_pattern] = PrepareRegexp.new(p || '()$')
112
+ options[:replace_text] ||= '--changed--'
113
+ }
114
+ op.on('--mark_text TEXT', 'Default: --changed--') {|t|
115
+ options[:replace_text] = t
116
+ }
117
+ op.on('-p', '--inpath REGEXP', String, 'Rename with match in full path.') {|p|
118
+ options[:pattern] = PrepareRegexp.new p
119
+ function = :rename_from_full_name
120
+ }
121
+ op.on('--prompt_name', 'Prompt for the new name of each file.') {
122
+ options[:source] = NamePromptSource.new
123
+ function = :rename_name_source
124
+ }
125
+ op.on('-r', '--replace [STRING]', String, 'Replace expression') {|r|
126
+ options[:replace] = r || ''
127
+ function = :rename_replace
128
+ }
129
+ op.on('-R', '--recursive', 'Operate on all files and folders recursively.') {
130
+ file_provider = Provider::File::Recursive
131
+ }
132
+ op.on('--reverse', 'Operate on files in reverse order.') {
133
+ reverse = true
134
+ }
135
+ op.on('--rm', 'Remove matching files.') {
136
+ function = :rename_remove
137
+ }
138
+ op.on('-s', '--search REGEXP', String, 'Search expression') {|s|
139
+ options[:search] = PrepareRegexp.new s
140
+ }
141
+ op.on('--sort', 'Sort output') { @@results.sorted = true }
142
+ op.on('--tape', 'Convert tape numbers ie. 1A, 1B, 2A, ... to regular numbers.') {
143
+ function = :rename_tape_numbers
144
+ }
145
+ op.on('-T', '--title', 'Convert capture to title case.') {
146
+ functon = :rename_capitalize
147
+ }
148
+ op.on('-u', '--up', 'Move files with match up to parent directory.') {
149
+ function = :rename_move_up
150
+ }
151
+ op.on('-q', '--quiet') { quiet = @@results.quiet = true }
152
+ op.on('-Q', '--silent') {
153
+ @@results.quiet = @@results.silent = silent = quiet = true;
154
+ options[:verbose] = print_matches = false }
155
+ op.on('-v', '--verbose', 'Vebose. Useful if you aren\'t getting the results you expect.') {
156
+ @@results.verbose = options[:verbose] = true
157
+ }
158
+ op.on('-V', '--more_verbose', 'Verbose + show breakdown of matchdata.') {
159
+ @@results.verbose = options[:verbose] = print_matches = true
160
+ }
161
+ op.on('-x', '--sideways', 'Display in rows instead of in columns.') {
162
+ @@results.sideways = true
163
+ }
164
+ op.on('-X', '--raw_output', 'Don\'t sort and keep everything on seperate lines.') {
165
+ @@results.lineways = true
166
+ }
167
+
168
+ paths = op.parse ARGV
169
+
170
+ if paths.empty?
171
+ paths = ['.']
172
+ end
173
+
174
+ if !options[:action]
175
+ puts 'An action must be specified: either -t, -c or -l' unless silent
176
+ exit
177
+ end
178
+
179
+ function = :rename_replace if !function
180
+
181
+ unless quiet
182
+ puts 'Rename File Set:'
183
+ puts "Using #{options[:action].to_s.upcase} mode."
184
+ if options[:confirm].is_a? Proc
185
+ puts 'CONFIRM changes.'
186
+ else
187
+ puts "DON'T confirm changes."
188
+ end
189
+ end
190
+
191
+ PrepareRegexp.each(options) do |k, v|
192
+ t = v.create(insensitive, print_matches, true) do |mode, output|
193
+ CommandLineInterface.output(mode, output)
194
+ end
195
+ t.k = k if print_matches
196
+ t
197
+ end
198
+
199
+ options[:file_provider] = file_provider.new(paths)
200
+ options[:file_provider].reverse if reverse
201
+
202
+ begin
203
+ renamer.run function, options do |mode, result, path, file|
204
+ CommandLineInterface.output(mode, result, path, file)
205
+ end
206
+ rescue RenamerError => e
207
+ CommandLineInterface.output(:error, "Error: #{e}")
208
+ ensure
209
+ @@results.display
210
+ end
211
+ end
212
+ end
213
+
214
+ CommandLineInterface.start
data/bin/rfsd.rb ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/ruby
2
+ $:.unshift File.join(File.split(__FILE__).first, '../lib')
3
+ require 'innate/reload'
4
+ require 'innate/debug'
5
+ require 'pp'
6
+ $DEBUG = true
7
+ $:.unshift File.expand_path(File.split(__FILE__).first)
8
+ require 'rfs'
data/description.txt ADDED
@@ -0,0 +1,24 @@
1
+ This is a comprehensive utility for renaming sets of files. It makes full use of ruby's regular expressions to provide a number of operations including:
2
+ * add (capture a number and add +/-x to it)
3
+ * count (replace capture with a number)
4
+ * file source (specify a file which has a sequence of replacement strings, one per line)
5
+ * fill (capture text from one file name and use it on subsequent ones)
6
+ * roman (convert to and from roman numerals)
7
+ * mark changes (perform a replace only if a captured section of the file changes (uses 2 regexps) )
8
+ * in path (replace with text captured from the file's full path)
9
+ * prompt (prompt for a new name for each file)
10
+ * replace (regular search and replace)
11
+ * tape (replace 1A, 1B, 2A, 2B, ... with 1, 2, 3, 4, ...)
12
+ * title (Do Title Casing in the Proper English Way. It can Handle Punctuation too.)
13
+ * up (move files to parent directory)
14
+
15
+ It has a number of safety features:
16
+ * filters (exclusive or inclusive)
17
+ * won't overwrite existing files unless --force is specified
18
+ * yes/all/no/cancel confirmations on by default (currently command-line only)
19
+ * 3 modes: list, test, commit. Test is default
20
+
21
+ It includes a full-featured command-line interface and an almost-full-featured Fox 1.2 interface which works pretty well but needs a bit of attention.
22
+
23
+ I have also written a number of very cool/useful library classes or extensions which I will include, but which should eventually be in their own project(s).
24
+