nrename 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/nrename ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path '../../lib/nrename', __FILE__
4
+
5
+ Nrename.run ARGV.dup
@@ -0,0 +1,35 @@
1
+ Feature: Options processing
2
+ In order to use the program conveniently
3
+ As a user
4
+ I want options to be parsed correctly
5
+
6
+ Scenario: Displaying help when run with "-h"
7
+ When I run `nrename -h`
8
+ Then the exit status should be 0
9
+ And the stdout should contain "Usage: nrename "
10
+
11
+ Scenario: Displaying help when run without any options
12
+ When I run `nrename`
13
+ Then the exit status should be 0
14
+ And the stdout should contain "Usage: nrename "
15
+
16
+ Scenario: Displaying version
17
+ When I run `nrename --version`
18
+ Then the exit status should be 0
19
+ And the stdout should match /^(\d+)\.\g'1'\.\g'1'$/
20
+
21
+ Scenario: Passing valid directory name
22
+ Given a directory named "foo_bar"
23
+ When I run `nrename foo_bar`
24
+ Then the exit status should be 0
25
+
26
+ Scenario: Passing invalid directory name
27
+ When I run `nrename no_dir`
28
+ Then the exit status should be 1
29
+ And the stderr should contain "no_dir is not a valid directory."
30
+
31
+ Scenario: Passing file instead of directory
32
+ Given an empty file named "foo"
33
+ When I run `nrename foo`
34
+ Then the exit status should be 1
35
+ And the stderr should contain "foo is not a valid directory."
@@ -0,0 +1,86 @@
1
+ Feature: Renaming files
2
+ In order to get my files nicely sortable
3
+ As a user
4
+ I want program to rename files correctly
5
+
6
+ Scenario: Renaming files in specified directory
7
+ Given a directory named "dir"
8
+ And the following empty files inside directory "dir":
9
+ | 1.txt |
10
+ | 10.txt |
11
+ | 0023.txt |
12
+ | 101.txt |
13
+ | 200.txt |
14
+ When I run `nrename -X dir`
15
+ Then the exit status should be 0
16
+ And the following files should exist inside directory "dir":
17
+ | 001.txt |
18
+ | 010.txt |
19
+ | 023.txt |
20
+ | 101.txt |
21
+ | 200.txt |
22
+
23
+ Scenario: Renaming files in current directory
24
+ Given a directory named "dir"
25
+ And the following empty files inside directory "dir":
26
+ | 1.txt |
27
+ | 10.txt |
28
+ When I cd to "dir"
29
+ And I run `nrename -X`
30
+ Then the exit status should be 0
31
+ And the following files should exist:
32
+ | 01.txt |
33
+ | 10.txt |
34
+
35
+ Scenario: Dry-run
36
+ Given a directory named "dir"
37
+ And the following empty files inside directory "dir":
38
+ | 1.txt |
39
+ | 10.txt |
40
+ | 0023.txt |
41
+ When I cd to "dir"
42
+ When I run `nrename`
43
+ Then the exit status should be 0
44
+ And the following files should exist:
45
+ | 1.txt |
46
+ | 10.txt |
47
+ | 0023.txt |
48
+ And the following files should not exist:
49
+ | 01.txt |
50
+ | 23.txt |
51
+
52
+ Scenario: Renaming files in multiple directories
53
+ Given a directory named "foo"
54
+ And a directory named "bar"
55
+ And the following empty files inside directory "foo":
56
+ | 1.txt |
57
+ | 010.txt |
58
+ And the following empty files inside directory "bar":
59
+ | 33.txt |
60
+ | 01234.txt |
61
+ And I run `nrename -X foo bar`
62
+ Then the exit status should be 0
63
+ And the following files should exist inside directory "foo":
64
+ | 01.txt |
65
+ | 10.txt |
66
+ And the following files should exist inside directory "bar":
67
+ | 0033.txt |
68
+ | 1234.txt |
69
+
70
+ Scenario: Renaming files recursively
71
+ Given a directory named "foo"
72
+ And a directory named "bar"
73
+ And the following empty files inside directory "foo":
74
+ | 1.txt |
75
+ | 010.txt |
76
+ And the following empty files inside directory "bar":
77
+ | 33.txt |
78
+ | 01234.txt |
79
+ And I run `nrename -XR`
80
+ Then the exit status should be 0
81
+ And the following files should exist inside directory "foo":
82
+ | 01.txt |
83
+ | 10.txt |
84
+ And the following files should exist inside directory "bar":
85
+ | 0033.txt |
86
+ | 1234.txt |
@@ -0,0 +1,19 @@
1
+ require 'aruba/cucumber'
2
+
3
+ ENV['PATH'] = "#{File.expand_path __FILE__, '../../../bin'}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
4
+
5
+ Given(/^the following empty files inside directory "([^"]*)":$/) do |directory, files|
6
+ files.raw.each do |file_row|
7
+ write_file "#{directory}/#{file_row[0]}", ''
8
+ end
9
+ end
10
+
11
+ Then(/^the stdout should match \/([^\/]*)\/$/) do |expected|
12
+ assert_matching_output expected, all_stdout
13
+ end
14
+
15
+ Then(/^the following files should exist inside directory "([^"]*)":$/) do |directory, files|
16
+ files.raw.each do |file_row|
17
+ check_file_presence ["#{directory}/#{file_row[0]}"], true
18
+ end
19
+ end
@@ -0,0 +1,60 @@
1
+ require 'pathname'
2
+ require 'fileutils'
3
+
4
+ module Nrename
5
+ class Directory
6
+ def initialize(dir)
7
+ @dir = Pathname.new dir
8
+
9
+ unless @dir.absolute?
10
+ fail ArgumentError, 'directory path should be absolute.'
11
+ end
12
+
13
+ opts = Nrename.options
14
+
15
+ @verbose = opts.verbose
16
+ @execute = opts.execute
17
+ @pattern = opts.pattern
18
+ end
19
+
20
+ def numbered_files
21
+ @numbered_files ||= @dir.children
22
+ .reject(&:directory?)
23
+ .select { |file| file.basename.to_s =~ @pattern }
24
+ end
25
+
26
+ def num_field_length
27
+ @num_field_length ||= max_number.to_s.size
28
+ end
29
+
30
+ def max_number
31
+ numbered_files.map(&method(:number_for)).max
32
+ end
33
+
34
+ def normalize
35
+ numbered_files.each do |old|
36
+ new = normalized_name_for(old)
37
+ next if old == new
38
+ FileUtils.mv old, new, {
39
+ :noop => !@execute,
40
+ :verbose => @verbose
41
+ }
42
+ end
43
+ end
44
+
45
+ def normalized_name_for(path)
46
+ dirname, filename = path.split
47
+ new_filename = filename.to_s
48
+ new_filename[@pattern, 1] = adjusted_number_string_for(path)
49
+ dirname + new_filename
50
+ end
51
+
52
+ def number_for(path)
53
+ path.basename.to_s[@pattern, 1].to_i
54
+ end
55
+
56
+ def adjusted_number_string_for(path)
57
+ number_for(path).to_s.rjust num_field_length, '0'
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,82 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+
4
+ module Nrename
5
+ module Options
6
+ def self.parse(args)
7
+ default_options = {
8
+ :dirs => [],
9
+ :execute => false,
10
+ :pattern => /(\d+)/,
11
+ :recursive => false,
12
+ :verbose => true
13
+ }
14
+
15
+ options = OpenStruct.new default_options
16
+
17
+ executable_name = File.basename $PROGRAM_NAME
18
+
19
+ # display help if called through 'nrename' executable
20
+ # and without arguments
21
+ if args.empty? && executable_name == 'nrename'
22
+ args << '--help'
23
+ end
24
+
25
+ OptionParser.new do |opts|
26
+ opts.banner = "Usage: #{executable_name} [OPTINS] [DIR]..."
27
+
28
+ opts.separator ''
29
+ opts.separator 'Options:'
30
+
31
+ opts.on '-X', '--execute', "Do actual work" do |x|
32
+ options.execute = x
33
+ end
34
+
35
+ opts.on '-R', '--recursive',
36
+ 'Process given directories recursively' do |rec|
37
+ options.recursive = rec
38
+ end
39
+
40
+ opts.on '-v', '--[no-]verbose', 'Run verbosely' do |v|
41
+ options.verbose = v
42
+ end
43
+
44
+ opts.on '-h', '--help', 'Display this message' do
45
+ puts opts
46
+ exit
47
+ end
48
+
49
+ opts.on '--version', 'Display version' do
50
+ puts VERSION
51
+ exit
52
+ end
53
+ end.parse!(args)
54
+
55
+ args.each do |arg|
56
+ dir = File.expand_path arg
57
+ if File.directory? dir
58
+ options.dirs << dir
59
+ else
60
+ warn "#{dir} is not a valid directory."
61
+ exit 1
62
+ end
63
+ end
64
+
65
+ if options.dirs.empty?
66
+ options.dirs << File.expand_path('.')
67
+ end
68
+
69
+ if options.recursive
70
+ options.dirs.dup.each do |dir|
71
+ Dir.glob "#{dir}/**/" do |subdir|
72
+ options.dirs << subdir.chomp('/')
73
+ end
74
+ end
75
+ end
76
+
77
+ options.dirs.uniq!
78
+
79
+ options
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,3 @@
1
+ module Nrename
2
+ VERSION = "0.0.1"
3
+ end
data/lib/nrename.rb ADDED
@@ -0,0 +1,20 @@
1
+ $:.push File.expand_path(File.dirname __FILE__)
2
+
3
+ require 'nrename/version'
4
+ require 'nrename/directory'
5
+ require 'nrename/options'
6
+
7
+ module Nrename
8
+ def self.options
9
+ # return default options if not alredy set by .run():
10
+ @options ||= Options.parse []
11
+ end
12
+
13
+ def self.run(args=[])
14
+ @options = Options.parse args
15
+
16
+ options.dirs
17
+ .map { |dir| Directory.new dir }
18
+ .each &:normalize
19
+ end
20
+ end
@@ -0,0 +1,142 @@
1
+ require 'spec_helper'
2
+
3
+ describe Nrename::Directory do
4
+ let(:test_dir) { '/tmp/test_dir' }
5
+
6
+ after :each do
7
+ rm_rf test_dir
8
+ end
9
+
10
+ describe '#numbered_files' do
11
+ it 'correctly determines number of numbered files' do
12
+ inside test_dir do
13
+ touch %w[1.rb 2.rb 3.rb]
14
+ end
15
+
16
+ dir = Nrename::Directory.new test_dir
17
+ dir.should have(3).numbered_files
18
+ end
19
+
20
+ it 'returns empty array when there is no num. files in dir' do
21
+ mkdir test_dir
22
+ dir = Nrename::Directory.new test_dir
23
+ dir.should have(:no).numbered_files
24
+ end
25
+
26
+ it 'does not care about non-numbered files' do
27
+ inside test_dir do
28
+ touch %w[1.rb 2.rb 3.rb foo.rb bar baz.java]
29
+ end
30
+
31
+ dir = Nrename::Directory.new test_dir
32
+ dir.should have(3).numbered_files
33
+ end
34
+
35
+ it 'does not care about directories' do
36
+ inside test_dir do
37
+ touch %w[1.rb 2.rb 3.rb]
38
+ mkdir %w[1 2 03 40 55]
39
+ end
40
+
41
+ dir = Nrename::Directory.new test_dir
42
+ dir.should have(3).numbered_files
43
+ end
44
+ end
45
+
46
+ describe '#field_length' do
47
+ it 'returns minimal number of digits to fit num of any file' do
48
+ inside test_dir do
49
+ touch %w[1 2 3 444 3234 5 6 7]
50
+ end
51
+
52
+ dir = Nrename::Directory.new test_dir
53
+ dir.num_field_length.should be == 4
54
+ end
55
+
56
+ it 'ignores starting zeros in number' do
57
+ inside test_dir do
58
+ touch %w[001 002 004 035]
59
+ end
60
+
61
+ dir = Nrename::Directory.new test_dir
62
+ dir.num_field_length.should be == 2
63
+ end
64
+ end
65
+
66
+ describe '#max_number' do
67
+ it 'returns maximum number for all file names' do
68
+ inside test_dir do
69
+ touch %w[1 2 3]
70
+ end
71
+
72
+ dir = Nrename::Directory.new test_dir
73
+ dir.max_number.should be == 3
74
+ end
75
+
76
+ it 'is not fooled by fancy file names' do
77
+ inside test_dir do
78
+ touch %w[001 foo003bar.java o98.c 30_999.rb]
79
+ end
80
+
81
+ dir = Nrename::Directory.new test_dir
82
+ dir.max_number.should be == 98
83
+ end
84
+ end
85
+
86
+ describe '#normalize' do
87
+ before do
88
+ Nrename.options.stub execute: true
89
+ end
90
+
91
+ it 'renames files so than they can be sorted properly' do
92
+ inside test_dir do
93
+ touch %w[1 2 3 10 11 004 005 006]
94
+ end
95
+
96
+ # turn off verbose output:
97
+ Nrename.options.stub verbose: false
98
+
99
+ dir = Nrename::Directory.new test_dir
100
+ dir.normalize
101
+
102
+ expected = %w[01 02 03 04 05 06 10 11]
103
+ renamed_files = Dir.entries(test_dir)
104
+ expected.each { |file| renamed_files.should include file }
105
+ end
106
+ end
107
+
108
+ describe '#normalized_name_for' do
109
+ it 'returns normalized name for file' do
110
+ dir = Nrename::Directory.new test_dir
111
+ file = Pathname.new(test_dir) + '1.txt'
112
+ dir.stub num_field_length: 4
113
+ new_name = dir.normalized_name_for(file).basename.to_s
114
+ new_name.should be == '0001.txt'
115
+ end
116
+ end
117
+
118
+ describe '#number_for' do
119
+ it 'returns number for file' do
120
+ inside test_dir do
121
+ touch 'av023cd.txt'
122
+ end
123
+
124
+ dir = Nrename::Directory.new test_dir
125
+ file = dir.numbered_files.first
126
+ dir.number_for(file).should be == 23
127
+ end
128
+ end
129
+
130
+ describe '#adjusted_number_string_for' do
131
+ it 'returns number string adjusted to field length' do
132
+ inside test_dir do
133
+ touch 'qwe0032rty.gif'
134
+ end
135
+
136
+ dir = Nrename::Directory.new test_dir
137
+ dir.stub num_field_length: 6
138
+ file = dir.numbered_files.first
139
+ dir.adjusted_number_string_for(file).should be == '000032'
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe Nrename::Options do
4
+ def parse_options(args)
5
+ Nrename::Options.parse args
6
+ end
7
+
8
+ describe 'verbosity' do
9
+ it 'is on by default (when no arguments provided)' do
10
+ parse_options([]).verbose.should be_true
11
+ end
12
+
13
+ it 'is off when or "--no-verbose" switch is provided' do
14
+ parse_options(%w[--no-verbose]).verbose.should be_false
15
+ end
16
+ end
17
+
18
+ describe 'recursive processing' do
19
+ it 'is off by default' do
20
+ parse_options([]).recursive.should be_false
21
+ end
22
+
23
+ it 'is on when "-R" of "--recursive" is provided' do
24
+ %w[-R --recursive].each do |option|
25
+ parse_options([option]).recursive.should be_true
26
+ end
27
+ end
28
+ end
29
+
30
+ describe 'directory arguments' do
31
+ let(:test_dir) { File.expand_path 'test_dir' }
32
+ let(:subdirs) { %w[aa bb cc] }
33
+
34
+ before :all do
35
+ inside test_dir do
36
+ mkdir subdirs
37
+ end
38
+ end
39
+
40
+ after :all do
41
+ rm_rf test_dir
42
+ end
43
+
44
+ it 'captures rest of arguments as directories to process' do
45
+ subdirs.each do |subdir|
46
+ subdir_path = File.expand_path subdir, test_dir
47
+ arg = File.join test_dir, subdir
48
+ parse_options([arg]).dirs.should include subdir_path
49
+ end
50
+ end
51
+
52
+ it 'captures current dir if no arguments provided' do
53
+ parse_options([]).dirs.should be == [File.expand_path('.')]
54
+ end
55
+
56
+ it 'recursively captures all subdirs when -R option provided' do
57
+ dirs = parse_options(['-R', test_dir]).dirs
58
+
59
+ dirs.should include test_dir
60
+
61
+ subdirs.each do |subdir|
62
+ dirs.should include File.join(test_dir, subdir)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,16 @@
1
+ require 'rspec'
2
+ require 'fileutils'
3
+
4
+ require File.expand_path '../../lib/nrename', __FILE__
5
+
6
+ module Support
7
+ def inside(dir, &block)
8
+ mkdir dir unless File.exists? dir
9
+ cd dir, &block
10
+ end
11
+ end
12
+
13
+ RSpec.configure do |config|
14
+ config.include FileUtils
15
+ config.include Support
16
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nrename
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Victor Deryagin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &81769620 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *81769620
25
+ - !ruby/object:Gem::Dependency
26
+ name: aruba
27
+ requirement: &81769410 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *81769410
36
+ description: ''
37
+ email:
38
+ - vderyagin@gmail.com
39
+ executables:
40
+ - nrename
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - bin/nrename
45
+ - lib/nrename/options.rb
46
+ - lib/nrename/version.rb
47
+ - lib/nrename/directory.rb
48
+ - lib/nrename.rb
49
+ - spec/spec_helper.rb
50
+ - spec/nrename/options_spec.rb
51
+ - spec/nrename/directory_spec.rb
52
+ - features/renaming_files.feature
53
+ - features/support/env.rb
54
+ - features/options_processing.feature
55
+ homepage: ''
56
+ licenses: []
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project: nrename
75
+ rubygems_version: 1.8.10
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: ''
79
+ test_files:
80
+ - spec/spec_helper.rb
81
+ - spec/nrename/options_spec.rb
82
+ - spec/nrename/directory_spec.rb
83
+ - features/renaming_files.feature
84
+ - features/support/env.rb
85
+ - features/options_processing.feature
86
+ has_rdoc: