rename_radically 0.2.0 → 0.3.3

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 (4) hide show
  1. checksums.yaml +5 -5
  2. data/bin/rnr +165 -169
  3. data/lib/rename_radically.rb +241 -417
  4. metadata +7 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 380219f5284979eaf6db64a4bf815683ce3cbbb7
4
- data.tar.gz: 5277e107bc576eeb5205399cb6f89620db75853f
2
+ SHA256:
3
+ metadata.gz: 1bc8dcc770e6a9e58d84bd85f9601ec33a4179e551f7ab30361abd6cc7b9e4b8
4
+ data.tar.gz: 72b9daf2e94ad2a889a2c25bc2304867b5a9e9308ed44714275d9c22d4fb26de
5
5
  SHA512:
6
- metadata.gz: e2c72642c51d8a9e0438f052080fd1fd08158ed22bb4e2dfa5994aae84305f2143b826f8869537050f427b766b14e67b4af9bc229fcb37a9401a1843a09f2f87
7
- data.tar.gz: 01798c59e5657c956d21b9ee31750656cc46efc36ee7a33c2207066f28ac3fbae20d6f1e9cc71f2d56d4727a2d69fcc33273ad73a74c76b5b24d803b842b7343
6
+ metadata.gz: d53ee4b6baa9edc014069cad31cec4e6da06944df8e5d08523ba862ebfa3f92ab0eed25d67903c7abe38e31c1f85161035feb0ae329353153e6a6cea88566faf
7
+ data.tar.gz: c7f272a9665672d7f2dfa682ee5d5d3c6b16508cba3f2a9bdd01da66f4c7b3b652b4b96418e39be7f815fbf8c1c2bbbc76d8962262ad3823edbe770b834165bf
data/bin/rnr CHANGED
@@ -1,193 +1,189 @@
1
1
  #! /usr/bin/env ruby
2
2
 
3
- require "rename_radically"
3
+ require 'pathname'
4
+ require 'rename_radically'
5
+ require 'yaml'
4
6
 
5
- # Help reference function:
6
- def help_reference()
7
- lines = Array.new
7
+
8
+
9
+ ###############################################################################
10
+ ### Helper functions ###
11
+ ###############################################################################
12
+
13
+ # Prints out the help reference.
14
+ def show_help( exit_value )
8
15
  name = "#{File.basename $0}"
9
16
 
