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.
- data/Rakefile +2 -2
- data/lib/vfs.rb +18 -0
- data/lib/vfs/entries/dir.rb +272 -0
- data/lib/vfs/entries/entry.rb +127 -0
- data/lib/vfs/entries/file.rb +185 -0
- data/lib/vfs/entries/universal_entry.rb +24 -0
- data/lib/vfs/entry_proxy.rb +38 -0
- data/lib/vfs/error.rb +4 -0
- data/lib/vfs/integration/string.rb +19 -0
- data/lib/vfs/path.rb +125 -0
- data/lib/vfs/storages/hash_fs.rb +194 -0
- data/lib/vfs/storages/local.rb +138 -0
- data/lib/vfs/storages/specification.rb +127 -0
- data/lib/vfs/support.rb +2 -0
- data/readme.md +110 -14
- data/spec/container_spec.rb +31 -0
- data/spec/dir_spec.rb +224 -0
- data/spec/entry_spec.rb +24 -0
- data/spec/file_spec.rb +189 -0
- data/spec/path_spec.rb +121 -0
- data/spec/spec_helper.rb +2 -9
- data/spec/storages/hash_fs_spec.rb +10 -0
- data/spec/storages/local_spec.rb +10 -0
- data/spec/universal_entry_spec.rb +42 -0
- metadata +25 -23
- data/lib/old/ssh.rb +0 -11
- data/lib/rsh.rb +0 -19
- data/lib/rsh/box.rb +0 -182
- data/lib/rsh/box/marks.rb +0 -29
- data/lib/rsh/drivers/abstract.rb +0 -15
- data/lib/rsh/drivers/local.rb +0 -48
- data/lib/rsh/drivers/ssh.rb +0 -147
- data/lib/rsh/gems.rb +0 -2
- data/lib/rsh/support.rb +0 -30
- data/spec/abstract_driver.rb +0 -82
- data/spec/abstract_driver/dir/dir2/file +0 -0
- data/spec/abstract_driver/local_file +0 -1
- data/spec/box_spec.rb +0 -109
- data/spec/box_spec/dir/dir2/file +0 -0
- data/spec/box_spec/local_file +0 -1
- data/spec/config.example.yml +0 -4
- data/spec/config.yml +0 -5
- data/spec/local_driver_spec.rb +0 -9
- data/spec/ssh_driver_spec.rb +0 -15
@@ -0,0 +1,127 @@
|
|
1
|
+
# use '$ gem install ruby_ext' to install.
|
2
|
+
require 'rspec_ext'
|
3
|
+
require 'ruby_ext'
|
4
|
+
|
5
|
+
shared_examples_for 'vfs storage' do
|
6
|
+
before :each do
|
7
|
+
@storage.open_fs do |fs|
|
8
|
+
@tmp_dir = fs.tmp
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
after :each do
|
13
|
+
@storage.open_fs do |fs|
|
14
|
+
attrs = fs.attributes(@tmp_dir)
|
15
|
+
fs.delete_dir @tmp_dir if attrs && attrs[:dir]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should respond to :local?' do
|
20
|
+
@storage.open_fs{|fs| fs.should respond_to(:local?)}
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should have root dir' do
|
24
|
+
@storage.open_fs do |fs|
|
25
|
+
fs.attributes('/').subset(:file, :dir).should == {file: false, dir: true}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "files" do
|
30
|
+
before :each do
|
31
|
+
@remote_file = "#{@tmp_dir}/remote_file"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "file attributes" do
|
35
|
+
@storage.open_fs do |fs|
|
36
|
+
fs.attributes(@remote_file).should == {}
|
37
|
+
fs.write_file(@remote_file, false){|w| w.call 'something'}
|
38
|
+
attrs = fs.attributes(@remote_file)
|
39
|
+
fs.attributes(@remote_file).subset(:file, :dir).should == {file: true, dir: false}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "read, write & append" do
|
44
|
+
@storage.open_fs do |fs|
|
45
|
+
fs.write_file(@remote_file, false){|w| w.call 'something'}
|
46
|
+
fs.attributes(@remote_file)[:file].should be_true
|
47
|
+
|
48
|
+
data = ""
|
49
|
+
fs.read_file(@remote_file){|buff| data << buff}
|
50
|
+
data.should == 'something'
|
51
|
+
|
52
|
+
# append
|
53
|
+
fs.write_file(@remote_file, true){|w| w.call ' another'}
|
54
|
+
data = ""
|
55
|
+
fs.read_file(@remote_file){|buff| data << buff}
|
56
|
+
data.should == 'something another'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "delete_file" do
|
61
|
+
@storage.open_fs do |fs|
|
62
|
+
fs.write_file(@remote_file, false){|w| w.call 'something'}
|
63
|
+
fs.attributes(@remote_file)[:file].should be_true
|
64
|
+
fs.delete_file(@remote_file)
|
65
|
+
fs.attributes(@remote_file).should == {}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe 'directories' do
|
71
|
+
# before :each do
|
72
|
+
# @from_local, @remote_path, @to_local = "#{@local_dir}/dir", "#{@tmp_dir}/upload", "#{@local_dir}/download"
|
73
|
+
# end
|
74
|
+
|
75
|
+
before :each do
|
76
|
+
@remote_dir = "#{@tmp_dir}/some_dir"
|
77
|
+
end
|
78
|
+
|
79
|
+
it "directory_exist?, create_dir, delete_dir" do
|
80
|
+
@storage.open_fs do |fs|
|
81
|
+
fs.attributes(@remote_dir).should == {}
|
82
|
+
fs.create_dir(@remote_dir)
|
83
|
+
fs.attributes(@remote_dir).subset(:file, :dir).should == {file: false, dir: true}
|
84
|
+
fs.delete_dir(@remote_dir)
|
85
|
+
fs.attributes(@remote_dir).should == {}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should delete not-empty directories' do
|
90
|
+
@storage.open_fs do |fs|
|
91
|
+
fs.create_dir(@remote_dir)
|
92
|
+
fs.create_dir("#{@remote_dir}/dir")
|
93
|
+
fs.write_file("#{@remote_dir}/dir/file", false){|w| w.call 'something'}
|
94
|
+
fs.delete_dir(@remote_dir)
|
95
|
+
fs.attributes(@remote_dir).should == {}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'each' do
|
100
|
+
@storage.open_fs do |fs|
|
101
|
+
list = {}
|
102
|
+
fs.each(@tmp_dir){|path, type| list[path] = type}
|
103
|
+
list.should be_empty
|
104
|
+
|
105
|
+
dir, file = "#{@tmp_dir}/dir", "#{@tmp_dir}/file"
|
106
|
+
fs.create_dir(dir)
|
107
|
+
fs.write_file(file, false){|w| w.call 'something'}
|
108
|
+
|
109
|
+
list = {}
|
110
|
+
fs.each(@tmp_dir){|path, type| list[path] = type}
|
111
|
+
list.should == {'dir' => :dir, 'file' => :file}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# it "upload_directory & download_directory" do
|
116
|
+
# upload_path_check = "#{@remote_path}/dir2/file"
|
117
|
+
# check_attributes upload_path_check, nil
|
118
|
+
# fs.upload_directory(@from_local, @remote_path)
|
119
|
+
# check_attributes upload_path_check, file: true, dir: false
|
120
|
+
#
|
121
|
+
# download_path_check = "#{@to_local}/dir2/file"
|
122
|
+
# File.exist?(download_path_check).should be_false
|
123
|
+
# fs.download_directory(@remote_path, @to_local)
|
124
|
+
# File.exist?(download_path_check).should be_true
|
125
|
+
# end
|
126
|
+
end
|
127
|
+
end
|
data/lib/vfs/support.rb
ADDED
data/readme.md
CHANGED
@@ -1,21 +1,117 @@
|
|
1
|
-
#
|
1
|
+
# Vfs - Virtual File System
|
2
2
|
|
3
|
-
|
3
|
+
Handy and simple abstraction over any storage that can represent concept of File and Directory (or at least part of it).
|
4
|
+
The Vfs for File System Storages is the same as ActiveRecord is for Relational Databases.
|
4
5
|
|
5
|
-
|
6
|
+
Currently, there are following implementations available:
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
- local file system
|
9
|
+
- remote file system (over ssh)
|
10
|
+
|
11
|
+
## Goals
|
12
|
+
|
13
|
+
- **handy, simple and clean** API.
|
14
|
+
- **high performance** - the same as by using low-level storage API, there should be no extra calls **.
|
15
|
+
- same API for different storages (Local FS, SSH, Hadoop, or any other , ...).
|
16
|
+
- should work **simultaneously with different storages**.
|
17
|
+
- small codebase, easy to extend by others.
|
18
|
+
- simple storage-driver implementation, easy add new storage types (Hadoop DFS, LDAP, Document Oriented DB, In-Memory, ...).
|
19
|
+
|
20
|
+
** all methods should have the same performance as native system calls, except for :move and :rename. Right now they are implemented
|
21
|
+
ASAP by using copy+destroy approach, will be fixed as soon as I'll have free time to do it.
|
22
|
+
|
23
|
+
## Code samples:
|
24
|
+
gem 'vfs' # Virtual File System
|
25
|
+
require 'vfs'
|
26
|
+
|
27
|
+
gem 'vos' # Virtual Operating System
|
28
|
+
require 'vos'
|
29
|
+
|
30
|
+
|
31
|
+
# Connections, let's deploy our 'cool_app' project from our local box to remote server
|
32
|
+
server = Vfs::Box.new(host: 'cool_app.com', ssh: {user: 'me', password: 'secret'})
|
33
|
+
me = '~'.to_dir
|
34
|
+
|
35
|
+
cool_app = server['apps/cool_app']
|
36
|
+
projects = me['projects']
|
37
|
+
|
38
|
+
|
39
|
+
# Working with dirs, copying dir from any source to any destination (local/remote/custom_storage_type)
|
40
|
+
projects['cool_app'].copy_to cool_app
|
41
|
+
|
42
|
+
|
43
|
+
# Working with files
|
44
|
+
dbc = cool_app.file('config/database.yml') # <= the 'config' dir not exist yet
|
45
|
+
dbc.write("user: root\npassword: secret") # <= now the 'database.yml' and parent 'config' has been created
|
46
|
+
dbc.content =~ /database/ # => false, we forgot to add the database
|
47
|
+
dbc.append("\ndatabase: mysql") # let's do it
|
48
|
+
|
49
|
+
dbc.update do |content| # and add host info
|
50
|
+
content + "\nhost: cool_app.com "
|
51
|
+
end
|
52
|
+
|
53
|
+
projects['cool_app/config/database.yml']. # or just overwrite it with our local dev version
|
54
|
+
copy_to! dbc
|
55
|
+
|
56
|
+
# there are also streaming support (read/write/append), please go to specs for docs
|
57
|
+
|
58
|
+
|
59
|
+
# Checks
|
60
|
+
cool_app['config'].exist? # => true
|
61
|
+
cool_app.dir('config').exist? # => true
|
62
|
+
cool_app.file('config').exist? # => false
|
13
63
|
|
64
|
+
cool_app['config'].dir? # => true
|
65
|
+
cool_app['config'].file? # => false
|
66
|
+
|
67
|
+
|
68
|
+
# Navigation
|
69
|
+
config = cool_app['config']
|
70
|
+
config.parent # => </apps/cool_app>
|
71
|
+
config['../..'] # => </>
|
72
|
+
config['../..'].dir? # => true
|
73
|
+
|
74
|
+
cool_app.entries # => list of dirs and files, also support &block
|
75
|
+
cool_app.files # => list of files, also support &block
|
76
|
+
cool_app.dirs # => list of dirs, also support &block
|
77
|
+
|
78
|
+
|
79
|
+
# For more please go to specs (create/update/move/copy/destroy/...)
|
80
|
+
|
81
|
+
## Integration with [Vos][vos] (Virtual Operating System)
|
82
|
+
|
83
|
+
server['apps/cool_app'].bash 'rails production'
|
84
|
+
|
85
|
+
For more details please go to [Vos][vos] project page.
|
86
|
+
|
87
|
+
# Why?
|
88
|
+
|
89
|
+
To easy my work: with local FS, remote FS (cluster management, deployment automation), and some specific systems like Hadoop DFS.
|
90
|
+
|
91
|
+
Because the API of standard File/Dir/FileUtils classes are just terrible. And there's the reason for it - the goal of thouse tools
|
92
|
+
is to provide 1-to-1 clone of underlying OS API, instead of provididing handy tool.
|
93
|
+
|
94
|
+
And if you want to use remote FS - things are getting even worse and more complicated (Net::SSH & Net::SFTP use a little
|
95
|
+
different API than local FS, and you has to remember all thouse little quirks).
|
96
|
+
|
14
97
|
## TODO
|
15
98
|
|
16
|
-
|
17
|
-
|
18
|
-
-
|
19
|
-
-
|
99
|
+
### v 0.1 (all done)
|
100
|
+
|
101
|
+
- Vos: Dir.bash
|
102
|
+
- File.append
|
103
|
+
- list of entries/files/dirs
|
104
|
+
- support for efficient copy for Local and SSH storages
|
105
|
+
|
106
|
+
### v 0.2 (not started)
|
107
|
+
|
108
|
+
- efficient (not copy/destroy) versions of move_to, rename
|
109
|
+
- glob search for directories: Dir['**/*.yml']
|
110
|
+
- access via attributes and helpers for unix chmod
|
111
|
+
- add storages: remote FS over HTTP.
|
112
|
+
|
113
|
+
### future
|
114
|
+
|
115
|
+
- add storages: Hadoop DFS, MongoDB, Amazon S3
|
20
116
|
|
21
|
-
[
|
117
|
+
[vos]: http://github.com/alexeypetrushin/vos
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Container' do
|
4
|
+
before :each do
|
5
|
+
@fs = '/'.to_entry_on(Vfs::Storages::HashFs.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should threat paths as UniversalEntry except it ends with '/'" do
|
9
|
+
@fs.should_receive(:entry).with('/a/b')
|
10
|
+
@fs['/a/b']
|
11
|
+
|
12
|
+
@fs.should_receive(:dir).with('/a/b')
|
13
|
+
@fs['/a/b/']
|
14
|
+
end
|
15
|
+
|
16
|
+
it '/' do
|
17
|
+
@fs[:some_path].should == @fs / :some_path
|
18
|
+
end
|
19
|
+
|
20
|
+
it "UniversalEntry should be wrapped inside of proxy, Dir and File should not" do
|
21
|
+
-> {@fs.dir.proxy?}.should raise_error(NoMethodError)
|
22
|
+
-> {@fs.file.proxy?}.should raise_error(NoMethodError)
|
23
|
+
@fs.entry.proxy?.should be_true
|
24
|
+
end
|
25
|
+
|
26
|
+
it "sometimes it also should inexplicitly guess that path is a Dir instead of UniversalEntry (but still wrap it inside of Proxy)" do
|
27
|
+
dir = @fs['/a/..']
|
28
|
+
dir.proxy?.should be_true
|
29
|
+
dir.should be_a(Vfs::Dir)
|
30
|
+
end
|
31
|
+
end
|
data/spec/dir_spec.rb
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Dir' do
|
4
|
+
before :each do
|
5
|
+
@fs = '/'.to_entry_on(Vfs::Storages::HashFs.new)
|
6
|
+
@path = @fs['/a/b/c']
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'existence' do
|
10
|
+
it "should check only dirs" do
|
11
|
+
@path.should_not exist
|
12
|
+
@path.file.create
|
13
|
+
@path.should be_file
|
14
|
+
@path.dir.should_not exist
|
15
|
+
@path.dir.create!
|
16
|
+
@path.should be_dir
|
17
|
+
@path.dir.should exist
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should not respond to read and write methods" do
|
22
|
+
-> {@path.dir.read}.should raise_error(NoMethodError)
|
23
|
+
-> {@path.dir.write}.should raise_error(NoMethodError)
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'create' do
|
27
|
+
it 'should be chainable' do
|
28
|
+
@path.dir.create.should == @path
|
29
|
+
@path.dir.create!.should == @path
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should create parent dirs if not exists' do
|
33
|
+
@path.parent.should_not exist
|
34
|
+
@path.dir.create
|
35
|
+
@path.should be_dir
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should silently exit if dir already exist' do
|
39
|
+
@path.dir.create
|
40
|
+
@path.dir.create
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should override existing file if override specified' do
|
44
|
+
@path.file.create
|
45
|
+
@path.should be_file
|
46
|
+
-> {@path.dir.create}.should raise_error(Vfs::Error, /exist/)
|
47
|
+
@path.dir.create!
|
48
|
+
@path.should be_dir
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should override existing dir if override specified' do
|
52
|
+
@path.dir.create
|
53
|
+
@path.should be_dir
|
54
|
+
@path.dir.create!
|
55
|
+
@path.should be_dir
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'destroying' do
|
60
|
+
it "should raise error if it's trying to destroy a file (unless force specified)" do
|
61
|
+
@path.file.create
|
62
|
+
-> {@path.dir.destroy}.should raise_error(Vfs::Error, /can't destroy File/)
|
63
|
+
@path.dir.destroy!
|
64
|
+
@path.entry.should_not exist
|
65
|
+
end
|
66
|
+
|
67
|
+
it "shouldn't raise if dir not exist" do
|
68
|
+
@path.dir.destroy
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should destroy recursivelly' do
|
72
|
+
dir = @path.dir
|
73
|
+
dir.create
|
74
|
+
dir.file('file').write 'something'
|
75
|
+
dir.dir('dir').create.tap do |dir|
|
76
|
+
dir.file('file2').write 'something2'
|
77
|
+
end
|
78
|
+
|
79
|
+
dir.destroy
|
80
|
+
dir.should_not exist
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should be chainable' do
|
84
|
+
@path.dir.destroy.should == @path
|
85
|
+
@path.dir.destroy!.should == @path
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'content' do
|
90
|
+
before :each do
|
91
|
+
@path.dir('dir').create
|
92
|
+
@path.dir('dir/another_dir').create
|
93
|
+
@path.file('file').create
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'entries' do
|
97
|
+
-> {@path['non_existing'].entries}.should raise_error(Vfs::Error, /not exist/)
|
98
|
+
@path['non_existing'].entries(bang: false).should == []
|
99
|
+
@path.entries.to_set.should be_eql([@path.dir('dir'), @path.file('file')].to_set)
|
100
|
+
list = []
|
101
|
+
@path.entries{|e| list << e}
|
102
|
+
list.to_set.should be_eql([@path.dir('dir'), @path.file('file')].to_set)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should raise error if trying :entries on file' do
|
106
|
+
@path.file('some_file').create
|
107
|
+
-> {@path.dir('some_file').entries}.should raise_error(/File/)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'files' do
|
111
|
+
@path.files.should be_eql([@path.file('file')])
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'dirs' do
|
115
|
+
@path.dirs.should be_eql([@path.dir('dir')])
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'has? & include?' do
|
119
|
+
@path.include?('dir').should be_true
|
120
|
+
@path.include?('dir/another_dir').should be_true
|
121
|
+
@path.include?('file').should be_true
|
122
|
+
@path.include?('non_existing').should be_false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe 'copying' do
|
127
|
+
before :each do
|
128
|
+
@from = @path.dir
|
129
|
+
@from.create
|
130
|
+
@from.file('file').write 'something'
|
131
|
+
@from.dir('dir').create.tap do |dir|
|
132
|
+
dir.file('file2').write 'something2'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should not copy to itself' do
|
137
|
+
-> {@from.copy_to @from}.should raise_error(Vfs::Error, /itself/)
|
138
|
+
end
|
139
|
+
|
140
|
+
def check_copy_for to, error_re1, error_re2
|
141
|
+
begin
|
142
|
+
target = @from.copy_to to
|
143
|
+
target['file'].read.should == 'something'
|
144
|
+
target['dir/file2'].read.should == 'something2'
|
145
|
+
target.should == to
|
146
|
+
rescue Exception => e
|
147
|
+
raise e unless e.message =~ error_re1
|
148
|
+
end
|
149
|
+
|
150
|
+
@from['dir/file2'].write! 'another'
|
151
|
+
-> {@from.copy_to to}.should raise_error(Vfs::Error, error_re2)
|
152
|
+
target = @from.copy_to! to
|
153
|
+
target['file'].read.should == 'something'
|
154
|
+
target['dir/file2'].read.should == 'another'
|
155
|
+
end
|
156
|
+
|
157
|
+
describe 'general copy' do
|
158
|
+
before :each do
|
159
|
+
# we using here another HashFs storage, to prevent :effective_dir_copy to be used
|
160
|
+
@to = '/'.to_entry_on(Vfs::Storages::HashFs.new)['to']
|
161
|
+
|
162
|
+
@from.storage.should_not_receive(:for_spec_helper_effective_copy_used)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should copy to file (and overwrite if forced)' do
|
166
|
+
check_copy_for @to.file, /can't copy Dir to File/, /can't copy Dir to File/
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'should copy to dir (and overwrite if forced)' do
|
170
|
+
check_copy_for @to.dir, nil, /exist/
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'should copy to UniversalEntry (and overwrite if forced)' do
|
174
|
+
check_copy_for @to.entry, nil, /exist/
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe 'effective copy' do
|
179
|
+
before :each do
|
180
|
+
# we using the same HashFs storage, so :effective_dir_copy will be used
|
181
|
+
@to = @fs['to']
|
182
|
+
|
183
|
+
@from.storage.should_receive(:for_spec_helper_effective_copy_used).at_least(1).times
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'should copy to file (and overwrite if forced)' do
|
187
|
+
check_copy_for @to.file, /can't copy Dir to File/, /can't copy Dir to File/
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'should copy to dir (and overwrite if forced)' do
|
191
|
+
check_copy_for @to.dir, nil, /exist/
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'should copy to UniversalEntry (and overwrite if forced)' do
|
195
|
+
check_copy_for @to.entry, nil, /exist/
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'should be chainable' do
|
200
|
+
to = @fs['to']
|
201
|
+
@from.copy_to(to).should == to
|
202
|
+
@from.copy_to!(to).should == to
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe 'moving' do
|
207
|
+
it 'move_to' do
|
208
|
+
from, to = @path.file('from'), @path.file('to')
|
209
|
+
from.should_receive(:copy_to).with(to, {})
|
210
|
+
from.should_receive(:destroy).with({})
|
211
|
+
from.move_to to
|
212
|
+
|
213
|
+
from.should_receive(:move_to).with(to, override: true)
|
214
|
+
from.move_to! to
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'should be chainable' do
|
218
|
+
from, to = @path.dir('from').create, @path.dir('to')
|
219
|
+
from.move_to(to).should == to
|
220
|
+
from.create
|
221
|
+
from.move_to!(to).should == to
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|