rubypath 0.1.0

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.
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