10
- # Help reference lines:
11
- # Header:
12
- lines.push "\e[33mReNameRadically: a simple file renamer who mostly " +
13
- "hates spaces.\e[0m"
14
- lines.push "\e[33mUsage:\e[0m"
15
- # Compact mode:
16
- lines.push "\e[34m#{name} <files>\e[0m: recursively renames the given " +
17
- "files and folders to a CamelCase format."
18
- # Widen mode:
19
- lines.push "\e[34m#{name} -w/--widen <files>\e[0m: recursively renames " +
20
- "the given files and folders adding spaces when needed."
21
- # Regex mode:
22
- lines.push "\e[34m#{name} -r/--regex <regex> <substitute> <files>\e[0m: " +
23
- "recursively renames the given files and folders replacing " +
24
- "any match of the given regex with the given substitute."
25
- # Renaming script mode:
26
- lines.push "\e[34m#{name} -s/--script <files>\e[0m: creates a bash script " +
27
- "to quickly rename the given files and folders, for whenever " +
28
- "the other modalities cannot yield the desired result."
29
- # Help switch:
30
- lines.push "\e[34m#{name} -h\e[0m: shows this help reference."
31
- # Footer:
32
- lines.push ""
33
- lines.push "\e[33mNOTE\e[0m: if no files are specified to a command, it " +
34
- "will process every file in the current folder."
35
- lines.push ""
36
- lines.push "See the configuration file located in \e[34m" +
37
- "#{ENV['HOME']}/.rnr\e[0m to add your personal tweaks."
38
-
39
- # Max 80 characters per line, but preserving words integrity! :3
40
- lines.each do |entry|
41
- # 80 characters line container:
42
- composed = ""
43
-
44
- # Split each message string on spaces:
45
- entry.split( " " ).each do |word|
46
- # If the current composed message plus the current word exceeds 80 is
47
- # within 80 characters, keep composing:
48
- if 80 >= "#{composed + " " + word}".length then
49
- # Add a space when needed:
50
- unless composed.empty? then
51
- composed += " "
52
- end
53
- composed += "#{word}"
54
-
55
- # If the current word would not fit in an 80 characters line, print the
56
- # current composed message, and start composing the next one with an
57
- # indentation of 2 spaces.
58
- else
59
- puts composed
60
- composed = " #{word}"
61
- end
62
- end
17
+ puts <<-EOF
18
+ \e[33mReNameRadically: a simple file renamer who mostly hates spaces.\e[0m
19
+ \e[33mUsage:\e[0m
20
+ \e[34m#{name} <files>\e[0m: recursively renames the given files and folders to a CamelCase format.
21
+ \e[34m#{name} -w/--widen <files>\e[0m: recursively renames the given files and folders adding spaces when needed.
22
+ \e[34m#{name} -r/--regex <regex> <substitute> <files>\e[0m: recursively renames the given files and folders replacing any match of the given regex with the given substitute.
23
+ \e[34m#{name} -s/--script <files>\e[0m: creates a bash script to quickly rename the given files and folders, for whenever the other modalities cannot yield the desired result.
24
+ \e[34m#{name} -h\e[0m: shows this help reference.
63
25
 
64
- # Finally, print the last line:
65
- puts composed
66
- end
26
+ \e[33mNOTE\e[0m: if no files are specified to a command, it will process every file in the current folder.
27
+
28
+ \e[33mNOTE\e[0m: the whole procedure can be simulated using the --dry-run flag.
29
+
30
+ \e[33mNOTE\e[0m: check out the configuration file located in \e[34m~/.config/rnr/config.yml\e[0m to add your personal tweaks.
31
+ EOF
32
+
33
+ exit exit_value.to_i
67
34
  end
68
35
 
69
- # Here it is! The main protagonist! The Renamer object!
70
- rnr = ReNameRadically.new
71
36
 
72
- # Help reference:
73
- if [ "-h", "--help" ].include? ARGV[0] and 1 == ARGV.size then
74
- help_reference
75
- exit 0
76
37
 
77
- # Renaming script:
78
- elsif [ "-s", "--script" ].include? ARGV[0] then
79
- tmp = Array.new ARGV
80
- tmp.shift
81
- tmp.uniq!
38
+ ###############################################################################
39
+ ### Logic entry point ###
40
+ ###############################################################################
82
41
 
83
- # No other parameters: run this for every file in the current directory
84
- # (except . and ..):
85
- if tmp.empty? then
86
- tmp = Dir.entries "."
42
+ # Help reference check:
43
+ if [ '-h', '--help' ].include? ARGV[ 0 ] then
44
+ show_help 0
45
+ end
87
46
 
88
- # Always remove "." and "..".
89
- tmp.delete "."
90
- tmp.delete ".."
47
+ # Create a config file for the application if it does not already exist:
48
+ config = Pathname.new "#{ENV[ 'HOME' ]}/.config/rnr/config.yml"
49
+ unless config.exist? then
50
+ config.dirname.mkpath
51
+ config.write <<-EOF
52
+ # Characters treated as spaces, which get removed while renaming a file in
53
+ # non-wide mode:
54
+ :as_spaces:
55
+ - "_"
56
+
57
+ # Characters after which a word should be capitalized in non-wide mode:
58
+ :delimiters:
59
+ - "-"
60
+ - "+"
61
+ - "("
62
+ - ")"
63
+ - "["
64
+ - "]"
65
+ - "{"
66
+ - "}"
67
+ - "'"
68
+ - "&"
69
+ - "."
70
+ - "!"
71
+ - "?"
72
+
73
+ # Characters after which must not be added a space in wide mode:
74
+ :ex_after:
75
+ - "'"
76
+ - "("
77
+ - "-"
78
+ - "<"
79
+ - "["
80
+ - "{"
81
+ - "."
82
+
83
+ # Characters before which must not be added a space in wide mode:
84
+ :ex_before:
85
+ - "."
86
+ - ","
87
+ - "?"
88
+ - "!"
89
+ - "'"
90
+ - ")"
91
+ - "]"
92
+ - "}"
93
+ - ">"
94
+ - "-"
95
+ - "_"
96
+
97
+ # Name of the script file created by renaming script mode:
98
+ :script: "REN.bash"
99
+
100
+ EOF
101
+
102
+ puts "Created a new config file: #{config.realpath}"
103
+ end
91
104
 
92
- # This script is sort of harmless, so there is not going to be a check
93
- # for user consent.
105
+ # Parse the config:
106
+ config = YAML.load_file config.to_s
107
+
108
+ # Initialize the renamer:
109
+ rnr = ReNameRadically.new as_spaces: config[ :as_spaces ],
110
+ delimiters: config[ :delimiters ],
111
+ ex_after: config[ :ex_after ],
112
+ ex_before: config[ :ex_before ],
113
+ script: config[ :script ],
114
+ dry_run: !ARGV.delete( '--dry-run' ).nil?
115
+
116
+ # Check the modality required by the user:
117
+ modality = :compact
118
+ r_pattern = ''
119
+ r_sub = ''
120
+ if ARGV[ 0 ].to_s[ 0 ] == '-' then
121
+ case ARGV.shift
122
+ when '-r', '--regex'
123
+ if ARGV.size < 2 then
124
+ show_help -1
125
+ end
126
+ modality = :regex
127
+ r_pattern = ARGV.shift
128
+ r_sub = ARGV.shift
129
+
130
+ when '-s', '--script'
131
+ modality = :script
132
+
133
+ when '-w', '--widen'
134
+ modality = :widen
94
135
  end
136
+ end
95
137
 
96
- rnr.createScript *tmp
97
-
98
- # Regex:
99
- elsif [ "-r", "--regex" ].include? ARGV[0] then
100
- tmp = Array.new ARGV
101
- tmp.shift
102
- regex = Regexp.new "#{tmp.shift}"
103
- substitute_with = "#{tmp.shift}".gsub! "\\", "\\\\"
104
- tmp.uniq!
105
-
106
- # 3 Parameters: everything in the current folder.
107
- if 3 == ARGV.size then
108
- tmp = Dir.entries "."
109
-
110
- # Always remove "." and "..".
111
- tmp.delete "."
112
- tmp.delete ".."
113
-
114
- # This could be quite dangerous if erroneously invoked (eg. in the user's
115
- # home directory). Better use a confirmation prompt:
116
- print "Do you really want to rename every file and folder in the " +
117
- "current directory (#{Dir.pwd})? [y/N] "
118
- answer = STDIN.gets.chomp
119
-
120
- # Abort from anything different than y/Y:
121
- unless "Y" == Unicode::capitalize( answer ) then
122
- puts "Operation aborted."
123
- exit 0
124
- end
125
-
126
- # User is a moron.
127
- elsif 3 > ARGV.size then
128
- puts "Must specify at one regex and one substitute for this modality."
129
- exit -1
138
+ # Outcome container:
139
+ outcome = Hash.new
140
+
141
+ # Files to move:
142
+ files = ARGV
143
+
144
+ # If ARGV is empty we gotta make sure the user actually wants to rename
145
+ # everything in the current folder:
146
+ if files.empty? then
147
+ # Ask for confirmation before doing anything:
148
+ print 'Do you really want to rename every file and folder in the ' \
149
+ "current directory (#{Dir.pwd})? [y/N] "
150
+ unless 'Y' == STDIN.gets.chomp.upcase then
151
+ puts 'Operation aborted.'
152
+ exit 0
130
153
  end
131
154
 
132
- rnr.regexRename *tmp, regex, substitute_with
133
-
134
- # Widen:
135
- elsif [ "-w", "--widen" ].include? ARGV[0] then
136
- tmp = Array.new ARGV
137
- tmp.shift
138
- tmp.uniq!
139
-
140
- # No other parameters: run this for every file in the current directory
141
- # (except . and ..):
142
- if tmp.empty? then
143
- tmp = Dir.entries "."
144
-
145
- # Always remove "." and "..".
146
- tmp.delete "."
147
- tmp.delete ".."
148
-
149
- # This could be quite dangerous if erroneously invoked (eg. in the user's
150
- # home directory). Better use a confirmation prompt:
151
- print "Do you really want to rename every file and folder in the " +
152
- "current directory (#{Dir.pwd})? [y/N] "
153
- answer = STDIN.gets.chomp
154
-
155
- # # Abort from anything different than y/Y:
156
- unless "Y" == Unicode::capitalize( answer ) then
157
- puts "Operation aborted."
158
- exit 0
159
- end
155
+ # If we got here add in every child of the current folder to ARGV:
156
+ files = Pathname.new( '.' ).children if files.empty?
157
+ end
158
+
159
+ # This one calls a different function than the rest:
160
+ if modality == :script then
161
+ outcome = rnr.create_renaming_script files
162
+
163
+ if outcome.nil? then
164
+ puts 'Unable to write the renaming script in the current folder.'
165
+ exit -2
160
166
  end
161
167
 
162
- rnr.widen *tmp
168
+ puts "Created script '#{rnr.script}'."
163
169
 
164
- # Compact:
170
+ # Everything else is straightforward:
165
171
  else
166
- tmp = Array.new ARGV
167
- tmp.uniq!
168
-
169
- # No other parameters: run this for every file in the current directory
170
- # (except . and ..):
171
- if tmp.empty? then
172
- tmp = Dir.entries "."
173
-
174
- # Always remove "." and "..".
175
- tmp.delete "."
176
- tmp.delete ".."
177
-
178
- # This could be quite dangerous if erroneously invoked (eg. in the user's
179
- # home directory). Better use a confirmation prompt:
180
- print "Do you really want to rename every file and folder in the " +
181
- "current directory (#{Dir.pwd})? [y/N] "
182
- answer = STDIN.gets.chomp
183
-
184
- # Abort from anything different than y/Y:
185
- unless "Y" == Unicode::capitalize( answer ) then
186
- puts "Operation aborted."
187
- exit 0
188
- end
189
- end
172
+ # Perform the renaming:
173
+ outcome = rnr.rename files: files,
174
+ modality: modality,
175
+ r_pattern: r_pattern,
176
+ r_sub: r_sub
177
+ end
178
+
179
+ # Warning about missing files and insufficient permissions:
180
+ puts 'File(s) not found:' if outcome[ :not_found ].any?
181
+ outcome[ :not_found ].each do |f|
182
+ puts " - #{f}"
183
+ end
190
184
 
191
- rnr.compact *tmp
185
+ puts 'Insufficient permissions to move:' if outcome[ :no_permissions ].any?
186
+ outcome[ :no_permissions ].each do |f|
187
+ puts " - #{f}"
192
188
  end
193
189
 
@@ -1,6 +1,6 @@
1
- require "pathname"
2
- require "unicode"
3
- require "yaml"
1
+ require 'pathname'
2
+
3
+
4
4
 
5
5
  =begin
6
6
  This is the ReNameRadically class.
@@ -16,481 +16,305 @@ regex:: replaces all occurrences of the given regex with the given
16
16
  renaming script:: creates a bash script to rename files, for whenever the
17
17
  other modalities cannot yield the desired result.
18
18
  =end
19
-
20
19
  class ReNameRadically
21
- @config # Location of the user config file.
22
- @as_spaces # Array of characters to be treated as spaces
23
- @delimiters # Array of characters to used as word delimiters
24
- @ex_after # Array of exceptions not to put a space after in widen mode.
25
- @ex_before # Array of exceptions not to put a space before in widen mode.
26
- @script # Script name for script renaming mode.
27
-
28
- # Constructor: takes the path of the config file as a parameter, and ensures
29
- # it contains the right info. If not, it gets created anew.
30
- def initialize( config_file = "#{ENV['HOME']}/.rnr" )
31
- @config = Pathname.new config_file
32
-
33
- # Attempt to read from the config file:
34
- begin
35
- loaded_config = YAML.load_file @config
36
- @as_spaces = loaded_config["as_spaces"]
37
- @delimiters = loaded_config["delimiters"]
38
- @ex_after = loaded_config["ex_after"]
39
- @ex_before = loaded_config["ex_before"]
40
- @script = loaded_config["script"]
41
-
42
- # Fastest way to check for data consistency:
43
- @as_spaces[0]
44
- @delimiters[0]
45
- @ex_after[0]
46
- @ex_before[0]
47
- @script = loaded_config["script"][0]
48
-
49
- # If it fails, create a new default config file:
50
- rescue
51
- # This is the best way I can think of to hardcode the default config file
52
- # without breaking the formatting.
53
- config = Array.new
54
- config.push "# Characters treated as spaces, which get removed while " +
55
- "renaming a file in"
56
- config.push "# non-wide mode:"
57
- config.push "as_spaces:"
58
- config.push "- \"_\""
59
- config.push " "
60
- config.push "# Characters after which a word should be capitalized in" +
61
- " non-wide mode:"
62
- config.push "delimiters:"
63
- config.push "- \"-\""
64
- config.push "- \"+\""
65
- config.push "- \"(\""
66
- config.push "- \")\""
67
- config.push "- \"[\""
68
- config.push "- \"]\""
69
- config.push "- \"{\""
70
- config.push "- \"}\""
71
- config.push "- \"'\""
72
- config.push "- \"&\""
73
- config.push "- \".\""
74
- config.push "- \"!\""
75
- config.push "- \"?\""
76
- config.push " "
77
- config.push "# Characters after which must not be added a space in " +
78
- "wide mode:"
79
- config.push "ex_after:"
80
- config.push "- \"'\""
81
- config.push "- \"(\""
82
- config.push "- \"-\""
83
- config.push "- \"<\""
84
- config.push "- \"[\""
85
- config.push "- \"{\""
86
- config.push "- \".\""
87
- config.push " "
88
- config.push "# Characters before which must not be added a space in " +
89
- "wide mode:"
90
- config.push "ex_before:"
91
- config.push "- \".\""
92
- config.push "- \",\""
93
- config.push "- \"?\""
94
- config.push "- \"!\""
95
- config.push "- \"'\""
96
- config.push "- \")\""
97
- config.push "- \"]\""
98
- config.push "- \"}\""
99
- config.push "- \">\""
100
- config.push "- \"-\""
101
- config.push "- \"_\""
102
- config.push " "
103
- config.push "# Name of the script file created by renaming script mode:"
104
- config.push "script:"
105
- config.push "- \"REN.bash\""
106
- config.push " "
107
-
108
- config = config.join "\n"
109
-
110
- loaded_config = YAML.load config
111
-
112
- # Attempt to create the file:
113
- begin
114
- File.open @config, "w" do |f|
115
- f.puts config
116
- end
117
- puts "Created a new config file: #{@config}"
118
-
119
- # ...And be sure that everything went right:
120
- loaded_config = YAML.load_file @config
121
-
122
- # Something went horribly wrong: maybe there's no home folder, or its
123
- # permissions are all wrong... So, screw the config file and use default
124
- # values for this ride.
125
- rescue
126
- puts "WARNING: could not read/write file #{@config}, you might want" +
127
- " to check out why."
128
- end
129
20
 
130
- # Finally, valorize these:
131
- @as_spaces = loaded_config["as_spaces"]
132
- @delimiters = loaded_config["delimiters"]
133
- @ex_after = loaded_config["ex_after"]
134
- @ex_before = loaded_config["ex_before"]
135
- @script = loaded_config["script"][0]
136
- end
21
+ #############################################################################
22
+ ### Attributes ###
23
+ #############################################################################
24
+
25
+ # Dry run flag.
26
+ attr_reader :dry_run
27
+
28
+ # Array of characters to be treated as spaces
29
+ attr_reader :as_spaces
30
+
31
+ # Array of characters to used as word delimiters
32
+ attr_reader :delimiters
33
+
34
+ # Array of exceptions not to put a space after in widen mode.
35
+ attr_reader :ex_after
36
+
37
+ # Array of exceptions not to put a space before in widen mode.
38
+ attr_reader :ex_before
39
+
40
+ # Script name for script renaming mode.
41
+ attr_reader :script
42
+
43
+
44
+
45
+ #############################################################################
46
+ ### Public instance methods ###
47
+ #############################################################################
48
+
49
+ # Constructor.
50
+ # Takes as parameters the config file path and the dry run flag.
51
+ # Creates the config file if needed.
52
+ def initialize( dry_run: false,
53
+ as_spaces: %w[ _ ],
54
+ delimiters: %w[ - + ( ) [ ] { } ' & . ! ? ],
55
+ ex_after: %w[ ' ( - < \[ { . ],
56
+ ex_before: %w[ . , ? ! ' ) \] } > - _ ],
57
+ script: 'REN.sh'
58
+ )
59
+ @dry_run = dry_run
60
+ @as_spaces = as_spaces
61
+ @delimiters = delimiters
62
+ @ex_after = ex_after
63
+ @ex_before = ex_before
64
+ @script = script
65
+
66
+ # Ensure any invalid values are not used:
67
+ @as_spaces = Array.new unless @as_spaces.is_a? Array
68
+ @delimiters = Array.new unless @delimiters.is_a? Array
69
+ @ex_after = Array.new unless @ex_after.is_a? Array
70
+ @ex_before = Array.new unless @ex_before.is_a? Array
71
+ @script = 'REN.sh' if [ '', '.', '..' ].include? @script.to_s
137
72
  end
138
73
 
139
- # Public method: checks if the given files exist, then prints a list of the
140
- # not found ones and returns an array containing the Pathname objects of the
141
- # found ones.
142
- public
143
- def checkFiles( *files )
144
- # This will contain the not found files:
145
- not_found = Array.new
146
74
 
147
- # This will contain files we do not have the permissions to move:
148
- no_permissions = Array.new
149
75
 
150
- # This will contain the valid files:
151
- ok = Array.new
76
+ # Simple tester function for strings.
77
+ def tester( text:, modality:, r_pattern: '', r_sub: '' )
78
+ # Turn this into a pathname:
79
+ text = Pathname.new text
152
80
 
153
- # Now, check each file:
154
- files.each do |entry|
155
- tmp = Pathname.new( entry )
81
+ return modality_regex text, r_pattern, r_sub if modality == :regex
82
+ return send( "modality_#{modality}", text ) unless modality == :regex
83
+ end
156
84
 
157
- # The file exists!
158
- if tmp.exist? and "." != tmp.to_s and ".." != tmp.to_s then
159
85
 
160
- # And we do have the permissions to move it!
161
- if tmp.dirname.writable? then
162
- ok.push tmp
163
86
 
164
- # Apparently, we cannot rename it:
165
- else
166
- no_permissions.push tmp
167
- end
87
+ # Recursively renames the given files using the given modality.
88
+ # Returns a hash of not found or unchangeable files.
89
+ def rename( files:, modality:, check_files: true, r_pattern: '', r_sub: '' )
90
+ # Data to return:
91
+ ret = Hash.new
168
92
 
169
- # The file has not been found:
170
- else
171
- not_found.push entry
172
- end
93
+ # If this is true then some extra steps have to be taken:
94
+ if check_files then
95
+ ret = check_files files
96
+ files = ret.delete :ok
173
97
  end
174
98
 
175
- # Print a list of not found files:
176
- not_found.each_with_index do |entry, idx|
177
- if 0 == idx then
178
- puts "The following files will be ignored (not found or invalid):"
179
- end
180
-
181
- puts "- #{entry}"
182
- end
99
+ # Then check each of them
100
+ files.each do |file|
101
+ # Special case for regex pattern:
102
+ if modality == :regex then
103
+ file = perform_rename file: file,
104
+ name: modality_regex( file, r_pattern, r_sub )
183
105
 
184
- # Print a list of files we do not have permission to rename:
185
- no_permissions.each_with_index do |entry, idx|
186
- if 0 == idx then
187
- puts "You lack the permissions to move the following files:"
106
+ # Rename the file using the right method:
107
+ else
108
+ file = perform_rename file: file,
109
+ name: send( "modality_#{modality}", file )
188
110
  end
189
111
 
190
- puts "- #{entry}"
112
+ # If we just renamed a folder, rename everything within it as well:
113
+ rename files: file.children,
114
+ modality: modality,
115
+ check_files: false,
116
+ r_pattern: r_pattern,
117
+ r_sub: r_sub \
118
+ if file.directory?
191
119
  end
192
120
 
193
- # Return the arraid containing the valid ones:
194
- return ok
121
+ # Return what files gave an error:
122
+ return ret
195
123
  end
196
124
 
197
- # Public method: will smartly rename a file (Pathname format) to
198
- # new_name, preserving the original path and extension. If another file with
199
- # the destination name already exists, the new one will have a number
200
- # appended to its name. Returns the new name of the file.
201
- public
202
- def smartRename( file, new_name )
203
- # Be sure to remove characters which are not allowed for file names:
204
- new_name.gsub! "/" ""
205
- new_name.gsub! "\0" ""
206
-
207
- # Also, the max name length is 255, including the extension:
208
- new_name.scan( /.{#{255 - "#{file.extname}".length}}/ )[0]
209
-
210
- # Hopefully, this is already the name that will be used:
211
- destination = Pathname.new "#{file.dirname}/#{new_name}#{file.extname}"
212
-
213
- # Rename the file only if the destination is different than the origin and
214
- # if the file name is not empty.
215
- unless file.basename == destination.basename or "#{new_name}".empty? or
216
- "." == new_name or ".." == new_name then
217
- # Index variable for worst-case scenario:
218
- index = 0
219
-
220
- # To be honest... If this goes beyond 2, the user is really just messing
221
- # with us.
222
- while destination.exist? do
223
- index += 1
224
- destination = Pathname.new "#{file.dirname}/#{new_name}-#{index}" +
225
- "#{file.extname}"
226
- end
227
-
228
- # Rename away!
229
- file.rename destination
230
- end
231
125
 
232
- # In any case, return the destination (Pathname format):
233
- return destination
234
- end
235
126
 
236
- # Private method, called through compact: renames a single file to a compact
237
- # CamelCase version. This contains the main logic of renaming files in such
238
- # way. Returns the new name of the renamed file.
239
- private
240
- def compactFile( file )
241
- # Get the file's basename, without its extension:
242
- file_name = file.basename( file.extname ).to_s
243
-
244
- # Replace the characters contained in the "as_spaces" field in the config
245
- # file with spaces:
246
- @as_spaces.each do |remove|
247
- file_name.gsub! remove, " "
248
- end
127
+ # Creates a bash script to rename files.
128
+ # Returns nil if the file cannot be created in the current folder.
129
+ # Otherwise returns a hash of not found or unchangeable files.
130
+ def create_renaming_script( files )
131
+ # Check for permissions first:
132
+ return nil unless Pathname.new( '.' ).dirname.writable?
249
133
 
250
- # Add a space after each delimiter:
251
- @delimiters.each do |delimiter|
252
- file_name.gsub! delimiter, "#{delimiter} "
253
- end
134
+ # Sanitize files first:
135
+ files = check_files files
254
136
 
255
- # Now split it into parts that should be capitalized!
256
- file_name = file_name.split " "
137
+ # Write the file:
138
+ File.open @script, 'w' do |f|
139
+ # Write the header and make it executable:
140
+ f.puts "#! /usr/bin/env bash\n\n"
141
+ f.chmod 0700
257
142
 
258
- # And actually capitalize them:
259
- file_name.each_with_index do |entry, idx|
260
- file_name[idx] = Unicode::capitalize entry
261
- end
262
-
263
- # Rename the file and return its new name:
264
- return smartRename file, file_name.join
265
- end
266
-
267
- # Private method, called through compact: recursively renames a folder and
268
- # its content to a compact CamelCase version.
269
- private
270
- def compactFolder( folder )
271
- # Rename the folder:
272
- new_folder_name = compactFile folder
273
-
274
- # Then rename everything it contains, recursively:
275
- new_folder_name.entries.each do |entry|
276
- # Ignore "." and "..", though.
277
- if "." != entry.to_s and ".." != entry.to_s then
278
- compact Pathname.new "#{new_folder_name.realpath}/#{entry}"
143
+ # Write in each file:
144
+ files.delete( :ok ).map do |file|
145
+ f.puts "mv '#{file}' \\\n '#{file}'\n\n"
279
146
  end
147
+
148
+ # As a last thing add in the self-destruction line:
149
+ f.puts "# Self-destruction line, you may not want to edit this:\n" \
150
+ "rm '#{@script}'\n\n"
280
151
  end
152
+
153
+ # Return what files gave an error:
154
+ return files
281
155
  end
282
156
 
283
- # Public method: checks if the given files exist via checkFiles, then calls
284
- # compactFile and compactFolder to process respectedly the given files and
285
- # folders.
286
- public
287
- def compact( *files )
288
- # First off: check if the files exist.
289
- existing = checkFiles *files
290
157
 
291
- # Behave differently for files and folders:
292
- existing.each do |entry|
293
- # Folders:
294
- if entry.directory? then
295
- compactFolder entry
296
158
 
297
- # Files:
298
- else
299
- compactFile entry
300
- end
301
- end
302
- end
159
+ #############################################################################
160
+ ### Private instance methods ###
161
+ #############################################################################
303
162
 
304
- # Private method, called through widen: renames a single file to a wide
305
- # version. This contains the main logic of renaming files in such way.
306
- # Returns the new name of the renamed file.
307
163
  private
308
- def widenFile( file )
309
- # Put the file's basename into an array, without its extension:
310
- file_name = file.basename( file.extname ).to_s
311
-
312
- # This will be the file's new name:
313
- new_file_name = ""
314
-
315
- # Read the file name character by character:
316
- file_name.chars.each do |c|
317
- # To avoid useless spaces, these rules must be respected to add a space
318
- # before the current character:
319
- # 1. c must not be a space.
320
- # 2. new_file_name must not be empty
321
- # 3. new_file_name[-1] must not be a space
322
- # 4. c must not be included in @ex_before
323
- # 5. new_file_name[-1] must not be included in @ex_after
324
- # 6. c and new_file_name[-1] must not both be numbers
325
- # 6. c must be equal to Unicode::capitalize c
326
- if c != " " and false == new_file_name.empty? and
327
- " " != new_file_name[-1] and false == ( @ex_before.include? c ) and
328
- false == ( @ex_after.include? new_file_name[-1] ) and
329
- ( nil == ( c =~ /[0-9]/ ) or
330
- nil == ( new_file_name[-1] =~ /[0-9]/ )
331
- ) and c == Unicode.capitalize( c ) then
332
- new_file_name += " "
333
- end
334
164
 
335
- # Always add the old character:
336
- new_file_name += c
165
+ # Renames a file, taking @dry_run into account.
166
+ # Takes as parameters a Pathname and a new name.
167
+ # The original file's extension is preserved.
168
+ # If a file with the new name already exists, appends a number to it.
169
+ # Returns the new file's pathname.
170
+ def perform_rename( file:, name:, counter: nil )
171
+ # Remove unwanted characters:
172
+ name.gsub!( /(\/|\0)/, "" )
173
+
174
+ # Add an underscore to the counter:
175
+ counter = "_#{counter}" unless counter.nil?
176
+
177
+ # Also the single file name is 255 characters, including the extension:
178
+ name = name[ 0..(255 - file.extname.length - counter.to_s.length) ]
179
+
180
+ # Compose the final name:
181
+ destination = Pathname.new "#{file.dirname}/" \
182
+ "#{name}#{counter}#{file.extname}"
183
+
184
+ # Only rename the file if:
185
+ # - the new name isn't '.' or '..'
186
+ # - the name isn't empty
187
+ # - the new name is different than the old one
188
+ if !name.match( /^\.{1,2}$/ ) and
189
+ !name.empty? and
190
+ file.basename != destination.basename
191
+ then
192
+ # If the destination already exist try again incrementing the counter:
193
+ if destination.exist? then
194
+ destination = perform_rename file: file,
195
+ name: name,
196
+ counter: (counter.to_s[ 1.. ].to_i + 1)
197
+
198
+ # If it's a dry run, simply print a log:
199
+ elsif @dry_run then
200
+ puts "DRY RUN: #{file} => #{destination}"
201
+ destination = file
202
+
203
+ # Otherwise, rename:
204
+ else
205
+ file.rename destination
206
+ end
337
207
  end
338
208
 
339
- # Rename the new file and return its new name:
340
- return smartRename file, new_file_name
209
+ # Always return the destination:
210
+ return destination
341
211
  end
342
212
 
343
- # Private method, called through widen: recursively renames a folder and
344
- # its content to a wide name.
345
- private
346
- def widenFolder( folder )
347
- # Rename the folder:
348
- new_folder_name = widenFile folder
349
-
350
- # Then rename everything it contains, recursively:
351
- new_folder_name.entries.each do |entry|
352
- # Ignore "." and "..", though.
353
- if "." != entry.to_s and ".." != entry.to_s then
354
- widen Pathname.new "#{new_folder_name.realpath}/#{entry}"
355
- end
356
- end
357
- end
358
213
 
359
- # Public method: checks if the given files exist via checkFiles, then calls
360
- # widenFile and widenFolder to process respectedly the given files and
361
- # folders.
214
+
215
+ # Checks if the given file names exist, prints a list of those not existing
216
+ # and without write permissions, then returns an array of pathnames of the
217
+ # rest.
362
218
  public
363
- def widen( *files )
364
- # First off: check if the files exist.
365
- existing = checkFiles *files
219
+ def check_files( files )
220
+ # Containers:
221
+ not_found = Array.new
222
+ no_permissions = Array.new
223
+ ok = Array.new
224
+
225
+ # Check each file:
226
+ files.uniq.sort_by { |e| e.to_s.downcase }.each do |file|
227
+ # Turn it into a pathname:
228
+ file = Pathname.new file.to_s
366
229
 
367
- # Behave differently for files and folders:
368
- existing.each do |entry|
369
- # Folders:
370
- if entry.directory? then
371
- widenFolder entry
230
+ # Ignore these:
231
+ next if file.basename.to_s.match( /^\.{1,2}$/ )
372
232
 
373
- # Files:
233
+ # Check where it belongs:
234
+ if !file.exist? then
235
+ not_found << file
236
+ elsif !file.writable? or !file.dirname.writable? then
237
+ no_permissions << file
374
238
  else
375
- widenFile entry
239
+ ok << file
376
240
  end
377
241
  end
242
+
243
+ # Return only the valid files:
244
+ return { ok: ok, not_found: not_found, no_permissions: no_permissions }
378
245
  end
379
246
 
380
- # Private method, called through regexRename: renames a single file using a
381
- # given regex. This contains the main logic of renaming files in such way.
382
- # Returns the new name of the renamed file.
383
- private
384
- def regexRenameFile( file, regex, with )
385
- # Get the file's basename, without its extension:
386
- file_name = file.basename( file.extname ).to_s
387
247
 
388
- # Apply the regex!
389
- file_name.gsub! regex, "#{with}"
390
248
 
391
- # Rename the file and return its new name:
392
- return smartRename file, file_name
393
- end
249
+ ### Renaming modalities
250
+ #############################################################################
394
251
 
395
- # Private method, called through regexRename: recursively renames a folder
396
- # and its content using a given regex.
397
- private
398
- def regexRenameFolder( folder, regex, with )
399
- # Rename the folder:
400
- new_folder_name = regexRenameFile folder, regex, "#{with}"
401
-
402
- # Then rename everything it contains, recursively:
403
- new_folder_name.entries.each do |entry|
404
- # Ignore "." and "..", though.
405
- if "." != entry.to_s and ".." != entry.to_s then
406
- regexRename Pathname.new( "#{new_folder_name.realpath}/#{entry}" ),
407
- regex, "#{with}"
408
- end
252
+ # Generates a compacted file name for the given pathname.
253
+ def modality_compact( file )
254
+ # Take in the original file's name:
255
+ name = file.basename( file.extname ).to_s
256
+ # Turn these into spaces:
257
+ .gsub( /(#{@as_spaces.map { |a| Regexp.escape a }.join '|'})/, ' ' )
258
+ # Add spaces after these:
259
+ .gsub( /(#{@delimiters.map { |a| Regexp.escape a }.join '|'})/, '\1 ' )
260
+ # Split it on spaces:
261
+ .split ' '
262
+
263
+ # Do the capitalization:
264
+ name.each_with_index do |word, idx|
265
+ # Ignore roman numbers:
266
+ name[ idx ] = word.capitalize unless word.match( /^[IVXLCDM]+$/ )
267
+ name[ idx ] = word.upcase if word.match( /^[ivxlcdm]+$/ )
409
268
  end
269
+
270
+ # Return the joined name:
271
+ return name.join
410
272
  end
411
273
 
412
- # Public method: checks if the given files exist via checkFiles, then calls
413
- # regexRenameFile and regexRenameFolder to process respectedly the given
414
- # files and folders.
415
- public
416
- def regexRename( *files, regex, with )
417
- # First off: check if the files exist.
418
- existing = checkFiles *files
419
274
 
420
- # Behave differently for files and folders:
421
- existing.each do |entry|
422
- # Folders:
423
- if entry.directory? then
424
- regexRenameFolder entry, regex, "#{with}"
425
275
 
426
- # Files:
427
- else
428
- regexRenameFile entry, regex, "#{with}"
276
+ # Generates a widened file name for the given pathname.
277
+ def modality_widen( file )
278
+ # This starts off empty:
279
+ name = ''
280
+
281
+ # Let's parse each character:
282
+ file.basename( file.extname ).to_s.chars.each do |c|
283
+ # To avoid extra spaces we have to follow these rules:
284
+ # 1. c must not be a space
285
+ # 2. name must not be empty
286
+ # 3. name[ -1 ] must not be a space
287
+ # 4. c must not be included in @ex_before
288
+ # 5. name[ -1 ] must not be included in @ex_after
289
+ # 6. c and name[ -1 ] must not both be numbers
290
+ # 7. c must be equal to c.upcase
291
+ # 8. c and name[ -1 ] must not be roman numbers
292
+ if c != ' ' and
293
+ !name.empty? and
294
+ name[ -1 ] != ' ' and
295
+ !@ex_before.include? c and
296
+ !@ex_after.include? name[ -1 ] and
297
+ !( c.match( /[0-9]/ ) and name[ -1 ].match( /[0-9]/ ) ) and
298
+ c == c.upcase and
299
+ !( c.match( /[IVXLCDM]/ ) and name[ -1 ].match( /[IVXLCDM]/ ) )
300
+ then
301
+ name += ' '
429
302
  end
430
- end
431
- end
432
303
 
433
- # Public method: checks if it's possible to create a file in the current
434
- # directory. If successful, then checks if the given files exist via
435
- # checkFiles, then creates a bash script to easily rename them.
436
- public
437
- def createScript( *files )
438
- # Pointless to go any further if the current directory is not writable:
439
- unless Pathname.new( "." ).dirname.writable? then
440
- puts "You do not have the permissions to create a file in this folder."
441
- exit -1
304
+ # Always add in the character:
305
+ name += c
442
306
  end
443
307
 
444
- # Now check if the files exist.
445
- existing = checkFiles *files
446
-
447
- # Now, gotta be sure that @script is not in the list of the files that
448
- # should be renamed:
449
- existing.delete Pathname.new @script
450
-
451
- # Just to be 100% sure:
452
- begin
453
- existing.each_with_index do |entry, idx|
454
- # Only the first time: create the script.
455
- if 0 == idx then
456
- File.open @script, "w" do |f|
457
- # Script header:
458
- f.puts "#!/usr/bin/env bash"
459
- f.puts ""
460
-
461
- # Make it executable:
462
- f.chmod 0700
463
- end
464
- end
465
-
466
- # Append the line to rename the current file:
467
- File.open @script, "a" do |f|
468
- f.puts "mv \"#{entry}\" \\"
469
- f.puts " \"#{entry}\""
470
- f.puts ""
471
- end
472
-
473
- # Only the last time: add the last touches to the script.
474
- if idx == existing.size - 1 then
475
- File.open @script, "a" do |f|
476
- # Self destruct line:
477
- f.puts "# Self-destruction line, you may not want to edit this:"
478
- f.puts "rm \"#{@script}\""
479
-
480
- # And an empty line at the end, because I'm that kind of guy.
481
- f.puts ""
482
- end
483
- end
484
- end
308
+ # Return the composed name:
309
+ return name
310
+ end
485
311
 
486
- # A little something for the user:
487
- puts "Created script file: #{@script}"
488
312
 
489
- # This should never happen... But, just in case...
490
- rescue
491
- puts "Something went wrong while writing the script file... " +
492
- "Are you sure you weren't messing with it?"
493
- end
313
+
314
+ # Generates a new file name for the given pathname using the given regex
315
+ # pattern and sub value.
316
+ def modality_regex( file, pattern, sub )
317
+ return file.basename( file.extname ).to_s.gsub( /#{pattern}/, sub.to_s )
494
318
  end
495
319
 
496
320
  end
metadata CHANGED
@@ -1,35 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rename_radically
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maurizio Oliveri
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-27 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: unicode
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '0.4'
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 0.4.4.2
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '0.4'
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: 0.4.4.2
11
+ date: 2022-05-06 00:00:00.000000000 Z
12
+ dependencies: []
33
13
  description: A simple (and probably dirty) files mass-renamer.
34
14
  email:
35
15
  - 6tsukiyami9@gmail.com
@@ -44,7 +24,7 @@ homepage: https://github.com/Soulsuke/ReNameR
44
24
  licenses:
45
25
  - GPL-3.0
46
26
  metadata: {}
47
- post_install_message:
27
+ post_install_message:
48
28
  rdoc_options: []
49
29
  require_paths:
50
30
  - lib
@@ -59,9 +39,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
59
39
  - !ruby/object:Gem::Version
60
40
  version: '0'
61
41
  requirements: []
62
- rubyforge_project:
63
- rubygems_version: 2.5.1
64
- signing_key:
42
+ rubygems_version: 3.3.7
43
+ signing_key:
65
44
  specification_version: 4
66
45
  summary: ReNameRadically
67
46
  test_files: []