rubypath 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rubypath.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubypath/version'
2
+
3
+ #
4
+ #
5
+ #
6
+ class Path
7
+ require 'rubypath/identity'
8
+ require 'rubypath/construction'
9
+ require 'rubypath/comparison'
10
+ require 'rubypath/extensions'
11
+
12
+ require 'rubypath/path_operations'
13
+ require 'rubypath/path_predicates'
14
+ require 'rubypath/file_operations'
15
+ require 'rubypath/file_predicates'
16
+ require 'rubypath/dir_operations'
17
+ require 'rubypath/io_operations'
18
+
19
+ require 'rubypath/mock'
20
+ require 'rubypath/backend'
21
+
22
+ end
23
+
24
+ module Kernel
25
+ def Path(*args)
26
+ Path.new *args
27
+ end
28
+ end
data/rubypath.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rubypath/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rubypath"
8
+ spec.version = Path::VERSION
9
+ spec.authors = ["Jan Graichen"]
10
+ spec.email = ["jg@altimos.de"]
11
+ spec.description = %q{Path library incorporating File, Dir, Pathname, IO methods as well as a virtual mock filesystem.}
12
+ spec.summary = %q{Path library incorporating File, Dir, Pathname, IO methods as well as a virtual mock filesystem.}
13
+ spec.homepage = "https://github.com/jgraichen/rubypath"
14
+ spec.license = "LGPLv3"
15
+
16
+ spec.files = Dir['**/*'].grep(%r{^((bin|lib|test|spec|features)/|.*\.gemspec|.*LICENSE.*|.*README.*|.*CHANGELOG.*)})
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Path do
4
+ describe 'Comparison' do
5
+ let(:path) { Path.new '/path/to/file' }
6
+
7
+ describe_method :eql?, aliases: [:==] do
8
+ context 'with Path object' do
9
+ it 'should compare paths (1)' do
10
+ res = path.send described_method, Path('/path/to/file')
11
+ expect(res).to be true
12
+ end
13
+
14
+ it 'should compare paths (1)' do
15
+ res = path.send described_method, Path('/path/to/another/file')
16
+ expect(res).to be false
17
+ end
18
+ end
19
+
20
+ context 'with String object' do
21
+ it 'should compare paths (1)' do
22
+ res = path.send described_method, '/path/to/file'
23
+ expect(res).to be true
24
+ end
25
+
26
+ it 'should compare paths (1)' do
27
+ res = path.send described_method, '/path/to/another/file'
28
+ expect(res).to be false
29
+ end
30
+ end
31
+
32
+ context 'with Pathname object' do
33
+ it 'should compare paths (1)' do
34
+ res = path.send described_method, Pathname.new('/path/to/file')
35
+ expect(res).to be true
36
+ end
37
+
38
+ it 'should compare paths (1)' do
39
+ res = path.send described_method,
40
+ Pathname.new('/path/to/another/file')
41
+ expect(res).to be false
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe Path do
4
+ describe 'Construction' do
5
+ let(:str) { '/path/to/file' }
6
+ let(:args) { [str] }
7
+ let(:path) { described_class.new(*args) }
8
+ subject { path }
9
+
10
+ describe_method :path, aliases: [:to_path, :to_s] do
11
+ subject { path.send described_method }
12
+
13
+ it { should eq str }
14
+
15
+ # Should not return same object as internal variable
16
+ # to avoid in-place modifications like
17
+ # `Path.new('/abc').path.delete!('abc')`
18
+ it { should_not equal path.send(:instance_variable_get, :@path) }
19
+ end
20
+
21
+ describe '#initialize' do
22
+ context 'w/o args' do
23
+ let(:args) { %w() }
24
+ it { expect(subject.path).to eq '' }
25
+ it { should be_a Path }
26
+ end
27
+
28
+ context 'with multiple strings' do
29
+ let(:args) { %w(path to a file.txt) }
30
+ it { expect(subject.path).to eq 'path/to/a/file.txt' }
31
+ it { should be_a Path }
32
+ end
33
+
34
+ context 'with Pathname' do
35
+ let(:args) { [Pathname.new('path/to/dir'), 'file.txt'] }
36
+ it { expect(subject.path).to eq 'path/to/dir/file.txt' }
37
+ it { should be_a Path }
38
+ end
39
+
40
+ context 'with Numerals' do
41
+ let(:args) { ['path', 5, 'to', 4.5, 'file.txt'] }
42
+ it { expect(subject.path).to eq 'path/5/to/4.5/file.txt' }
43
+ it { should be_a Path }
44
+ end
45
+ end
46
+
47
+ describe 'class' do
48
+ describe '#new' do
49
+ context 'with Path as argument' do
50
+ let(:args) { [Path.new('/abc')] }
51
+ it('should return same object') { should equal args.first }
52
+ end
53
+
54
+ context 'w/o args' do
55
+ let(:args) { Array.new }
56
+ it('should return Path::EMPTY') { should equal Path::EMPTY }
57
+ end
58
+ end
59
+
60
+ describe '#like?' do
61
+ subject { Path.like? obj }
62
+
63
+ context 'positive list' do
64
+ {
65
+ 'Path' => Path.new('/path/to/file.ext'),
66
+ 'Pathname' => Pathname.new('/path/to/file.ext'),
67
+ 'String' => '/path/to/file.ext',
68
+ '#to_path' => Class.new{ def to_path; '/path/to/file.ext' end }.new,
69
+ '#path' => Class.new{ def path; '/path/to/file.ext' end }.new
70
+ }.each do |name, example|
71
+ let(:obj) { example.dup }
72
+ it("should accept #{name}") { should be true }
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#like_path' do
78
+ subject { Path.like_path obj }
79
+
80
+ context 'positive list' do
81
+ {
82
+ 'Path' => Path.new('/path/to/file.ext'),
83
+ 'Pathname' => Pathname.new('/path/to/file.ext'),
84
+ 'String' => '/path/to/file.ext',
85
+ '#to_path' => Class.new{ def to_path; '/path/to/file.ext' end }.new,
86
+ '#path' => Class.new{ def path; '/path/to/file.ext' end }.new
87
+ }.each do |name, example|
88
+ let(:obj) { example.dup }
89
+ it("should get path from #{name}") { should eq '/path/to/file.ext' }
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '#to_proc' do
95
+ it 'should allow to use Path as block' do
96
+ expect(%w(path1 path2).map(&Path)).to eq [Path('path1'), Path('path2')]
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,146 @@
1
+ require 'spec_helper'
2
+
3
+ describe Path do
4
+ describe 'Directory Operations' do
5
+ with_backends :mock, :sys do
6
+ describe 'class' do
7
+ describe_method :glob do
8
+ before do
9
+ Path.mock do |root|
10
+ root.mkfile '/file.txt'
11
+ root.mkfile '/lib/path.rb'
12
+ root.mkfile '/lib/path/dir.rb'
13
+ root.mkfile '/lib/path/file.rb'
14
+ root.mkfile '/lib/path/ext.rb'
15
+ end
16
+ end
17
+ subject { ->(*args){ Path.glob(*args) } }
18
+
19
+ it 'should return matching files (I)' do
20
+ expect(subject.call('/*')).to match_array %w(/file.txt /lib)
21
+ end
22
+
23
+ it 'should return matching files (II)' do
24
+ expect(subject.call('/**/*.rb')).to match_array \
25
+ %w(/lib/path.rb /lib/path/dir.rb
26
+ /lib/path/file.rb /lib/path/ext.rb)
27
+ end
28
+
29
+ it 'should return matching files (III)' do
30
+ expect(subject.call('/**/{dir,ext}.rb')).to match_array \
31
+ %w(/lib/path/dir.rb /lib/path/ext.rb)
32
+ end
33
+
34
+ it 'should return matching files (IV)' do
35
+ expect(subject.call('/lib/*.rb')).to match_array %w(/lib/path.rb)
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#glob' do
41
+ it 'should delegate to class#glob' do
42
+ expect(Path).to receive(:glob)
43
+ .with('/abc\[\]/.\*\{\}/file/**/{a,b}.rb', 10).and_return([])
44
+
45
+ Path('/abc[]/.*{}/file').glob('**/{a,b}.rb', 10)
46
+ end
47
+ end
48
+
49
+ describe '#mkdir' do
50
+ context 'w/o arg' do
51
+ let(:dir) { Path '/dir' }
52
+ before { expect(dir).to_not be_existent }
53
+ subject { dir.mkdir }
54
+
55
+ it 'should create directory' do
56
+ expect(subject).to be_directory
57
+ end
58
+
59
+ it 'should return path to directory' do
60
+ expect(subject).to eq '/dir'
61
+ end
62
+
63
+ it { should be_a Path }
64
+
65
+ context 'in non-existent parent directory' do
66
+ let(:dir) { Path '/non-ext/dir' }
67
+ before { expect(dir).to_not be_existent }
68
+ before { expect(dir.parent).to_not be_existent }
69
+ subject { dir.mkdir }
70
+
71
+ it 'should raise some error' do
72
+ expect{ subject }.to raise_error(
73
+ Errno::ENOENT, 'No such file or directory - /non-ext/dir')
74
+ end
75
+ end
76
+ end
77
+
78
+ context 'with arg' do
79
+ let(:dir) { Path '/' }
80
+ let(:args) { ['fuu'] }
81
+ before { expect(dir.join(*args)).to_not be_existent }
82
+ subject { dir.mkdir(*args) }
83
+
84
+ it 'should create directory' do
85
+ expect(subject).to be_directory
86
+ end
87
+
88
+ it 'should return path to directory' do
89
+ expect(subject).to eq '/fuu'
90
+ end
91
+
92
+ it { should be_a Path }
93
+ end
94
+ end
95
+
96
+ describe_method :mkpath, aliases: [:mkdir_p] do
97
+ let(:dir) { Path '/path/to/dir' }
98
+ before { expect(dir).to_not be_existent }
99
+ before { expect(dir.parent).to_not be_existent }
100
+ subject { dir.send(described_method) }
101
+
102
+ it 'should create directories' do
103
+ expect(subject).to be_directory
104
+ end
105
+
106
+ it 'should return path to directory' do
107
+ expect(subject).to eq '/path/to/dir'
108
+ end
109
+
110
+ it { should be_a Path }
111
+ end
112
+
113
+ describe_method :entries do
114
+ let(:path) { Path '/' }
115
+ let(:args) { Array.new }
116
+ subject { path.send described_method, *args }
117
+
118
+ context 'with directory with children' do
119
+ before do
120
+ path.touch 'file.a'
121
+ path.touch 'file.b'
122
+ path.mkdir 'dir.a'
123
+ path.mkdir 'dir.b'
124
+ end
125
+
126
+ it 'should list of entries' do
127
+ expect(subject).to match_array %w(.. . file.a file.b dir.a dir.b)
128
+ end
129
+
130
+ it 'should return list of Path objects' do
131
+ subject.each{|e| expect(e).to be_a Path }
132
+ end
133
+ end
134
+
135
+ context 'with non-existent directory' do
136
+ let(:path) { Path '/non-existent-dir' }
137
+
138
+ it 'should raise error' do
139
+ expect{ subject }.to raise_error(
140
+ Errno::ENOENT, 'No such file or directory - /non-existent-dir')
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,270 @@
1
+ require 'spec_helper'
2
+
3
+ describe Path do
4
+ describe 'Extensions' do
5
+ let(:path) { Path '/path/to/template.de.html.slim' }
6
+ let(:dotfile) { Path '/path/to/.dotfile' }
7
+ let(:dotfile_ext) { Path '/path/to/.dotfile.en.sh' }
8
+
9
+ describe_method :extensions, aliases: [:exts] do
10
+ subject { path.send described_method }
11
+
12
+ it 'should return all file extensions' do
13
+ should eq %w(de html slim)
14
+ end
15
+
16
+ context 'dotfile w/o ext' do
17
+ let(:path) { dotfile }
18
+
19
+ it 'should not return dotfile name as extension' do
20
+ should eq Array.new
21
+ end
22
+ end
23
+
24
+ context 'dotfile with ext' do
25
+ let(:path) { dotfile_ext }
26
+
27
+ it 'should only return dotfile extension' do
28
+ should eq %w(en sh)
29
+ end
30
+ end
31
+ end
32
+
33
+ describe '#extname' do
34
+ subject { path.extname }
35
+
36
+ it 'should return file extensions including dot' do
37
+ should eq '.slim'
38
+ end
39
+
40
+ context 'dotfile w/o ext' do
41
+ let(:path) { dotfile }
42
+
43
+ it 'should not return dotfile name as extension' do
44
+ should eq ''
45
+ end
46
+ end
47
+
48
+ context 'dotfile with ext' do
49
+ let(:path) { dotfile_ext }
50
+
51
+ it 'should only return dotfile extension' do
52
+ should eq '.sh'
53
+ end
54
+ end
55
+ end
56
+
57
+ describe '#pure_name' do
58
+ subject { path.pure_name }
59
+
60
+ it 'should return file name without extensions' do
61
+ should eq 'template'
62
+ end
63
+
64
+ context 'dotfile w/o ext' do
65
+ let(:path) { dotfile }
66
+
67
+ it 'should return dotfile name' do
68
+ should eq '.dotfile'
69
+ end
70
+ end
71
+
72
+ context 'dotfile with ext' do
73
+ let(:path) { dotfile_ext }
74
+
75
+ it 'should return dotfile name w/o exts' do
76
+ should eq '.dotfile'
77
+ end
78
+ end
79
+ end
80
+
81
+ describe_method :extension, aliases: [:ext] do
82
+ subject { path.send described_method }
83
+
84
+ it 'should return last file extensions' do
85
+ should eq 'slim'
86
+ end
87
+ end
88
+
89
+ describe_method :replace_extensions do
90
+ let(:path) { Path "#{base}file#{exts}" }
91
+
92
+ shared_examples 'extensions replacement' do
93
+ context 'with array' do
94
+ subject { path.send described_method, %w(en txt) }
95
+
96
+ it 'should replace all file extensions' do
97
+ should eq "#{base}file.en.txt"
98
+ end
99
+
100
+ it { should be_a Path }
101
+ end
102
+
103
+ context 'with multiple arguments' do
104
+ subject { path.send described_method, *%w(en txt) }
105
+
106
+ it 'should replace all file extensions' do
107
+ should eq "#{base}file.en.txt"
108
+ end
109
+
110
+ it { should be_a Path }
111
+ end
112
+ end
113
+
114
+ shared_examples 'w/o ext' do
115
+ let(:exts) { '' }
116
+ it_behaves_like 'extensions replacement'
117
+
118
+ context 'with replacement hash' do
119
+ subject{ path.send(described_method, 'txt' => 'html') }
120
+
121
+ it 'should replace all file extensions' do
122
+ should eq "#{base}file"
123
+ end
124
+
125
+ it { should be_a Path }
126
+ end
127
+ end
128
+
129
+ shared_examples 'with single ext' do
130
+ let(:exts) { '.txt' }
131
+ it_behaves_like 'extensions replacement'
132
+
133
+ context 'with replacement hash' do
134
+ subject { path.send(described_method, 'txt' => 'html') }
135
+
136
+ it 'should replace all file extensions' do
137
+ should eq "#{base}file.html"
138
+ end
139
+
140
+ it { should be_a Path }
141
+ end
142
+ end
143
+
144
+ shared_examples 'with multiple ext' do
145
+ let(:exts) { '.en.html.slim' }
146
+ it_behaves_like 'extensions replacement'
147
+
148
+ context 'with replacement hash' do
149
+ subject { path.send(described_method, 'en' => 'de') }
150
+
151
+ it 'should replace all file extensions' do
152
+ should eq "#{base}file.de.html.slim"
153
+ end
154
+
155
+ it { should be_a Path }
156
+ end
157
+ end
158
+
159
+ context 'with path' do
160
+ let(:base) { '/path/to/' }
161
+ it_behaves_like 'w/o ext'
162
+ it_behaves_like 'with single ext'
163
+ it_behaves_like 'with multiple ext'
164
+ end
165
+
166
+ context 'with filename only' do
167
+ let(:base) { '' }
168
+ it_behaves_like 'w/o ext'
169
+ it_behaves_like 'with single ext'
170
+ it_behaves_like 'with multiple ext'
171
+ end
172
+
173
+ context 'with relative file path (I)' do
174
+ let(:base) { './' }
175
+ it_behaves_like 'w/o ext'
176
+ it_behaves_like 'with single ext'
177
+ it_behaves_like 'with multiple ext'
178
+ end
179
+
180
+ context 'with relative file path (II)' do
181
+ let(:base) { 'path/' }
182
+ it_behaves_like 'w/o ext'
183
+ it_behaves_like 'with single ext'
184
+ it_behaves_like 'with multiple ext'
185
+ end
186
+ end
187
+
188
+ describe_method :replace_extension do
189
+ let(:path) { Path "#{base}#{file}#{ext}" }
190
+
191
+ shared_examples 'extension replacement' do
192
+ context 'with array' do
193
+ subject { path.send described_method, %w(mobile txt) }
194
+
195
+ it 'should replace last file extensions' do
196
+ should eq "#{base}#{file}.mobile.txt"
197
+ end
198
+
199
+ it { should be_a Path }
200
+ end
201
+
202
+ context 'with multiple arguments' do
203
+ subject { path.send described_method, *%w(mobile txt) }
204
+
205
+ it 'should replace last file extensions' do
206
+ should eq "#{base}#{file}.mobile.txt"
207
+ end
208
+
209
+ it { should be_a Path }
210
+ end
211
+
212
+ context 'with single string' do
213
+ subject { path.send described_method, 'haml' }
214
+
215
+ it 'should replace last file extensions' do
216
+ should eq "#{base}#{file}.haml"
217
+ end
218
+
219
+ it { should be_a Path }
220
+ end
221
+ end
222
+
223
+ shared_examples 'w/o ext' do
224
+ let(:file) { 'file' }
225
+ let(:ext) { '' }
226
+ it_behaves_like 'extension replacement'
227
+ end
228
+
229
+ shared_examples 'with single ext' do
230
+ let(:file) { 'file' }
231
+ let(:ext) { '.txt' }
232
+ it_behaves_like 'extension replacement'
233
+ end
234
+
235
+ shared_examples 'with multiple ext' do
236
+ let(:file) { 'file.de' }
237
+ let(:ext) { '.txt' }
238
+ it_behaves_like 'extension replacement'
239
+ end
240
+
241
+ context 'on path file' do
242
+ let(:base) { '/path/to/file/' }
243
+ it_behaves_like 'w/o ext'
244
+ it_behaves_like 'with single ext'
245
+ it_behaves_like 'with multiple ext'
246
+ end
247
+
248
+ context 'on relative path file' do
249
+ let(:base) { 'to/file/' }
250
+ it_behaves_like 'w/o ext'
251
+ it_behaves_like 'with single ext'
252
+ it_behaves_like 'with multiple ext'
253
+ end
254
+
255
+ context 'on relative root path file' do
256
+ let(:base) { './' }
257
+ it_behaves_like 'w/o ext'
258
+ it_behaves_like 'with single ext'
259
+ it_behaves_like 'with multiple ext'
260
+ end
261
+
262
+ context 'on filename only' do
263
+ let(:base) { '' }
264
+ it_behaves_like 'w/o ext'
265
+ it_behaves_like 'with single ext'
266
+ it_behaves_like 'with multiple ext'
267
+ end
268
+ end
269
+ end
270
+ end