vfs 0.4.8 → 0.5.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.
- checksums.yaml +7 -0
- data/lib/vfs/drivers/local.rb +7 -25
- data/lib/vfs/drivers/specification.rb +3 -6
- data/lib/vfs/entries/dir.rb +18 -33
- data/lib/vfs/entries/entry.rb +9 -21
- data/lib/vfs/entries/file.rb +8 -16
- data/lib/vfs/entries/universal_entry.rb +5 -9
- data/lib/vfs/entry_proxy.rb +1 -3
- metadata +6 -20
- data/Rakefile +0 -11
- data/readme.md +0 -143
- data/spec/container_spec.rb +0 -31
- data/spec/dir_spec.rb +0 -253
- data/spec/entry_spec.rb +0 -42
- data/spec/file_spec.rb +0 -215
- data/spec/misc_spec.rb +0 -19
- data/spec/path_spec.rb +0 -127
- data/spec/spec_helper.rb +0 -50
- data/spec/storages/local_spec.rb +0 -24
- data/spec/storages/local_spec/emptygit +0 -0
- data/spec/universal_entry_spec.rb +0 -69
data/Rakefile
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
require 'rake_ext'
|
2
|
-
|
3
|
-
project(
|
4
|
-
name: "vfs",
|
5
|
-
gem: true,
|
6
|
-
summary: "Virtual File System - simple and unified API over different storages (Local, S3, SFTP, ...)",
|
7
|
-
# version: '0.4.0',
|
8
|
-
|
9
|
-
author: "Alexey Petrushin",
|
10
|
-
homepage: "http://alexeypetrushin.github.com/vfs"
|
11
|
-
)
|
data/readme.md
DELETED
@@ -1,143 +0,0 @@
|
|
1
|
-
**Documentation:** http://alexeypetrushin.github.com/vfs
|
2
|
-
|
3
|
-
Virtual File System provides **clean, simple and unified API over different storages** (Local File System, AWS S3, SFTP, ...).
|
4
|
-
|
5
|
-
- very simple and intuitive API.
|
6
|
-
- same API for different storages.
|
7
|
-
- work simultaneously with multiple storages.
|
8
|
-
- small codebase, easy to learn and extend.
|
9
|
-
- driver implementation is very simple, it is easy to create new drivers.
|
10
|
-
|
11
|
-
Such unified API is possible because although the API of storages are different the core concept are almost the same.
|
12
|
-
|
13
|
-
Install Vfs with Rubygems:
|
14
|
-
|
15
|
-
gem install vfs
|
16
|
-
|
17
|
-
Once installed, You can proceed with the [basic example][basics], there's also [S3 version][s3_basics] and [SFTP version][ssh_basics] (also [S3 backup][s3_backup] and [SSH/SFTP deployment][ssh_deployment] examples availiable).
|
18
|
-
|
19
|
-
You can report bugs and discuss features on the [issues page][issues].
|
20
|
-
|
21
|
-
## Sample
|
22
|
-
|
23
|
-
``` ruby
|
24
|
-
# Preparing sandbox for our sample and cleaning it before starting
|
25
|
-
# (ignore the `$sandbox` variable, it's needed to reuse this code in S3 and SSH samples).
|
26
|
-
require 'vfs'
|
27
|
-
sandbox = $sandbox || '/tmp/vfs_sandbox'.to_dir.destroy
|
28
|
-
|
29
|
-
# Creating simple Hello World project.
|
30
|
-
project = sandbox['hello_world']
|
31
|
-
|
32
|
-
# Writing readme file (note that parent dirs where created automatically).
|
33
|
-
project['readme.txt'].write 'My App'
|
34
|
-
|
35
|
-
# We can assign files and dirs to variables, now the `readme` variable refers to our readme.txt file.
|
36
|
-
readme = project['readme.txt']
|
37
|
-
|
38
|
-
# Let's ensure that it's all ok with our readme file and check its attributes.
|
39
|
-
p readme.name # => readme.txt
|
40
|
-
p [readme.basename, readme.extension] # => ['readme', 'txt']
|
41
|
-
p readme.path # => /.../readme.txt
|
42
|
-
p readme.exist? # => true
|
43
|
-
p readme.file? # => true
|
44
|
-
p readme.dir? # => false
|
45
|
-
p readme.size # => 6
|
46
|
-
p readme.created_at # => 2011-09-09 13:20:43 +0400
|
47
|
-
p readme.updated_at # => 2011-09-09 13:20:43 +0400
|
48
|
-
|
49
|
-
# Reading - You can read all at once or do it sequentially (input stream
|
50
|
-
# will be automatically splitted into chunks of reasonable size).
|
51
|
-
p readme.read # => "My shiny App"
|
52
|
-
readme.read{|chunk| p chunk} # => "My shiny App"
|
53
|
-
|
54
|
-
# The same for writing - write all at once or do it sequentially
|
55
|
-
# (if there's no file it will be created, if it exists it will be rewriten).
|
56
|
-
readme.write "My App v2"
|
57
|
-
readme.write{|stream| stream.write "My App v3"}
|
58
|
-
p readme.read # => "My shiny App v3"
|
59
|
-
|
60
|
-
# Appending content to existing file.
|
61
|
-
readme.append "How to install ..."
|
62
|
-
p readme.size # => 27
|
63
|
-
|
64
|
-
# Copying and Moving. It also works exactly the same
|
65
|
-
# way if You copy or move files and dirs to other storages.
|
66
|
-
readme.copy_to project['docs/readme.txt']
|
67
|
-
p project['docs/readme.txt'].exist? # => true
|
68
|
-
p readme.exist? # => true
|
69
|
-
|
70
|
-
readme.move_to project['docs/readme.txt']
|
71
|
-
p project['docs/readme.txt'].exist? # => true
|
72
|
-
p readme.exist? # => false
|
73
|
-
|
74
|
-
# Let's add empty Rakefile to our project.
|
75
|
-
project['Rakefile'].write
|
76
|
-
|
77
|
-
# Operations with directories - checking our project exists and not empty.
|
78
|
-
p project.exist? # => true
|
79
|
-
p project.empty? # => false
|
80
|
-
|
81
|
-
# Listing dir content. There are two versions of methods -
|
82
|
-
# without block the result will be Array of Entries, with block
|
83
|
-
# it will iterate over directory sequentially.
|
84
|
-
p project.entries # => [/.../docs, /.../Rakefile]
|
85
|
-
p project.files # => [/.../Rakefile]
|
86
|
-
p project.dirs # => [/.../docs]
|
87
|
-
project.entries do |entry| # => ["docs", false]
|
88
|
-
p [entry.name, entry.file?] # => ["Rakefile", true]
|
89
|
-
end
|
90
|
-
p project.include?('Rakefile') # => true
|
91
|
-
|
92
|
-
# You can also use glob (if storage support it).
|
93
|
-
if project.driver.local?
|
94
|
-
p project.entries('**/Rake*') # => [/.../Rakefile]
|
95
|
-
p project['**/Rake*'] # => [/.../Rakefile]
|
96
|
-
end
|
97
|
-
|
98
|
-
# The result of dir listing is just an array of Entries, so
|
99
|
-
# You can use it to do interesting things. For example this code will
|
100
|
-
# calculates the size of sources in our project.
|
101
|
-
if project.driver.local?
|
102
|
-
project['**/*.rb'].collect(&:size).reduce(0, :+)
|
103
|
-
end
|
104
|
-
|
105
|
-
# Copying and moving - let's create another project by cloning our hello_world.
|
106
|
-
project.copy_to sandbox['another_project']
|
107
|
-
p sandbox['another_project'].entries # => [/.../docs, .../Rakefile]
|
108
|
-
|
109
|
-
# Cleaning sandbox.
|
110
|
-
sandbox.destroy
|
111
|
-
```
|
112
|
-
|
113
|
-
## Integration with [Vos][vos] (Virtual Operating System)
|
114
|
-
|
115
|
-
Vfs can be used toghether with the Virtual Operating System Tool, and while the Vfs covers all the I/O operations the Vos provides support for remote command execution.
|
116
|
-
You can use this combination to fully control remote machines, for example - I'm using it to manage my production servers (setup, administration, deployment, migration, ...).
|
117
|
-
|
118
|
-
For more details please go to [Vos][vos] project page.
|
119
|
-
You can also take look at the actual configuration I'm using to control my servers [My Cluster][my_cluster] (in conjunction with small configuration tool [Cluster Management][cluster_management]).
|
120
|
-
|
121
|
-
# Why?
|
122
|
-
|
123
|
-
To easy my work: with local FS, remote FS, and some specific systems like Hadoop DFS.
|
124
|
-
|
125
|
-
Because the API of standard File/Dir/FileUtils classes are just terrible. And there's the reason for it - the goal of thouse tools is to provide 1-to-1 clone of underlying OS API, instead of provididing handy tool.
|
126
|
-
|
127
|
-
And if you want to use remote FS - things are getting even worse and more complicated (Net::SSH & Net::SFTP use a little
|
128
|
-
different API than local FS, and you has to remember all thouse little quirks).
|
129
|
-
|
130
|
-
## License
|
131
|
-
|
132
|
-
Copyright (c) Alexey Petrushin http://petrush.in, released under the MIT license.
|
133
|
-
|
134
|
-
[vos]: http://github.com/alexeypetrushin/vos
|
135
|
-
[cluster_management]: http://github.com/alexeypetrushin/cluster_management
|
136
|
-
[my_cluster]: http://github.com/alexeypetrushin/my_cluster
|
137
|
-
|
138
|
-
[basics]: http://alexeypetrushin.github.com/vfs/basics.html
|
139
|
-
[s3_basics]: http://alexeypetrushin.github.com/vfs/s3_basics.html
|
140
|
-
[s3_backup]: http://alexeypetrushin.github.com/vfs/s3_backup.html
|
141
|
-
[ssh_basics]: http://alexeypetrushin.github.com/vfs/ssh_basics.html
|
142
|
-
[ssh_deployment]: http://alexeypetrushin.github.com/vfs/ssh_deployment.html
|
143
|
-
[issues]: https://github.com/alexeypetrushin/vfs/issues
|
data/spec/container_spec.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe 'Container' do
|
4
|
-
with_test_dir
|
5
|
-
|
6
|
-
it "should threat paths as UniversalEntry except it ends with '/'" do
|
7
|
-
test_dir.should_receive(:entry).with('tmp/a/b')
|
8
|
-
test_dir['tmp/a/b']
|
9
|
-
|
10
|
-
test_dir.should_receive(:dir).with('tmp/a/b')
|
11
|
-
test_dir['tmp/a/b/']
|
12
|
-
end
|
13
|
-
|
14
|
-
it '/' do
|
15
|
-
test_dir[:some_path].should == test_dir / :some_path
|
16
|
-
test_dir[:some_path][:another_path].should == test_dir / :some_path / :another_path
|
17
|
-
end
|
18
|
-
|
19
|
-
it "UniversalEntry should be wrapped inside of proxy, Dir and File should not" do
|
20
|
-
-> {test_dir.dir.proxy?}.should raise_error(NoMethodError)
|
21
|
-
-> {test_dir.file.proxy?}.should raise_error(NoMethodError)
|
22
|
-
test_dir.entry.proxy?.should be_true
|
23
|
-
end
|
24
|
-
|
25
|
-
it "sometimes it also should inexplicitly guess that path is a Dir instead of UniversalEntry (but still wrap it inside of Proxy)" do
|
26
|
-
|
27
|
-
dir = test_dir['tmp/a/..']
|
28
|
-
dir.proxy?.should be_true
|
29
|
-
dir.should be_a(Vfs::Dir)
|
30
|
-
end
|
31
|
-
end
|
data/spec/dir_spec.rb
DELETED
@@ -1,253 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe 'Dir' do
|
4
|
-
with_test_dir
|
5
|
-
|
6
|
-
before do
|
7
|
-
@path = test_dir['a/b/c']
|
8
|
-
end
|
9
|
-
|
10
|
-
describe 'existence' do
|
11
|
-
it "should check only dirs" do
|
12
|
-
@path.should_not exist
|
13
|
-
@path.file.create
|
14
|
-
@path.should be_file
|
15
|
-
@path.dir.should_not exist
|
16
|
-
@path.dir.create
|
17
|
-
@path.should be_dir
|
18
|
-
@path.dir.should exist
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
it "should not respond to read and write methods" do
|
23
|
-
-> {@path.dir.read}.should raise_error(NoMethodError)
|
24
|
-
-> {@path.dir.write}.should raise_error(NoMethodError)
|
25
|
-
end
|
26
|
-
|
27
|
-
describe 'create' do
|
28
|
-
it 'should be chainable' do
|
29
|
-
@path.dir.create.should == @path
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'should create parent dirs if not exists' do
|
33
|
-
@path.parent.should_not exist
|
34
|
-
@path.dir.create
|
35
|
-
@path.should be_dir
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'should silently exit if dir already exist' do
|
39
|
-
@path.dir.create
|
40
|
-
@path.dir.create
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'should override existing file' do
|
44
|
-
@path.file.create
|
45
|
-
@path.should be_file
|
46
|
-
@path.dir.create
|
47
|
-
@path.should be_dir
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'should not override existing dir with content' do
|
51
|
-
dir = @path.dir
|
52
|
-
dir.create
|
53
|
-
file = dir.file :file
|
54
|
-
file.create
|
55
|
-
file.should exist
|
56
|
-
|
57
|
-
dir.create
|
58
|
-
file.should exist
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
describe 'destroying' do
|
63
|
-
it "should destroy a file" do
|
64
|
-
@path.file.create
|
65
|
-
@path.dir.destroy
|
66
|
-
@path.entry.should_not exist
|
67
|
-
end
|
68
|
-
|
69
|
-
it "shouldn't raise if dir not exist" do
|
70
|
-
@path.dir.destroy
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'should destroy recursivelly' do
|
74
|
-
dir = @path.dir
|
75
|
-
dir.create
|
76
|
-
dir.file(:file).write 'something'
|
77
|
-
dir.dir(:dir).create.tap do |dir|
|
78
|
-
dir.file(:file2).write 'something2'
|
79
|
-
end
|
80
|
-
|
81
|
-
dir.destroy
|
82
|
-
dir.should_not exist
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'should be chainable' do
|
86
|
-
@path.dir.destroy.should == @path
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
describe 'entries, files, dirs' do
|
91
|
-
before do
|
92
|
-
@path.dir('dir').create
|
93
|
-
@path.dir('dir/another_dir').create
|
94
|
-
@path.file('file').create
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'entries' do
|
98
|
-
-> {@path['non_existing'].entries}.should raise_error(Vfs::Error, /not exist/)
|
99
|
-
@path['non_existing'].entries(bang: false).should == []
|
100
|
-
@path.entries.to_set.should be_eql([@path.entry('dir'), @path.entry('file')].to_set)
|
101
|
-
list = []
|
102
|
-
@path.entries{|e| list << e}
|
103
|
-
list.to_set.should be_eql([@path.entry('dir'), @path.entry('file')].to_set)
|
104
|
-
end
|
105
|
-
|
106
|
-
it 'entries with type' do
|
107
|
-
@path.entries(type: true).to_set.should be_eql([@path.dir('dir'), @path.file('file')].to_set)
|
108
|
-
end
|
109
|
-
|
110
|
-
it "glob search support" do
|
111
|
-
@path.dir('dir_a').create
|
112
|
-
@path.file('file_a').create
|
113
|
-
@path.dir('dir_b').create
|
114
|
-
@path.entries('*_a').collect(&:name).sort.should == %w(dir_a file_a)
|
115
|
-
end
|
116
|
-
|
117
|
-
it 'should raise error if trying :entries on file' do
|
118
|
-
@path.file('some_file').create
|
119
|
-
-> {@path.dir('some_file').entries}.should raise_error(/File/)
|
120
|
-
end
|
121
|
-
|
122
|
-
it 'files' do
|
123
|
-
@path.files.should be_eql([@path.file('file')])
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'dirs' do
|
127
|
-
@path.dirs.should be_eql([@path.dir('dir')])
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'has? & include?' do
|
131
|
-
@path.include?('dir').should be_true
|
132
|
-
@path.include?('dir/another_dir').should be_true
|
133
|
-
@path.include?('file').should be_true
|
134
|
-
@path.include?('non_existing').should be_false
|
135
|
-
end
|
136
|
-
|
137
|
-
it 'empty?' do
|
138
|
-
@path.empty?.should be_false
|
139
|
-
@path.dir('empty_dir').create.empty?.should be_true
|
140
|
-
end
|
141
|
-
|
142
|
-
it "should threat ['**/*.rb'] as glob" do
|
143
|
-
@path['**/*nother*'].first.name.should == 'another_dir'
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
describe 'copying' do
|
148
|
-
before do
|
149
|
-
@from = @path.dir
|
150
|
-
@from.create
|
151
|
-
@from.file('file').write 'something'
|
152
|
-
@from.dir('dir').create.tap do |dir|
|
153
|
-
dir.file('file2').write 'something2'
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
it 'should not copy to itself' do
|
158
|
-
-> {@from.copy_to @from}.should raise_error(Vfs::Error, /itself/)
|
159
|
-
end
|
160
|
-
|
161
|
-
shared_examples_for 'copy_to behavior' do
|
162
|
-
it 'should copy to file and overwrite it' do
|
163
|
-
@from.copy_to @to.file
|
164
|
-
@to['file'].read.should == 'something'
|
165
|
-
end
|
166
|
-
|
167
|
-
it 'should override files' do
|
168
|
-
@from.copy_to @to
|
169
|
-
|
170
|
-
@from['dir/file2'].write 'another'
|
171
|
-
@from.copy_to @to
|
172
|
-
@to['dir/file2'].read.should == 'another'
|
173
|
-
end
|
174
|
-
|
175
|
-
it 'should copy to UniversalEntry (and overwrite)' do
|
176
|
-
@from.copy_to @to.entry
|
177
|
-
|
178
|
-
@from.copy_to @to.entry
|
179
|
-
@to['file'].read.should == 'something'
|
180
|
-
end
|
181
|
-
|
182
|
-
it "shouldn't delete existing content of directory" do
|
183
|
-
@to.dir.create
|
184
|
-
@to.file('existing_file').write 'existing_content'
|
185
|
-
@to.dir('existing_dir').create
|
186
|
-
@to.file('dir/existing_file2').write 'existing_content2'
|
187
|
-
|
188
|
-
@from.copy_to @to
|
189
|
-
# copied files
|
190
|
-
@to['file'].read.should == 'something'
|
191
|
-
@to['dir/file2'].read.should == 'something2'
|
192
|
-
# shouldn't delete already existing files
|
193
|
-
@to.file('existing_file').read.should == 'existing_content'
|
194
|
-
@to.dir('existing_dir').should exist
|
195
|
-
@to.file('dir/existing_file2').read.should == 'existing_content2'
|
196
|
-
end
|
197
|
-
|
198
|
-
it 'should be chainable' do
|
199
|
-
@from.copy_to(@to).should == @to
|
200
|
-
end
|
201
|
-
|
202
|
-
it "should override without deleting other files" do
|
203
|
-
@from.copy_to(@to).should == @to
|
204
|
-
@to.file('other_file').write 'other'
|
205
|
-
|
206
|
-
@from.copy_to(@to).should == @to
|
207
|
-
@to.file('other_file').read.should == 'other'
|
208
|
-
end
|
209
|
-
|
210
|
-
it "should raise error if try to copy file as dir" do
|
211
|
-
dir = @from.dir 'file'
|
212
|
-
dir.file?.should be_true
|
213
|
-
-> {dir.copy_to @to}.should raise_error(Vfs::Error)
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
describe 'general copy' do
|
218
|
-
it_should_behave_like 'copy_to behavior'
|
219
|
-
|
220
|
-
before do
|
221
|
-
# prevenging usage of :efficient_dir_copy
|
222
|
-
# Vfs::Dir.dont_use_efficient_dir_copy = true
|
223
|
-
|
224
|
-
@to = test_dir['to']
|
225
|
-
end
|
226
|
-
# after do
|
227
|
-
# Vfs::Dir.dont_use_efficient_dir_copy = false
|
228
|
-
# end
|
229
|
-
end
|
230
|
-
|
231
|
-
# describe 'effective copy' do
|
232
|
-
# it_should_behave_like 'copy_to behavior'
|
233
|
-
#
|
234
|
-
# before do
|
235
|
-
# @to = test_dir['to']
|
236
|
-
# end
|
237
|
-
# end
|
238
|
-
end
|
239
|
-
|
240
|
-
describe 'moving' do
|
241
|
-
it 'move_to' do
|
242
|
-
from, to = @path.file('from'), @path.file('to')
|
243
|
-
from.should_receive(:copy_to).with(to)
|
244
|
-
from.should_receive(:destroy).with()
|
245
|
-
from.move_to to
|
246
|
-
end
|
247
|
-
|
248
|
-
it 'should be chainable' do
|
249
|
-
from, to = @path.dir('from').create, @path.dir('to')
|
250
|
-
from.move_to(to).should == to
|
251
|
-
end
|
252
|
-
end
|
253
|
-
end
|
data/spec/entry_spec.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe 'Entry' do
|
4
|
-
with_test_dir
|
5
|
-
|
6
|
-
before do
|
7
|
-
@path = test_dir['a/b/c']
|
8
|
-
end
|
9
|
-
|
10
|
-
it "name" do
|
11
|
-
@path.name.should == 'c'
|
12
|
-
end
|
13
|
-
|
14
|
-
it "string integration" do
|
15
|
-
'/'.to_entry.path.should == '/'
|
16
|
-
'a'.to_entry.path.should == "./a"
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'tmp' do
|
20
|
-
tmp = test_dir.tmp
|
21
|
-
tmp.should be_dir
|
22
|
-
tmp.destroy
|
23
|
-
|
24
|
-
tmp = nil
|
25
|
-
test_dir.tmp do |path|
|
26
|
-
tmp = path
|
27
|
-
tmp.should be_dir
|
28
|
-
end
|
29
|
-
tmp.should_not exist
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'should respond to local?' do
|
33
|
-
test_dir.should respond_to(:local?)
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'created_at, updated_at, size' do
|
37
|
-
file = test_dir.file('file').write 'data'
|
38
|
-
file.created_at.class.should == Time
|
39
|
-
file.updated_at.class.should == Time
|
40
|
-
file.size.should == 4
|
41
|
-
end
|
42
|
-
end
|