vfs 0.3.15 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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