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,35 +1,35 @@
|
|
1
1
|
module AbsoluteRenamer
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
-
print "Do you want to rename this files ? [y/N] "
|
9
|
-
begin
|
10
|
-
resp = STDIN.readline.chomp.downcase
|
11
|
-
rescue Exception => e
|
12
|
-
puts "\nExiting renamer"
|
13
|
-
exit(0)
|
14
|
-
end
|
15
|
-
return resp == "y"
|
16
|
-
end
|
17
|
-
true
|
2
|
+
class InteractivePlugin < AbsoluteRenamer::IPlugin
|
3
|
+
def before_batch_renaming
|
4
|
+
ask_for_confirmation(:once, 'Do you want to rename this files ?') do
|
5
|
+
conf[:files].each do |file|
|
6
|
+
file.display_change
|
18
7
|
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def before_file_renaming(params)
|
12
|
+
ask_for_confirmation(:always, 'Do you want to rename this file ?') do
|
13
|
+
params[:file].display_change
|
14
|
+
end
|
15
|
+
end
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
true
|
17
|
+
def ask_for_confirmation(interactivity, message, waited_answer = 'y')
|
18
|
+
if conf[:options][:interactive] == interactivity
|
19
|
+
yield
|
20
|
+
|
21
|
+
print "#{message} [y/N] "
|
22
|
+
|
23
|
+
begin
|
24
|
+
resp = STDIN.readline.chomp.downcase
|
25
|
+
rescue
|
26
|
+
puts "\nExiting renamer"
|
27
|
+
exit(0)
|
33
28
|
end
|
29
|
+
|
30
|
+
return resp == waited_answer
|
31
|
+
end
|
32
|
+
true
|
34
33
|
end
|
34
|
+
end
|
35
35
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module AbsoluteRenamer
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
2
|
+
class ListingParser < AbsoluteRenamer::IParser
|
3
|
+
def self.add_options(parser, options)
|
4
|
+
parser.on('-l', '--list', "Only display how files will be renamed") do
|
5
|
+
options[:listing] = true
|
6
|
+
end
|
8
7
|
end
|
8
|
+
end
|
9
9
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
module AbsoluteRenamer
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
end
|
9
|
-
return false
|
10
|
-
end
|
11
|
-
true
|
2
|
+
class ListingPlugin < AbsoluteRenamer::IPlugin
|
3
|
+
def before_batch_renaming
|
4
|
+
listing = conf[:options][:listing] || false
|
5
|
+
if listing
|
6
|
+
conf[:files].each do |file|
|
7
|
+
file.display_change
|
12
8
|
end
|
9
|
+
return false
|
10
|
+
end
|
11
|
+
true
|
13
12
|
end
|
13
|
+
end
|
14
14
|
end
|
@@ -3,58 +3,58 @@ require 'absolute_renamer/iplugin'
|
|
3
3
|
require 'absolute_renamer/use_config'
|
4
4
|
|
5
5
|
begin
|
6
|
-
|
6
|
+
require 'rubygems'
|
7
7
|
rescue LoadError
|
8
8
|
end
|
9
9
|
|
10
10
|
module AbsoluteRenamer
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def find_gems
|
33
|
-
installed_gems = Gem.source_index.find_name(/.*AbsoluteRenamer-.*/).map(&:name).uniq || []
|
34
|
-
installed_gems.each do |gem_name|
|
35
|
-
@gems[gem_name] = { :lib => gem_name, :version => '>= 0' }
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def find_gems_from_conf
|
40
|
-
if conf[:gems]
|
41
|
-
conf[:gems].each do |gem_name, gem_infos|
|
42
|
-
@gems[gem_name] = gem_infos ||= {}
|
43
|
-
@gems[gem_name][:lib] ||= gem_name
|
44
|
-
@gems[gem_name][:version] ||= ">= 0"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def exclude_gems
|
50
|
-
if @conf[:exclude]
|
51
|
-
@gems.reject! { |gem_name, gem_infos| @conf[:exclude].include? gem_name }
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def load_core
|
56
|
-
require 'absolute_renamer/core-packages/core-packages'
|
57
|
-
end
|
11
|
+
# Class in charge of loading external modules.
|
12
|
+
class External
|
13
|
+
class << self
|
14
|
+
include AbsoluteRenamer::UseConfig
|
15
|
+
|
16
|
+
def load_gems
|
17
|
+
if defined?(Gem)
|
18
|
+
@gems = {}
|
19
|
+
|
20
|
+
find_gems
|
21
|
+
find_gems_from_conf
|
22
|
+
exclude_gems
|
23
|
+
|
24
|
+
@gems.each do |gem_name, gem_infos|
|
25
|
+
puts "Loading gem #{gem_name} (#{gem_infos[:version]}) : #{gem_infos[:lib]}" if conf[:debug]
|
26
|
+
gem gem_name, gem_infos[:version]
|
27
|
+
require gem_infos[:lib]
|
28
|
+
end
|
58
29
|
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_gems
|
33
|
+
installed_gems = Gem.source_index.find_name(/.*AbsoluteRenamer-.*/).map(&:name).uniq || []
|
34
|
+
installed_gems.each do |gem_name|
|
35
|
+
@gems[gem_name] = { :lib => gem_name, :version => '>= 0' }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_gems_from_conf
|
40
|
+
if conf[:gems]
|
41
|
+
conf[:gems].each do |gem_name, gem_infos|
|
42
|
+
@gems[gem_name] = gem_infos ||= {}
|
43
|
+
@gems[gem_name][:lib] ||= gem_name
|
44
|
+
@gems[gem_name][:version] ||= ">= 0"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def exclude_gems
|
50
|
+
if @conf[:exclude]
|
51
|
+
@gems.reject! { |gem_name, gem_infos| @conf[:exclude].include? gem_name }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def load_core
|
56
|
+
require 'absolute_renamer/core-packages/core-packages'
|
57
|
+
end
|
59
58
|
end
|
59
|
+
end
|
60
60
|
end
|
@@ -4,105 +4,104 @@ require 'absolute_renamer/use_config'
|
|
4
4
|
require 'absolute_renamer/libs/file'
|
5
5
|
|
6
6
|
module AbsoluteRenamer
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
# Class that represents each file to be renamed.
|
8
|
+
# It contains all informations about a file and, in the end,
|
9
|
+
# processes to its renaming.
|
10
|
+
class FileInfo
|
11
|
+
include AbsoluteRenamer::UseConfig
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
attr_accessor :name, :new_name,
|
14
|
+
:path, :real_path,
|
15
|
+
:ext, :dir, :dir_path,
|
16
|
+
:level
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
18
|
+
# Initializes a FileInfo.
|
19
|
+
# path: the relative or absolute path of the file.
|
20
|
+
def initialize(path)
|
21
|
+
@path = path
|
22
|
+
@real_path = File.expand_path(@path)
|
23
|
+
@dir_path = File.dirname(@real_path)
|
24
|
+
@dir = File.directory?(@real_path)
|
25
|
+
@name = File.basename(@real_path)
|
26
|
+
unless @dir
|
27
|
+
@ext = File.extname(@name, conf[:options][:dots])
|
28
|
+
@name.gsub!(Regexp.new('.' << @ext << '$'), '') unless @ext.empty?
|
29
|
+
@level = 0
|
30
|
+
else
|
31
|
+
@level = @real_path.split('/').size
|
32
|
+
end
|
33
|
+
end
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
# Returns a description of a FileInfo
|
36
|
+
# some_fileinfo.inspect # => "File: hello_world pdf"
|
37
|
+
def inspect
|
38
|
+
"File: #{@name} #{@ext}"
|
39
|
+
end
|
41
40
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
# Displays the action that will be done on the file.
|
42
|
+
# some_fileinfo.display_change # => "rename a_file.txt --> A_File.TXT"
|
43
|
+
def display_change
|
44
|
+
puts "#{color conf[:options][:mode]} #{@real_path.sub(Dir.pwd+'/', '')} #{color '-->'} #{new_path.sub(Dir.pwd+'/', '')}"
|
45
|
+
end
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
47
|
+
# Returns a text colorized in red.
|
48
|
+
# color('hello') #=> "\e[31mhello\e[0m"
|
49
|
+
def color(text)
|
50
|
+
"\e[31m#{text}\e[0m"
|
51
|
+
end
|
53
52
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
53
|
+
# Returns the new path of the file.
|
54
|
+
def new_path
|
55
|
+
if conf[:options][:dest].nil? or conf[:options][:dest].empty?
|
56
|
+
File.join(@dir_path, @new_name)
|
57
|
+
else
|
58
|
+
File.join(conf[:options][:dest], @new_name)
|
59
|
+
end
|
60
|
+
end
|
62
61
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
62
|
+
# Renames the file.
|
63
|
+
def rename
|
64
|
+
display_change
|
65
|
+
File.rename(@real_path, new_path)
|
66
|
+
end
|
68
67
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
78
|
-
else
|
79
|
-
File.copy(@real_path, new_path, false)
|
80
|
-
end
|
68
|
+
# Copies the file.
|
69
|
+
def copy
|
70
|
+
display_change
|
71
|
+
if @dir
|
72
|
+
if @real_path != conf[:options][:dest]
|
73
|
+
FileUtils.cp_r(@real_path, conf[:options][:dest])
|
74
|
+
else
|
75
|
+
puts "#{real_path} ignored"
|
81
76
|
end
|
77
|
+
else
|
78
|
+
File.copy(@real_path, new_path, false)
|
79
|
+
end
|
80
|
+
end
|
82
81
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
82
|
+
# Moves a file. Moving to the same directories is just like renaming.
|
83
|
+
def move
|
84
|
+
display_change
|
85
|
+
File.move(@real_path, new_path, false)
|
86
|
+
end
|
88
87
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
88
|
+
# Creates a symbolic link to the file.
|
89
|
+
def link
|
90
|
+
display_change
|
91
|
+
begin
|
92
|
+
File.symlink(@real_path, new_path)
|
93
|
+
rescue NotImplemented
|
94
|
+
puts "Error: cannot create symlinks"
|
95
|
+
end
|
96
|
+
end
|
99
97
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
98
|
+
# Overriding the comparison operator to sort paths
|
99
|
+
# based on their depth.
|
100
|
+
def <=>(file_info)
|
101
|
+
if (self.level == file_info.level)
|
102
|
+
return (file_info.name <=> self.name)
|
103
|
+
end
|
104
|
+
file_info.level <=> self.level
|
107
105
|
end
|
106
|
+
end
|
108
107
|
end
|
@@ -1,86 +1,83 @@
|
|
1
1
|
require 'absolute_renamer/with_children'
|
2
2
|
|
3
3
|
module AbsoluteRenamer
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
# Modules parent class.
|
5
|
+
# Modules must inherit of it.
|
6
|
+
class IModule < AbsoluteRenamer::WithChildren
|
7
|
+
def initialize
|
8
|
+
@filters = []
|
9
|
+
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
# Returns the classname symbol
|
12
|
+
def self.symbol
|
13
|
+
name.intern
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
# pattern('test') #=> '(\[(.)?test\])'
|
19
|
-
def pattern(pattern_string)
|
20
|
-
Regexp.new "(\\[(.)?#{pattern_string}\\])"
|
21
|
-
end
|
16
|
+
def self.process(file, name_format, ext_format)
|
17
|
+
@mods ||= {}
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
val = mod.call(val)
|
30
|
-
end
|
31
|
-
val
|
32
|
-
end
|
19
|
+
self.children.each do |mod|
|
20
|
+
mod_sym = mod.symbol
|
21
|
+
@mods[mod_sym] ||= mod.new
|
22
|
+
name_format = @mods[mod_sym].process(file, name_format)
|
23
|
+
ext_format = @mods[mod_sym].process(file, ext_format, :ext) unless file.dir
|
24
|
+
end
|
33
25
|
|
34
|
-
|
35
|
-
|
36
|
-
# The pattern is a regular expression obtained by concatening
|
37
|
-
# the +@filters+ variable with "|".
|
38
|
-
#
|
39
|
-
# file: a FileInfo instance
|
40
|
-
# format: the format string used to rename the file
|
41
|
-
# type: the type of the renaming format (:name or :ext)
|
42
|
-
def process(file, format, type = :name)
|
43
|
-
return format if @filters.empty?
|
26
|
+
[name_format, ext_format]
|
27
|
+
end
|
44
28
|
|
45
|
-
|
46
|
-
|
47
|
-
|
29
|
+
# Returns a format pattern generated from pattern_string that
|
30
|
+
# can be used to match strings like [*test] in the filename format
|
31
|
+
# pattern('test') #=> '(\[(.)?test\])'
|
32
|
+
def pattern(pattern_string)
|
33
|
+
Regexp.new "(\\[(.)?#{pattern_string}\\])"
|
34
|
+
end
|
48
35
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
36
|
+
# Returns a value modified using a modifier defined in the Case module
|
37
|
+
# modifiy('value', '&') #=> 'VALUE'
|
38
|
+
# modifiy('value', '*') #=> 'Value'
|
39
|
+
def modify(val, modifier)
|
40
|
+
modification = CaseModule.actions[modifier]
|
41
|
+
val = CaseModule.send(modification, val) unless modification.nil?
|
42
|
+
val
|
43
|
+
end
|
44
|
+
|
45
|
+
# Process a +file+ by searching for a known pattern in its name
|
46
|
+
# and replacing it by the corresponding value.
|
47
|
+
# The pattern is a regular expression obtained by concatening
|
48
|
+
# the +@filters+ variable with "|".
|
49
|
+
#
|
50
|
+
# file: a FileInfo instance
|
51
|
+
# format: the format string used to rename the file
|
52
|
+
# type: the type of the renaming format (:name or :ext)
|
53
|
+
def process(file, format, type = :name)
|
54
|
+
return format if @filters.empty?
|
63
55
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
#
|
68
|
-
# file: a FileInfo instance
|
69
|
-
# infos: the matched values depending of the pattern
|
70
|
-
# type: the type of the renaming format (:name or :ext)
|
71
|
-
def interpret(file, infos, type)
|
72
|
-
modifier = infos[2]
|
73
|
-
action = infos[3]
|
56
|
+
result = []
|
57
|
+
pattern = Regexp.union @filters
|
58
|
+
idx = format.index(pattern)
|
74
59
|
|
75
|
-
|
60
|
+
while idx
|
61
|
+
matched = pattern.match(format).to_a
|
62
|
+
part = format.partition(matched[0])
|
63
|
+
result.push(part[0])
|
64
|
+
result.push self.interpret(file, matched, type)
|
65
|
+
format = part[2]
|
66
|
+
idx = format.index(pattern)
|
67
|
+
end
|
68
|
+
result.push(format)
|
69
|
+
format.replace(result.join)
|
70
|
+
end
|
76
71
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
72
|
+
# Interprets a matched pattern.
|
73
|
+
# Searchs for the corresponding callback
|
74
|
+
# in the current module and call it.
|
75
|
+
#
|
76
|
+
# file: a FileInfo instance
|
77
|
+
# infos: the matched values depending of the pattern
|
78
|
+
# type: the type of the renaming format (:name or :ext)
|
79
|
+
def interpret(file, infos, type)
|
80
|
+
# This method has to be overriden in every module
|
85
81
|
end
|
82
|
+
end
|
86
83
|
end
|