simonc-AbsoluteRenamer 0.9.1 → 0.9.2

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.
@@ -0,0 +1,83 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{AbsoluteRenamer}
8
+ s.version = "0.9.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Simon COURTOIS"]
12
+ s.date = %q{2009-09-22}
13
+ s.default_executable = %q{absrenamer}
14
+ s.description = %q{Unlike many batch renaming tools, AbsoluteRenamer is able to rename folders.
15
+ AbsoluteRenamer is modular and can be extended to adapt itself to any kind of file
16
+ or to add new options and features.}
17
+ s.email = %q{happynoff@free.fr}
18
+ s.executables = ["absrenamer"]
19
+ s.extra_rdoc_files = [
20
+ "LICENSE",
21
+ "README.rdoc"
22
+ ]
23
+ s.files = [
24
+ ".document",
25
+ ".gitignore",
26
+ "AbsoluteRenamer.gemspec",
27
+ "LICENSE",
28
+ "README.rdoc",
29
+ "Rakefile",
30
+ "VERSION",
31
+ "bin/absrenamer",
32
+ "conf/absrenamer/absrenamer.conf",
33
+ "lib/absolute_renamer.rb",
34
+ "lib/absolute_renamer/config.rb",
35
+ "lib/absolute_renamer/external.rb",
36
+ "lib/absolute_renamer/external/modules/core/case/module.rb",
37
+ "lib/absolute_renamer/external/modules/core/general/module.rb",
38
+ "lib/absolute_renamer/external/parsers/core/general/parser.rb",
39
+ "lib/absolute_renamer/external/parsers/interactive/parser.rb",
40
+ "lib/absolute_renamer/external/parsers/listing/parser.rb",
41
+ "lib/absolute_renamer/external/plugins/interactive/plugin.rb",
42
+ "lib/absolute_renamer/external/plugins/listing/plugin.rb",
43
+ "lib/absolute_renamer/file_info.rb",
44
+ "lib/absolute_renamer/imodule.rb",
45
+ "lib/absolute_renamer/iparser.rb",
46
+ "lib/absolute_renamer/iplugin.rb",
47
+ "lib/absolute_renamer/libs/file.rb",
48
+ "lib/absolute_renamer/libs/string.rb",
49
+ "lib/absolute_renamer/parser.rb",
50
+ "lib/absolute_renamer/use_config.rb",
51
+ "lib/absolute_renamer/with_children.rb",
52
+ "test/absolute_renamer_test.rb",
53
+ "test/config_test.rb",
54
+ "test/file_info_test.rb",
55
+ "test/file_test.rb",
56
+ "test/test_helper.rb"
57
+ ]
58
+ s.homepage = %q{http://github.com/simonc/AbsoluteRenamer}
59
+ s.rdoc_options = ["--charset=UTF-8"]
60
+ s.require_paths = ["lib"]
61
+ s.rubygems_version = %q{1.3.5}
62
+ s.summary = %q{AbsoluteRenamer is a very powerful tool that helps files and directories renaming using the Krename syntax.}
63
+ s.test_files = [
64
+ "test/absolute_renamer_test.rb",
65
+ "test/config_test.rb",
66
+ "test/file_info_test.rb",
67
+ "test/file_test.rb",
68
+ "test/test_helper.rb"
69
+ ]
70
+
71
+ if s.respond_to? :specification_version then
72
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
73
+ s.specification_version = 3
74
+
75
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
76
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
77
+ else
78
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
79
+ end
80
+ else
81
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
82
+ end
83
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.1
1
+ 0.9.2
@@ -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,122 @@
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.1"
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
+ if (conf[:options][:dir] and conf[:options][:rec])
49
+ conf[:files].sort! { |a, b| AbsoluteRenamer::FileInfo.compare_level(a, b) }
50
+ end
51
+ mods.clear
52
+ end
53
+
54
+ # For each file/dir replaces his name by his new name.
55
+ #
56
+ # Calls the following entry points :
57
+ # - before_batch_renaming ;
58
+ # - before_file_renaming ;
59
+ # - after_file_renaming ;
60
+ # - after_batch_renaming.
61
+ def do_renaming
62
+ if call_entry_point(:before_batch_renaming)
63
+ conf[:files].each do |file|
64
+ if call_entry_point(:before_file_renaming, :file => file)
65
+ if file.respond_to?(conf[:options][:mode])
66
+ file.send(conf[:options][:mode])
67
+ end
68
+ call_entry_point(:after_file_renaming, :file => file)
69
+ end
70
+ end
71
+ call_entry_point(:after_batch_renaming)
72
+ end
73
+ end
74
+
75
+ # Loads the plugins list in the @plugins variable.
76
+ def load_plugins
77
+ @plugins = {}
78
+ AbsoluteRenamer::IPlugin.children.each do |plugin|
79
+ @plugins[plugin.symbol] = plugin.new
80
+ end
81
+ end
82
+
83
+ # Calls the given entry point for each plugin available
84
+ #
85
+ # Ask to each plugin if it implements the entry point
86
+ # and calls it with params if it isn't null.
87
+ # It keeps going will all plugins return true.
88
+ #
89
+ # returns true if all plugins returned true
90
+ def call_entry_point(ep, params = nil)
91
+ puts "Plugin Entry Point: #{ep}" if conf[:debug]
92
+ keep_going = true
93
+ @plugins.each_value do |plugin|
94
+ if plugin.respond_to?(ep)
95
+ keep_going &= params.nil? ? plugin.send(ep) : plugin.send(ep, params)
96
+ end
97
+ end
98
+ keep_going
99
+ end
100
+
101
+ # Applies replacements
102
+ #
103
+ # The replacements are stored in conf[options][replacements][moment]
104
+ # and are provided as command line paramters.
105
+ #
106
+ # format represents the string in which the replacements are done
107
+ # moment determines which replacements are done (on old or new name)
108
+ def do_replacements(format, moment)
109
+ begin
110
+ replacements = conf[:options][:replacements][moment]
111
+ rescue NoMethodError
112
+ replacements = nil
113
+ end
114
+ unless replacements.nil?
115
+ replacements.each do |repl|
116
+ format.gsub!(repl[:pattern], repl[:replace])
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ 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
@@ -0,0 +1,74 @@
1
+ # TODO ajouter une option pour la gestion du nombre de points avant l'extension
2
+ module AbsoluteRenamer
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
+
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
+
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
+
21
+ parser.on('-f', '--format FORMAT',
22
+ 'Format string used as model') do |format|
23
+ options[:format] = format
24
+ @format_given = true
25
+ end
26
+
27
+ parser.on('-x', '--ext-format FORMAT',
28
+ 'Format string used as model for the extension') do |format|
29
+ options[:ext_format] = format
30
+ end
31
+
32
+ parser.on('-R', '--recursive',
33
+ 'Rename files in subdirectories recursively') do
34
+ options[:rec] = true
35
+ end
36
+
37
+ parser.on('--maxdepth N', Integer, 'Maximum recursion depth') do |depth|
38
+ options[:maxdepth] = depth
39
+ end
40
+
41
+ parser.on('-m', '--mode MODE', [:rename, :copy, :move, :link],
42
+ 'Renaming mode. Can be used with --dest DEST.',
43
+ ' rename: simply rename files',
44
+ ' copy: make a copy of each file in DEST with its new name',
45
+ ' move: move each file in DEST with its new name',
46
+ ' link: create a symbolic link to each file in DEST' <<
47
+ ' with its new name') do |mode|
48
+ options[:mode] = mode
49
+ end
50
+
51
+ parser.on('--dest DEST', 'Destination directory' <<
52
+ ' for copy, move and link modes') do |dest|
53
+ options[:dest] = File.expand_path(dest)
54
+ end
55
+
56
+ parser.on('-d', '--directories', 'Directories handling') do
57
+ options[:dir] = true
58
+ end
59
+ end
60
+
61
+ def self.add_replacement(data, options, regexp = false)
62
+ pattern,replace = data[0..1]
63
+ replace ||= ''
64
+ pattern = Regexp.new(pattern) if regexp
65
+ moment = @format_given.nil? ? :before : :after
66
+ options[:replacements] ||= {}
67
+ options[:replacements][moment] ||= []
68
+ options[:replacements][moment] << {:type => pattern.class,
69
+ :pattern => pattern,
70
+ :replace => replace
71
+ }
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,20 @@
1
+ module AbsoluteRenamer
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
+
8
+ parser.on('-I', 'Prompt once before batch renamming') do
9
+ options[:interactive] = :once
10
+ end
11
+
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
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ module AbsoluteRenamer
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
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,35 @@
1
+ module AbsoluteRenamer
2
+ class InteractivePlugin < AbsoluteRenamer::IPlugin
3
+ def before_batch_renaming
4
+ if conf[:options][:interactive] == :once
5
+ conf[:files].each do |file|
6
+ file.display_change
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
18
+ end
19
+
20
+ def before_file_renaming(params)
21
+ if conf[:options][:interactive] == :always
22
+ params[:file].display_change
23
+ print "Do you want to rename this file ? [y/N] "
24
+ begin
25
+ resp = STDIN.readline.chomp.downcase
26
+ rescue Exception => e
27
+ puts "\nExiting renamer"
28
+ exit(0)
29
+ end
30
+ return resp == "y"
31
+ end
32
+ true
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,14 @@
1
+ module AbsoluteRenamer
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
8
+ end
9
+ return false
10
+ end
11
+ true
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,102 @@
1
+ require 'ftools'
2
+ require 'fileutils'
3
+ require 'absolute_renamer/use_config'
4
+ require 'absolute_renamer/libs/file'
5
+
6
+ module AbsoluteRenamer
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
+
13
+ attr_accessor :name, :new_name,
14
+ :path, :real_path,
15
+ :ext, :dir, :dir_path,
16
+ :level
17
+
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
+ # TODO utiliser une conf :dot
28
+ @ext = File.extname(@name)
29
+ @name.gsub!(Regexp.new('.' << @ext << '$'), '') unless @ext.empty?
30
+ @level = 0
31
+ else
32
+ @level = @real_path.split('/').size
33
+ end
34
+ end
35
+
36
+ # Returns a description of a FileInfo
37
+ # some_fileinfo.inspect # => "File: hello_world pdf"
38
+ def inspect
39
+ "File: #{@name} #{@ext}"
40
+ end
41
+
42
+ # Displays the action that will be done on the file.
43
+ # some_fileinfo.display_change # => "rename a_file.txt --> A_File.TXT"
44
+ def display_change
45
+ puts "#{conf[:options][:mode]} #{@real_path.sub(Dir.pwd+'/', '')} --> #{new_path.sub(Dir.pwd+'/', '')}"
46
+ end
47
+
48
+ # Returns the new path of the file.
49
+ def new_path
50
+ if conf[:options][:dest].nil? or conf[:options][:dest].empty?
51
+ File.join(@dir_path, @new_name)
52
+ else
53
+ File.join(conf[:options][:dest], @new_name)
54
+ end
55
+ end
56
+
57
+ # Renames the file.
58
+ def rename
59
+ display_change
60
+ File.rename(@real_path, new_path)
61
+ end
62
+
63
+ # Copies the file.
64
+ def copy
65
+ display_change
66
+ if @dir
67
+ if @real_path != conf[:options][:dest]
68
+ FileUtils.cp_r(@real_path, conf[:options][:dest])
69
+ else
70
+ puts "#{real_path} ignored"
71
+ end
72
+ else
73
+ File.copy(@real_path, new_path, false)
74
+ end
75
+ end
76
+
77
+ # Moves a file. Moving to the same directories is just like renaming.
78
+ def move
79
+ display_change
80
+ File.move(@real_path, new_path, false)
81
+ end
82
+
83
+ # Creates a symbolic link to the file.
84
+ def link
85
+ display_change
86
+ begin
87
+ File.symlink(@real_path, new_path)
88
+ rescue NotImplemented
89
+ # TODO trouver mieux
90
+ puts "Error: cannot create symlinks"
91
+ end
92
+ end
93
+
94
+ def self.compare_level(a, b)
95
+ if (a.level == b.level)
96
+ return (a.name <= b.name) ? -1 : 1
97
+ else
98
+ return (a.level < b.level) ? 1 : -1
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,68 @@
1
+ require 'absolute_renamer/with_children'
2
+
3
+ module AbsoluteRenamer
4
+ # Modules parent class.
5
+ # Modules must inherit of it.
6
+ class IModule < AbsoluteRenamer::WithChildren
7
+ def initialize
8
+ @filters = []
9
+ end
10
+
11
+ # Returns the classname symbol
12
+ def self.symbol
13
+ name.intern
14
+ end
15
+
16
+ # Process a +file+ by searching for a known pattern in its name
17
+ # and replacing it by the corresponding value.
18
+ # The pattern is a regular expression obtained by concatening
19
+ # the +@filters+ variable with "|".
20
+ #
21
+ # file: a FileInfo instance
22
+ # format: the format string used to rename the file
23
+ # type: the type of the renaming format (:name or :ext)
24
+ def process(file, format, type = :name)
25
+ return format if @filters.empty?
26
+
27
+ str = format
28
+ result = []
29
+ pattern = Regexp.new(@filters.join('|'))
30
+
31
+ idx = str.index(pattern)
32
+ while idx
33
+ matched = pattern.match(str).to_a.compact
34
+ part = str.partition(matched[0])
35
+ result.push(part[0])
36
+ val = self.interpret(file, matched, type)
37
+ result.push(val)
38
+ str = part[2]
39
+ idx = str.index(pattern)
40
+ end
41
+ result.push(str) unless str.empty?
42
+ format.replace(result.join)
43
+ format
44
+ end
45
+
46
+ # Interprets a matched pattern.
47
+ # Searchs for the corresponding callback
48
+ # in the current module and call it.
49
+ #
50
+ # file: a FileInfo instance
51
+ # infos: the matched values depending of the pattern
52
+ # type: the type of the renaming format (:name or :ext)
53
+ def interpret(file, infos, type)
54
+ modifier = infos[2]
55
+ action = infos[3]
56
+
57
+ return conf[:options][:default_string] unless self.respond_to?(action.intern)
58
+
59
+ ap = self.method(action.intern)
60
+ val = ap.call(file, infos, type)
61
+ unless modifier.empty?
62
+ mp = CaseModule.method(CaseModule.actions[modifier])
63
+ val = mp.call(val)
64
+ end
65
+ val.empty? ? conf[:options][:default_string] : val
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,8 @@
1
+ require 'absolute_renamer/with_children'
2
+
3
+ module AbsoluteRenamer
4
+ # Parsers parent class.
5
+ # Parsers must inherit of it.
6
+ class IParser < AbsoluteRenamer::WithChildren
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ require 'absolute_renamer/with_children'
2
+
3
+ module AbsoluteRenamer
4
+ # Plugins parent class.
5
+ # Plugins must inherit of it.
6
+ class IPlugin < AbsoluteRenamer::WithChildren
7
+ def self.symbol
8
+ name.intern
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # Extension of existing File class.
2
+ class << File
3
+ # Returns the extension of a file.
4
+ # path: the path of the file
5
+ # dot: starting from the end, number of dots to count before cuting extension.
6
+ def extname(path, dot = 1)
7
+ pattern = (0...dot).inject('') { |pat,x| pat << '\.[^\.]+' } << '$'
8
+ ext = File.basename(path).match(pattern).to_s
9
+ ext.empty? ? "" : ext[1..ext.length]
10
+ end
11
+ end
@@ -0,0 +1,27 @@
1
+ # Extension of existing String class
2
+ class String
3
+ # Returns a camelized version of a string
4
+ # word_separator: a regular expression used to separate words.
5
+ # str = "hello.THE world"
6
+ # str.camelize # => "Hello.The World"
7
+ # str.camelize(/[\.]/) # => "Hello.The world"
8
+ # str # => "hello.THE world"
9
+ def camelize(word_separators = /[\W_]/)
10
+ self.clone.camelize!(word_separators)
11
+ end
12
+
13
+ # Camelizes a string and returns it.
14
+ # str = "Hello.THE World"
15
+ # str.camelize! # => "Hello.The World"
16
+ # str.camelize!(/[\.]/) # => "Hello.The World"
17
+ # str # => "Hello.The World"
18
+ def camelize!(word_separators = /[\W_]/)
19
+ self.downcase!
20
+ self.each_char.each_with_index do |c,i|
21
+ if self[i-1].chr =~ word_separators or i.zero?
22
+ self[i] = c.upcase if c =~ /[a-z]/
23
+ end
24
+ end
25
+ self
26
+ end
27
+ end
@@ -0,0 +1,91 @@
1
+ require 'optparse'
2
+ require 'absolute_renamer/iparser'
3
+ require 'absolute_renamer/use_config'
4
+ require 'pp'
5
+
6
+ module AbsoluteRenamer
7
+ # Class in charge of the command line parsing.
8
+ class Parser
9
+ class << self
10
+ include AbsoluteRenamer::UseConfig
11
+
12
+ # Calls all registred parsers.
13
+ # The parsers are written in configuration files.
14
+ # The core parsers are automaticaly added.
15
+ def parse_cmd_line
16
+ parsers_dir = conf[:path][:parsers]
17
+ parsers = conf[:parsers]
18
+
19
+ core_parsers = ['general']
20
+
21
+ parsers += core_parsers.map! { |core_parser| File.join('core', core_parser) }
22
+
23
+ parsers.each do |parser|
24
+ parser_file = File.join(parsers_dir, parser, 'parser.rb')
25
+ begin
26
+ if require parser_file
27
+ puts "Loaded: #{parser_file}" if conf[:debug]
28
+ end
29
+ rescue LoadError => e
30
+ STDERR.puts(e)
31
+ end
32
+ end
33
+
34
+ ARGV.options do |parser|
35
+ begin
36
+ list = AbsoluteRenamer::IParser.children
37
+ list.each do |class_name|
38
+ class_name.add_options(parser, conf[:options])
39
+ end
40
+ parser.parse!
41
+ rescue RuntimeError => ex
42
+ STDERR.puts(ex)
43
+ exit 1
44
+ end
45
+ conf[:files] = self.get_files(ARGV, 0) || []
46
+ conf[:options][:maxdepth] ||= 0
47
+ conf[:options][:interactive] ||= :never
48
+ conf[:options][:mode] ||= :rename
49
+ pp conf.get if conf[:debug]
50
+ end
51
+ end
52
+
53
+ # Creates a list of all files and directories to rename.
54
+ # All options that have not been matched are considered as path.
55
+ # For directories, if the recursive otpion is set to true (conf[:rec] == true),
56
+ # files are searched in sub directories.
57
+ #
58
+ # list: a list of path to explore
59
+ # depth: maximum recursion depth
60
+ #
61
+ # Returns the files/directories list
62
+ def get_files(list, depth)
63
+ files = []
64
+ options = conf[:options]
65
+
66
+ list.each do |entry|
67
+ return files unless File.exists?(entry)
68
+ is_dir = File.directory?(entry)
69
+ mod_dir = options[:dir]
70
+ depth_ok = (depth < options[:maxdepth] or options[:maxdepth].zero?)
71
+ mod_rec = (options[:rec] and depth_ok)
72
+
73
+ add_dir = (is_dir and mod_dir)
74
+ add_file = (!is_dir and !mod_dir)
75
+ add_sub = (is_dir and (mod_rec or (!mod_dir and depth < 1)))
76
+
77
+ files << FileInfo.new(entry) if (add_dir or add_file)
78
+ files += self.get_files(self.get_subentries(entry), depth + 1) if (add_sub)
79
+ end
80
+ files
81
+ end
82
+
83
+ # Returns files and directories contained in +path+.
84
+ def get_subentries(path)
85
+ files = Dir.entries(path)
86
+ files.delete_if { |file| file[0,1] == '.' }
87
+ files.collect! { |file| path + '/' + file }
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,11 @@
1
+ require 'absolute_renamer/config'
2
+
3
+ module AbsoluteRenamer
4
+ # Module that provide configuration usage.
5
+ module UseConfig
6
+ # Returns the configuration class
7
+ def conf
8
+ @conf ||= AbsoluteRenamer::Config
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ require 'absolute_renamer/use_config'
2
+
3
+ module AbsoluteRenamer
4
+ # Class allowing childs listing.
5
+ class WithChildren
6
+ include AbsoluteRenamer::UseConfig
7
+
8
+ @children = []
9
+ class << self
10
+ attr_reader :children
11
+
12
+ # Inheritance callback.
13
+ # When a class inherit from a WithChildren class, it is added to
14
+ # the childs list of this class.
15
+ # This list is available as the +children+ attribute.
16
+ def inherited(by)
17
+ @children << by
18
+ by.instance_variable_set(:@children, [])
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,7 +1,7 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class AbsoluterenamerTest < Test::Unit::TestCase
4
- should "probably rename this file and start testing for real" do
5
- flunk "hey buddy, you should probably rename this file and start testing for real"
4
+ should "be true" do
5
+ assert true
6
6
  end
7
7
  end
@@ -0,0 +1,45 @@
1
+ require 'test_helper'
2
+
3
+ class ConfigTest < Test::Unit::TestCase
4
+ context "The Config instance" do
5
+
6
+ should "exist" do
7
+ assert_not_nil AbsoluteRenamer::Config
8
+ end
9
+
10
+ should "raise ENOENT if config file not found" do
11
+ assert_raise(Errno::ENOENT) do
12
+ AbsoluteRenamer::Config.load('a file that must not be found')
13
+ end
14
+ end
15
+
16
+ should "not raise ENOENT if config file is found" do
17
+ assert_nothing_raised(Errno::ENOENT) do
18
+ AbsoluteRenamer::Config.load('conf/absrenamer/absrenamer.conf')
19
+ end
20
+ end
21
+
22
+ context "with a loaded config file" do
23
+ setup do
24
+ AbsoluteRenamer::Config.load('conf/absrenamer/absrenamer.conf')
25
+ end
26
+
27
+ should "be able to set and get config options" do
28
+ AbsoluteRenamer::Config.set(:test_key, :test_val)
29
+ assert_equal(:test_val, AbsoluteRenamer::Config.get(:test_key))
30
+ end
31
+
32
+ should "be able to set and get a config option with the nil key" do
33
+ AbsoluteRenamer::Config.set(nil, :test_val)
34
+ assert_equal(:test_val, AbsoluteRenamer::Config.get(nil))
35
+ end
36
+
37
+ should "be able to set config options using []" do
38
+ AbsoluteRenamer::Config[:test_key] = :test_val
39
+ assert_equal(:test_val, AbsoluteRenamer::Config[:test_key])
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,71 @@
1
+ require 'test_helper'
2
+
3
+ class FileInfoTest < Test::Unit::TestCase
4
+ context "A FileInfo instance" do
5
+ context "loaded with some/path/to/a_file.txt" do
6
+
7
+ setup do
8
+ @pwd = Dir.pwd
9
+ @fileinfo = AbsoluteRenamer::FileInfo.new('some/path/to/a_file.txt')
10
+ end
11
+
12
+ should "have as name : a_file" do
13
+ assert_equal('a_file', @fileinfo.name)
14
+ end
15
+
16
+ should "have as dir_path : pwd/some/path/to" do
17
+ assert_equal(@pwd + '/some/path/to', @fileinfo.dir_path)
18
+ end
19
+
20
+ should "have as path : some/path/to/a_file.txt" do
21
+ assert_equal('some/path/to/a_file.txt', @fileinfo.path)
22
+ end
23
+
24
+ should "have as real_path : pwd/some/path/to/a_file.txt" do
25
+ assert_equal(@pwd + '/some/path/to/a_file.txt', @fileinfo.real_path)
26
+ end
27
+
28
+ should "have as ext : txt" do
29
+ assert_equal('txt', @fileinfo.ext)
30
+ end
31
+
32
+ should "have as dir : false" do
33
+ assert_equal(false, @fileinfo.dir)
34
+ end
35
+
36
+ end
37
+
38
+ context "loaded with lib/absolute_renamer" do
39
+
40
+ setup do
41
+ @pwd = Dir.pwd
42
+ @fileinfo = AbsoluteRenamer::FileInfo.new('lib/absolute_renamer')
43
+ end
44
+
45
+ should "have as name : absolute_renamer" do
46
+ assert_equal('absolute_renamer', @fileinfo.name)
47
+ end
48
+
49
+ should "have as dir_path : pwd/lib" do
50
+ assert_equal(@pwd + '/lib', @fileinfo.dir_path)
51
+ end
52
+
53
+ should "have as path : lib/absolute_renamer" do
54
+ assert_equal('lib/absolute_renamer', @fileinfo.path)
55
+ end
56
+
57
+ should "have as real_path : pwd/lib/absolute_renamer" do
58
+ assert_equal(@pwd + '/lib/absolute_renamer', @fileinfo.real_path)
59
+ end
60
+
61
+ should "have as ext : nil" do
62
+ assert_nil @fileinfo.ext
63
+ end
64
+
65
+ should "have as dir : true" do
66
+ assert_equal(true, @fileinfo.dir)
67
+ end
68
+
69
+ end
70
+ end
71
+ end
data/test/file_test.rb ADDED
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+
3
+ # Called LibFileTest instead of FileTest to avoid ruby errors
4
+ class LibFileTest < Test::Unit::TestCase
5
+ context "The File class" do
6
+
7
+ should "return txt for a_file.txt when calling extname" do
8
+ assert_equal("txt", File.extname('a_file.txt'))
9
+ end
10
+
11
+ should "return an empty string for a_file when calling extname" do
12
+ assert_equal("", File.extname('a_file'))
13
+ end
14
+
15
+ should "return tar.gz for archive.tar.gz when calling extname with 2 dots" do
16
+ assert_equal("tar.gz", File.extname('archive.tar.gz', 2))
17
+ end
18
+
19
+ should "return gz for archive.tar.gz when calling extname" do
20
+ assert_equal("gz", File.extname('archive.tar.gz'))
21
+ end
22
+
23
+ should "return an empty string for . and a_file. when calling extname" do
24
+ assert_equal("", File.extname('.'))
25
+ assert_equal("", File.extname('fichier.'))
26
+ end
27
+
28
+ should "return test for .test when calling extname" do
29
+ assert_equal("test", File.extname('.test'))
30
+ end
31
+
32
+ end
33
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simonc-AbsoluteRenamer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon COURTOIS
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-09-21 00:00:00 -07:00
12
+ date: 2009-09-22 00:00:00 -07:00
13
13
  default_executable: absrenamer
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -34,10 +34,36 @@ extra_rdoc_files:
34
34
  files:
35
35
  - .document
36
36
  - .gitignore
37
+ - AbsoluteRenamer.gemspec
37
38
  - LICENSE
38
39
  - README.rdoc
39
40
  - Rakefile
40
41
  - VERSION
42
+ - bin/absrenamer
43
+ - conf/absrenamer/absrenamer.conf
44
+ - lib/absolute_renamer.rb
45
+ - lib/absolute_renamer/config.rb
46
+ - lib/absolute_renamer/external.rb
47
+ - lib/absolute_renamer/external/modules/core/case/module.rb
48
+ - lib/absolute_renamer/external/modules/core/general/module.rb
49
+ - lib/absolute_renamer/external/parsers/core/general/parser.rb
50
+ - lib/absolute_renamer/external/parsers/interactive/parser.rb
51
+ - lib/absolute_renamer/external/parsers/listing/parser.rb
52
+ - lib/absolute_renamer/external/plugins/interactive/plugin.rb
53
+ - lib/absolute_renamer/external/plugins/listing/plugin.rb
54
+ - lib/absolute_renamer/file_info.rb
55
+ - lib/absolute_renamer/imodule.rb
56
+ - lib/absolute_renamer/iparser.rb
57
+ - lib/absolute_renamer/iplugin.rb
58
+ - lib/absolute_renamer/libs/file.rb
59
+ - lib/absolute_renamer/libs/string.rb
60
+ - lib/absolute_renamer/parser.rb
61
+ - lib/absolute_renamer/use_config.rb
62
+ - lib/absolute_renamer/with_children.rb
63
+ - test/absolute_renamer_test.rb
64
+ - test/config_test.rb
65
+ - test/file_info_test.rb
66
+ - test/file_test.rb
41
67
  - test/test_helper.rb
42
68
  has_rdoc: false
43
69
  homepage: http://github.com/simonc/AbsoluteRenamer
@@ -68,4 +94,7 @@ specification_version: 3
68
94
  summary: AbsoluteRenamer is a very powerful tool that helps files and directories renaming using the Krename syntax.
69
95
  test_files:
70
96
  - test/absolute_renamer_test.rb
97
+ - test/config_test.rb
98
+ - test/file_info_test.rb
99
+ - test/file_test.rb
71
100
  - test/test_helper.rb