AbsoluteRenamer 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/AbsoluteRenamer.gemspec +26 -87
- data/Gemfile +4 -0
- data/README.rdoc +3 -1
- data/Rakefile +2 -59
- data/bin/absrenamer +4 -6
- data/conf/absrenamer/absrenamer.conf +1 -1
- data/lib/absolute_renamer.rb +99 -103
- data/lib/absolute_renamer/config.rb +43 -34
- data/lib/absolute_renamer/core-packages/core-case/module.rb +23 -24
- data/lib/absolute_renamer/core-packages/core-general/module.rb +106 -128
- data/lib/absolute_renamer/core-packages/core-general/parser.rb +75 -64
- data/lib/absolute_renamer/core-packages/core-interactive/parser.rb +15 -15
- data/lib/absolute_renamer/core-packages/core-interactive/plugin.rb +29 -29
- data/lib/absolute_renamer/core-packages/core-listing/parser.rb +6 -6
- data/lib/absolute_renamer/core-packages/core-listing/plugin.rb +10 -10
- data/lib/absolute_renamer/external.rb +48 -48
- data/lib/absolute_renamer/file_info.rb +87 -88
- data/lib/absolute_renamer/imodule.rb +69 -72
- data/lib/absolute_renamer/iparser.rb +4 -4
- data/lib/absolute_renamer/iplugin.rb +6 -6
- data/lib/absolute_renamer/libs/file.rb +8 -8
- data/lib/absolute_renamer/libs/hash.rb +23 -23
- data/lib/absolute_renamer/libs/string.rb +21 -21
- data/lib/absolute_renamer/parser.rb +57 -58
- data/lib/absolute_renamer/use_config.rb +6 -6
- data/lib/absolute_renamer/version.rb +3 -0
- data/lib/absolute_renamer/with_children.rb +15 -15
- data/test/config_test.rb +1 -8
- data/test/file_info_test.rb +23 -22
- data/test/imodule_test.rb +1 -8
- metadata +54 -18
- data/VERSION +0 -1
@@ -1,31 +1,30 @@
|
|
1
1
|
module AbsoluteRenamer
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
2
|
+
class CaseModule < AbsoluteRenamer::IModule
|
3
|
+
class << self
|
4
|
+
def actions
|
5
|
+
{
|
6
|
+
'*' => :camelize,
|
7
|
+
'&' => :upper,
|
8
|
+
'%' => :lower,
|
9
|
+
'$' => :original
|
10
|
+
}
|
11
|
+
end
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
def camelize(str)
|
14
|
+
str.camelize
|
15
|
+
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
def original(str)
|
18
|
+
str
|
19
|
+
end
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
def lower(str)
|
22
|
+
str.downcase
|
23
|
+
end
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
25
|
+
def upper(str)
|
26
|
+
str.upcase
|
27
|
+
end
|
30
28
|
end
|
29
|
+
end
|
31
30
|
end
|
@@ -1,131 +1,109 @@
|
|
1
1
|
module AbsoluteRenamer
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# matches counters like # ### #{2} ##{2;42} or [length-42]
|
32
|
-
@misc_filters = [
|
33
|
-
/\//,
|
34
|
-
/#+(\{.*\})?/,
|
35
|
-
/\[length(--?\d+)?\]/
|
36
|
-
]
|
37
|
-
|
38
|
-
@filters = @case_filters + @part_filters + @misc_filters
|
39
|
-
end
|
40
|
-
|
41
|
-
def interpret(file, infos, type)
|
42
|
-
if (infos[0].length == 1)
|
43
|
-
self.method(@actions[infos[0][0].chr]).call(file, infos, type)
|
44
|
-
elsif (infos[0][1..6] == 'length')
|
45
|
-
self.length(file, infos, type)
|
46
|
-
elsif (infos[0][0].chr == '[')
|
47
|
-
self.file_part(file, infos, type)
|
48
|
-
elsif (infos[0][0].chr == '#')
|
49
|
-
self.count(file, infos, type)
|
50
|
-
else
|
51
|
-
infos[0][1].chr
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def file_camelize(file, infos, type)
|
56
|
-
file.send(type).camelize
|
57
|
-
end
|
58
|
-
|
59
|
-
def file_original(file, infos, type)
|
60
|
-
file.send(type)
|
61
|
-
end
|
62
|
-
|
63
|
-
def file_downcase(file, infos, type)
|
64
|
-
file.send(type).downcase
|
65
|
-
end
|
66
|
-
|
67
|
-
def file_upcase(file, infos, type)
|
68
|
-
file.send(type).upcase
|
69
|
-
end
|
70
|
-
|
71
|
-
def file_strip(file, infos, type)
|
72
|
-
file.send(type).strip
|
73
|
-
end
|
74
|
-
|
75
|
-
def file_part(file, infos, type)
|
76
|
-
matched = infos[0].match(/(\[([^\d])?(\d+)(((;)(\d+))|((-)(\d+)?))?\])/)
|
77
|
-
|
78
|
-
x = matched[3].to_i - 1
|
79
|
-
y = matched[7] || matched[10]
|
80
|
-
y = y.to_i unless y.nil?
|
81
|
-
action = matched[6] || matched[9]
|
82
|
-
|
83
|
-
str = file.send(type)
|
84
|
-
|
85
|
-
if (action == '-')
|
86
|
-
y -= 1 unless y.nil?
|
87
|
-
y ||= str.length
|
88
|
-
val = str[x..y]
|
89
|
-
elsif (action == ';')
|
90
|
-
val = str[x, y]
|
91
|
-
else
|
92
|
-
val = str[x].chr
|
93
|
-
end
|
94
|
-
|
95
|
-
val ||= ''
|
96
|
-
|
97
|
-
modify val, matched[2]
|
98
|
-
end
|
99
|
-
|
100
|
-
def count(file, infos, type)
|
101
|
-
matched = infos[0].match(/(#+)(\{((-?\d+)(;(-?\d+)?)?)?\})?/)
|
102
|
-
@counter ||= []
|
103
|
-
@last ||= nil
|
104
|
-
@current ||= 0
|
105
|
-
|
106
|
-
@current = 0 if @last != file
|
107
|
-
@current += 1 if @last == file
|
108
|
-
|
109
|
-
start = matched[4] || 1
|
110
|
-
step = matched[6] || 1
|
111
|
-
start = start.to_i
|
112
|
-
step = step.to_i
|
113
|
-
|
114
|
-
@counter[@current] ||= {:start => start,
|
115
|
-
:step => step,
|
116
|
-
:current => start - step
|
117
|
-
}
|
118
|
-
|
119
|
-
@counter[@current][:current] += @counter[@current][:step]
|
120
|
-
@last = file
|
121
|
-
val = @counter[@current][:current].to_s.rjust(matched[1].length, '0')
|
122
|
-
val.gsub!(/(0+)-/, '-\1')
|
123
|
-
val
|
124
|
-
end
|
125
|
-
|
126
|
-
def length(file, infos, type)
|
127
|
-
matched = infos[0].match(/\[length(-(-?\d+))?\]/)
|
128
|
-
file.name.length - matched[2].to_i
|
129
|
-
end
|
2
|
+
class GeneralModule < AbsoluteRenamer::IModule
|
3
|
+
def initialize
|
4
|
+
@case_filters = [
|
5
|
+
/\\\*/, # \*
|
6
|
+
/\\\$/, # \$
|
7
|
+
/\\%/, # \%
|
8
|
+
/\\&/, # \&
|
9
|
+
/\\\\/, # \\
|
10
|
+
/\*/, # *
|
11
|
+
/\$/, # $
|
12
|
+
/%/, # %
|
13
|
+
/&/, # &
|
14
|
+
/\\/ # \
|
15
|
+
]
|
16
|
+
|
17
|
+
# matches strings like [42-43] [42-] [*42-43] [42;43] etc...
|
18
|
+
@part_filters = [
|
19
|
+
pattern('(\d+)((-(\d+)?)|(;\d+))?')
|
20
|
+
]
|
21
|
+
|
22
|
+
# matches counters like # ### #{2} ##{2;42} or [length-42]
|
23
|
+
@misc_filters = [
|
24
|
+
/\//,
|
25
|
+
/#+(\{.*\})?/,
|
26
|
+
/\[length(--?\d+)?\]/
|
27
|
+
]
|
28
|
+
|
29
|
+
@filters = @case_filters + @part_filters + @misc_filters
|
130
30
|
end
|
31
|
+
|
32
|
+
def interpret(file, infos, type)
|
33
|
+
if (infos[0][0].chr == '#')
|
34
|
+
count(file, infos)
|
35
|
+
elsif (infos[0].length == 1)
|
36
|
+
file_case(file, infos[0], type)
|
37
|
+
elsif (infos[0][1..6] == 'length')
|
38
|
+
length(file, infos)
|
39
|
+
elsif (infos[0][0].chr == '[')
|
40
|
+
file_part(file, infos, type)
|
41
|
+
else
|
42
|
+
infos[0][1].chr
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def file_case(file, modifier, type)
|
47
|
+
file_info = file.send(type)
|
48
|
+
|
49
|
+
if modifier.match /\\/
|
50
|
+
file_info.strip!
|
51
|
+
else
|
52
|
+
file_info = modify(file_info, modifier)
|
53
|
+
end
|
54
|
+
|
55
|
+
file_info
|
56
|
+
end
|
57
|
+
|
58
|
+
def file_part(file, infos, type)
|
59
|
+
matched = infos[0].match(/(\[([^\d])?(\d+)(((;)(\d+))|((-)(\d+)?))?\])/)
|
60
|
+
|
61
|
+
x = matched[3].to_i - 1
|
62
|
+
y = matched[7] || matched[10]
|
63
|
+
y = y.to_i unless y.nil?
|
64
|
+
action = matched[6] || matched[9]
|
65
|
+
|
66
|
+
str = file.send(type)
|
67
|
+
|
68
|
+
if (action == '-')
|
69
|
+
y -= 1 unless y.nil?
|
70
|
+
y ||= str.length
|
71
|
+
val = str[x..y]
|
72
|
+
elsif (action == ';')
|
73
|
+
val = str[x, y]
|
74
|
+
else
|
75
|
+
val = str[x].chr
|
76
|
+
end
|
77
|
+
|
78
|
+
val ||= ''
|
79
|
+
|
80
|
+
modify val, matched[2]
|
81
|
+
end
|
82
|
+
|
83
|
+
def count(file, infos)
|
84
|
+
matched = infos[0].match(/(#+)(\{((-?\d+)(;(-?\d+)?)?)?\})?/)
|
85
|
+
@counter ||= []
|
86
|
+
@last ||= nil
|
87
|
+
|
88
|
+
@current = (@last != file) ? 0 : @current + 1
|
89
|
+
|
90
|
+
@counter[@current] ||= {
|
91
|
+
:value => (matched[4] || 1).to_i,
|
92
|
+
:step => (matched[6] || 1).to_i,
|
93
|
+
}
|
94
|
+
|
95
|
+
val = @counter[@current][:value].to_s.rjust(matched[1].length, '0')
|
96
|
+
val.gsub!(/(0+)-/, '-\1')
|
97
|
+
|
98
|
+
@counter[@current][:value] += @counter[@current][:step]
|
99
|
+
@last = file
|
100
|
+
|
101
|
+
val
|
102
|
+
end
|
103
|
+
|
104
|
+
def length(file, infos)
|
105
|
+
matched = infos[0].match(/\[length(-(-?\d+))?\]/)
|
106
|
+
file.name.length - matched[2].to_i
|
107
|
+
end
|
108
|
+
end
|
131
109
|
end
|
@@ -1,78 +1,89 @@
|
|
1
1
|
# TODO ajouter une option pour la gestion du nombre de points avant l'extension
|
2
2
|
module AbsoluteRenamer
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
class GeneralParser < AbsoluteRenamer::IParser
|
4
|
+
def self.add_options(parser, options)
|
5
|
+
parser.banner << ' [file]...'
|
6
|
+
parser.on_tail('-h', '--help', 'Display this help screen') do
|
7
|
+
puts parser
|
8
|
+
exit 0
|
9
|
+
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
parser.on('-r', '--replace PATTERN,REPLACEMENT', Array,
|
12
|
+
'String replacement ex:"pattern,replacement" ') do |data|
|
13
|
+
self.add_replacement(data, options)
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
parser.on('-e', '--regexp-replace PATTERN,REPLACEMENT', Array,
|
17
|
+
'String replacement using regexp') do |data|
|
18
|
+
self.add_replacement(data, options, true)
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
parser.on('-f', '--format FORMAT',
|
22
|
+
'Format string used as model') do |format|
|
23
|
+
options[:format] = format
|
24
|
+
@format_given = true
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
raise "Format cannot contain the / character." if format.match(/\//)
|
27
|
+
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
parser.on('-x', '--ext-format FORMAT',
|
30
|
+
'Format string used as model for the extension') do |format|
|
31
|
+
options[:ext_format] = format
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
raise "Format cannot contain the / character." if format.match(/\//)
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
36
|
+
parser.on('--no-ext', '--no-extension', 'Removes the extension') do
|
37
|
+
options[:no_ext] = true
|
38
|
+
end
|
40
39
|
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
parser.on('--dots N', Integer, 'Number of dots to get file extension') do |dots|
|
41
|
+
options[:dots] = dots
|
42
|
+
end
|
44
43
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
' move: move each file in DEST with its new name',
|
50
|
-
' link: create a symbolic link to each file in DEST' <<
|
51
|
-
' with its new name') do |mode|
|
52
|
-
options[:mode] = mode
|
53
|
-
end
|
44
|
+
parser.on('-R', '--recursive',
|
45
|
+
'Rename files in subdirectories recursively') do
|
46
|
+
options[:rec] = true
|
47
|
+
end
|
54
48
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
parser.on('-d', '--directories', 'Directories handling') do
|
61
|
-
options[:dir] = true
|
62
|
-
end
|
63
|
-
end
|
49
|
+
parser.on('--maxdepth N', Integer, 'Maximum recursion depth') do |depth|
|
50
|
+
options[:maxdepth] = depth
|
51
|
+
end
|
64
52
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
53
|
+
parser.on('-m', '--mode MODE', [:rename, :copy, :move, :link],
|
54
|
+
'Renaming mode. Can be used with --dest DEST.',
|
55
|
+
' rename: simply rename files',
|
56
|
+
' copy: make a copy of each file in DEST with its new name',
|
57
|
+
' move: move each file in DEST with its new name',
|
58
|
+
' link: create a symbolic link to each file in DEST' <<
|
59
|
+
' with its new name') do |mode|
|
60
|
+
options[:mode] = mode
|
61
|
+
end
|
62
|
+
|
63
|
+
parser.on('--dest DEST', 'Destination directory' <<
|
64
|
+
' for copy, move and link modes') do |dest|
|
65
|
+
options[:dest] = File.expand_path(dest)
|
66
|
+
end
|
67
|
+
|
68
|
+
parser.on('-d', '--directories', 'Directories handling') do
|
69
|
+
options[:dir] = true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.add_replacement(data, options, regexp = false)
|
74
|
+
pattern, replace = data[0..1]
|
75
|
+
replace ||= ''
|
76
|
+
pattern = Regexp.new(pattern) if regexp
|
77
|
+
moment = @format_given.nil? ? :before : :after
|
78
|
+
options[:replacements] ||= {
|
79
|
+
:before => [],
|
80
|
+
:after => []
|
81
|
+
}
|
82
|
+
options[:replacements][moment] << {
|
83
|
+
:type => pattern.class,
|
84
|
+
:pattern => pattern,
|
85
|
+
:replace => replace
|
86
|
+
}
|
77
87
|
end
|
78
|
-
end
|
88
|
+
end
|
89
|
+
end
|
@@ -1,20 +1,20 @@
|
|
1
1
|
module AbsoluteRenamer
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
class InteractiveParser < AbsoluteRenamer::IParser
|
3
|
+
def self.add_options(parser, options)
|
4
|
+
parser.on('-i', 'Prompt before each renamming') do
|
5
|
+
options[:interactive] = :always
|
6
|
+
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
parser.on('-I', 'Prompt once before batch renamming') do
|
9
|
+
options[:interactive] = :once
|
10
|
+
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
12
|
+
parser.on('--interactive [WHEN]', [:always, :never, :once],
|
13
|
+
'Prompt according to WHEN: never, once (-I), or always (-i).',
|
14
|
+
'Without WHEN, prompt always') do |w|
|
15
|
+
w = :always if w.nil?
|
16
|
+
options[:interactive] = w
|
17
|
+
end
|
19
18
|
end
|
19
|
+
end
|
20
20
|
end
|