AbsoluteRenamer 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/AbsoluteRenamer.gemspec +40 -0
  2. data/COPYING +340 -0
  3. data/ChangeLog +0 -0
  4. data/LICENCE +17 -0
  5. data/Manifest.txt +42 -0
  6. data/README +34 -0
  7. data/Rakefile +20 -0
  8. data/bin/absrenamer +54 -0
  9. data/conf/absrenamer/absrenamer.conf +34 -0
  10. data/lib/absolute_renamer.rb +119 -0
  11. data/lib/absolute_renamer/config.rb +40 -0
  12. data/lib/absolute_renamer/external.rb +56 -0
  13. data/lib/absolute_renamer/external/modules/core/case/module.rb +31 -0
  14. data/lib/absolute_renamer/external/modules/core/general/module.rb +126 -0
  15. data/lib/absolute_renamer/external/parsers/core/general/parser.rb +74 -0
  16. data/lib/absolute_renamer/external/parsers/interactive/parser.rb +20 -0
  17. data/lib/absolute_renamer/external/parsers/listing/parser.rb +9 -0
  18. data/lib/absolute_renamer/external/plugins/interactive/plugin.rb +35 -0
  19. data/lib/absolute_renamer/external/plugins/listing/plugin.rb +14 -0
  20. data/lib/absolute_renamer/file_info.rb +90 -0
  21. data/lib/absolute_renamer/imodule.rb +68 -0
  22. data/lib/absolute_renamer/iparser.rb +8 -0
  23. data/lib/absolute_renamer/iplugin.rb +11 -0
  24. data/lib/absolute_renamer/libs/file.rb +11 -0
  25. data/lib/absolute_renamer/libs/string.rb +27 -0
  26. data/lib/absolute_renamer/parser.rb +91 -0
  27. data/lib/absolute_renamer/use_config.rb +11 -0
  28. data/lib/absolute_renamer/with_children.rb +22 -0
  29. data/man/man8/absrenamer.8 +67 -0
  30. data/script/console +10 -0
  31. data/script/destroy +14 -0
  32. data/script/generate +14 -0
  33. data/setup.rb +1585 -0
  34. data/test/unit/features/test_absrenamer.conf +34 -0
  35. data/test/unit/tc_config.rb +28 -0
  36. data/test/unit/tc_file.rb +16 -0
  37. data/test/unit/tc_fileinfo.rb +17 -0
  38. data/test/unit/tc_imodule.rb +29 -0
  39. data/test/unit/tc_iplugin.rb +8 -0
  40. data/test/unit/tc_parser.rb +13 -0
  41. data/test/unit/tc_string.rb +24 -0
  42. data/test/unit/test_suite.rb +28 -0
  43. metadata +120 -0
