io 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/Rakefile +10 -0
- data/lib/vfs.rb +20 -0
- data/lib/vfs/drivers/local.rb +175 -0
- data/lib/vfs/drivers/specification.rb +169 -0
- data/lib/vfs/entries/dir.rb +253 -0
- data/lib/vfs/entries/entry.rb +147 -0
- data/lib/vfs/entries/file.rb +154 -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 +125 -0
- data/lib/vfs/vfs.rb +38 -0
- data/readme.md +119 -0
- data/spec/container_spec.rb +31 -0
- data/spec/dir_spec.rb +249 -0
- data/spec/entry_spec.rb +42 -0
- data/spec/file_spec.rb +210 -0
- data/spec/misc_spec.rb +19 -0
- data/spec/path_spec.rb +125 -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 +73 -0
- metadata +68 -0
data/Rakefile
ADDED
data/lib/vfs.rb
ADDED
@@ -0,0 +1,175 @@
|
|
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; @out.write data end
|
10
|
+
end
|
11
|
+
|
12
|
+
DEFAULT_BUFFER = 1000 * 1024
|
13
|
+
|
14
|
+
def initialize root = ''
|
15
|
+
@root = root
|
16
|
+
end
|
17
|
+
|
18
|
+
def open &block
|
19
|
+
block.call self if block
|
20
|
+
end
|
21
|
+
def close; end
|
22
|
+
|
23
|
+
attr_writer :buffer
|
24
|
+
def buffer
|
25
|
+
@buffer || DEFAULT_BUFFER
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Attributes
|
30
|
+
#
|
31
|
+
def attributes path
|
32
|
+
path = with_root path
|
33
|
+
|
34
|
+
stat = ::File.stat path
|
35
|
+
attrs = {}
|
36
|
+
attrs[:file] = !!stat.file?
|
37
|
+
attrs[:dir] = !!stat.directory?
|
38
|
+
|
39
|
+
# attributes special for file system
|
40
|
+
attrs[:created_at] = stat.ctime
|
41
|
+
attrs[:updated_at] = stat.mtime
|
42
|
+
attrs[:size] = stat.size if attrs[:file]
|
43
|
+
attrs
|
44
|
+
rescue Errno::ENOTDIR
|
45
|
+
nil
|
46
|
+
rescue Errno::ENOENT
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def set_attributes path, attrs
|
51
|
+
# TODO2 set attributes
|
52
|
+
not_implemented
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
#
|
57
|
+
# File
|
58
|
+
#
|
59
|
+
def read_file path, &block
|
60
|
+
path = with_root path
|
61
|
+
::File.open path, 'r' do |is|
|
62
|
+
while buff = is.gets(self.buffer || DEFAULT_BUFFER)
|
63
|
+
block.call buff
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def write_file original_path, append, &block
|
69
|
+
path = with_root original_path
|
70
|
+
|
71
|
+
option = append ? 'a' : 'w'
|
72
|
+
::File.open path, option do |out|
|
73
|
+
block.call Writer.new(out)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def delete_file path
|
78
|
+
path = with_root path
|
79
|
+
::File.delete path
|
80
|
+
end
|
81
|
+
|
82
|
+
# def move_file from, to
|
83
|
+
# FileUtils.mv from, to
|
84
|
+
# end
|
85
|
+
|
86
|
+
|
87
|
+
#
|
88
|
+
# Dir
|
89
|
+
#
|
90
|
+
def create_dir path
|
91
|
+
path = with_root path
|
92
|
+
::Dir.mkdir path
|
93
|
+
end
|
94
|
+
|
95
|
+
def delete_dir original_path
|
96
|
+
path = with_root original_path
|
97
|
+
::FileUtils.rm_r path
|
98
|
+
end
|
99
|
+
|
100
|
+
def each_entry path, query, &block
|
101
|
+
path = with_root path
|
102
|
+
|
103
|
+
if query
|
104
|
+
path_with_trailing_slash = path == '/' ? path : "#{path}/"
|
105
|
+
::Dir["#{path_with_trailing_slash}#{query}"].each do |absolute_path|
|
106
|
+
name = absolute_path.sub path_with_trailing_slash, ''
|
107
|
+
block.call name, ->{::File.directory?(absolute_path) ? :dir : :file}
|
108
|
+
# if ::File.directory? absolute_path
|
109
|
+
# block.call relative_path, :dir
|
110
|
+
# else
|
111
|
+
# block.call relative_path, :file
|
112
|
+
# end
|
113
|
+
end
|
114
|
+
else
|
115
|
+
::Dir.foreach path do |name|
|
116
|
+
next if name == '.' or name == '..'
|
117
|
+
block.call name, ->{::File.directory?("#{path}/#{name}") ? :dir : :file}
|
118
|
+
# if ::File.directory? "#{path}/#{relative_name}"
|
119
|
+
# block.call relative_name, :dir
|
120
|
+
# else
|
121
|
+
# block.call relative_name, :file
|
122
|
+
# end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# def efficient_dir_copy from, to, override
|
128
|
+
# return false if override # FileUtils.cp_r doesn't support this behaviour
|
129
|
+
#
|
130
|
+
# from.driver.open_fs do |from_fs|
|
131
|
+
# to.driver.open_fs do |to_fs|
|
132
|
+
# if from_fs.local? and to_fs.local?
|
133
|
+
# FileUtils.cp_r from.path, to.path
|
134
|
+
# true
|
135
|
+
# else
|
136
|
+
# false
|
137
|
+
# end
|
138
|
+
# end
|
139
|
+
# end
|
140
|
+
# end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Other
|
144
|
+
#
|
145
|
+
def local?; true end
|
146
|
+
|
147
|
+
def tmp &block
|
148
|
+
path = "/tmp/#{rand(10**6)}"
|
149
|
+
# tmp_dir = "#{::Dir.tmpdir}/#{rand(10**6)}"
|
150
|
+
if block
|
151
|
+
begin
|
152
|
+
::FileUtils.mkdir_p with_root(path)
|
153
|
+
block.call path
|
154
|
+
ensure
|
155
|
+
::FileUtils.rm_r with_root(path) if ::File.exist? with_root(path)
|
156
|
+
end
|
157
|
+
else
|
158
|
+
::FileUtils.mkdir_p with_root(path)
|
159
|
+
path
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def to_s; '' end
|
164
|
+
|
165
|
+
protected
|
166
|
+
def root
|
167
|
+
@root || raise('root not defined!')
|
168
|
+
end
|
169
|
+
|
170
|
+
def with_root path
|
171
|
+
path == '/' ? root : root + path
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
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
|
@@ -0,0 +1,253 @@
|
|
1
|
+
module Vfs
|
2
|
+
class Dir < Entry
|
3
|
+
#
|
4
|
+
# Container
|
5
|
+
#
|
6
|
+
def [] path
|
7
|
+
path = path.to_s
|
8
|
+
if path =~ /.+[\/]$/
|
9
|
+
path = path.sub /\/$/, ''
|
10
|
+
dir path
|
11
|
+
else
|
12
|
+
entry path
|
13
|
+
end
|
14
|
+
end
|
15
|
+
alias_method :/, :[]
|
16
|
+
|
17
|
+
|
18
|
+
#
|
19
|
+
# Attributes
|
20
|
+
#
|
21
|
+
alias_method :exist?, :dir?
|
22
|
+
|
23
|
+
|
24
|
+
#
|
25
|
+
# CRUD
|
26
|
+
#
|
27
|
+
def create options = {}
|
28
|
+
driver.open do
|
29
|
+
try = 0
|
30
|
+
begin
|
31
|
+
try += 1
|
32
|
+
driver.create_dir path
|
33
|
+
rescue StandardError => error
|
34
|
+
entry = self.entry
|
35
|
+
attrs = entry.get
|
36
|
+
if attrs and attrs[:file] #entry.exist?
|
37
|
+
entry.destroy
|
38
|
+
elsif attrs and attrs[:dir]
|
39
|
+
# dir already exist, no need to recreate it
|
40
|
+
return self
|
41
|
+
else
|
42
|
+
parent = self.parent
|
43
|
+
if parent.exist?
|
44
|
+
# some unknown error
|
45
|
+
raise error
|
46
|
+
else
|
47
|
+
parent.create(options)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
try < 2 ? retry : raise(error)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def destroy options = {}
|
58
|
+
destroy_entry :dir, :file
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
#
|
63
|
+
# Content
|
64
|
+
#
|
65
|
+
def entries *args, &block
|
66
|
+
raise "invalid arguments #{args.inspect}!" if args.size > 2
|
67
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
68
|
+
query = args.first
|
69
|
+
options[:bang] = true unless options.include? :bang
|
70
|
+
filter = options[:filter]
|
71
|
+
type_required = options[:type]
|
72
|
+
|
73
|
+
driver.open do
|
74
|
+
begin
|
75
|
+
list = []
|
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
|
+
|
84
|
+
entry = if type == :dir
|
85
|
+
dir(name)
|
86
|
+
elsif type == :file
|
87
|
+
file(name)
|
88
|
+
else
|
89
|
+
entry(name)
|
90
|
+
end
|
91
|
+
block ? block.call(entry) : (list << entry)
|
92
|
+
end
|
93
|
+
block ? nil : list
|
94
|
+
rescue StandardError => error
|
95
|
+
attrs = get
|
96
|
+
if attrs and attrs[:file]
|
97
|
+
raise Error, "can't query entries on File ('#{self}')!"
|
98
|
+
elsif attrs and attrs[:dir]
|
99
|
+
# some unknown error
|
100
|
+
raise error
|
101
|
+
else
|
102
|
+
# TODO2 remove :bang
|
103
|
+
raise Error, "'#{self}' not exist!" if options[:bang]
|
104
|
+
[]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
alias_method :each, :entries
|
110
|
+
|
111
|
+
def files *args, &block
|
112
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
113
|
+
|
114
|
+
options[:filter] = :file
|
115
|
+
args << options
|
116
|
+
entries *args, &block
|
117
|
+
end
|
118
|
+
|
119
|
+
def dirs *args, &block
|
120
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
121
|
+
|
122
|
+
options[:filter] = :dir
|
123
|
+
args << options
|
124
|
+
entries *args, &block
|
125
|
+
end
|
126
|
+
|
127
|
+
def include? name
|
128
|
+
entry[name].exist?
|
129
|
+
end
|
130
|
+
alias_method :has?, :include?
|
131
|
+
|
132
|
+
def empty?
|
133
|
+
catch :break do
|
134
|
+
entries{|e| throw :break, false}
|
135
|
+
true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
#
|
141
|
+
# Transfers
|
142
|
+
#
|
143
|
+
def copy_to to, options = {}
|
144
|
+
options[:bang] = true unless options.include? :bang
|
145
|
+
|
146
|
+
raise Error, "invalid argument, destination should be a Entry (#{to})!" unless to.is_a? Entry
|
147
|
+
raise Error, "you can't copy to itself" if self == to
|
148
|
+
|
149
|
+
target = if to.is_a? File
|
150
|
+
to.dir
|
151
|
+
elsif to.is_a? Dir
|
152
|
+
to.dir #(name)
|
153
|
+
elsif to.is_a? UniversalEntry
|
154
|
+
# raise "can't copy Dir to File ('#{self}')!" if to.file? and !options[:override]
|
155
|
+
to.dir #.create
|
156
|
+
else
|
157
|
+
raise "can't copy to unknown Entry!"
|
158
|
+
end
|
159
|
+
|
160
|
+
# efficient_dir_copy(target, options) || unefficient_dir_copy(target, options)
|
161
|
+
unefficient_dir_copy(target, options)
|
162
|
+
|
163
|
+
target
|
164
|
+
end
|
165
|
+
|
166
|
+
def move_to to, options = {}
|
167
|
+
copy_to to, options
|
168
|
+
destroy options
|
169
|
+
to
|
170
|
+
end
|
171
|
+
|
172
|
+
# class << self
|
173
|
+
# attr_accessor :dont_use_efficient_dir_copy
|
174
|
+
# end
|
175
|
+
|
176
|
+
protected
|
177
|
+
def unefficient_dir_copy to, options
|
178
|
+
to.create options
|
179
|
+
entries options.merge(type: true) do |e|
|
180
|
+
if e.is_a? Dir
|
181
|
+
e.copy_to to.dir(e.name), options
|
182
|
+
elsif e.is_a? File
|
183
|
+
e.copy_to to.file(e.name), options
|
184
|
+
else
|
185
|
+
raise 'internal error'
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
|
191
|
+
# def efficient_dir_copy to, options
|
192
|
+
# return false if self.class.dont_use_efficient_dir_copy
|
193
|
+
#
|
194
|
+
# driver.open do
|
195
|
+
# try = 0
|
196
|
+
# begin
|
197
|
+
# try += 1
|
198
|
+
# self.class.efficient_dir_copy(self, to, options[:override])
|
199
|
+
# rescue StandardError => error
|
200
|
+
# unknown_errors = 0
|
201
|
+
#
|
202
|
+
# attrs = get
|
203
|
+
# if attrs and attrs[:file]
|
204
|
+
# raise Error, "can't copy File as a Dir ('#{self}')!"
|
205
|
+
# elsif attrs and attrs[:dir]
|
206
|
+
# # some unknown error (but it also maybe caused by to be fixed error in 'to')
|
207
|
+
# unknown_errors += 1
|
208
|
+
# else
|
209
|
+
# raise Error, "'#{self}' not exist!" if options[:bang]
|
210
|
+
# return true
|
211
|
+
# end
|
212
|
+
#
|
213
|
+
# attrs = to.get
|
214
|
+
# if attrs and attrs[:file]
|
215
|
+
# if options[:override]
|
216
|
+
# to.destroy
|
217
|
+
# else
|
218
|
+
# raise Vfs::Error, "entry #{to} already exist!"
|
219
|
+
# end
|
220
|
+
# elsif attrs and attrs[:dir]
|
221
|
+
# unknown_errors += 1
|
222
|
+
# # if options[:override]
|
223
|
+
# # to.destroy
|
224
|
+
# # else
|
225
|
+
# # dir_already_exist = true
|
226
|
+
# # # raise Vfs::Error, "entry #{to} already exist!"
|
227
|
+
# # end
|
228
|
+
# else # parent not exist
|
229
|
+
# parent = to.parent
|
230
|
+
# if parent.exist?
|
231
|
+
# # some unknown error (but it also maybe caused by already fixed error in 'from')
|
232
|
+
# unknown_errors += 1
|
233
|
+
# else
|
234
|
+
# parent.create(options)
|
235
|
+
# end
|
236
|
+
# end
|
237
|
+
#
|
238
|
+
# raise error if unknown_errors > 1
|
239
|
+
# try < 2 ? retry : raise(error)
|
240
|
+
# end
|
241
|
+
# end
|
242
|
+
# end
|
243
|
+
#
|
244
|
+
# def self.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)
|
247
|
+
# } or
|
248
|
+
# to.driver.open{
|
249
|
+
# driver.respond_to?(:efficient_dir_copy) and driver.efficient_dir_copy(from, to, override)
|
250
|
+
# }
|
251
|
+
# end
|
252
|
+
end
|
253
|
+
end
|