vfs 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/Rakefile +2 -2
  2. data/lib/vfs.rb +18 -0
  3. data/lib/vfs/entries/dir.rb +272 -0
  4. data/lib/vfs/entries/entry.rb +127 -0
  5. data/lib/vfs/entries/file.rb +185 -0
  6. data/lib/vfs/entries/universal_entry.rb +24 -0
  7. data/lib/vfs/entry_proxy.rb +38 -0
  8. data/lib/vfs/error.rb +4 -0
  9. data/lib/vfs/integration/string.rb +19 -0
  10. data/lib/vfs/path.rb +125 -0
  11. data/lib/vfs/storages/hash_fs.rb +194 -0
  12. data/lib/vfs/storages/local.rb +138 -0
  13. data/lib/vfs/storages/specification.rb +127 -0
  14. data/lib/vfs/support.rb +2 -0
  15. data/readme.md +110 -14
  16. data/spec/container_spec.rb +31 -0
  17. data/spec/dir_spec.rb +224 -0
  18. data/spec/entry_spec.rb +24 -0
  19. data/spec/file_spec.rb +189 -0
  20. data/spec/path_spec.rb +121 -0
  21. data/spec/spec_helper.rb +2 -9
  22. data/spec/storages/hash_fs_spec.rb +10 -0
  23. data/spec/storages/local_spec.rb +10 -0
  24. data/spec/universal_entry_spec.rb +42 -0
  25. metadata +25 -23
  26. data/lib/old/ssh.rb +0 -11
  27. data/lib/rsh.rb +0 -19
  28. data/lib/rsh/box.rb +0 -182
  29. data/lib/rsh/box/marks.rb +0 -29
  30. data/lib/rsh/drivers/abstract.rb +0 -15
  31. data/lib/rsh/drivers/local.rb +0 -48
  32. data/lib/rsh/drivers/ssh.rb +0 -147
  33. data/lib/rsh/gems.rb +0 -2
  34. data/lib/rsh/support.rb +0 -30
  35. data/spec/abstract_driver.rb +0 -82
  36. data/spec/abstract_driver/dir/dir2/file +0 -0
  37. data/spec/abstract_driver/local_file +0 -1
  38. data/spec/box_spec.rb +0 -109
  39. data/spec/box_spec/dir/dir2/file +0 -0
  40. data/spec/box_spec/local_file +0 -1
  41. data/spec/config.example.yml +0 -4
  42. data/spec/config.yml +0 -5
  43. data/spec/local_driver_spec.rb +0 -9
  44. data/spec/ssh_driver_spec.rb +0 -15
