ruby_clone 1.0.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/.gitignore +18 -0
- data/Gemfile +4 -0
- data/Guardfile +10 -0
- data/LICENSE +22 -0
- data/README.md +329 -0
- data/Rakefile +2 -0
- data/bin/ruby_clone +16 -0
- data/lib/ruby_clone/backup_utils.rb +41 -0
- data/lib/ruby_clone/dsl.rb +79 -0
- data/lib/ruby_clone/options.rb +38 -0
- data/lib/ruby_clone/profile.rb +128 -0
- data/lib/ruby_clone/rsync.rb +94 -0
- data/lib/ruby_clone/version.rb +3 -0
- data/lib/ruby_clone.rb +11 -0
- data/ruby_clone.gemspec +21 -0
- data/spec/ruby_clone/backup_utils_spec.rb +82 -0
- data/spec/ruby_clone/dsl_spec.rb +259 -0
- data/spec/ruby_clone/options_spec.rb +60 -0
- data/spec/ruby_clone/profile_spec.rb +222 -0
- data/spec/ruby_clone/rsync_spec.rb +309 -0
- data/spec/spec_helper.rb +1 -0
- metadata +106 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'pty'
|
2
|
+
|
3
|
+
module RubyClone
|
4
|
+
|
5
|
+
class RSync
|
6
|
+
|
7
|
+
attr_writer :dry_run
|
8
|
+
|
9
|
+
def initialize(output)
|
10
|
+
@configurations = { options: '-Cavh --stats --progress', show_command: true, show_output: true }
|
11
|
+
@exclude_patterns = []
|
12
|
+
@include_patterns = []
|
13
|
+
@output = output
|
14
|
+
@profiles = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def profiles=(profile)
|
18
|
+
@profiles[profile.name] = profile
|
19
|
+
@last_profile = profile
|
20
|
+
end
|
21
|
+
|
22
|
+
def last_profile
|
23
|
+
@last_profile
|
24
|
+
end
|
25
|
+
|
26
|
+
def update_configurations(configurations)
|
27
|
+
@configurations.merge! configurations
|
28
|
+
end
|
29
|
+
|
30
|
+
def exclude_pattern=(path)
|
31
|
+
@exclude_patterns << path
|
32
|
+
end
|
33
|
+
|
34
|
+
def include_pattern=(path)
|
35
|
+
@include_patterns << path
|
36
|
+
end
|
37
|
+
|
38
|
+
def rsync_command(profile_name)
|
39
|
+
profile = @profiles[profile_name.to_s]
|
40
|
+
|
41
|
+
if profile
|
42
|
+
raise SyntaxError, "Empty Profile not allowed for profile #{profile} with no 'from folder'" unless profile.from_folder
|
43
|
+
raise SyntaxError, "Empty Profile not allowed for profile #{profile} with no 'to folder'" unless profile.to_folder
|
44
|
+
|
45
|
+
create_rsync_command profile
|
46
|
+
else
|
47
|
+
raise ArgumentError, "Profile #{profile_name} not found"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def run(profile_name)
|
52
|
+
command = rsync_command(profile_name)
|
53
|
+
@output.puts "\n#{command}\n\n" if @configurations[:show_command]
|
54
|
+
|
55
|
+
pty = @pty || PTY
|
56
|
+
|
57
|
+
pty.spawn(command) do |r, w, pid|
|
58
|
+
r.each { |line| print line } if @configurations[:show_output]
|
59
|
+
end
|
60
|
+
|
61
|
+
profile = @profiles[profile_name.to_s]
|
62
|
+
profile.to_folder.delete_files
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def create_rsync_command(profile)
|
68
|
+
command = "rsync #{@configurations[:options]} "
|
69
|
+
|
70
|
+
command << "-n " if @dry_run
|
71
|
+
|
72
|
+
command << @include_patterns.map { |e| "--include=#{e}" }.join(" ") + " "
|
73
|
+
command << @exclude_patterns.map { |e| "--exclude=#{e}" }.join(" ")
|
74
|
+
|
75
|
+
command << " #{profile.from_folder.to_command}"
|
76
|
+
command << " #{profile.to_folder.to_command}"
|
77
|
+
command << " #{create_ssh_command(profile)}"
|
78
|
+
|
79
|
+
command << " #{profile.from_folder} #{profile.to_folder}"
|
80
|
+
command.gsub(/\s+/, " ")
|
81
|
+
end
|
82
|
+
|
83
|
+
def create_ssh_command(profile)
|
84
|
+
from_folder = profile.from_folder
|
85
|
+
to_folder = profile.to_folder
|
86
|
+
|
87
|
+
if from_folder.ssh? && to_folder.ssh?
|
88
|
+
raise SyntaxError, 'The source and destination cannot both be remote.'
|
89
|
+
elsif from_folder.ssh? || to_folder.ssh?
|
90
|
+
"-e ssh"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/ruby_clone.rb
ADDED
data/ruby_clone.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/ruby_clone/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Frederico Benevides"]
|
6
|
+
gem.email = ["fredbene@gmail.com"]
|
7
|
+
gem.description = %q{Ruby clone is a command line tool to work with Rsync using DSL!}
|
8
|
+
gem.summary = %q{Ruby_clone is high level script of Rsync. Use Ruby DSL to work with RSync! }
|
9
|
+
gem.homepage = "http://github.com/fredericobenevides/ruby_clone"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "ruby_clone"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = RubyClone::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency "guard", "~> 1.3.2"
|
19
|
+
gem.add_development_dependency "guard-rspec", "~> 1.2.1"
|
20
|
+
gem.add_development_dependency "rspec", "~> 2.11.0"
|
21
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module RubyClone
|
4
|
+
|
5
|
+
describe BackupUtils do
|
6
|
+
|
7
|
+
describe "Deleting files" do
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
@ruby_clone_suffix = '_rbcl'
|
11
|
+
|
12
|
+
@paths = [ "/my_path/sub_path1/file1#{@ruby_clone_suffix}_20120921",
|
13
|
+
"/my_path/sub_path1/file1#{@ruby_clone_suffix}_20120922",
|
14
|
+
"/my_path/sub_path1/file1#{@ruby_clone_suffix}_20120923",
|
15
|
+
|
16
|
+
"/my_path/sub_path1/file2#{@ruby_clone_suffix}_20120922",
|
17
|
+
"/my_path/sub_path2/file1#{@ruby_clone_suffix}_20120922",
|
18
|
+
"/my_path/sub_path2/file2#{@ruby_clone_suffix}_20120923"]
|
19
|
+
|
20
|
+
File.stub(:exists?).with(any_args()).and_return(true)
|
21
|
+
|
22
|
+
FileTest.stub(:file?).with(any_args()).and_return false
|
23
|
+
FileTest.stub(:file?).with(@paths[0]).and_return true
|
24
|
+
FileTest.stub(:file?).with(@paths[1]).and_return true
|
25
|
+
FileTest.stub(:file?).with(@paths[2]).and_return true
|
26
|
+
FileTest.stub(:file?).with(@paths[3]).and_return true
|
27
|
+
FileTest.stub(:file?).with(@paths[4]).and_return true
|
28
|
+
FileTest.stub(:file?).with(@paths[5]).and_return true
|
29
|
+
|
30
|
+
Find.stub(:find).and_yield('/my_path').and_yield('/my_path/sub_path1')
|
31
|
+
.and_yield(@paths[0]).and_yield(@paths[1]).and_yield(@paths[2]).and_yield(@paths[3])
|
32
|
+
.and_yield('/my_path/sub_path2')
|
33
|
+
.and_yield(@paths[4]).and_yield(@paths[5])
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "valid" do
|
38
|
+
|
39
|
+
it "should create a hash with [path_files_without_suffix, path_files] that points to the files with same name" do
|
40
|
+
paths = BackupUtils.find_files_with_same_name '/my_path', @ruby_clone_suffix
|
41
|
+
|
42
|
+
paths.should == { '/my_path/sub_path1/file1' => [@paths[0], @paths[1], @paths[2]],
|
43
|
+
'/my_path/sub_path1/file2' => [@paths[3]],
|
44
|
+
'/my_path/sub_path2/file1' => [@paths[4]],
|
45
|
+
'/my_path/sub_path2/file2' => [@paths[5]] }
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should delete old files with same name but different 'suffix' when passed the limit" do
|
49
|
+
FileUtils.should_receive(:remove_entry).with(@paths[0])
|
50
|
+
FileUtils.should_receive(:remove_entry).with(@paths[1])
|
51
|
+
FileUtils.should_not_receive(:remove_entry).with(@paths[2])
|
52
|
+
|
53
|
+
BackupUtils.delete_files('/my_path', @ruby_clone_suffix, 1)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "invalid" do
|
59
|
+
|
60
|
+
it "should not delete files with path that doesn't exist" do
|
61
|
+
File.stub(:exists?).with(any_args()).and_return(false)
|
62
|
+
Find.should_not_receive(:find)
|
63
|
+
|
64
|
+
BackupUtils.delete_files('/my_path', @ruby_clone_suffix, 5)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should not delete files with same name but different 'suffix' when it didn't pass the limit" do
|
68
|
+
FileUtils.should_not_receive(:remove_entry)
|
69
|
+
BackupUtils.delete_files('/my_path', @ruby_clone_suffix, 5)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should not delete files with same name but different 'suffix' when the found files is on the limit" do
|
73
|
+
FileUtils.should_not_receive(:remove_entry)
|
74
|
+
BackupUtils.delete_files('/my_path', @ruby_clone_suffix, 3)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module RubyClone
|
4
|
+
|
5
|
+
describe "DSL" do
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
class DummyClass
|
9
|
+
end
|
10
|
+
DummyClass.extend RubyClone::DSL
|
11
|
+
|
12
|
+
@rsync_options = '-Cavh --stats --progress'
|
13
|
+
@rsync_command = "rsync #{@rsync_options}"
|
14
|
+
@folders = "/from_folder /to_folder"
|
15
|
+
end
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
@rsync = DummyClass.rsync_new_instance
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "Creating a Profile" do
|
22
|
+
|
23
|
+
describe "valid" do
|
24
|
+
|
25
|
+
it "should create the following command 'rsync -Cav --stats /from_folder /to_folder'" do
|
26
|
+
DummyClass.profile('backup1') do
|
27
|
+
DummyClass.from('/from_folder')
|
28
|
+
DummyClass.to('/to_folder')
|
29
|
+
end
|
30
|
+
|
31
|
+
@rsync.rsync_command('backup1').should == "#{@rsync_command} #{@folders}"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "invalid" do
|
37
|
+
|
38
|
+
it "should raise SyntaxError with message 'Empty Profile not allowed' for profile with no block" do
|
39
|
+
lambda do
|
40
|
+
DummyClass.profile('backup')
|
41
|
+
end.should raise_error(SyntaxError, 'Empty Profile not allowed')
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should raise SyntaxError with message 'Empty Profile not allowed' for empty block" do
|
45
|
+
lambda do
|
46
|
+
DummyClass.profile('backup') { }
|
47
|
+
end.should raise_error(SyntaxError, 'Empty Profile not allowed')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#config" do
|
53
|
+
|
54
|
+
it "should as default have a default configurations" do
|
55
|
+
|
56
|
+
DummyClass.profile('backup1') do
|
57
|
+
DummyClass.from('/from_folder')
|
58
|
+
DummyClass.to('/to_folder')
|
59
|
+
end
|
60
|
+
|
61
|
+
@rsync.instance_eval { @configurations }.should == { options: @rsync_options, show_command: true, show_output: true }
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should change the default configurations when the config has new values" do
|
65
|
+
DummyClass.config options: '-Cav', show_command: false, show_output: false
|
66
|
+
|
67
|
+
DummyClass.profile('backup1') do
|
68
|
+
DummyClass.from('/from_folder')
|
69
|
+
DummyClass.to('/to_folder')
|
70
|
+
end
|
71
|
+
|
72
|
+
@rsync.instance_eval { @configurations }.should == { options: '-Cav', show_command: false, show_output: false }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#from" do
|
77
|
+
|
78
|
+
it "should create exclude paths just for the profile 'backup1' if exclude DSL is not on the top" do
|
79
|
+
DummyClass.profile('backup1') do
|
80
|
+
DummyClass.from('/from_folder') do
|
81
|
+
DummyClass.exclude_pattern '/exclude_pattern1'
|
82
|
+
end
|
83
|
+
DummyClass.to('/to_folder')
|
84
|
+
end
|
85
|
+
|
86
|
+
DummyClass.profile('backup2') do
|
87
|
+
DummyClass.from('/from_folder')
|
88
|
+
DummyClass.to('/to_folder')
|
89
|
+
end
|
90
|
+
|
91
|
+
@rsync.rsync_command('backup1').should == "#{@rsync_command} --exclude=/exclude_pattern1 #{@folders}"
|
92
|
+
@rsync.rsync_command('backup2').should == "#{@rsync_command} #{@folders}"
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should create include just for the profile 'backup1' if include_pattern DSL is set up only in the profile" do
|
96
|
+
DummyClass.profile('backup1') do
|
97
|
+
DummyClass.from('/from_folder') do
|
98
|
+
DummyClass.include_pattern '/include_pattern1'
|
99
|
+
end
|
100
|
+
DummyClass.to('/to_folder')
|
101
|
+
end
|
102
|
+
|
103
|
+
DummyClass.profile('backup2') do
|
104
|
+
DummyClass.from('/from_folder')
|
105
|
+
DummyClass.to('/to_folder')
|
106
|
+
end
|
107
|
+
|
108
|
+
@rsync.rsync_command('backup1').should == "#{@rsync_command} --include=/include_pattern1 #{@folders}"
|
109
|
+
@rsync.rsync_command('backup2').should == "#{@rsync_command} #{@folders}"
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should have the following options '-e ssh user@server:/from_folder /to_folder' when is ssh" do
|
113
|
+
DummyClass.profile('backup1') do
|
114
|
+
DummyClass.from('/from_folder', ssh: "user@server")
|
115
|
+
DummyClass.to('/to_folder')
|
116
|
+
end
|
117
|
+
|
118
|
+
@rsync.rsync_command('backup1').should == "#{@rsync_command} -e ssh user@server:/from_folder /to_folder"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "#to" do
|
123
|
+
|
124
|
+
it "should have '--delete' when options 'delete' is set up" do
|
125
|
+
DummyClass.profile('backup1') do
|
126
|
+
DummyClass.from('/from_folder')
|
127
|
+
DummyClass.to('/to_folder', delete: true)
|
128
|
+
end
|
129
|
+
|
130
|
+
@rsync.rsync_command('backup1').should == "#{@rsync_command} --delete #{@folders}"
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should have '--delete-excluded' when options 'delete_excluded' is set up" do
|
134
|
+
DummyClass.profile('backup1') do
|
135
|
+
DummyClass.from('/from_folder')
|
136
|
+
DummyClass.to('/to_folder', delete_excluded: true)
|
137
|
+
end
|
138
|
+
|
139
|
+
@rsync.rsync_command('backup1').should == "#{@rsync_command} --delete-excluded #{@folders}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "#exclude" do
|
144
|
+
|
145
|
+
it "should create exclude paths for all profiles if exclude DSL is on the top" do
|
146
|
+
DummyClass.exclude_pattern '/exclude_top_path'
|
147
|
+
|
148
|
+
DummyClass.profile('backup1') do
|
149
|
+
DummyClass.from('/from_folder')
|
150
|
+
DummyClass.to('/to_folder')
|
151
|
+
end
|
152
|
+
|
153
|
+
DummyClass.profile('backup2') do
|
154
|
+
DummyClass.from('/from_folder')
|
155
|
+
DummyClass.to('/to_folder')
|
156
|
+
end
|
157
|
+
|
158
|
+
@rsync.rsync_command('backup1').should == "#{@rsync_command} --exclude=/exclude_top_path #{@folders}"
|
159
|
+
@rsync.rsync_command('backup2').should == "#{@rsync_command} --exclude=/exclude_top_path #{@folders}"
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should include the exclude path from the top and profile if exclude DSL are set up in the top and in profile" do
|
163
|
+
DummyClass.exclude_pattern 'exclude_top_path'
|
164
|
+
|
165
|
+
DummyClass.profile('backup1') do
|
166
|
+
DummyClass.from('/from_folder') do
|
167
|
+
DummyClass.exclude_pattern'/exclude_pattern1'
|
168
|
+
end
|
169
|
+
DummyClass.to('/to_folder')
|
170
|
+
end
|
171
|
+
|
172
|
+
DummyClass.profile('backup2') do
|
173
|
+
DummyClass.from('/from_folder')
|
174
|
+
DummyClass.to('/to_folder')
|
175
|
+
end
|
176
|
+
|
177
|
+
@rsync.rsync_command('backup1').should == "#{@rsync_command} --exclude=exclude_top_path --exclude=/exclude_pattern1 #{@folders}"
|
178
|
+
@rsync.rsync_command('backup2').should == "#{@rsync_command} --exclude=exclude_top_path #{@folders}"
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
describe "#include_pattern" do
|
184
|
+
|
185
|
+
it "should create include for all profiles if include_pattern DSL is on the top" do
|
186
|
+
DummyClass.include_pattern '/include_top_path'
|
187
|
+
|
188
|
+
DummyClass.profile('backup1') do
|
189
|
+
DummyClass.from('/from_folder')
|
190
|
+
DummyClass.to('/to_folder')
|
191
|
+
end
|
192
|
+
|
193
|
+
DummyClass.profile('backup2') do
|
194
|
+
DummyClass.from('/from_folder')
|
195
|
+
DummyClass.to('/to_folder')
|
196
|
+
end
|
197
|
+
|
198
|
+
@rsync.rsync_command('backup1').should == "#{@rsync_command} --include=/include_top_path #{@folders}"
|
199
|
+
@rsync.rsync_command('backup2').should == "#{@rsync_command} --include=/include_top_path #{@folders}"
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should create the include on the top and in profile if include_pattern DSL are set up in the top and in profile" do
|
203
|
+
DummyClass.include_pattern '/include_top_path'
|
204
|
+
|
205
|
+
DummyClass.profile('backup1') do
|
206
|
+
DummyClass.from('/from_folder') do
|
207
|
+
DummyClass.include_pattern '/include_pattern1'
|
208
|
+
end
|
209
|
+
DummyClass.to('/to_folder')
|
210
|
+
end
|
211
|
+
|
212
|
+
DummyClass.profile('backup2') do
|
213
|
+
DummyClass.from('/from_folder')
|
214
|
+
DummyClass.to('/to_folder')
|
215
|
+
end
|
216
|
+
|
217
|
+
@rsync.rsync_command('backup1').should == "#{@rsync_command} --include=/include_top_path --include=/include_pattern1 #{@folders}"
|
218
|
+
@rsync.rsync_command('backup2').should == "#{@rsync_command} --include=/include_top_path #{@folders}"
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
describe "#backup" do
|
224
|
+
|
225
|
+
it "should include the backup commands when the backup is set up" do
|
226
|
+
DummyClass.profile('backup1') do
|
227
|
+
DummyClass.from('/from_folder')
|
228
|
+
DummyClass.to('/to_folder') do
|
229
|
+
DummyClass.backup('/backup_folder')
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
@rsync.rsync_command('backup1').should =~ /#{@rsync_command} -b --suffix=_rbcl_\d{8} --backup-dir=\/backup_folder #{@folders}/
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should include the suffix when the option suffix: is set up" do
|
237
|
+
DummyClass.profile('backup1') do
|
238
|
+
DummyClass.from('/from_folder')
|
239
|
+
DummyClass.to('/to_folder') do
|
240
|
+
DummyClass.backup('/backup_folder', suffix: '_my_suffix')
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
@rsync.rsync_command('backup1').should == "#{@rsync_command} -b --suffix=_rbcl_my_suffix --backup-dir=/backup_folder #{@folders}"
|
245
|
+
end
|
246
|
+
|
247
|
+
it "should not include any suffix if the option disable_suffix is true" do
|
248
|
+
DummyClass.profile('backup1') do
|
249
|
+
DummyClass.from('/from_folder')
|
250
|
+
DummyClass.to('/to_folder') do
|
251
|
+
DummyClass.backup('/backup_folder', suffix: '_my_suffix', disable_suffix: true)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
@rsync.rsync_command('backup1').should == "#{@rsync_command} -b --backup-dir=/backup_folder #{@folders}"
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module RubyClone
|
5
|
+
|
6
|
+
describe Options do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@output = StringIO.new
|
10
|
+
@options = Options.new(@output)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "Help messages" do
|
14
|
+
|
15
|
+
it "should show if ARGV is empty" do
|
16
|
+
opts = @options.parse([])
|
17
|
+
opts.on.to_s.should =~ /-h, --help\s+This message.*/
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should show if ARGV have the option '-h'" do
|
21
|
+
@options.parse(%w[-h])
|
22
|
+
|
23
|
+
@output.seek 0
|
24
|
+
@output.read.should =~ /-h, --help\s+This message.*/
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not show if ARGV have another option that exists and is not '-h'" do
|
28
|
+
@options.parse(%w[-b /my_file])
|
29
|
+
|
30
|
+
@output.seek 0
|
31
|
+
@output.read.should_not =~ /-h, --help\s+This message.*/
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should have the banner with message 'Usage: ruby_clone [options] profile'" do
|
35
|
+
opts = @options.parse([])
|
36
|
+
opts.banner.should == 'Usage: ruby_clone [options] profile'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "Backup file" do
|
41
|
+
|
42
|
+
it "should as default the backup file path be '~/.ruby_clone'" do
|
43
|
+
@options.parse(%w[backup_profile])
|
44
|
+
@options.configurations[:backup_file].should == '~/.ruby_clone'
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should set a new backup file location with the options is '-b'" do
|
48
|
+
@options.parse(%w[-b /my_file backup_profile])
|
49
|
+
@options.configurations[:backup_file].should == '/my_file'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should set dry-run mode when the option is '-d'" do
|
54
|
+
@options.parse(%w[-d])
|
55
|
+
@options.configurations[:dry_run].should be_true
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|