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.
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