AbsoluteRenamer 1.1.0 → 1.1.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/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
|