rfs 0.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/MIT-LICENSE +7 -0
- data/Rakefile.rb +193 -0
- data/bin/RenameFileSet.rbw +14 -0
- data/bin/rfs.rb +214 -0
- data/bin/rfsd.rb +8 -0
- data/description.txt +24 -0
- data/lib/RenameFileSet.bak.rb +372 -0
- data/lib/errors.rb +15 -0
- data/lib/filters.rb +68 -0
- data/lib/gui.rb +216 -0
- data/lib/innate/array.rb +104 -0
- data/lib/innate/coverage.rb +315 -0
- data/lib/innate/debug.rb +12 -0
- data/lib/innate/debugger.rb +944 -0
- data/lib/innate/file.rb +5 -0
- data/lib/innate/filelines.rb +148 -0
- data/lib/innate/kernel.rb +41 -0
- data/lib/innate/metaid.rb +30 -0
- data/lib/innate/mkdirs.rb +12 -0
- data/lib/innate/regexp.rb +80 -0
- data/lib/innate/reload.rb +11 -0
- data/lib/innate/roman.rb +72 -0
- data/lib/innate/scriptlines.rb +60 -0
- data/lib/innate/string.rb +56 -0
- data/lib/innate/test/all_tests.rb +11 -0
- data/lib/innate/test/files/mkdirs_dummy.file +1 -0
- data/lib/innate/test/files/reloadtarget.rb +5 -0
- data/lib/innate/test/files/reloadtarget1.rb +5 -0
- data/lib/innate/test/files/reloadtarget2.rb +5 -0
- data/lib/innate/test/test_coverage.rb +13 -0
- data/lib/innate/test/testarray.rb +98 -0
- data/lib/innate/test/testfile.rb +15 -0
- data/lib/innate/test/testfilelines.rb +106 -0
- data/lib/innate/test/testkernel.rb +30 -0
- data/lib/innate/test/testmkdirs.rb +19 -0
- data/lib/innate/test/testregexp.rb +86 -0
- data/lib/innate/test/testreload.rb +61 -0
- data/lib/innate/test/testroman.rb +54 -0
- data/lib/innate/test/testscriptlines.rb +39 -0
- data/lib/innate/test/teststring.rb +52 -0
- data/lib/innate/test/testtitlecase.rb +20 -0
- data/lib/innate/titlecase.rb +64 -0
- data/lib/innate/tracerequire.rb +22 -0
- data/lib/namesource.rb +53 -0
- data/lib/options.rb +64 -0
- data/lib/providers.rb +93 -0
- data/lib/regexp.rb +56 -0
- data/lib/rename_functions.rb +142 -0
- data/lib/renamer.rb +210 -0
- data/lib/results.rb +83 -0
- data/lib/test/test_helper.rb +147 -0
- data/tests/dir/file1 +0 -0
- data/tests/dir/file2 +0 -0
- data/tests/dir/file3 +0 -0
- data/tests/test_helper.rb +147 -0
- data/tests/test_rename_functions.rb +605 -0
- 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
|
+
|