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/lib/regexp.rb ADDED
@@ -0,0 +1,56 @@
1
+ class PrepareRegexp
2
+ def initialize(pattern)
3
+ @s = pattern
4
+ end
5
+
6
+ #def new_regexp(pattern)
7
+ #begin
8
+ #Regexp.new(pattern)
9
+ #end
10
+
11
+ def create(ignore_case, verbose, roman, &b)
12
+ begin
13
+ c = verbose ? VerboseRegexp : Regexp
14
+ o = ignore_case ? Regexp::IGNORECASE : nil
15
+ s = roman ? @s.gsub('(roman)', "(#{Regexp.ROMAN_PATTERN})") : @s
16
+ r = c.new s, o
17
+ r.result_proc = b if verbose
18
+ r
19
+ rescue
20
+ b.call :error, "Error parsing regular expression:\n#{$!}"
21
+ nil
22
+ end
23
+ end
24
+
25
+ def self.each(options)
26
+ options[:filter].each do |f|
27
+ if f.regexp.is_a? PrepareRegexp
28
+ f.regexp = yield :filter, f.regexp
29
+ end
30
+ end
31
+ options.each do |k, v|
32
+ if v.is_a? PrepareRegexp
33
+ options[k] = yield k, v
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ class VerboseRegexp < Regexp
40
+ attr_accessor :k
41
+ attr_accessor :result_proc
42
+
43
+ def match(*a)
44
+ m = super
45
+ s = "MatchData breakdown for #{@k}: "
46
+ if m
47
+ s += "\n" + m.display_breakdown
48
+ else
49
+ s += 'no match'
50
+ end
51
+ @result_proc.call :match, s
52
+ m
53
+ end
54
+ end
55
+
56
+
@@ -0,0 +1,142 @@
1
+
2
+ module RenameFunctions
3
+ protected
4
+ def rename_replace
5
+ rename_each_match do |file, md|
6
+ subst(file, md, opt(:replace, String))
7
+ end
8
+ end
9
+
10
+ def rename_name_source
11
+ rename_each_match do |file, md, path|
12
+ subst(file, md, opt(:source, NameSource).name(file, md, path))
13
+ end
14
+ end
15
+
16
+ def rename_add
17
+ if opt(:add, Numeric) > 0
18
+ opt(:file_provider, Provider::File::Base).reverse
19
+ end
20
+ rename_each_match do |file, md|
21
+ int = active_capture(md).to_i rescue int = active_capture(md) rescue int = 0
22
+ subst(file, md, (int + opt(:add, Numeric)).to_s)
23
+ end
24
+ end
25
+
26
+ def rename_count
27
+ count = 0
28
+ rename_each_match do |file, md|
29
+ count += 1
30
+ subst(file, md, count.to_s)
31
+ end
32
+ end
33
+
34
+ def rename_from_full_name
35
+ r = opt(:pattern, Regexp)
36
+ rename_each_match do |file, md, path|
37
+ fn = File.expand_path(File.join(path, file))
38
+ m = r.match(fn)
39
+ verbose {"match on fullname: #{m.display}"}
40
+ if m
41
+ subst(file, md, active_capture(m))
42
+ else
43
+ [:message, "no match in full name: #{fn}"]
44
+ end
45
+ end
46
+ end
47
+
48
+ def rename_move_up
49
+ rename_each_match do |file, md, path|
50
+ p = File.split(path).first
51
+ unless p == '\\'
52
+ [:full_path, File.join(p, file)]
53
+ else
54
+ nil
55
+ end
56
+ end
57
+ end
58
+
59
+ def rename_roman
60
+ s = @options[:search]
61
+ if s.is_a? Regexp
62
+ @options[:search] = s.interpolate_roman
63
+ verbose {"search: #{@options[:search].to_s}"}
64
+ end
65
+ rename_each_match do |file, md|
66
+ subst(file, md, active_capture(md).toggle_roman) if active_capture(md).length > 0
67
+ end
68
+ end
69
+
70
+ def rename_mark_change
71
+ prev = ''
72
+ rename_each_match do |file, md|
73
+ m = active_capture(md)
74
+ unless prev == m
75
+ prev = m
76
+ subst(file, opt(:replace_pattern, Regexp).match(file), opt(:replace_text, String))
77
+ end
78
+ end
79
+ end
80
+
81
+ def rename_tape_numbers
82
+ rename_each_match do |file, md|
83
+ t = active_capture(md)
84
+ n = t.to_i * 2 + (/a/i =~ t[-1,1] ? -1 : 0)
85
+ n += 2 if n < 1
86
+ subst(file, md, n.to_s)
87
+ end
88
+ end
89
+
90
+ def rename_capitalize
91
+ al = opt(:always_lower, Array) rescue %w{ vol
92
+ mp3 wav txt avi nfo pdf rar
93
+ zip rb ape sfk mpg rm qt doc }
94
+ l = opt(:lower, Array) rescue String.LOWER
95
+ dc = opt(:double_cap) rescue String.DOUBLE_CAP
96
+ ca = opt(:cap_after) rescue String.CAP_AFTER
97
+ sp = opt(:split_pattern) rescue String.SPLIT_PATTERN
98
+ u = opt(:upper) rescue String.UPPER
99
+
100
+ rename_each_match do |file, md|
101
+ o = active_capture(md)
102
+ r = o.title_case(:always_lower => al,
103
+ :lower => l,
104
+ :double_cap => dc,
105
+ :cap_after => ca,
106
+ :split_pattern => sp,
107
+ :upper => u)
108
+ subst(file, md, r)
109
+ end
110
+ end
111
+
112
+ def rename_fill
113
+ fillRE = opt :fill, Regexp
114
+ fillText = ''
115
+ verbose {"fill pattern: #{fillRE.to_s}"}
116
+ rename_each do |file, md|
117
+ if md or fillText
118
+ m = active_capture(md) if md
119
+ if md and m.length > 0 and not (fillRE and fillRE.match(file))
120
+ fillText = m
121
+ [:message, "source: #{file}"]
122
+ else
123
+ md = fillRE.match(file)
124
+ if md
125
+ [:rename, subst(file, md, fillText)]
126
+ else
127
+ [:message, "* No capture to fill * #{file}"]
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ def rename_remove
135
+ rename_each_match do |file, md, path|
136
+ :delete
137
+ end
138
+ end
139
+ end
140
+
141
+
142
+
data/lib/renamer.rb ADDED
@@ -0,0 +1,210 @@
1
+ require 'innate/mkdirs'
2
+ require 'innate/roman'
3
+ require 'innate/titlecase'
4
+ require 'providers'
5
+ require 'rename_functions'
6
+ require 'stringio'
7
+ require 'errors'
8
+ require 'options'
9
+ require 'filters'
10
+ require 'regexp'
11
+ require 'namesource'
12
+
13
+ class Renamer
14
+ def run(name, options, &block)
15
+ @result_proc = block
16
+ @options = options
17
+ method(name).call
18
+ end
19
+
20
+ def rename_functions
21
+ methods.grep(/^rename_/)
22
+ end
23
+
24
+ include RenameFunctions
25
+ include Options
26
+
27
+ protected
28
+
29
+ def subst(src, matchdata, repl)
30
+ return src unless matchdata
31
+ matchdata.sub repl, options[:capture_num]
32
+ end
33
+
34
+ def result(type, t, path = nil, file = nil)
35
+ @result_proc.call type, t.to_s, path, file
36
+ end
37
+
38
+ def verbose
39
+ @result_proc.call(:verbose, yield) if @options[:verbose]
40
+ end
41
+
42
+ def actual_rename(o, n)
43
+ File.mkdirs File.split(n).first
44
+ if File.directory? n
45
+ raise DirectoryExistsError.new("Can't overwrite an existing directory.")
46
+ else
47
+ File.rename(o, n)
48
+ end
49
+ end
50
+
51
+ def actual_delete(fn)
52
+ if File.directory? fn
53
+ Dir.delete fn
54
+ else
55
+ File.delete fn
56
+ end
57
+ end
58
+
59
+ def confirm(path, oldfn, newfn, full_path)
60
+ return true if @confirm == :all || !@options[:confirm]
61
+ exists = @options[:force] && already_exists?(path, oldfn, newfn, full_path)
62
+ @confirm = opt(:confirm, Proc, Method).call path, oldfn, newfn, full_path, exists, (@confirm || :yes)
63
+ verbose {"confirmation: #{@confirm}"}
64
+ case @confirm
65
+ when :all, :yes
66
+ return true
67
+ when :no
68
+ return false
69
+ when :cancel
70
+ throw :cancel
71
+ end
72
+ end
73
+
74
+ def dest_file(path, newfn, full_path)
75
+ if full_path
76
+ fn = newfn
77
+ else
78
+ fn = File.join(path, newfn)
79
+ end
80
+ end
81
+
82
+ def active_capture(matchdata)
83
+ n = @options[:capture_num] || 1
84
+ matchdata[n] || matchdata[0]
85
+ end
86
+
87
+ def already_exists?(path, oldfn, newfn, full_path)
88
+ fn = dest_file(path, newfn, full_path)
89
+ if File.exists? fn
90
+ fn.downcase != File.join(path, oldfn).downcase
91
+ else
92
+ false
93
+ end
94
+ end
95
+
96
+ def rename(path, oldfn, newfn, full_path = false)
97
+ old_full = File.join path, oldfn
98
+ verbose {"rename: `#{old_full}` -> `#{newfn}`"}
99
+ unless oldfn == newfn or (full_path and old_full == newfn)
100
+ t = newfn
101
+ fn = dest_file path, newfn, full_path
102
+ if !@options[:force] and already_exists?(path, oldfn, newfn, full_path)
103
+ result :fileerror, 'File already exists', path, oldfn
104
+ elsif @options[:action] == :commit
105
+ if confirm path, oldfn, newfn, full_path
106
+ begin
107
+ actual_rename old_full, fn
108
+ rescue => e
109
+ result :fileerror, "#{e.class}: #{e.message}", path, oldfn
110
+ else
111
+ result :ok, t, path, oldfn
112
+ end
113
+ else
114
+ result :skipped, oldfn, path, oldfn
115
+ end
116
+ else
117
+ result :ok, t, path, oldfn
118
+ end
119
+ end
120
+ end
121
+
122
+ def delete(path, file)
123
+ fn = File.join path, file
124
+ verbose {"delete: `#{fn}` "}
125
+ if @options[:action] == :commit
126
+ if confirm path, file, '<<deleted>>', false
127
+ begin
128
+ actual_delete fn
129
+ rescue => e
130
+ result :fileerror, "#{e.class}: #{e.message}", path, file
131
+ else
132
+ result :ok, "<<#{file}>>", path, file
133
+ end
134
+ else
135
+ result :skipped, file, path, file
136
+ end
137
+ else
138
+ result :ok, "<<#{file}>>", path, file
139
+ end
140
+ end
141
+
142
+ def rename_each
143
+ if @options[:action] == :list
144
+ @options[:file_provider].each do |path, file|
145
+ opt_if(:filter, Filter) do |filter|
146
+ unless filter.filter path, file
147
+ verbose { "filtered: #{file}" }
148
+ next
149
+ end
150
+ end
151
+ result :ok, "#{file}#{(File.directory?(File.join(path, file)) ? '/' : '')}", path, file
152
+ end
153
+ elsif @options[:action] == :commit || @options[:action] == :preview
154
+ catch :cancel do
155
+ search = opt :search, Regexp
156
+ filter = opt_if(:filter, Filter)
157
+ @options[:file_provider].each do |path, file|
158
+ verbose {'-------------------------'}
159
+ if filter; unless filter.filter path, file
160
+ verbose { "filtered: #{file}" }
161
+ next
162
+ end end
163
+ verbose {"file: #{File.join path, file}"}
164
+ md = search.match(file)
165
+ verbose {"match: #{md.display}"}
166
+ begin
167
+ action, text = r = yield(file, md, path)
168
+ verbose {"action: #{action.inspect}"}
169
+ if action == :rename
170
+ rename path, file, text if text
171
+ elsif action == :full_path
172
+ rename path, file, text, true if text
173
+ elsif action == :message
174
+ result :message, text, path, file
175
+ elsif r == :delete
176
+ delete path, file
177
+ elsif r
178
+ result :fileerror, "Unrecognised response: #{r.inspect}", path, file
179
+ end
180
+ rescue => e
181
+ if e.is_a? RenamerError or (!$DEBUG and !$TESTING)
182
+ t = file
183
+ if md
184
+ t = md.display
185
+ end
186
+ result :fileerror, "Error: #{e.class} on #{t}\n#{e.message}", path, file
187
+ else
188
+ raise e
189
+ end
190
+ end
191
+ end
192
+ end
193
+ else
194
+ raise ActionError.new
195
+ end
196
+ end
197
+
198
+ def rename_each_match
199
+ rename_each do |file, md, path|
200
+ r = yield(file, md, path) if md
201
+ if r.is_a? String
202
+ [:rename, r]
203
+ else
204
+ r
205
+ end
206
+ end
207
+ end
208
+
209
+ end
210
+
data/lib/results.rb ADDED
@@ -0,0 +1,83 @@
1
+ class CollectResults
2
+ attr_accessor :verbose, :quiet, :silent, :sideways, :lineways, :sorted, :list
3
+ attr_accessor :out
4
+ attr_reader :results
5
+
6
+ def initialize(out_io)
7
+ @verbose = false
8
+ @quiet = false
9
+ @silent = false
10
+ @sideways = false
11
+ @lineways = false
12
+ @sorted = false
13
+ @list = false
14
+
15
+ @out = out_io
16
+
17
+ @results = Hash.new do |h, k|
18
+ case k when :fileerror, :ok, :skipped
19
+ h[k] = Hash.new {|hh, kk| hh[kk] = []}
20
+ else
21
+ h[k] = []
22
+ end
23
+ end
24
+ end
25
+
26
+ def output(mode, text, path = nil, file = nil)
27
+ if @verbose
28
+ @out.puts "#{mode}: #{text}"
29
+ else
30
+ case mode when :ok, :skipped
31
+ @results[mode][path] << text
32
+ when :fileerror
33
+ @results[mode][path] << "#{file}: #{text}"
34
+ else
35
+ @results[mode] << text
36
+ end
37
+ end
38
+ end
39
+
40
+ def display
41
+ result = ""
42
+ unless @silent
43
+ m = @sideways ? :rows : :columns
44
+ ok = 0
45
+ skipped = 0
46
+ errors = 0
47
+ @results.each do |mode, value|
48
+ case mode when :fileerror, :ok, :skipped
49
+ value.collect.sort.each do |k, v|
50
+ case mode when :ok: ok += v.length
51
+ when :skipped: skipped += v.length
52
+ when :fileerror: errors += v.length
53
+ end
54
+ s = "\n#{File.expand_path(k)}"
55
+ s += ": #{mode.to_s.upcase} - #{v.length} files" unless @quiet or @list
56
+ @out.puts s
57
+ v = v.sort if @sorted
58
+ if @lineways
59
+ @out.puts v.join("\n")
60
+ else
61
+ @out.puts v.send(m, 80)
62
+ end
63
+ end
64
+ else
65
+ @out.puts "\n#{mode.to_s.upcase}"
66
+ if @lineways
67
+ @out.puts value.join("\n")
68
+ else
69
+ @out.puts value.send(m, 80)
70
+ end
71
+ end
72
+ end
73
+ unless @quiet
74
+ @out.print "\nOK: #{ok}" if ok.nonzero?
75
+ @out.print "\nSKIPPED: #{skipped}" if skipped.nonzero?
76
+ @out.print "\nERRORS: #{errors}" if errors.nonzero?
77
+ @out.puts "\nTOTAL: #{ok + skipped + errors}"
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+