@@ -0,0 +1,20 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/absolute_renamer'
3
+
4
+ $hoe = Hoe.new('AbsoluteRenamer', AbsoluteRenamer::VERSION) do |p|
5
+ p.developer('Simon COURTOIS', 'happynoff@free.fr')
6
+ p.changes = p.paragraphs_of("ChangeLog", 0..1).join("\n\n")
7
+ p.rubyforge_name = p.name
8
+
9
+ p.extra_dev_deps = [
10
+ ['newgem', ">= #{::Newgem::VERSION}"]
11
+ ]
12
+
13
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
14
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
15
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
16
+ p.rsync_args = '-av --delete --ignore-errors'
17
+ end
18
+
19
+ require 'newgem/tasks' # load /tasks/*.rake
20
+ Dir['tasks/**/*.rake'].each { |t| load t }
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # = Synopsis
5
+ #
6
+ # Does a batch renaming on files and directories
7
+ #
8
+ # = Usage
9
+ #
10
+ # absrenamer [options] [file]...
11
+ #
12
+ # = Description
13
+ #
14
+ # AbsoluteRenamer is a very powerful tool that helps files and directories
15
+ # renaming using the Krename syntax.
16
+ #
17
+ # It is extendable by adding new command line parsers,
18
+ # new renaming modules and plugins.
19
+ #
20
+ # Parsers allow to add new command line options.
21
+ # Modules allow to add new renaming patterns (like ID3 tags).
22
+ # Plugins allow to add new features like file listing instead of renaming.
23
+ #
24
+ # = Exemple
25
+ #
26
+ # absrenamer-f '[1;2]_[*4-]' *.mp3
27
+ # # => takes the first two characters of the original name
28
+ # # and Camelizes from the fourth to the end.
29
+ #
30
+ # = Copyright (c) 2009 Simon COURTOIS
31
+ # Licensed under the GNU Public License
32
+
33
+ $:.unshift File.dirname(__FILE__) << '/../lib'
34
+
35
+ require 'absolute_renamer'
36
+ require 'absolute_renamer/config'
37
+ require 'absolute_renamer/parser'
38
+ require 'absolute_renamer/external'
39
+
40
+ config_path = File.dirname(__FILE__)+'/../conf/absrenamer/absrenamer.conf'
41
+ config_path = '/etc/absrenamer/absrenamer.conf' if File.exists?('/etc/absrenamer/absrenamer.conf')
42
+ custom_conf = '~/.absrenamerrc'
43
+
44
+ AbsoluteRenamer::Config.load(config_path)
45
+ AbsoluteRenamer::Config.load(custom_conf) if File.exists? custom_conf
46
+
47
+ AbsoluteRenamer::Parser.parse_cmd_line
48
+
49
+ AbsoluteRenamer::External.load_modules
50
+ AbsoluteRenamer::External.load_plugins
51
+
52
+ AbsoluteRenamer::Processor.load_plugins
53
+ AbsoluteRenamer::Processor.create_names_list
54
+ AbsoluteRenamer::Processor.do_renaming
@@ -0,0 +1,34 @@
1
+ :debug: false
2
+
3
+ :options:
4
+ # Prompt :never, :once or :always before renaming
5
+ :interactive: :never
6
+ :format: '$'
7
+ :ext_format: '$'
8
+
9
+ # Renaming mode: :rename, :copy, :move, :link
10
+ :mode: :rename
11
+ :maxdepth: 0
12
+
13
+ # used when a value is not available (pdfAuthor for a PNG file, ...)
14
+ :default_string: '---'
15
+
16
+ # Word separator, regular expression statements can be used
17
+ # default: '\W_'
18
+ # This string will be used in AbsoluteRenamer just like this :
19
+ # [^WORD_SEPARATOR]
20
+ # Then, \W_ => [^\W_]
21
+ :word_separator: '\W_'
22
+
23
+ :path:
24
+ :modules: absolute_renamer/external/modules
25
+ :parsers: absolute_renamer/external/parsers
26
+ :plugins: absolute_renamer/external/plugins
27
+
28
+ :parsers:
29
+ - listing
30
+ - interactive
31
+
32
+ :plugins:
33
+ - listing
34
+ - interactive
@@ -0,0 +1,119 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'absolute_renamer/config'
5
+ require 'absolute_renamer/with_children'
6
+ require 'absolute_renamer/external'
7
+ require 'absolute_renamer/parser'
8
+ require 'absolute_renamer/file_info'
9
+ require 'absolute_renamer/libs/file'
10
+ require 'absolute_renamer/libs/string'
11
+ require 'absolute_renamer/use_config'
12
+
13
+ # top level module of AbsoluteRenamer.
14
+ module AbsoluteRenamer
15
+ VERSION = "0.9.0"
16
+
17
+ # The main class of AbsoluteRenamer.
18
+ #
19
+ # Organizes the files and directories renaming process.
20
+ class Processor
21
+ class << self
22
+ include AbsoluteRenamer::UseConfig
23
+
24
+ # Creates the new names for each file passed to AbsoluteRenamer.
25
+ #
26
+ # Asks to each module if he as something to replace
27
+ # in the name of each file.
28
+ #
29
+ # Calls the +before_names_generation+ entry point.
30
+ def create_names_list
31
+ call_entry_point(:before_names_generation)
32
+ return if conf[:files].empty?
33
+ mods = {}
34
+ conf[:files].each do |file|
35
+ name = conf[:options][:format].clone
36
+ ext = conf[:options][:ext_format].clone
37
+ do_replacements(file.name, :before)
38
+ AbsoluteRenamer::IModule.children.each do |mod|
39
+ mod_sym = mod.symbol
40
+ mods[mod_sym] ||= mod.new
41
+ name = mods[mod_sym].process(file, name)
42
+ ext = mods[mod_sym].process(file, ext, :ext) unless file.dir
43
+ end
44
+ do_replacements(name, :after)
45
+ file.new_name = name
46
+ file.new_name << '.' << ext unless (file.dir or file.ext.empty?)
47
+ end
48
+ mods.clear
49
+ end
50
+
51
+ # For each file/dir replaces his name by his new name.
52
+ #
53
+ # Calls the following entry points :
54
+ # - before_batch_renaming ;
55
+ # - before_file_renaming ;
56
+ # - after_file_renaming ;
57
+ # - after_batch_renaming.
58
+ def do_renaming
59
+ if call_entry_point(:before_batch_renaming)
60
+ conf[:files].each do |file|
61
+ if call_entry_point(:before_file_renaming, :file => file)
62
+ if file.respond_to?(conf[:options][:mode])
63
+ file.send(conf[:options][:mode])
64
+ end
65
+ call_entry_point(:after_file_renaming, :file => file)
66
+ end
67
+ end
68
+ call_entry_point(:after_batch_renaming)
69
+ end
70
+ end
71
+
72
+ # Loads the plugins list in the @plugins variable.
73
+ def load_plugins
74
+ @plugins = {}
75
+ AbsoluteRenamer::IPlugin.children.each do |plugin|
76
+ @plugins[plugin.symbol] = plugin.new
77
+ end
78
+ end
79
+
80
+ # Calls the given entry point for each plugin available
81
+ #
82
+ # Ask to each plugin if it implements the entry point
83
+ # and calls it with params if it isn't null.
84
+ # It keeps going will all plugins return true.
85
+ #
86
+ # returns true if all plugins returned true
87
+ def call_entry_point(ep, params = nil)
88
+ puts "Plugin Entry Point: #{ep}" if conf[:debug]
89
+ keep_going = true
90
+ @plugins.each_value do |plugin|
91
+ if plugin.respond_to?(ep)
92
+ keep_going &= params.nil? ? plugin.send(ep) : plugin.send(ep, params)
93
+ end
94
+ end
95
+ keep_going
96
+ end
97
+
98
+ # Applies replacements
99
+ #
100
+ # The replacements are stored in conf[options][replacements][moment]
101
+ # and are provided as command line paramters.
102
+ #
103
+ # format represents the string in which the replacements are done
104
+ # moment determines which replacements are done (on old or new name)
105
+ def do_replacements(format, moment)
106
+ begin
107
+ replacements = conf[:options][:replacements][moment]
108
+ rescue NoMethodError
109
+ replacements = nil
110
+ end
111
+ unless replacements.nil?
112
+ replacements.each do |repl|
113
+ format.gsub!(repl[:pattern], repl[:replace])
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,40 @@
1
+ require 'yaml'
2
+
3
+ module AbsoluteRenamer
4
+ # Class handeling the configuration.
5
+ class Config
6
+ class << self
7
+ # Open and load a Yaml file into the +@conf+ variable.
8
+ # config_path: path of the file to load.
9
+ def load(config_path)
10
+ @conf = YAML::load_file(config_path)
11
+
12
+ @conf[:options] ||= {}
13
+ @conf[:options][:format] ||= '$'
14
+ @conf[:options][:ext_format] ||= '$'
15
+ end
16
+
17
+ # Returns a configuration value identified by +key+.
18
+ # If +key+ is ignored, returns the +@conf+ hash.
19
+ def get(key = nil)
20
+ return @conf[key] if @conf.has_key?(key)
21
+ return @conf
22
+ end
23
+
24
+ # Sets a configuration value in the +@conf+ variable.
25
+ def set(key, value = '')
26
+ @conf[key] = value
27
+ end
28
+
29
+ # Returns a configuration value identified by +key+.
30
+ def [](key)
31
+ @conf[key]
32
+ end
33
+
34
+ # Sets a configuration value in the +@conf+ variable.
35
+ def []=(key, value)
36
+ @conf[key] = value
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,56 @@
1
+ require 'absolute_renamer/imodule'
2
+ require 'absolute_renamer/iplugin'
3
+ require 'absolute_renamer/use_config'
4
+
5
+ module AbsoluteRenamer
6
+ # Class in charge of loading +modules+ and +plugins+.
7
+ class External
8
+ class << self
9
+ include AbsoluteRenamer::UseConfig
10
+
11
+ # Loads the additional and core modules.
12
+ # The modules list is get from the conf[:modules] variable.
13
+ # The core modules are loaded after the additional ones.
14
+ #
15
+ # See also load
16
+ def load_modules
17
+ puts "[Loading modules]" if conf[:debug]
18
+
19
+ modules = conf[:modules]
20
+ load(modules, :modules, 'module.rb') unless modules.nil?
21
+
22
+ core_modules = ['case', 'general'].map! { |core_module| File.join('core', core_module) }
23
+ load(core_modules, :modules, 'module.rb')
24
+ end
25
+
26
+ # Loads the plugins.
27
+ # The plugins list is get from the conf[:plugins] variable.
28
+ #
29
+ # See also load
30
+ def load_plugins
31
+ puts "[Loading plugins]" if conf[:debug]
32
+
33
+ load(conf[:plugins], :plugins, 'plugin.rb')
34
+ end
35
+
36
+ # Loads an external list (+modules+ or +plugins+)
37
+ # externals: a list of +externals+ names to load.
38
+ # type: a symbol defining which type of external to load
39
+ # file: the filename to require to load the +externals+
40
+ def load(externals, type, file)
41
+ ext_dir = conf[:path][type]
42
+
43
+ externals.each do |external|
44
+ ext_to_load = File.join(ext_dir, external, file)
45
+ begin
46
+ if require ext_to_load
47
+ puts "Loaded: #{ext_to_load}" if conf[:debug]
48
+ end
49
+ rescue LoadError => e
50
+ STDERR.puts(e)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,31 @@
1
+ module AbsoluteRenamer
2
+ class CaseModule < AbsoluteRenamer::IModule
3
+ class << self
4
+ attr_reader :actions
5
+
6
+ def actions
7
+ @actions ||= {'*' => :camelize,
8
+ '&' => :upper,
9
+ '%' => :lower,
10
+ '$' => :original
11
+ }
12
+ end
13
+
14
+ def camelize(str)
15
+ str.camelize
16
+ end
17
+
18
+ def original(str)
19
+ str
20
+ end
21
+
22
+ def lower(str)
23
+ str.downcase
24
+ end
25
+
26
+ def upper(str)
27
+ str.upcase
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,126 @@
1
+ module AbsoluteRenamer
2
+ class GeneralModule < AbsoluteRenamer::IModule
3
+ def initialize
4
+ @actions = {'*' => :file_camelize,
5
+ '$' => :file_original,
6
+ '%' => :file_downcase,
7
+ '&' => :file_upcase,
8
+ '\\' => :file_strip,
9
+ '#' => :count
10
+ }
11
+
12
+ @case_filters = ['(\\\\\*)', # \*
13
+ '(\\\\\$)', # \$
14
+ '(\\\%)', # \%
15
+ '(\\\&)', # \&
16
+ '(\\\\\\\)', # \\
17
+ '(\*)', # *
18
+ '(\$)', # $
19
+ '(%)', # %
20
+ '(&)', # &
21
+ '(\\\)' # \
22
+ ]
23
+
24
+ # matches strings like [42-43] [42-] [*42-43] [42;43] etc...
25
+ @part_filters = ['(\[(.)?(\d+)(((-)(\d+)?)|((;)(\d+)))?\])']
26
+
27
+ # matches counters like # ### #{2} ##{2;42} or [length-42]
28
+ @misc_filters = ['(/)', '(#+(\{.*\})?)', '(\[length(--?\d+)?\])']
29
+
30
+ @filters = @case_filters + @part_filters + @misc_filters
31
+ end
32
+
33
+ def interpret(file, infos, type)
34
+ if (infos[0].length == 1)
35
+ self.method(@actions[infos[0][0].chr]).call(file, infos, type)
36
+ elsif (infos[0][1..6] == 'length')
37
+ self.length(file, infos, type)
38
+ elsif (infos[0][0].chr == '[')
39
+ self.file_part(file, infos, type)
40
+ elsif (infos[0][0].chr == '#')
41
+ self.count(file, infos, type)
42
+ else
43
+ infos[0][1].chr
44
+ end
45
+ end
46
+
47
+ def file_camelize(file, infos, type)
48
+ file.send(type).camelize
49
+ end
50
+
51
+ def file_original(file, infos, type)
52
+ file.send(type)
53
+ end
54
+
55
+ def file_downcase(file, infos, type)
56
+ file.send(type).downcase
57
+ end
58
+
59
+ def file_upcase(file, infos, type)
60
+ file.send(type).upcase
61
+ end
62
+
63
+ def file_strip(file, infos, type)
64
+ file.send(type).strip
65
+ end
66
+
67
+ def file_part(file, infos, type)
68
+ matched = infos[0].match(/(\[([^\d])?(\d+)(((;)(\d+))|((-)(\d+)?))?\])/)
69
+
70
+ modifier = matched[2]
71
+ x = matched[3].to_i - 1
72
+ y = matched[7] || matched[10]
73
+ y = y.to_i unless y.nil?
74
+ action = matched[6] || matched[9]
75
+
76
+ str = file.send(type)
77
+
78
+ if (action == '-')
79
+ y -= 1 unless y.nil?
80
+ y ||= str.length
81
+ val = str[x..y]
82
+ elsif (action == ';')
83
+ val = str[x, y]
84
+ else
85
+ val = str[x].chr
86
+ end
87
+
88
+ unless modifier.nil?
89
+ mp = CaseModule.method(CaseModule.actions[modifier])
90
+ val = mp.call(val)
91
+ end
92
+ val
93
+ end
94
+
95
+ def count(file, infos, type)
96
+ matched = infos[0].match(/(#+)(\{((-?\d+)(;(-?\d+)?)?)?\})?/)
97
+ @counter ||= []
98
+ @last ||= nil
99
+ @current ||= 0
100
+
101
+ @current = 0 if @last != file
102
+ @current += 1 if @last == file
103
+
104
+ start = matched[4] || 1
105
+ step = matched[6] || 1
106
+ start = start.to_i
107
+ step = step.to_i
108
+
109
+ @counter[@current] ||= {:start => start,
110
+ :step => step,
111
+ :current => start - step
112
+ }
113
+
114
+ @counter[@current][:current] += @counter[@current][:step]
115
+ @last = file
116
+ val = @counter[@current][:current].to_s.rjust(matched[1].length, '0')
117
+ val.gsub!(/(0+)-/, '-\1')
118
+ val
119
+ end
120
+
121
+ def length(file, infos, type)
122
+ matched = infos[0].match(/\[length(-(-?\d+))?\]/)
123
+ file.name.length - matched[2].to_i
124
+ end
125
+ end
126
+ end