AbsoluteRenamer 0.9.0

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.
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