vfs 0.3.15 → 0.4.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 CHANGED
@@ -4,6 +4,7 @@ project(
4
4
  name: "vfs",
5
5
  gem: true,
6
6
  summary: "Virtual File System",
7
+ version: '0.4.0',
7
8
 
8
9
  author: "Alexey Petrushin",
9
10
  homepage: "http://github.com/alexeypetrushin/vfs"
data/lib/vfs.rb CHANGED
@@ -1,6 +1,7 @@
1
- %w(
2
- support
1
+ require 'fileutils'
2
+ require 'set'
3
3
 
4
+ %w(
4
5
  path
5
6
  error
6
7
 
@@ -11,9 +12,9 @@
11
12
 
12
13
  entry_proxy
13
14
 
14
- storages/local
15
+ drivers/local
15
16
 
16
- integration/string
17
+ integration
17
18
 
18
19
  vfs
19
20
  ).each{|f| require "vfs/#{f}"}
@@ -0,0 +1,179 @@
1
+ require 'tempfile'
2
+
3
+ module Vfs
4
+ module Drivers
5
+ class Local
6
+ class Writer
7
+ def initialize out; @out = out end
8
+
9
+ def write data
10
+ @out.write data
11
+ end
12
+ end
13
+
14
+ DEFAULT_BUFFER = 1000 * 1024
15
+
16
+ def initialize options = {}
17
+ options = options.clone
18
+ @root = options.delete(:root) || ''
19
+ raise "invalid options #{options}" unless options.empty?
20
+ end
21
+
22
+ def open &block
23
+ block.call self if block
24
+ end
25
+ def close; end
26
+
27
+ attr_writer :buffer
28
+ def buffer
29
+ @buffer || DEFAULT_BUFFER
30
+ end
31
+
32
+ #
33
+ # Attributes
34
+ #
35
+ def attributes path
36
+ path = with_root path
37
+
38
+ stat = ::File.stat path
39
+ attrs = {}
40
+ attrs[:file] = !!stat.file?
41
+ attrs[:dir] = !!stat.directory?
42
+
43
+ # attributes special for file system
44
+ attrs[:created_at] = stat.ctime
45
+ attrs[:updated_at] = stat.mtime
46
+ attrs[:size] = stat.size if attrs[:file]
47
+ attrs
48
+ rescue Errno::ENOTDIR
49
+ nil
50
+ rescue Errno::ENOENT
51
+ nil
52
+ end
53
+
54
+ def set_attributes path, attrs
55
+ # TODO2 set attributes
56
+ not_implemented
57
+ end
58
+
59
+
60
+ #
61
+ # File
62
+ #
63
+ def read_file path, &block
64
+ path = with_root path
65
+ ::File.open path, 'r' do |is|
66
+ while buff = is.gets(self.buffer || DEFAULT_BUFFER)
67
+ block.call buff
68
+ end
69
+ end
70
+ end
71
+
72
+ def write_file original_path, append, &block
73
+ path = with_root original_path
74
+
75
+ option = append ? 'a' : 'w'
76
+ ::File.open path, "#{option}b" do |out|
77
+ block.call Writer.new(out)
78
+ end
79
+ end
80
+
81
+ def delete_file path
82
+ path = with_root path
83
+ ::File.delete path
84
+ end
85
+
86
+ # def move_file from, to
87
+ # FileUtils.mv from, to
88
+ # end
89
+
90
+
91
+ #
92
+ # Dir
93
+ #
94
+ def create_dir path
95
+ path = with_root path
96
+ ::Dir.mkdir path
97
+ end
98
+
99
+ def delete_dir original_path
100
+ path = with_root original_path
101
+ ::FileUtils.rm_r path
102
+ end
103
+
104
+ def each_entry path, query, &block
105
+ path = with_root path
106
+
107
+ if query
108
+ path_with_trailing_slash = path == '/' ? path : "#{path}/"
109
+ ::Dir["#{path_with_trailing_slash}#{query}"].each do |absolute_path|
110
+ name = absolute_path.sub path_with_trailing_slash, ''
111
+ block.call name, ->{::File.directory?(absolute_path) ? :dir : :file}
112
+ # if ::File.directory? absolute_path
113
+ # block.call relative_path, :dir
114
+ # else
115
+ # block.call relative_path, :file
116
+ # end
117
+ end
118
+ else
119
+ ::Dir.foreach path do |name|
120
+ next if name == '.' or name == '..'
121
+ block.call name, ->{::File.directory?("#{path}/#{name}") ? :dir : :file}
122
+ # if ::File.directory? "#{path}/#{relative_name}"
123
+ # block.call relative_name, :dir
124
+ # else
125
+ # block.call relative_name, :file
126
+ # end
127
+ end
128
+ end
129
+ end
130
+
131
+ # def efficient_dir_copy from, to, override
132
+ # return false if override # FileUtils.cp_r doesn't support this behaviour
133
+ #
134
+ # from.driver.open_fs do |from_fs|
135
+ # to.driver.open_fs do |to_fs|
136
+ # if from_fs.local? and to_fs.local?
137
+ # FileUtils.cp_r from.path, to.path
138
+ # true
139
+ # else
140
+ # false
141
+ # end
142
+ # end
143
+ # end
144
+ # end
145
+
146
+ #
147
+ # Other
148
+ #
149
+ def local?; true end
150
+
151
+ def tmp &block
152
+ path = "/tmp/#{rand(10**6)}"
153
+ # tmp_dir = "#{::Dir.tmpdir}/#{rand(10**6)}"
154
+ if block
155
+ begin
156
+ ::FileUtils.mkdir_p with_root(path)
157
+ block.call path
158
+ ensure
159
+ ::FileUtils.rm_r with_root(path) if ::File.exist? with_root(path)
160
+ end
161
+ else
162
+ ::FileUtils.mkdir_p with_root(path)
163
+ path
164
+ end
165
+ end
166
+
167
+ def to_s; '' end
168
+
169
+ protected
170
+ def root
171
+ @root || raise('root not defined!')
172
+ end
173
+
174
+ def with_root path
175
+ path == '/' ? root : root + path
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,169 @@
1
+ require 'rspec_ext'
2
+ require 'ruby_ext'
3
+
4
+ shared_examples_for 'vfs driver basic' do
5
+ it 'should respond to :local?' do
6
+ @driver.should respond_to(:local?)
7
+ end
8
+
9
+ it "should provide open method" do
10
+ @driver.open
11
+ @driver.open{'result'}.should == 'result'
12
+ end
13
+ end
14
+
15
+ shared_examples_for 'vfs driver attributes basic' do
16
+ it 'should have root dir' do
17
+ attrs = @driver.attributes('/')
18
+ attrs[:dir].should be_true
19
+ attrs[:file].should be_false
20
+ end
21
+
22
+ it "attributes should return nil if there's no entry" do
23
+ @driver.attributes('/non_existing_entry').should be_nil
24
+ end
25
+ end
26
+
27
+ shared_examples_for 'vfs driver files' do
28
+ it "file attributes" do
29
+ @driver.attributes('/file').should be_nil
30
+
31
+ @driver.write_file('/file', false){|w| w.write 'something'}
32
+ attrs = @driver.attributes('/file')
33
+ attrs[:file].should be_true
34
+ attrs[:dir].should be_false
35
+ end
36
+
37
+ it "read, write, append" do
38
+ # write
39
+ @driver.write_file('/file', false){|w| w.write 'something'}
40
+ @driver.attributes('/file')[:file].should == true
41
+
42
+ # read
43
+ data = ""
44
+ @driver.read_file('/file'){|buff| data << buff}
45
+ data.should == 'something'
46
+
47
+ # append
48
+ @driver.write_file('/file', true){|w| w.write ' another'}
49
+ data = ""
50
+ @driver.read_file('/file'){|buff| data << buff}
51
+ data.should == 'something another'
52
+ end
53
+
54
+ it "delete_file" do
55
+ @driver.write_file('/file', false){|w| w.write 'something'}
56
+ @driver.attributes('/file')[:file].should be_true
57
+ @driver.delete_file('/file')
58
+ @driver.attributes('/file').should be_nil
59
+ end
60
+ end
61
+
62
+ shared_examples_for 'vfs driver full attributes for files' do
63
+ it "attributes for files" do
64
+ @driver.write_file('/file', false){|w| w.write 'something'}
65
+ attrs = @driver.attributes('/file')
66
+ attrs[:file].should be_true
67
+ attrs[:dir].should be_false
68
+ attrs[:created_at].class.should == Time
69
+ attrs[:updated_at].class.should == Time
70
+ attrs[:size].should == 9
71
+ end
72
+ end
73
+
74
+ shared_examples_for 'vfs driver dirs' do
75
+ it "directory crud" do
76
+ @driver.attributes('/dir').should be_nil
77
+
78
+ @driver.create_dir('/dir')
79
+ attrs = @driver.attributes('/dir')
80
+ attrs[:file].should be_false
81
+ attrs[:dir].should be_true
82
+
83
+ @driver.delete_dir('/dir')
84
+ @driver.attributes('/dir').should be_nil
85
+ end
86
+
87
+ it 'should delete not-empty directories' do
88
+ @driver.create_dir('/dir')
89
+ @driver.create_dir('/dir/dir2')
90
+ @driver.write_file('/dir/dir2/file', false){|w| w.write 'something'}
91
+ @driver.attributes('/dir').should_not be_nil
92
+
93
+ @driver.delete_dir('/dir')
94
+ @driver.attributes('/dir').should be_nil
95
+ end
96
+
97
+ it 'each' do
98
+ -> {@driver.each_entry('/not_existing_dir', nil){|path, type| list[path] = type}}.should raise_error
99
+
100
+ @driver.create_dir '/dir'
101
+ @driver.create_dir('/dir/dir2')
102
+ @driver.write_file('/dir/file', false){|w| w.write 'something'}
103
+
104
+ list = {}
105
+ @driver.each_entry '/dir', nil do |path, type|
106
+ type = type.call if type.is_a? Proc
107
+ list[path] = type
108
+ end
109
+
110
+ list.should == {'dir2' => :dir, 'file' => :file}
111
+ end
112
+
113
+ # it "upload_directory & download_directory" do
114
+ # upload_path_check = "#{@remote_path}/dir2/file"
115
+ # check_attributes upload_path_check, nil
116
+ # @driver.upload_directory(@from_local, @remote_path)
117
+ # check_attributes upload_path_check, file: true, dir: false
118
+ #
119
+ # download_path_check = "#{@to_local}/dir2/file"
120
+ # File.exist?(download_path_check).should be_false
121
+ # @driver.download_directory(@remote_path, @to_local)
122
+ # File.exist?(download_path_check).should be_true
123
+ # end
124
+ end
125
+
126
+ shared_examples_for 'vfs driver query' do
127
+ it 'each with query' do
128
+ @driver.create_dir '/dir'
129
+ @driver.create_dir('/dir/dir_a')
130
+ @driver.create_dir('/dir/dir_b')
131
+ @driver.write_file('/dir/file_a', false){|w| w.write 'something'}
132
+
133
+ list = {}
134
+ @driver.each_entry '/dir', '*_a' do |path, type|
135
+ type = type.call if type.is_a? Proc
136
+ list[path] = type
137
+ end
138
+
139
+ list.should == {'dir_a' => :dir, 'file_a' => :file}
140
+ end
141
+ end
142
+
143
+ shared_examples_for 'vfs driver full attributes for dirs' do
144
+ it "attributes for dirs" do
145
+ @driver.create_dir('/dir')
146
+ attrs = @driver.attributes('/dir')
147
+ attrs[:file].should be_false
148
+ attrs[:dir].should be_true
149
+ attrs[:created_at].class.should == Time
150
+ attrs[:updated_at].class.should == Time
151
+ attrs.should_not include(:size)
152
+ end
153
+ end
154
+
155
+ shared_examples_for 'vfs driver tmp dir' do
156
+ it "tmp dir" do
157
+ dir = @driver.tmp
158
+ @driver.attributes(dir).should_not be_nil
159
+ @driver.delete_dir dir
160
+ @driver.attributes(dir).should be_nil
161
+
162
+ dir = nil
163
+ @driver.tmp do |tmp_dir|
164
+ dir = tmp_dir
165
+ @driver.attributes(dir).should_not be_nil
166
+ end
167
+ @driver.attributes(dir).should be_nil
168
+ end
169
+ end
@@ -25,20 +25,16 @@ module Vfs
25
25
  # CRUD
26
26
  #
27
27
  def create options = {}
28
- storage.open do |fs|
28
+ driver.open do
29
29
  try = 0
30
30
  begin
31
31
  try += 1
32
- fs.create_dir path
32
+ driver.create_dir path
33
33
  rescue StandardError => error
34
34
  entry = self.entry
35
35
  attrs = entry.get
36
36
  if attrs and attrs[:file] #entry.exist?
37
- if options[:override]
38
- entry.destroy
39
- else
40
- raise Error, "entry #{self} already exist!"
41
- end
37
+ entry.destroy
42
38
  elsif attrs and attrs[:dir]
43
39
  # dir already exist, no need to recreate it
44
40
  return self
@@ -57,36 +53,9 @@ module Vfs
57
53
  end
58
54
  self
59
55
  end
60
- def create! options = {}
61
- options[:override] = true
62
- create options
63
- end
64
56
 
65
57
  def destroy options = {}
66
- storage.open do |fs|
67
- begin
68
- fs.delete_dir path
69
- rescue StandardError => e
70
- attrs = get
71
- if attrs and attrs[:file]
72
- if options[:force]
73
- file.destroy
74
- else
75
- raise Error, "can't destroy File #{dir} (You are trying to destroy it as if it's a Dir)"
76
- end
77
- elsif attrs and attrs[:dir]
78
- # unknown internal error
79
- raise e
80
- else
81
- # do nothing, file already not exist
82
- end
83
- end
84
- end
85
- self
86
- end
87
- def destroy! options = {}
88
- options[:force] = true
89
- destroy options
58
+ destroy_entry :dir, :file
90
59
  end
91
60
 
92
61
 
@@ -98,19 +67,26 @@ module Vfs
98
67
  options = args.last.is_a?(Hash) ? args.pop : {}
99
68
  query = args.first
100
69
  options[:bang] = true unless options.include? :bang
70
+ filter = options[:filter]
71
+ type_required = options[:type]
101
72
 
102
- storage.open do |fs|
73
+ driver.open do
103
74
  begin
104
75
  list = []
105
- # query option is optional and supported only for some storages (local fs for example)
106
- fs.each_entry path, query do |name, type|
107
- next if options[:filter] and options[:filter] != type
76
+ # query option is optional and supported only for some drivers (local driver for example)
77
+ driver.each_entry path, query do |name, type|
78
+ # for performance reasons some drivers may return the type of entry as
79
+ # optionally evaluated callback.
80
+ type = type.call if (filter or type_required) and type.is_a?(Proc)
81
+
82
+ next if filter and (filter != type)
83
+
108
84
  entry = if type == :dir
109
85
  dir(name)
110
86
  elsif type == :file
111
87
  file(name)
112
88
  else
113
- raise 'invalid entry type!'
89
+ entry(name)
114
90
  end
115
91
  block ? block.call(entry) : (list << entry)
116
92
  end
@@ -120,9 +96,10 @@ module Vfs
120
96
  if attrs and attrs[:file]
121
97
  raise Error, "can't query entries on File ('#{self}')!"
122
98
  elsif attrs and attrs[:dir]
123
- # unknown error
99
+ # some unknown error
124
100
  raise error
125
101
  else
102
+ # TODO2 remove :bang
126
103
  raise Error, "'#{self}' not exist!" if options[:bang]
127
104
  []
128
105
  end
@@ -153,7 +130,10 @@ module Vfs
153
130
  alias_method :has?, :include?
154
131
 
155
132
  def empty?
156
- entries.empty?
133
+ catch :break do
134
+ entries{|e| throw :break, false}
135
+ true
136
+ end
157
137
  end
158
138
 
159
139
 
@@ -167,7 +147,6 @@ module Vfs
167
147
  raise Error, "you can't copy to itself" if self == to
168
148
 
169
149
  target = if to.is_a? File
170
- raise Error, "can't copy Dir to File ('#{self}')!" unless options[:override]
171
150
  to.dir
172
151
  elsif to.is_a? Dir
173
152
  to.dir #(name)
@@ -183,20 +162,12 @@ module Vfs
183
162
 
184
163
  target
185
164
  end
186
- def copy_to! to, options = {}
187
- options[:override] = true
188
- copy_to to, options
189
- end
190
165
 
191
166
  def move_to to, options = {}
192
167
  copy_to to, options
193
168
  destroy options
194
169
  to
195
170
  end
196
- def move_to! to, options = {}
197
- options[:override] = true
198
- move_to to, options
199
- end
200
171
 
201
172
  # class << self
202
173
  # attr_accessor :dont_use_efficient_dir_copy
@@ -205,7 +176,7 @@ module Vfs
205
176
  protected
206
177
  def unefficient_dir_copy to, options
207
178
  to.create options
208
- entries options do |e|
179
+ entries options.merge(type: true) do |e|
209
180
  if e.is_a? Dir
210
181
  e.copy_to to.dir(e.name), options
211
182
  elsif e.is_a? File
@@ -220,7 +191,7 @@ module Vfs
220
191
  # def efficient_dir_copy to, options
221
192
  # return false if self.class.dont_use_efficient_dir_copy
222
193
  #
223
- # storage.open_fs do |fs|
194
+ # driver.open do
224
195
  # try = 0
225
196
  # begin
226
197
  # try += 1
@@ -271,11 +242,11 @@ module Vfs
271
242
  # end
272
243
  #
273
244
  # def self.efficient_dir_copy from, to, override
274
- # from.storage.open_fs{|fs|
275
- # fs.respond_to?(:efficient_dir_copy) and fs.efficient_dir_copy(from, to, override)
245
+ # from.driver.open{
246
+ # driver.respond_to?(:efficient_dir_copy) and driver.efficient_dir_copy(from, to, override)
276
247
  # } or
277
- # to.storage.open_fs{|fs|
278
- # fs.respond_to?(:efficient_dir_copy) and fs.efficient_dir_copy(from, to, override)
248
+ # to.driver.open{
249
+ # driver.respond_to?(:efficient_dir_copy) and driver.efficient_dir_copy(from, to, override)
279
250
  # }
280
251
  # end
281
252
  end