simonc-AbsoluteRenamer 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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