@@ -0,0 +1,24 @@
1
+ module Vfs
2
+ class UniversalEntry < Entry
3
+ #
4
+ # Attributes
5
+ #
6
+ def exist?
7
+ attrs = get
8
+ !!(attrs[:dir] or attrs[:file])
9
+ end
10
+
11
+
12
+ #
13
+ # CRUD
14
+ #
15
+ def destroy
16
+ storage.open_fs do |fs|
17
+ attrs = get
18
+ fs.delete_dir path if attrs[:dir]
19
+ fs.delete_file path if attrs[:file]
20
+ end
21
+ self
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ #
2
+ # It allows you dynamically (magically) switch between UniversalEntry/Dir/File
3
+ #
4
+ module Vfs
5
+ class EntryProxy < BasicObject
6
+ attr_reader :_target
7
+ # WRAP = [:[], :entry, :dir, :file].to_set
8
+
9
+ def initialize entry
10
+ raise 'something wrong happening here!' if entry.respond_to?(:proxy?) and entry.proxy?
11
+ self._target = entry
12
+ end
13
+
14
+ def proxy?
15
+ true
16
+ end
17
+
18
+ protected :==, :equal?, :!, :!=
19
+ protected
20
+ attr_writer :_target
21
+
22
+ def method_missing m, *a, &b
23
+ unless _target.respond_to? m
24
+ if ::Vfs::UniversalEntry.method_defined? m
25
+ self.target = _target.entry
26
+ elsif ::Vfs::Dir.method_defined? m
27
+ self._target = _target.dir
28
+ elsif ::Vfs::File.method_defined? m
29
+ self._target = _target.file
30
+ end
31
+ end
32
+
33
+ _target.send m, *a, &b
34
+
35
+ # return WRAP.include?(m) ? EntryProxy.new(result) : result
36
+ end
37
+ end
38
+ end
data/lib/vfs/error.rb ADDED
@@ -0,0 +1,4 @@
1
+ module Vfs
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,19 @@
1
+ class String
2
+ def to_entry_on storage = nil
3
+ path = self
4
+ storage ||= Vfs::Storages::Local.new
5
+
6
+ Vfs::Dir.new(storage, '/')[path]
7
+ end
8
+ alias_method :to_entry, :to_entry_on
9
+
10
+ def to_file_on storage = nil
11
+ to_entry_on(storage).file
12
+ end
13
+ alias_method :to_file, :to_file_on
14
+
15
+ def to_dir_on storage = nil
16
+ to_entry_on(storage).dir
17
+ end
18
+ alias_method :to_dir, :to_dir_on
19
+ end
data/lib/vfs/path.rb ADDED
@@ -0,0 +1,125 @@
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 != '.'
88
+ return nil, false unless result.size > 0
89
+ result.pop
90
+ probably_dir ||= true
91
+ # elsif part == '.'
92
+ # # do nothing
93
+ else
94
+ result << part
95
+ probably_dir &&= false
96
+ end
97
+ end
98
+ end
99
+ normalized_path = result.join('/')
100
+
101
+ probably_dir ||= true if normalized_path.empty?
102
+
103
+ return "#{root}#{'/' unless root == '/' or normalized_path.empty?}#{normalized_path}", probably_dir
104
+ end
105
+ end
106
+
107
+ # protected
108
+ # def delete_dir_mark
109
+ # path = path.to_s.sub(%r{/$}, '')
110
+ # end
111
+ #
112
+ #
113
+ # def root_path? path
114
+ # path =~ /^[#{ROOT_SYMBOLS}]$/
115
+ # end
116
+ #
117
+ # def split_path path
118
+ # path.split(/#{ROOT_SYMBOLS}/)
119
+ # end
120
+ #
121
+ # def dir_mark? path
122
+ # path =~ %r{/$}
123
+ # end
124
+ end
125
+ end
@@ -0,0 +1,194 @@
1
+ #
2
+ # Dirty and uneficient In Memory FS, mainly for tests.
3
+ #
4
+ module Vfs
5
+ module Storages
6
+ class HashFs < Hash
7
+ def initialize
8
+ super
9
+ self['/'] = {dir: true}
10
+ end
11
+
12
+
13
+ def open_fs &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 path, &block
102
+ base, name = split_path path
103
+ assert cd(base)[name], :include?, :dir
104
+ cd(base)[name].each do |relative_name, content|
105
+ next if relative_name.is_a? Symbol
106
+ if content[:dir]
107
+ block.call relative_name, :dir
108
+ else
109
+ block.call relative_name, :file
110
+ end
111
+ end
112
+ end
113
+
114
+ def efficient_dir_copy from, to
115
+ from.storage.open_fs do |from_fs|
116
+ to.storage.open_fs do |to_fs|
117
+ if from_fs == to_fs
118
+ for_spec_helper_effective_copy_used
119
+
120
+ from_base, from_name = split_path from.path
121
+ assert cd(from_base)[from_name], :include?, :dir
122
+
123
+ to_base, to_name = split_path to.path
124
+ assert_not cd(to_base), :include?, to_name
125
+
126
+ cd(to_base)[to_name] = cd(from_base)[from_name]
127
+
128
+ true
129
+ else
130
+ false
131
+ end
132
+ end
133
+ end
134
+ end
135
+ def for_spec_helper_effective_copy_used; end
136
+
137
+ # def upload_directory from_local_path, to_remote_path
138
+ # FileUtils.cp_r from_local_path, to_remote_path
139
+ # end
140
+ #
141
+ # def download_directory from_remote_path, to_local_path
142
+ # FileUtils.cp_r from_remote_path, to_local_path
143
+ # end
144
+
145
+
146
+ #
147
+ # Other
148
+ #
149
+ def local?; true end
150
+
151
+ def to_s; 'hash_fs' end
152
+
153
+ def tmp &block
154
+ tmp_dir = "/tmp_#{rand(10**6)}"
155
+ create_dir tmp_dir
156
+ if block
157
+ begin
158
+ block.call tmp_dir
159
+ ensure
160
+ delete_dir tmp_dir
161
+ end
162
+ else
163
+ tmp_dir
164
+ end
165
+ end
166
+
167
+ protected
168
+ def assert obj, method, arg
169
+ raise "#{obj} should #{method} #{arg}" unless obj.send method, arg
170
+ end
171
+
172
+ def assert_not obj, method, arg
173
+ raise "#{obj} should not #{method} #{arg}" if obj.send method, arg
174
+ end
175
+
176
+ def split_path path
177
+ parts = path[1..-1].split('/')
178
+ parts.unshift '/'
179
+ name = parts.pop
180
+ return parts, name
181
+ end
182
+
183
+ def cd parts
184
+ current = self
185
+ iterator = parts.clone
186
+ while iterator.first
187
+ current = current[iterator.first]
188
+ iterator.shift
189
+ end
190
+ current
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,138 @@
1
+ require 'tempfile'
2
+
3
+ module Vfs
4
+ module Storages
5
+ class Local
6
+ module LocalVfsHelper
7
+ DEFAULT_BUFFER = 1024*128
8
+
9
+ attr_writer :buffer
10
+ def buffer
11
+ @buffer || DEFAULT_BUFFER
12
+ end
13
+
14
+ #
15
+ # Attributes
16
+ #
17
+ def attributes path
18
+ stat = ::File.stat path
19
+ attrs = {}
20
+ attrs[:file] = stat.file?
21
+ attrs[:dir] = stat.directory?
22
+ attrs
23
+ rescue Errno::ENOENT
24
+ {}
25
+ end
26
+
27
+ def set_attributes path, attrs
28
+ raise 'not supported'
29
+ end
30
+
31
+
32
+ #
33
+ # File
34
+ #
35
+ def read_file path, &block
36
+ ::File.open path, 'r' do |is|
37
+ while buff = is.gets(self.buffer || DEFAULT_BUFFER)
38
+ block.call buff
39
+ end
40
+ end
41
+ end
42
+
43
+ def write_file path, append, &block
44
+ option = append ? 'a' : 'w'
45
+ ::File.open path, option do |os|
46
+ writer = -> buff {os.write buff}
47
+ block.call writer
48
+ end
49
+ end
50
+
51
+ def delete_file path
52
+ ::File.delete path
53
+ end
54
+
55
+ # def move_file from, to
56
+ # FileUtils.mv from, to
57
+ # end
58
+
59
+
60
+ #
61
+ # Dir
62
+ #
63
+ def create_dir path
64
+ ::Dir.mkdir path
65
+ end
66
+
67
+ def delete_dir path
68
+ FileUtils.rm_r path
69
+ end
70
+
71
+ def each path, &block
72
+ ::Dir.foreach path do |relative_name|
73
+ next if relative_name == '.' or relative_name == '..'
74
+ if ::File.directory? "#{path}/#{relative_name}"
75
+ block.call relative_name, :dir
76
+ else
77
+ block.call relative_name, :file
78
+ end
79
+ end
80
+ end
81
+
82
+ def efficient_dir_copy from, to
83
+ from.storage.open_fs do |from_fs|
84
+ to.storage.open_fs do |to_fs|
85
+ if from_fs.local? and to_fs.local?
86
+ FileUtils.cp_r from.path, to.path
87
+ true
88
+ else
89
+ false
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ # def move_dir path
96
+ # raise 'not supported'
97
+ # end
98
+
99
+ # def upload_directory from_local_path, to_remote_path
100
+ # FileUtils.cp_r from_local_path, to_remote_path
101
+ # end
102
+ #
103
+ # def download_directory from_remote_path, to_local_path
104
+ # FileUtils.cp_r from_remote_path, to_local_path
105
+ # end
106
+
107
+
108
+ #
109
+ # Other
110
+ #
111
+ def local?; true end
112
+
113
+ def tmp &block
114
+ tmp_dir = "#{::Dir.tmpdir}/#{rand(10**3)}"
115
+ if block
116
+ begin
117
+ create_dir tmp_dir
118
+ block.call tmp_dir
119
+ ensure
120
+ delete_dir tmp_dir
121
+ end
122
+ else
123
+ create_dir tmp_dir
124
+ tmp_dir
125
+ end
126
+ end
127
+
128
+ def to_s; '' end
129
+ end
130
+
131
+ include LocalVfsHelper
132
+
133
+ def open_fs &block
134
+ block.call self
135
+ end
136
+ end
137
+ end
138
+ end