vfs-momolog 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.
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/Rakefile +2 -0
- data/docs/basics.rb +116 -0
- data/docs/s3_backup.rb +29 -0
- data/docs/s3_backup/app/files/bos.png +0 -0
- data/docs/s3_basics.rb +19 -0
- data/docs/s3_sandbox.rb +24 -0
- data/docs/site/404.html +8 -0
- data/docs/site/basics.html +305 -0
- data/docs/site/index.html +8 -0
- data/docs/site/s3_backup.html +111 -0
- data/docs/site/s3_basics.html +84 -0
- data/docs/site/s3_sandbox.html +99 -0
- data/docs/site/ssh_basics.html +84 -0
- data/docs/site/ssh_deployment.html +125 -0
- data/docs/site/ssh_sandbox.html +103 -0
- data/docs/ssh_basics.rb +19 -0
- data/docs/ssh_deployment.rb +35 -0
- data/docs/ssh_deployment/app/app.rb +0 -0
- data/docs/ssh_sandbox.rb +28 -0
- data/lib/vfs.rb +18 -0
- data/lib/vfs/drivers/local.rb +180 -0
- data/lib/vfs/drivers/specification.rb +169 -0
- data/lib/vfs/entries/dir.rb +256 -0
- data/lib/vfs/entries/entry.rb +152 -0
- data/lib/vfs/entries/file.rb +155 -0
- data/lib/vfs/entries/universal_entry.rb +24 -0
- data/lib/vfs/entry_proxy.rb +42 -0
- data/lib/vfs/error.rb +4 -0
- data/lib/vfs/integration.rb +30 -0
- data/lib/vfs/path.rb +123 -0
- data/lib/vfs/version.rb +3 -0
- data/lib/vfs/vfs.rb +38 -0
- data/old/hash_fs.rb +216 -0
- data/old/hash_fs_spec.rb +10 -0
- data/readme.md +143 -0
- data/spec/container_spec.rb +31 -0
- data/spec/dir_spec.rb +253 -0
- data/spec/entry_spec.rb +42 -0
- data/spec/file_spec.rb +215 -0
- data/spec/misc_spec.rb +19 -0
- data/spec/path_spec.rb +127 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/storages/local_spec.rb +24 -0
- data/spec/storages/local_spec/emptygit +0 -0
- data/spec/universal_entry_spec.rb +69 -0
- data/todo.md +19 -0
- data/vfs.gemspec +17 -0
- metadata +105 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module Vfs
|
2
|
+
class UniversalEntry < Entry
|
3
|
+
#
|
4
|
+
# Attributes
|
5
|
+
#
|
6
|
+
def exist?
|
7
|
+
!!get
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def copy_to to, options = {}
|
12
|
+
from = file? ? to_file : to_dir
|
13
|
+
from.copy_to to, options
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
#
|
18
|
+
# CRUD
|
19
|
+
#
|
20
|
+
def destroy
|
21
|
+
destroy_entry
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#
|
2
|
+
# It allows dynamically (magically) switching between UniversalEntry/Dir/File
|
3
|
+
#
|
4
|
+
module Vfs
|
5
|
+
class EntryProxy < BasicObject
|
6
|
+
attr_reader :_target
|
7
|
+
|
8
|
+
def initialize entry
|
9
|
+
raise 'something wrong happening here!' if entry.respond_to?(:proxy?) and entry.proxy?
|
10
|
+
self._target = entry
|
11
|
+
end
|
12
|
+
|
13
|
+
def proxy?
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
protected :==, :equal?, :!, :!=
|
18
|
+
protected
|
19
|
+
attr_writer :_target
|
20
|
+
|
21
|
+
def respond_to? m
|
22
|
+
super or
|
23
|
+
::Vfs::UniversalEntry.method_defined?(m) or
|
24
|
+
::Vfs::Dir.method_defined?(m) or
|
25
|
+
::Vfs::File.method_defined?(m)
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing m, *a, &b
|
29
|
+
unless _target.respond_to? m
|
30
|
+
if ::Vfs::UniversalEntry.method_defined? m
|
31
|
+
self.target = _target.entry
|
32
|
+
elsif ::Vfs::Dir.method_defined? m
|
33
|
+
self._target = _target.dir
|
34
|
+
elsif ::Vfs::File.method_defined? m
|
35
|
+
self._target = _target.file
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
_target.send m, *a, &b
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/vfs/error.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
class String
|
2
|
+
def to_entry_on driver = nil
|
3
|
+
path = self
|
4
|
+
driver ||= Vfs.default_driver
|
5
|
+
|
6
|
+
path = "./#{path}" unless path =~ /^[\/\.\~]/
|
7
|
+
Vfs::Entry.new(driver, path).entry
|
8
|
+
end
|
9
|
+
alias_method :to_entry, :to_entry_on
|
10
|
+
|
11
|
+
def to_file_on driver = nil
|
12
|
+
to_entry_on(driver).file
|
13
|
+
end
|
14
|
+
alias_method :to_file, :to_file_on
|
15
|
+
|
16
|
+
def to_dir_on driver = nil
|
17
|
+
to_entry_on(driver).dir
|
18
|
+
end
|
19
|
+
alias_method :to_dir, :to_dir_on
|
20
|
+
end
|
21
|
+
|
22
|
+
class File
|
23
|
+
def to_entry
|
24
|
+
path.to_entry
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_file
|
28
|
+
path.to_file
|
29
|
+
end
|
30
|
+
end
|
data/lib/vfs/path.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
module Vfs
|
2
|
+
class Path < String
|
3
|
+
def initialize path = '/', options = {}
|
4
|
+
if options[:skip_normalization]
|
5
|
+
super path
|
6
|
+
@probably_dir = options[:probably_dir]
|
7
|
+
else
|
8
|
+
Path.validate! path
|
9
|
+
path, probably_dir = Path.normalize_to_string path
|
10
|
+
raise "invalid path '#{path}' (you are outside of the root)!" unless path
|
11
|
+
super path
|
12
|
+
@probably_dir = probably_dir
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def + path = ''
|
17
|
+
path = path.to_s
|
18
|
+
Path.validate! path, false
|
19
|
+
|
20
|
+
if Path.absolute?(path)
|
21
|
+
Path.normalize path
|
22
|
+
elsif path.empty?
|
23
|
+
self
|
24
|
+
else
|
25
|
+
Path.normalize "#{self}#{'/' unless self == '/'}#{path}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def parent
|
30
|
+
self + '..'
|
31
|
+
end
|
32
|
+
|
33
|
+
def probably_dir?
|
34
|
+
!!@probably_dir
|
35
|
+
end
|
36
|
+
|
37
|
+
def name
|
38
|
+
unless @name
|
39
|
+
root = self[0..0]
|
40
|
+
@name ||= split('/').last || root
|
41
|
+
end
|
42
|
+
@name
|
43
|
+
end
|
44
|
+
|
45
|
+
class << self
|
46
|
+
def absolute? path
|
47
|
+
path =~ /^[\/~\/]|^\.$|^\.\//
|
48
|
+
end
|
49
|
+
|
50
|
+
def valid? path, forbid_relative = true, &block
|
51
|
+
result, err = if forbid_relative and !absolute?(path)
|
52
|
+
[false, "path must be started with '/', or '.'"]
|
53
|
+
elsif path =~ /.+\/~$|.+\/$|\/\.$/
|
54
|
+
[false, "path can't be ended with '/', '/~', or '/.'"]
|
55
|
+
elsif path =~ /\/\/|\/~\/|\/\.\//
|
56
|
+
[false, "path can't include '/./', '/~/', '//' combinations!"]
|
57
|
+
# elsif path =~ /.+[~]|\/\.\//
|
58
|
+
# [false, "'~', or '.' can be present only at the begining of string"]
|
59
|
+
else
|
60
|
+
[true, nil]
|
61
|
+
end
|
62
|
+
|
63
|
+
block.call err if block and !result and err
|
64
|
+
result
|
65
|
+
end
|
66
|
+
|
67
|
+
def normalize path
|
68
|
+
path, probably_dir = normalize_to_string path
|
69
|
+
unless path
|
70
|
+
nil
|
71
|
+
else
|
72
|
+
Path.new(path, skip_normalization: true, probably_dir: probably_dir)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def validate! path, forbid_relative = true
|
77
|
+
valid?(path, forbid_relative){|error| raise "invalid path '#{path}' (#{error})!"}
|
78
|
+
end
|
79
|
+
|
80
|
+
def normalize_to_string path
|
81
|
+
root = path[0..0]
|
82
|
+
result, probably_dir = [], false
|
83
|
+
|
84
|
+
parts = path.split('/')[1..-1]
|
85
|
+
if parts
|
86
|
+
parts.each do |part|
|
87
|
+
if part == '..' and (root != '.' or (root == '.' and result.size > 0))
|
88
|
+
return nil, false unless result.size > 0
|
89
|
+
result.pop
|
90
|
+
probably_dir ||= true
|
91
|
+
else
|
92
|
+
result << part
|
93
|
+
probably_dir &&= false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
normalized_path = result.join('/')
|
98
|
+
|
99
|
+
probably_dir ||= true if normalized_path.empty?
|
100
|
+
|
101
|
+
return "#{root}#{'/' unless root == '/' or normalized_path.empty?}#{normalized_path}", probably_dir
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# protected
|
106
|
+
# def delete_dir_mark
|
107
|
+
# path = path.to_s.sub(%r{/$}, '')
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
#
|
111
|
+
# def root_path? path
|
112
|
+
# path =~ /^[#{ROOT_SYMBOLS}]$/
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# def split_path path
|
116
|
+
# path.split(/#{ROOT_SYMBOLS}/)
|
117
|
+
# end
|
118
|
+
#
|
119
|
+
# def dir_mark? path
|
120
|
+
# path =~ %r{/$}
|
121
|
+
# end
|
122
|
+
end
|
123
|
+
end
|
data/lib/vfs/version.rb
ADDED
data/lib/vfs/vfs.rb
ADDED
@@ -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
|
data/old/hash_fs.rb
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
#
|
2
|
+
# Very dirty and inefficient In Memory File System, mainly for tests.
|
3
|
+
#
|
4
|
+
module Vfs
|
5
|
+
module Drivers
|
6
|
+
class HashFs < Hash
|
7
|
+
def initialize
|
8
|
+
super
|
9
|
+
self['/'] = {dir: true}
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def open &block
|
14
|
+
block.call self
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Attributes
|
19
|
+
#
|
20
|
+
def attributes path
|
21
|
+
base, name = split_path path
|
22
|
+
|
23
|
+
# if path == '/'
|
24
|
+
# return {dir: true, file: false}
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
stat = cd(base)[name]
|
28
|
+
attrs = {}
|
29
|
+
attrs[:file] = !!stat[:file]
|
30
|
+
attrs[:dir] = !!stat[:dir]
|
31
|
+
attrs
|
32
|
+
rescue Exception
|
33
|
+
{}
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_attributes path, attrs
|
37
|
+
raise 'not supported'
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
#
|
42
|
+
# File
|
43
|
+
#
|
44
|
+
def read_file path, &block
|
45
|
+
base, name = split_path path
|
46
|
+
assert cd(base)[name], :include?, :file
|
47
|
+
block.call cd(base)[name][:content]
|
48
|
+
end
|
49
|
+
|
50
|
+
def write_file path, append, &block
|
51
|
+
base, name = split_path path
|
52
|
+
|
53
|
+
os = if append
|
54
|
+
file = cd(base)[name]
|
55
|
+
file ? file[:content] : ''
|
56
|
+
else
|
57
|
+
assert_not cd(base), :include?, name
|
58
|
+
''
|
59
|
+
end
|
60
|
+
writer = -> buff {os << buff}
|
61
|
+
block.call writer
|
62
|
+
|
63
|
+
cd(base)[name] = {file: true, content: os}
|
64
|
+
end
|
65
|
+
|
66
|
+
def delete_file path
|
67
|
+
base, name = split_path path
|
68
|
+
assert cd(base)[name], :include?, :file
|
69
|
+
cd(base).delete name
|
70
|
+
end
|
71
|
+
|
72
|
+
# def move_file path
|
73
|
+
# raise 'not supported'
|
74
|
+
# end
|
75
|
+
|
76
|
+
|
77
|
+
#
|
78
|
+
# Dir
|
79
|
+
#
|
80
|
+
def create_dir path
|
81
|
+
base, name = split_path path
|
82
|
+
assert_not cd(base), :include?, name
|
83
|
+
cd(base)[name] = {dir: true}
|
84
|
+
end
|
85
|
+
|
86
|
+
def delete_dir path
|
87
|
+
base, name = split_path path
|
88
|
+
assert cd(base)[name], :include?, :dir
|
89
|
+
# empty = true
|
90
|
+
# cd(base)[name].each do |key, value|
|
91
|
+
# empty = false if key.is_a? String
|
92
|
+
# end
|
93
|
+
# raise 'you are trying to delete not empty dir!' unless empty
|
94
|
+
cd(base).delete name
|
95
|
+
end
|
96
|
+
|
97
|
+
# def move_dir path
|
98
|
+
# raise 'not supported'
|
99
|
+
# end
|
100
|
+
|
101
|
+
def each_entry path, query, &block
|
102
|
+
raise "hash_fs not support :each_entry with query!" if query
|
103
|
+
|
104
|
+
base, name = split_path path
|
105
|
+
assert cd(base)[name], :include?, :dir
|
106
|
+
cd(base)[name].each do |relative_name, content|
|
107
|
+
next if relative_name.is_a? Symbol
|
108
|
+
if content[:dir]
|
109
|
+
block.call relative_name, :dir
|
110
|
+
else
|
111
|
+
block.call relative_name, :file
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def efficient_dir_copy from, to, override
|
117
|
+
from.driver.open do |from_fs|
|
118
|
+
to.driver.open do |to_fs|
|
119
|
+
if from_fs == to_fs
|
120
|
+
for_spec_helper_effective_copy_used
|
121
|
+
|
122
|
+
_efficient_dir_copy from.path, to.path, override
|
123
|
+
true
|
124
|
+
else
|
125
|
+
false
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def _efficient_dir_copy from_path, to_path, override
|
132
|
+
from_base, from_name = split_path from_path
|
133
|
+
assert cd(from_base)[from_name], :include?, :dir
|
134
|
+
|
135
|
+
to_base, to_name = split_path to_path
|
136
|
+
# assert_not cd(to_base), :include?, to_name
|
137
|
+
|
138
|
+
if cd(to_base).include? to_name
|
139
|
+
if cd(to_base)[to_name][:dir]
|
140
|
+
each_entry from_path, nil do |name, type|
|
141
|
+
if type == :dir
|
142
|
+
_efficient_dir_copy "#{from_path}/#{name}", "#{to_path}/#{name}", override
|
143
|
+
else
|
144
|
+
raise "file #{to_path}/#{name} already exist!" if cd(to_base)[to_name].include?(name) and !override
|
145
|
+
cd(to_base)[to_name][name] = cd(from_base)[from_name][name].clone
|
146
|
+
end
|
147
|
+
end
|
148
|
+
else
|
149
|
+
raise "can't copy dir #{from_path} to file #{to_path}!"
|
150
|
+
end
|
151
|
+
else
|
152
|
+
cd(to_base)[to_name] = {dir: true}
|
153
|
+
_efficient_dir_copy from_path, to_path, override
|
154
|
+
end
|
155
|
+
end
|
156
|
+
protected :_efficient_dir_copy
|
157
|
+
def for_spec_helper_effective_copy_used; end
|
158
|
+
|
159
|
+
# def upload_directory from_local_path, to_remote_path
|
160
|
+
# FileUtils.cp_r from_local_path, to_remote_path
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# def download_directory from_remote_path, to_local_path
|
164
|
+
# FileUtils.cp_r from_remote_path, to_local_path
|
165
|
+
# end
|
166
|
+
|
167
|
+
|
168
|
+
#
|
169
|
+
# Other
|
170
|
+
#
|
171
|
+
def local?; true end
|
172
|
+
|
173
|
+
def to_s; 'hash_fs' end
|
174
|
+
|
175
|
+
def tmp &block
|
176
|
+
tmp_dir = "/tmp_#{rand(10**6)}"
|
177
|
+
create_dir tmp_dir
|
178
|
+
if block
|
179
|
+
begin
|
180
|
+
block.call tmp_dir
|
181
|
+
ensure
|
182
|
+
delete_dir tmp_dir
|
183
|
+
end
|
184
|
+
else
|
185
|
+
tmp_dir
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
protected
|
190
|
+
def assert obj, method, arg
|
191
|
+
raise "#{obj} should #{method} #{arg}" unless obj.send method, arg
|
192
|
+
end
|
193
|
+
|
194
|
+
def assert_not obj, method, arg
|
195
|
+
raise "#{obj} should not #{method} #{arg}" if obj.send method, arg
|
196
|
+
end
|
197
|
+
|
198
|
+
def split_path path
|
199
|
+
parts = path[1..-1].split('/')
|
200
|
+
parts.unshift '/'
|
201
|
+
name = parts.pop
|
202
|
+
return parts, name
|
203
|
+
end
|
204
|
+
|
205
|
+
def cd parts
|
206
|
+
current = self
|
207
|
+
iterator = parts.clone
|
208
|
+
while iterator.first
|
209
|
+
current = current[iterator.first]
|
210
|
+
iterator.shift
|
211
|
+
end
|
212
|
+
current
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|