io 0.0.1

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.
@@ -0,0 +1,38 @@
1
+ module Vfs
2
+ class << self
3
+ def default_driver
4
+ ::Vfs::Drivers::Local.new
5
+ end
6
+
7
+ def to_entry
8
+ '/'.to_entry
9
+ end
10
+
11
+ def to_file
12
+ to_entry.file
13
+ end
14
+
15
+ def to_dir
16
+ to_entry.dir
17
+ end
18
+
19
+ # def [] path
20
+ # to_entry[path]
21
+ # end
22
+ # alias_method :/, :[]
23
+
24
+ %w(
25
+ entry dir file
26
+ entries dirs files
27
+ [] /
28
+ tmp
29
+ ).each do |m|
30
+ script = <<-RUBY
31
+ def #{m} *a, &b
32
+ to_entry.#{m} *a, &b
33
+ end
34
+ RUBY
35
+ eval script, binding, __FILE__, __LINE__
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,119 @@
1
+ # Vfs - Virtual File System
2
+
3
+ Handy and simple abstraction over any storage that can represent concept of File and Directory (or at least part of it).
4
+ The Vfs for File System is kinda the same as ActiveRecord is for Relational Databases.
5
+
6
+ Currently, there are following implementations available:
7
+
8
+ - local file system
9
+ - remote file system (over ssh)
10
+
11
+ ## Goals
12
+
13
+ - **handy, simple and clean** API.
14
+ - same API for different storages (Local FS, SSH, Hadoop, or any other , ...).
15
+ - should work **simultaneously with different storages**.
16
+ - small codebase, easy to extend by others.
17
+ - simple storage-driver implementation, easy add new storage types (Hadoop DFS, LDAP, Document Oriented DB, In-Memory, ...).
18
+
19
+ **Performance**:
20
+
21
+ - sometimes there's extra call to check if file or dir exist before overriding it
22
+ - copy: right now it doesn't use FileUtils.cp_r, it walks on the directory tree and copy each entry individually, so it's probably a little slover.
23
+ - right now :move and :rename implemented ASAP by copy & destroy, will be fixed as soon as I'll have time to do it.
24
+
25
+ ## Installation
26
+
27
+ ``` bash
28
+ $ gem install vfs
29
+ $ gem install vos
30
+ ```
31
+
32
+ ## Code samples:
33
+
34
+ ``` ruby
35
+ require 'vfs' # Virtual File System
36
+ require 'vos' # Virtual Operating System
37
+
38
+ # Connections, let's deploy our 'cool_app' project from our local box to remote server
39
+
40
+ server = Box.new('cool_app.com') # it will use id_rsa, or You can add {user: 'me', password: 'secret'}
41
+ me = '~'.to_dir # handy shortcut for local FS
42
+
43
+ deploy_dir = server['apps/cool_app']
44
+ projects = me['projects']
45
+
46
+ # Working with dirs, copying dir from any source to any destination (local/remote/custom_storage_type)
47
+
48
+ projects['cool_app'].copy_to deploy_dir
49
+
50
+ # Working with files
51
+
52
+ dbc = deploy_dir.file('config/database.yml') # <= the 'config' dir not exist yet
53
+ dbc.write("user: root\npassword: secret") # <= now the 'database.yml' and parent 'config' has been created
54
+ dbc.read =~ /database/ # => false, we forgot to add the database
55
+ dbc.append("\ndatabase: mysql") # let's do it
56
+
57
+ dbc.update do |content| # and add host info
58
+ content + "\nhost: cool_app.com "
59
+ end
60
+
61
+ projects['cool_app/config/database.yml']. # or just overwrite it with our local dev version
62
+ copy_to! dbc
63
+ ```
64
+
65
+ There are also streaming support (read/write/append) with &block, please go to specs for details
66
+
67
+ # Checks
68
+
69
+ ``` ruby
70
+ deploy_dir['config'].exist? # => true
71
+ deploy_dir.dir('config').exist? # => true
72
+ deploy_dir.file('config').exist? # => false
73
+
74
+ deploy_dir['config'].dir? # => true
75
+ deploy_dir['config'].file? # => false
76
+ ```
77
+
78
+ # Navigation
79
+
80
+ ``` ruby
81
+ config = deploy_dir['config']
82
+ config.parent # => </apps/cool_app>
83
+ config['../..'] # => </>
84
+ config['../..'].dir? # => true
85
+
86
+ deploy_dir.entries # => list of dirs and files, also support &block
87
+ deploy_dir.files # => list of files, also support &block
88
+ deploy_dir.dirs # => list of dirs, also support &block
89
+ ```
90
+
91
+ For more please go to specs (create/update/move/copy/destroy/...)
92
+
93
+ ## Integration with [Vos][vos] (Virtual Operating System)
94
+
95
+ ```ruby
96
+ server['apps/cool_app'].bash 'rails production'
97
+ ```
98
+
99
+ For more details please go to [Vos][vos] project page.
100
+ Or checkout configuration I use to control my production servers [My Cluster][my_cluster] in conjunction with small
101
+ configuration tool [Cluster Management][cluster_management].
102
+
103
+ # Why?
104
+
105
+ To easy my work: with local FS, remote FS (cluster management, deployment automation), and some specific systems like Hadoop DFS.
106
+
107
+ Because the API of standard File/Dir/FileUtils classes are just terrible. And there's the reason for it - the goal of thouse tools
108
+ is to provide 1-to-1 clone of underlying OS API, instead of provididing handy tool.
109
+
110
+ And if you want to use remote FS - things are getting even worse and more complicated (Net::SSH & Net::SFTP use a little
111
+ different API than local FS, and you has to remember all thouse little quirks).
112
+
113
+ ## License
114
+
115
+ Copyright (c) Alexey Petrushin http://petrush.in, released under the MIT license.
116
+
117
+ [vos]: http://github.com/alexeypetrushin/vos
118
+ [cluster_management]: http://github.com/alexeypetrushin/cluster_management
119
+ [my_cluster]: http://github.com/alexeypetrushin/my_cluster
@@ -0,0 +1,31 @@
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
@@ -0,0 +1,249 @@
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
+ end
142
+
143
+ describe 'copying' do
144
+ before do
145
+ @from = @path.dir
146
+ @from.create
147
+ @from.file('file').write 'something'
148
+ @from.dir('dir').create.tap do |dir|
149
+ dir.file('file2').write 'something2'
150
+ end
151
+ end
152
+
153
+ it 'should not copy to itself' do
154
+ -> {@from.copy_to @from}.should raise_error(Vfs::Error, /itself/)
155
+ end
156
+
157
+ shared_examples_for 'copy_to behavior' do
158
+ it 'should copy to file and overwrite it' do
159
+ @from.copy_to @to.file
160
+ @to['file'].read.should == 'something'
161
+ end
162
+
163
+ it 'should override files' do
164
+ @from.copy_to @to
165
+
166
+ @from['dir/file2'].write 'another'
167
+ @from.copy_to @to
168
+ @to['dir/file2'].read.should == 'another'
169
+ end
170
+
171
+ it 'should copy to UniversalEntry (and overwrite)' do
172
+ @from.copy_to @to.entry
173
+
174
+ @from.copy_to @to.entry
175
+ @to['file'].read.should == 'something'
176
+ end
177
+
178
+ it "shouldn't delete existing content of directory" do
179
+ @to.dir.create
180
+ @to.file('existing_file').write 'existing_content'
181
+ @to.dir('existing_dir').create
182
+ @to.file('dir/existing_file2').write 'existing_content2'
183
+
184
+ @from.copy_to @to
185
+ # copied files
186
+ @to['file'].read.should == 'something'
187
+ @to['dir/file2'].read.should == 'something2'
188
+ # shouldn't delete already existing files
189
+ @to.file('existing_file').read.should == 'existing_content'
190
+ @to.dir('existing_dir').should exist
191
+ @to.file('dir/existing_file2').read.should == 'existing_content2'
192
+ end
193
+
194
+ it 'should be chainable' do
195
+ @from.copy_to(@to).should == @to
196
+ end
197
+
198
+ it "should override without deleting other files" do
199
+ @from.copy_to(@to).should == @to
200
+ @to.file('other_file').write 'other'
201
+
202
+ @from.copy_to(@to).should == @to
203
+ @to.file('other_file').read.should == 'other'
204
+ end
205
+
206
+ it "should raise error if try to copy file as dir" do
207
+ dir = @from.dir 'file'
208
+ dir.file?.should be_true
209
+ -> {dir.copy_to @to}.should raise_error(Vfs::Error)
210
+ end
211
+ end
212
+
213
+ describe 'general copy' do
214
+ it_should_behave_like 'copy_to behavior'
215
+
216
+ before do
217
+ # prevenging usage of :efficient_dir_copy
218
+ # Vfs::Dir.dont_use_efficient_dir_copy = true
219
+
220
+ @to = test_dir['to']
221
+ end
222
+ # after do
223
+ # Vfs::Dir.dont_use_efficient_dir_copy = false
224
+ # end
225
+ end
226
+
227
+ # describe 'effective copy' do
228
+ # it_should_behave_like 'copy_to behavior'
229
+ #
230
+ # before do
231
+ # @to = test_dir['to']
232
+ # end
233
+ # end
234
+ end
235
+
236
+ describe 'moving' do
237
+ it 'move_to' do
238
+ from, to = @path.file('from'), @path.file('to')
239
+ from.should_receive(:copy_to).with(to)
240
+ from.should_receive(:destroy).with()
241
+ from.move_to to
242
+ end
243
+
244
+ it 'should be chainable' do
245
+ from, to = @path.dir('from').create, @path.dir('to')
246
+ from.move_to(to).should == to
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,42 @@
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