vfs 0.0.4 → 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/Rakefile +2 -2
- data/lib/vfs.rb +18 -0
- data/lib/vfs/entries/dir.rb +272 -0
- data/lib/vfs/entries/entry.rb +127 -0
- data/lib/vfs/entries/file.rb +185 -0
- data/lib/vfs/entries/universal_entry.rb +24 -0
- data/lib/vfs/entry_proxy.rb +38 -0
- data/lib/vfs/error.rb +4 -0
- data/lib/vfs/integration/string.rb +19 -0
- data/lib/vfs/path.rb +125 -0
- data/lib/vfs/storages/hash_fs.rb +194 -0
- data/lib/vfs/storages/local.rb +138 -0
- data/lib/vfs/storages/specification.rb +127 -0
- data/lib/vfs/support.rb +2 -0
- data/readme.md +110 -14
- data/spec/container_spec.rb +31 -0
- data/spec/dir_spec.rb +224 -0
- data/spec/entry_spec.rb +24 -0
- data/spec/file_spec.rb +189 -0
- data/spec/path_spec.rb +121 -0
- data/spec/spec_helper.rb +2 -9
- data/spec/storages/hash_fs_spec.rb +10 -0
- data/spec/storages/local_spec.rb +10 -0
- data/spec/universal_entry_spec.rb +42 -0
- metadata +25 -23
- data/lib/old/ssh.rb +0 -11
- data/lib/rsh.rb +0 -19
- data/lib/rsh/box.rb +0 -182
- data/lib/rsh/box/marks.rb +0 -29
- data/lib/rsh/drivers/abstract.rb +0 -15
- data/lib/rsh/drivers/local.rb +0 -48
- data/lib/rsh/drivers/ssh.rb +0 -147
- data/lib/rsh/gems.rb +0 -2
- data/lib/rsh/support.rb +0 -30
- data/spec/abstract_driver.rb +0 -82
- data/spec/abstract_driver/dir/dir2/file +0 -0
- data/spec/abstract_driver/local_file +0 -1
- data/spec/box_spec.rb +0 -109
- data/spec/box_spec/dir/dir2/file +0 -0
- data/spec/box_spec/local_file +0 -1
- data/spec/config.example.yml +0 -4
- data/spec/config.yml +0 -5
- data/spec/local_driver_spec.rb +0 -9
- data/spec/ssh_driver_spec.rb +0 -15
data/Rakefile
CHANGED
@@ -2,8 +2,8 @@ require 'rake_ext'
|
|
2
2
|
|
3
3
|
project(
|
4
4
|
name: "vfs",
|
5
|
-
version: "0.0
|
6
|
-
summary: "
|
5
|
+
version: "0.1.0",
|
6
|
+
summary: "Virtual File System",
|
7
7
|
|
8
8
|
author: "Alexey Petrushin",
|
9
9
|
homepage: "http://github.com/alexeypetrushin/vfs"
|
data/lib/vfs.rb
ADDED
@@ -0,0 +1,272 @@
|
|
1
|
+
module Vfs
|
2
|
+
class Dir < Entry
|
3
|
+
#
|
4
|
+
# Container
|
5
|
+
#
|
6
|
+
def [] path
|
7
|
+
path = path.to_s
|
8
|
+
if path =~ /.+[\/]$/
|
9
|
+
path = path.sub /\/$/, ''
|
10
|
+
dir path
|
11
|
+
else
|
12
|
+
entry path
|
13
|
+
end
|
14
|
+
end
|
15
|
+
alias_method :/, :[]
|
16
|
+
|
17
|
+
|
18
|
+
#
|
19
|
+
# Attributes
|
20
|
+
#
|
21
|
+
alias_method :exist?, :dir?
|
22
|
+
|
23
|
+
|
24
|
+
#
|
25
|
+
# CRUD
|
26
|
+
#
|
27
|
+
def create options = {}
|
28
|
+
storage.open_fs do |fs|
|
29
|
+
try = 0
|
30
|
+
begin
|
31
|
+
try += 1
|
32
|
+
fs.create_dir path
|
33
|
+
rescue StandardError => error
|
34
|
+
entry = self.entry
|
35
|
+
attrs = entry.get
|
36
|
+
if attrs[:file] #entry.exist?
|
37
|
+
if options[:override]
|
38
|
+
entry.destroy
|
39
|
+
else
|
40
|
+
raise Error, "entry #{self} already exist!"
|
41
|
+
end
|
42
|
+
elsif attrs[:dir]
|
43
|
+
# do nothing
|
44
|
+
else
|
45
|
+
parent = self.parent
|
46
|
+
if parent.exist?
|
47
|
+
# some unknown error
|
48
|
+
raise error
|
49
|
+
else
|
50
|
+
parent.create(options)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
retry if try < 2
|
55
|
+
end
|
56
|
+
end
|
57
|
+
self
|
58
|
+
end
|
59
|
+
def create! options = {}
|
60
|
+
options[:override] = true
|
61
|
+
create options
|
62
|
+
end
|
63
|
+
|
64
|
+
def destroy options = {}
|
65
|
+
storage.open_fs do |fs|
|
66
|
+
begin
|
67
|
+
fs.delete_dir path
|
68
|
+
rescue StandardError => e
|
69
|
+
attrs = get
|
70
|
+
if attrs[:file]
|
71
|
+
if options[:force]
|
72
|
+
file.destroy
|
73
|
+
else
|
74
|
+
raise Error, "can't destroy File #{dir} (You are trying to destroy it as if it's a Dir)"
|
75
|
+
end
|
76
|
+
elsif attrs[:dir]
|
77
|
+
# unknown internal error
|
78
|
+
raise e
|
79
|
+
else
|
80
|
+
# do nothing, file already not exist
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
self
|
85
|
+
end
|
86
|
+
def destroy! options = {}
|
87
|
+
options[:force] = true
|
88
|
+
destroy options
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
#
|
93
|
+
# Content
|
94
|
+
#
|
95
|
+
def entries options = {}, &block
|
96
|
+
options[:bang] = true unless options.include? :bang
|
97
|
+
storage.open_fs do |fs|
|
98
|
+
begin
|
99
|
+
list = []
|
100
|
+
fs.each path do |name, type|
|
101
|
+
next if options[:filter] and options[:filter] != type
|
102
|
+
entry = if type == :dir
|
103
|
+
dir(name)
|
104
|
+
elsif type == :file
|
105
|
+
file(name)
|
106
|
+
else
|
107
|
+
raise 'invalid entry type!'
|
108
|
+
end
|
109
|
+
block ? block.call(entry) : (list << entry)
|
110
|
+
end
|
111
|
+
block ? nil : list
|
112
|
+
rescue StandardError => error
|
113
|
+
attrs = get
|
114
|
+
if attrs[:file]
|
115
|
+
raise Error, "can't query entries on File ('#{self}')!"
|
116
|
+
elsif attrs[:dir]
|
117
|
+
# unknown error
|
118
|
+
raise error
|
119
|
+
else
|
120
|
+
raise Error, "'#{self}' not exist!" if options[:bang]
|
121
|
+
[]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
alias_method :each, :entries
|
127
|
+
|
128
|
+
def files options = {}, &block
|
129
|
+
options[:filter] = :file
|
130
|
+
entries options, &block
|
131
|
+
end
|
132
|
+
|
133
|
+
def dirs options = {}, &block
|
134
|
+
options[:filter] = :dir
|
135
|
+
entries options, &block
|
136
|
+
end
|
137
|
+
|
138
|
+
def include? name
|
139
|
+
entry[name].exist?
|
140
|
+
end
|
141
|
+
alias_method :has?, :include?
|
142
|
+
|
143
|
+
|
144
|
+
#
|
145
|
+
# Transfers
|
146
|
+
#
|
147
|
+
def copy_to to, options = {}
|
148
|
+
options[:bang] = true unless options.include? :bang
|
149
|
+
|
150
|
+
raise Error, 'invalid argument' unless to.is_a? Entry
|
151
|
+
raise Error, "you can't copy to itself" if self == to
|
152
|
+
|
153
|
+
target = if to.is_a? File
|
154
|
+
raise Error, "can't copy Dir to File ('#{self}')!" unless options[:override]
|
155
|
+
to.dir
|
156
|
+
elsif to.is_a? Dir
|
157
|
+
to.dir #(name)
|
158
|
+
elsif to.is_a? UniversalEntry
|
159
|
+
# raise "can't copy Dir to File ('#{self}')!" if to.file? and !options[:override]
|
160
|
+
to.dir #.create
|
161
|
+
else
|
162
|
+
raise "can't copy to unknown Entry!"
|
163
|
+
end
|
164
|
+
|
165
|
+
storage.open_fs do |fs|
|
166
|
+
try = 0
|
167
|
+
begin
|
168
|
+
try += 1
|
169
|
+
self.class.efficient_dir_copy(self, target) || self.class.unefficient_dir_copy(self, target)
|
170
|
+
rescue StandardError => error
|
171
|
+
unknown_errors = 0
|
172
|
+
|
173
|
+
attrs = get
|
174
|
+
if attrs[:file]
|
175
|
+
raise Error, "can't copy File as a Dir ('#{self}')!"
|
176
|
+
elsif attrs[:dir]
|
177
|
+
# some unknown error (but it also maybe caused by to be fixed error in 'target')
|
178
|
+
unknown_errors += 1
|
179
|
+
else
|
180
|
+
raise Error, "'#{self}' not exist!" if options[:bang]
|
181
|
+
return target
|
182
|
+
end
|
183
|
+
|
184
|
+
attrs = target.get
|
185
|
+
if attrs[:file]
|
186
|
+
if options[:override]
|
187
|
+
to.destroy
|
188
|
+
else
|
189
|
+
raise Vfs::Error, "entry #{target} already exist!"
|
190
|
+
end
|
191
|
+
elsif attrs[:dir]
|
192
|
+
if options[:override]
|
193
|
+
to.destroy
|
194
|
+
else
|
195
|
+
raise Vfs::Error, "entry #{target} already exist!"
|
196
|
+
end
|
197
|
+
else
|
198
|
+
parent = to.parent
|
199
|
+
if parent.exist?
|
200
|
+
# some unknown error (but it also maybe caused by already fixed error in 'from')
|
201
|
+
unknown_errors += 1
|
202
|
+
else
|
203
|
+
parent.create(options)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
raise error if unknown_errors > 1
|
208
|
+
|
209
|
+
retry if try < 2
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
target
|
214
|
+
end
|
215
|
+
def copy_to! to, options = {}
|
216
|
+
options[:override] = true
|
217
|
+
copy_to to, options
|
218
|
+
end
|
219
|
+
|
220
|
+
def move_to to, options = {}
|
221
|
+
copy_to to, options
|
222
|
+
destroy options
|
223
|
+
to
|
224
|
+
end
|
225
|
+
def move_to! to, options = {}
|
226
|
+
options[:override] = true
|
227
|
+
move_to to, options
|
228
|
+
end
|
229
|
+
|
230
|
+
protected
|
231
|
+
def self.unefficient_dir_copy from, to
|
232
|
+
from.storage.open_fs do |from_fs|
|
233
|
+
to.storage.open_fs do |to_fs|
|
234
|
+
to_fs.create_dir to.path
|
235
|
+
from_fs.each from.path do |name, type|
|
236
|
+
if type == :dir
|
237
|
+
unefficient_dir_copy from.dir(name), to.dir(name)
|
238
|
+
elsif type == :file
|
239
|
+
to_fs.write_file to.file(name).path, false do |writer|
|
240
|
+
from_fs.read_file from.file(name).path do |buff|
|
241
|
+
writer.call buff
|
242
|
+
end
|
243
|
+
end
|
244
|
+
else
|
245
|
+
raise 'invalid entry type!'
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# target.create options
|
252
|
+
# entries do |e|
|
253
|
+
# if e.is_a? Dir
|
254
|
+
# e.copy_to target.dir(e.name), options
|
255
|
+
# elsif e.is_a? File
|
256
|
+
# e.copy_to target.file(e.name), options
|
257
|
+
# else
|
258
|
+
# raise 'internal error'
|
259
|
+
# end
|
260
|
+
# end
|
261
|
+
end
|
262
|
+
|
263
|
+
def self.efficient_dir_copy from, to
|
264
|
+
from.storage.open_fs{|fs|
|
265
|
+
fs.respond_to?(:efficient_dir_copy) and fs.efficient_dir_copy(from, to)
|
266
|
+
} or
|
267
|
+
to.storage.open_fs{|fs|
|
268
|
+
fs.respond_to?(:efficient_dir_copy) and fs.efficient_dir_copy(from, to)
|
269
|
+
}
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Vfs
|
2
|
+
class Entry
|
3
|
+
attr_reader :storage, :path, :path_cache
|
4
|
+
|
5
|
+
def initialize *args
|
6
|
+
if args.size == 1 and args.first.is_a? Entry
|
7
|
+
entry = args.first
|
8
|
+
@path_cache = entry.path_cache
|
9
|
+
@storage, @path = entry.storage, entry.path
|
10
|
+
else
|
11
|
+
storage, path = *args
|
12
|
+
@path_cache = Path.new path
|
13
|
+
@storage, @path = storage, path_cache.to_s
|
14
|
+
end
|
15
|
+
raise "storage not defined!" unless self.storage
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
#
|
20
|
+
# Navigation
|
21
|
+
#
|
22
|
+
def parent
|
23
|
+
Dir.new(storage, path_cache + '..')
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
#
|
28
|
+
# Transformations
|
29
|
+
#
|
30
|
+
def dir path = nil
|
31
|
+
if path
|
32
|
+
new_path = path_cache + path
|
33
|
+
Dir.new storage, new_path
|
34
|
+
else
|
35
|
+
Dir.new self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
alias_method :to_dir, :dir
|
39
|
+
|
40
|
+
def file path = nil
|
41
|
+
if path
|
42
|
+
new_path = path_cache + path
|
43
|
+
File.new storage, new_path
|
44
|
+
else
|
45
|
+
File.new self
|
46
|
+
end
|
47
|
+
end
|
48
|
+
alias_method :to_file, :file
|
49
|
+
|
50
|
+
def entry path = nil
|
51
|
+
entry = if path
|
52
|
+
|
53
|
+
new_path = path_cache + path
|
54
|
+
klass = new_path.probably_dir? ? Dir : UniversalEntry
|
55
|
+
entry = klass.new storage, new_path
|
56
|
+
else
|
57
|
+
UniversalEntry.new self
|
58
|
+
end
|
59
|
+
EntryProxy.new entry
|
60
|
+
end
|
61
|
+
alias_method :to_entry, :entry
|
62
|
+
|
63
|
+
|
64
|
+
#
|
65
|
+
# Attributes
|
66
|
+
#
|
67
|
+
def get attr_name = nil
|
68
|
+
attrs = storage.open_fs{|fs| fs.attributes(path)}
|
69
|
+
attr_name ? attrs[attr_name] : attrs
|
70
|
+
end
|
71
|
+
|
72
|
+
def set options
|
73
|
+
not_implemented
|
74
|
+
end
|
75
|
+
|
76
|
+
def dir?
|
77
|
+
!!get(:dir)
|
78
|
+
end
|
79
|
+
|
80
|
+
def file?
|
81
|
+
!!get(:file)
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
#
|
86
|
+
# Micelaneous
|
87
|
+
#
|
88
|
+
def name
|
89
|
+
path_cache.name
|
90
|
+
end
|
91
|
+
|
92
|
+
def tmp &block
|
93
|
+
storage.open_fs do |fs|
|
94
|
+
if block
|
95
|
+
fs.tmp do |path|
|
96
|
+
block.call Dir.new(storage, path)
|
97
|
+
end
|
98
|
+
else
|
99
|
+
Dir.new storage, fs.tmp
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
#
|
106
|
+
# Utils
|
107
|
+
#
|
108
|
+
def inspect
|
109
|
+
"#{storage}#{':' unless storage.to_s.empty?}#{path}"
|
110
|
+
end
|
111
|
+
alias_method :to_s, :inspect
|
112
|
+
|
113
|
+
def == other
|
114
|
+
return false unless other.is_a? Entry
|
115
|
+
storage == other.storage and path == other.path
|
116
|
+
end
|
117
|
+
|
118
|
+
def hash
|
119
|
+
storage.hash + path.hash
|
120
|
+
end
|
121
|
+
|
122
|
+
def eql? other
|
123
|
+
return false unless other.class == self.class
|
124
|
+
storage.eql?(other.storage) and path.eql?(other.path)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
module Vfs
|
2
|
+
class File < Entry
|
3
|
+
#
|
4
|
+
# Attributes
|
5
|
+
#
|
6
|
+
alias_method :exist?, :file?
|
7
|
+
|
8
|
+
|
9
|
+
#
|
10
|
+
# CRUD
|
11
|
+
#
|
12
|
+
def read options = {}, &block
|
13
|
+
options[:bang] = true unless options.include? :bang
|
14
|
+
storage.open_fs do |fs|
|
15
|
+
begin
|
16
|
+
if block
|
17
|
+
fs.read_file path, &block
|
18
|
+
else
|
19
|
+
data = ""
|
20
|
+
fs.read_file(path){|buff| data << buff}
|
21
|
+
data
|
22
|
+
end
|
23
|
+
rescue StandardError => e
|
24
|
+
raise Vrs::Error, "can't read Dir #{self}!" if dir.exist?
|
25
|
+
attrs = get
|
26
|
+
if attrs[:file]
|
27
|
+
# unknown internal error
|
28
|
+
raise e
|
29
|
+
elsif attrs[:dir]
|
30
|
+
raise Error, "You are trying to read Dir '#{self}' as if it's a File!"
|
31
|
+
else
|
32
|
+
if options[:bang]
|
33
|
+
raise Error, "file #{self} not exist!"
|
34
|
+
else
|
35
|
+
block ? block.call('') : ''
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def content options = {}
|
43
|
+
read options
|
44
|
+
end
|
45
|
+
|
46
|
+
def create options = {}
|
47
|
+
write '', options
|
48
|
+
self
|
49
|
+
end
|
50
|
+
def create! options = {}
|
51
|
+
options[:override] = true
|
52
|
+
create options
|
53
|
+
end
|
54
|
+
|
55
|
+
def write *args, &block
|
56
|
+
storage.open_fs do |fs|
|
57
|
+
try = 0
|
58
|
+
begin
|
59
|
+
try += 1
|
60
|
+
if block
|
61
|
+
options = args.first || {}
|
62
|
+
else
|
63
|
+
data, options = *args
|
64
|
+
options ||= {}
|
65
|
+
end
|
66
|
+
raise "can't do :override and :append at the same time!" if options[:override] and options[:append]
|
67
|
+
if block
|
68
|
+
fs.write_file(path, options[:append], &block)
|
69
|
+
else
|
70
|
+
fs.write_file(path, options[:append]){|writer| writer.call data}
|
71
|
+
end
|
72
|
+
rescue StandardError => error
|
73
|
+
entry = self.entry
|
74
|
+
if entry.exist?
|
75
|
+
if options[:override]
|
76
|
+
entry.destroy
|
77
|
+
else
|
78
|
+
raise Error, "entry #{self} already exist!"
|
79
|
+
end
|
80
|
+
else
|
81
|
+
parent = self.parent
|
82
|
+
if parent.exist?
|
83
|
+
# some unknown error
|
84
|
+
raise error
|
85
|
+
else
|
86
|
+
parent.create(options)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
retry if try < 2
|
91
|
+
end
|
92
|
+
end
|
93
|
+
self
|
94
|
+
end
|
95
|
+
def write! *args, &block
|
96
|
+
args << {} unless args.last.is_a? Hash
|
97
|
+
args.last[:override] = true
|
98
|
+
write *args, &block
|
99
|
+
end
|
100
|
+
|
101
|
+
def destroy options = {}
|
102
|
+
storage.open_fs do |fs|
|
103
|
+
begin
|
104
|
+
fs.delete_file path
|
105
|
+
self
|
106
|
+
rescue StandardError => e
|
107
|
+
attrs = get
|
108
|
+
if attrs[:dir]
|
109
|
+
if options[:force]
|
110
|
+
dir.destroy
|
111
|
+
else
|
112
|
+
raise Error, "can't destroy Dir #{dir} (you are trying to destroy it as if it's a File)"
|
113
|
+
end
|
114
|
+
elsif attrs[:file]
|
115
|
+
# unknown internal error
|
116
|
+
raise e
|
117
|
+
else
|
118
|
+
# do nothing, file already not exist
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
self
|
123
|
+
end
|
124
|
+
def destroy! options = {}
|
125
|
+
options[:force] = true
|
126
|
+
destroy options
|
127
|
+
end
|
128
|
+
|
129
|
+
def append *args, &block
|
130
|
+
if block
|
131
|
+
options = args.first || {}
|
132
|
+
else
|
133
|
+
data, options = *args
|
134
|
+
options ||= {}
|
135
|
+
end
|
136
|
+
|
137
|
+
options[:append] = true
|
138
|
+
write data, options, &block
|
139
|
+
end
|
140
|
+
|
141
|
+
def update options = {}, &block
|
142
|
+
options[:override] = true
|
143
|
+
data = read options
|
144
|
+
write block.call(data), options
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
#
|
149
|
+
# Transfers
|
150
|
+
#
|
151
|
+
def copy_to to, options = {}
|
152
|
+
raise Error, "you can't copy to itself" if self == to
|
153
|
+
|
154
|
+
target = if to.is_a? File
|
155
|
+
to
|
156
|
+
elsif to.is_a? Dir
|
157
|
+
to.file #(name)
|
158
|
+
elsif to.is_a? UniversalEntry
|
159
|
+
to.file
|
160
|
+
else
|
161
|
+
raise "can't copy to unknown Entry!"
|
162
|
+
end
|
163
|
+
|
164
|
+
target.write options do |writer|
|
165
|
+
read(options){|buff| writer.call buff}
|
166
|
+
end
|
167
|
+
|
168
|
+
target
|
169
|
+
end
|
170
|
+
def copy_to! to, options = {}
|
171
|
+
options[:override] = true
|
172
|
+
copy_to to, options
|
173
|
+
end
|
174
|
+
|
175
|
+
def move_to to, options = {}
|
176
|
+
copy_to to, options
|
177
|
+
destroy options
|
178
|
+
to
|
179
|
+
end
|
180
|
+
def move_to! to, options = {}
|
181
|
+
options[:override] = true
|
182
|
+
move_to to, options
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|