jmstacey-cfbackup 0.5.0 → 0.6.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/CHANGELOG.markdown CHANGED
@@ -1,6 +1,19 @@
1
1
  CFBackup ChangeLog
2
2
  ==================
3
3
 
4
+ 0.6.0 2009-05-03
5
+ -----------------
6
+ * Added example_scripts to RDoc.
7
+ * gem creates cfconfig.yml in /etc for reference
8
+ * CFBackup looks in multiple places for config file
9
+ * Hidden directory in $HOME ($HOME/.cfconfig.yml)
10
+ * Non-hidden in current directory (./cfconfig.yml)
11
+ * In /etc/cfconfig.yml
12
+ * Refactoring and new usage with --action push|pull|delete
13
+ * Pulling files implemented
14
+ * Deleting files implemented
15
+ * Initial unit tests completed
16
+
4
17
  0.5.0 2009-04-18
5
18
  -----------------
6
19
 
data/README.markdown CHANGED
@@ -1,9 +1,7 @@
1
1
  CFBackup
2
2
  =========
3
3
 
4
- CFBackup is a small ruby program that transfers files or directories from the
5
- local machine to a Cloud Files container. It is meant to serve as a useful tool
6
- for automated backups.
4
+ CFBackup is a small ruby program that transfers files or directories from the local machine to a Cloud Files container. It is meant to serve as a useful tool for automated backups.
7
5
 
8
6
  Features
9
7
  -----------
@@ -11,21 +9,50 @@ Features
11
9
  * Backup a single file or directory (recursion uses pseudo directories)
12
10
  * Pipe data straight to container
13
11
  * Free transfers over local Rackspace network for Slicehost/Cloud Server
14
- customers in DFW1 datacenter
12
+ customers in DFW1 datacenter
15
13
 
16
14
  Requirements
17
15
  --------------
18
16
 
19
- TODO: Complete this area
20
17
  * ruby-cloudfiles
21
18
 
19
+ Notes:
20
+ * If you install CFBackup as a gem, all of the dependencies _should_ automatically be installed for you.
21
+ * Ubuntu Users: The Ubuntu rubygems package will installs executables outside your normal PATH. You will
22
+ need to update it or create a symlink to access cfbackup from anywhere. See the wiki for more information.reating a symlink.
23
+
22
24
  Install
23
25
  -----------
24
26
 
25
27
  * gem sources -a http://gems.github.com
26
28
  * sudo gem install jmstacey-cfbackup
27
29
 
28
- Depending on what Operating System you're using, you may be required to install libs that are outside of the gem world.
30
+ Configuration
31
+ -----------
32
+
33
+ CFBackup will look in the following places (in order) for the configuration file named cfconfig.yml
34
+
35
+ * Hidden in home directory (~/.cfbackup.yml)
36
+ * Non-hidden in present working directory (./cfbackup.yml)
37
+ * In etc (/etc/cfbackup.yml)
38
+
39
+ The configuration file can be overridden at any time with the --config_file option
40
+
41
+ Configuration
42
+ -----------
43
+
44
+ Usage: cfbackup.rb --action push|pull|delete options --container CONTAINER
45
+ --action ACTION Action to perform: push, pull, or delete.
46
+ --pipe_data Pipe data from another application and stream it to Cloud Files
47
+ -r, --recursive Traverse local path recursivley
48
+ -v, --verbose Run verbosely
49
+ --local_path LOCAL_PATH Local path or file
50
+ --container CONTAINER Cloud Files container name
51
+ --version Show current version
52
+ --config_file PATH Use specified config file, rather than the default
53
+ --local_net Use unmetered connection in DFW1 (only applicable to Slicehost or Mosso Cloud Server customers)
54
+
55
+ The wiki has usage examples and some sample automation scripts can be found in the example_scripts directory.
29
56
 
30
57
  Copyright
31
58
  ------------
data/Rakefile CHANGED
@@ -10,7 +10,7 @@ begin
10
10
  gem.homepage = "http://github.com/jmstacey/cfbackup"
11
11
  gem.authors = ["Jon Stacey"]
12
12
 
13
- # dependencies
13
+ # Dependencies
14
14
  gem.add_dependency('jmstacey-ruby-cloudfiles', '>=1.3.3')
15
15
 
16
16
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
@@ -39,7 +39,6 @@ rescue LoadError
39
39
  end
40
40
  end
41
41
 
42
-
43
42
  task :default => :test
44
43
 
45
44
  require 'rake/rdoctask'
@@ -54,6 +53,7 @@ Rake::RDocTask.new do |rdoc|
54
53
  rdoc.rdoc_dir = 'rdoc'
55
54
  rdoc.title = "cfbackup #{version}"
56
55
  rdoc.rdoc_files.include('README*')
56
+ rdoc.rdoc_files.include('example_scripts/**')
57
57
  rdoc.rdoc_files.include('lib/**/*.rb')
58
58
  end
59
59
 
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 5
4
- :patch: 0
3
+ :minor: 6
4
+ :patch: 0
data/bin/cfbackup CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'rubygems'
3
- require 'CFBackup'
3
+ require 'cfbackup'
4
4
 
5
5
  backup = CFBackup.new(ARGV)
6
6
  backup::run
data/lib/OptCFBackup.rb CHANGED
@@ -12,14 +12,14 @@ class OptCFBackup
12
12
  # Initializes object with command line arguments passed
13
13
  def initialize(args)
14
14
 
15
- @banner = "Usage: cfbackup.rb [options] --pipe_data|--local_path PATH --container CONTAINER"
15
+ @banner = "Usage: cfbackup.rb --action push|pull|delete options --container CONTAINER"
16
16
 
17
17
  @options = OpenStruct.new
18
- self.options.config = ['~/.cfconfig.yml', './cfconfig.yml', '/etc/cfconfig.yml']
18
+ self.options.config = ["#{ENV['HOME']}/.cfconfig.yml", './cfconfig.yml', '/etc/cfconfig.yml']
19
+ self.options.action = ''
19
20
  self.options.pipe_data = false
20
21
  self.options.show_ver = false
21
22
  self.options.recursive = false
22
- self.options.restore = false
23
23
  self.options.local_net = false
24
24
  self.options.container = ''
25
25
  self.options.local_path = ''
@@ -29,6 +29,10 @@ class OptCFBackup
29
29
  opts = OptionParser.new do |opts|
30
30
  opts.banner = self.banner
31
31
 
32
+ opts.on("--action ACTION", "Action to perform: push, pull, or delete.") do |action|
33
+ self.options.action = action
34
+ end
35
+
32
36
  opts.on("--pipe_data", "Pipe data from another application and stream it to Cloud Files") do |pipe|
33
37
  self.options.pipe_data = pipe
34
38
  end
@@ -50,15 +54,12 @@ class OptCFBackup
50
54
  clean_remote_path unless (self.options.remote_path.nil?)
51
55
  end
52
56
 
53
- opts.on("--restore", "Restore files to local path") do |restore|
54
- self.options.restore = restore
55
- end
56
-
57
57
  opts.on("--version", "Show current version") do |version|
58
58
  self.options.show_ver = version
59
59
  end
60
60
 
61
- opts.on("--config_file PATH", "Use specified config file, rather than the default") do |config|
61
+ opts.on("--config_file PATH", "Use specified config file, rather than the default") do |config|
62
+ self.options.config = Array.new()
62
63
  self.options.config << config
63
64
  end
64
65
 
@@ -70,7 +71,7 @@ class OptCFBackup
70
71
 
71
72
  opts.parse!(args)
72
73
 
73
- end # parse()
74
+ end # initialize()
74
75
 
75
76
  private
76
77
 
@@ -78,7 +79,7 @@ class OptCFBackup
78
79
  if self.options.remote_path[0,1] == "/"
79
80
  self.options.remote_path.slice!(0)
80
81
  end
81
- # Won't work for piped data. Might result in "text.txt/"
82
+ # Follwoig won't work for piped data. Might result in "text.txt/"
82
83
  # self.options.remote_path = self.options.remote_path + "/" unless (self.options.remote_path[-1,1] == "/")
83
84
  end
84
85
 
data/lib/cfbackup.rb CHANGED
@@ -15,8 +15,9 @@
15
15
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
16
 
17
17
  require 'rubygems'
18
+ require 'ftools'
18
19
  require 'cloudfiles'
19
- require 'OptCFBackup'
20
+ require 'optcfbackup'
20
21
  require 'yaml'
21
22
 
22
23
  class CFBackup
@@ -27,8 +28,13 @@ class CFBackup
27
28
  # Special case if the version is requested
28
29
  if @opts.options.show_ver
29
30
  version_file = File.join(File.dirname(__FILE__), '..', 'VERSION.yml')
30
- config = YAML::load(File.open(version_file))
31
- show_error("CFBackup v#{config[:major]}.#{config[:minor]}.#{config[:patch]}")
31
+ if File.exist?(version_file)
32
+ config = YAML.load(File.read(version_file))
33
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
34
+ else
35
+ version = "unkown version"
36
+ end
37
+ show_error("CFBackup #{version}")
32
38
  end
33
39
 
34
40
  # Locate and load config file
@@ -40,23 +46,26 @@ class CFBackup
40
46
  end
41
47
  show_error('Error: Unable to locate config file.') unless (@conf != nil)
42
48
 
49
+ prep_connection
50
+
43
51
  end # initialize()
44
52
 
45
53
  def run
46
54
 
47
55
  show_error() unless (@opts.options.container != "")
48
56
 
49
- if @opts.options.pipe_data
50
- prep_container
51
- run_piped_data
52
- elsif @opts.options.local_path != ""
53
- prep_container
54
-
55
- if @opts.options.restore
56
- run_restore
57
+ # Run appropriate action
58
+ case @opts.options.action
59
+ when 'push'
60
+ if @opts.options.pipe_data
61
+ push_piped_data
57
62
  else
58
- run_backup
63
+ push_files
59
64
  end
65
+ when 'pull'
66
+ pull_files()
67
+ when 'delete'
68
+ delete_files
60
69
  else
61
70
  show_error()
62
71
  end
@@ -65,8 +74,7 @@ class CFBackup
65
74
 
66
75
  private
67
76
 
68
- def prep_container
69
-
77
+ def prep_connection
70
78
  # Establish connection
71
79
  show_verbose "Establishing connection...", false
72
80
  @cf = CloudFiles::Connection.new(@conf["username"], @conf["api_key"]);
@@ -76,30 +84,38 @@ class CFBackup
76
84
  if @opts.options.local_net
77
85
  @cf.storagehost = 'snet-storage.clouddrive.com'
78
86
  end
87
+ end # prep_connection()
79
88
 
80
- # Check for the container. If it doesn't exist, create it.
81
- unless @cf.container_exists?(@opts.options.container)
82
- show_verbose "Conainer '#{@opts.options.container}' does not exist. Creating it...", false
83
- @cf.create_container(@opts.options.container)
84
- show_verbose " done."
89
+ def prep_container(create_container = true)
90
+ # Check for the container. If it doesn't exist, create it if allowed
91
+ if !@cf.container_exists?(@opts.options.container)
92
+ if create_container
93
+ show_verbose "Container '#{@opts.options.container}' does not exist. Creating it...", false
94
+ @cf.create_container(@opts.options.container)
95
+ show_verbose " done."
96
+ else
97
+ show_error("Error: Container '#{@opts.options.container}' does not exist.")
98
+ end
85
99
  end
86
100
 
87
101
  @container = @cf.container(@opts.options.container)
88
-
89
- end # prepConnection()
102
+ end # prep_cnnection()
90
103
 
91
- def run_piped_data
92
- puts "Warning: 5GB per stream cap"
104
+ def push_piped_data
105
+ prep_container
106
+
107
+ puts "Warning: 5GB maximum filesize"
93
108
  object = @container.create_object(@opts.options.remote_path, true)
94
109
  object.write("STDIN")
95
- end
110
+ end # push_piped_data()
96
111
 
97
- def run_backup
112
+ def push_files
113
+ prep_container
98
114
 
99
115
  path = @opts.options.local_path
100
-
116
+ pwd = Dir.getwd # Save current directory so we can come back
117
+
101
118
  if FileTest::file?(path)
102
- Dir.chdir(File::dirname(path))
103
119
  glob_options = File.join(File::basename(path))
104
120
  elsif @opts.options.recursive
105
121
  Dir.chdir(path)
@@ -111,41 +127,173 @@ class CFBackup
111
127
  files = Dir.glob(glob_options)
112
128
 
113
129
  # Upload file(s)
114
- files.each do |file|
130
+ counter = 1
131
+ show_verbose "There are #{files.length} files to process."
132
+ files.each do |file|
115
133
  file = file.sub(/\.\//, '')
116
134
  if file == "" || file[0,1] == "." || FileTest.directory?(file)
135
+ show_verbose "(#{counter}/#{files.length}) Skipping #{file}"
136
+ counter += 1
117
137
  next
118
138
  end
119
139
 
120
- show_verbose "Uploading #{file}...", false
121
- object = @container.create_object(file, true)
140
+ show_verbose "(#{counter}/#{files.length}) Pushing file #{file}...", false
141
+
142
+ if @opts.options.remote_path.to_s == ''
143
+ remote_path = file
144
+ else
145
+ remote_path = File.join(@opts.options.remote_path, file)
146
+ end
147
+
148
+ object = @container.create_object(remote_path, true)
122
149
  object.load_from_filename(file)
150
+
123
151
  show_verbose " done."
152
+ counter += 1
124
153
  end # files.each
125
154
 
126
- end # run_backup()
155
+ Dir.chdir(pwd) # Go back to original directory
156
+
157
+ end # push_files()
158
+
159
+ def pull_files
160
+ prep_container(false)
161
+
162
+ file = object_file?
163
+ objects = get_objects_array
164
+
165
+ # Process objects
166
+ counter = 1
167
+ show_verbose "There are #{objects.length} objects to process."
168
+ objects.each do |object_name|
169
+ object = @container.object(object_name)
170
+ if object.content_type == "application/directory"
171
+ show_verbose "(#{counter}/#{objects.length}) Pseudo directory #{object.name}"
172
+ counter += 1
173
+ next
174
+ end
175
+
176
+ path_info = File.split(@opts.options.local_path.to_s)
177
+ file_info = File.split(object.name.to_s)
178
+
179
+ if file # Dealing with a single file pull
180
+ if @opts.options.local_path.to_s == ''
181
+ filepath = file_info[1].to_s # Use current directory and original name
182
+ else
183
+ if File.exist?(@opts.options.local_path.to_s)
184
+ # The file exists, so we will overwrite it
185
+ filepath = File.join(@opts.options.local_path.to_s)
186
+ else
187
+ # If the file doesn't exist, a new name may have been given.
188
+ # Test the path.
189
+ if File.exist?(path_info[0])
190
+ # A new name was given with a valid path
191
+ filepath = File.join(path_info[0], path_info[1])
192
+ else
193
+ # The given path is not valid
194
+ show_error("cfbackup: #{file_info[0]}: No such file or directory.")
195
+ end
196
+ end
197
+ end
198
+ else # Dealing with a multi-object pull
199
+ if @opts.options.local_path.to_s == ''
200
+ filepath = object.name.to_s # Use current directory with object name
201
+ dir_path = file_info[0]
202
+ else
203
+ if File.directory?(@opts.options.local_path.to_s)
204
+ filepath = File.join(@opts.options.local_path.to_s, object.name.to_s)
205
+ dir_path = File.join(@opts.options.local_path, file_info[0])
206
+ else
207
+ # We can't copy a directory to a file...
208
+ show_error("cfbackup: #{@container.name}:#{@opts.options.remote_path.to_s}/ is a directory (not copied).")
209
+ end
210
+ end
211
+ File.makedirs(dir_path) # Create subdirectories as needed
212
+ end
213
+
214
+ show_verbose "(#{counter}/#{objects.length}) Pulling object #{object.name}...", false
215
+ object.save_to_filename(filepath)
216
+ show_verbose " done"
217
+ counter += 1
218
+ end
219
+ end # pull_files()
220
+
221
+ def delete_files
222
+ prep_container(false)
223
+
224
+ if object_file?
225
+ show_verbose "Deleting object #{@opts.options.remote_path.to_s}"
226
+ @container.delete_object(@opts.options.remote_path.to_s)
227
+ else
228
+ if !@opts.options.recursive
229
+ show_error("Error: #{@opts.options.remote_path} is a directory but the the recursive option was not specified. Doing nothing.")
230
+ else
231
+ objects = get_objects_array
232
+
233
+ # Process objects
234
+ counter = 1
235
+ show_verbose "There are #{objects.length} objects to process."
236
+ objects.each do |object_name|
237
+ show_verbose "(#{counter}/#{objects.length}) Deleting object #{object_name}...", false
238
+ @container.delete_object(object_name)
239
+ show_verbose " done"
240
+ counter += 1
241
+ end
242
+ end
243
+ end
244
+
245
+ end # delete_files()
246
+
247
+
248
+ # Helper methods below
249
+
250
+ def object_file?
251
+
252
+ file = false
253
+ unless @opts.options.remote_path.to_s == ''
254
+ if @container.object_exists?(@opts.options.remote_path)
255
+ if @container.object(@opts.options.remote_path).content_type != "application/directory"
256
+ file = true
257
+ if @opts.options.recursive
258
+ puts "Warning: This is a file so the recursive option is meaningless."
259
+ end
260
+ end
261
+ else
262
+ show_error("The object #{@opts.options.remote_path} does not exist")
263
+ end
264
+ end
265
+
266
+ return file
267
+ end
127
268
 
128
- def run_restore
269
+ def get_objects_array
129
270
 
130
- # TODO: Implement run_restore
131
- # We have to do a bit of fancy footwork to make directories work
132
- puts "Oops! Restore hasn't been implemented yet. Help me out and submit a patch :-)"
271
+ file = object_file?
133
272
 
134
- end # run_restore()
273
+ # Get array of objects to process
274
+ objects = Array.new
275
+ if file
276
+ objects << @opts.options.remote_path.to_s
277
+ elsif @opts.options.recursive
278
+ # Use prefix instead of path so that "subdirectories" are included
279
+ objects = @container.objects(:prefix => @opts.options.remote_path)
280
+ else
281
+ objects = @container.objects(:path => @opts.options.remote_path)
282
+ end
283
+
284
+ return objects
285
+ end
135
286
 
136
287
  # Shows given message if verbose output is turned on
137
288
  def show_verbose(message, line_break = true)
138
-
139
289
  unless !@opts.options.verbose
140
290
  if line_break
141
291
  puts message
142
292
  else
143
293
  print message
144
294
  end
145
-
146
295
  $stdout.flush
147
296
  end
148
-
149
297
  end # show_verbose()
150
298
 
151
299
  # Show error message, banner and exit
@@ -155,8 +303,4 @@ class CFBackup
155
303
  exit
156
304
  end # show_error()
157
305
 
158
- def parse_container_path(container)
159
- # Split based on :
160
- end # parse_container_path()
161
-
162
306
  end # class CFBackup
@@ -1,7 +1,107 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class CfbackupTest < Test::Unit::TestCase
4
- should "probably rename this file and start testing for real" do
5
- flunk "hey buddy, you should probably rename this file and start testing for real"
4
+ TEST_DIR = 'test/tmp'
5
+
6
+ context "A backup" do
7
+
8
+ context "with a single file push" do
9
+ setup do
10
+ mock_ARGV = ['--action', 'push', '--local_path', 'test/data/data.txt', '--container', 'test', '--config_file', 'test/cfconfig.yml', '-v']
11
+ @backup = CFBackup.new(mock_ARGV)
12
+ end
13
+
14
+ should "return true when file sucessfully sent" do
15
+ assert @backup.run
16
+ end
17
+ end
18
+
19
+ context "with a recursive directory push" do
20
+ setup do
21
+ mock_ARGV = ['--action', 'push', '-r', '--local_path', 'test/data', '--container', 'test', '--config_file', 'test/cfconfig.yml', '-v']
22
+ @backup = CFBackup.new(mock_ARGV)
23
+ end
24
+
25
+ should "return true when all files/directories successfully sent" do
26
+ assert @backup.run
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ context "A restore" do
33
+
34
+ context "with a single file pull" do
35
+ file = 'data.txt'
36
+ filepath = File.join(TEST_DIR, file)
37
+
38
+ setup do
39
+ mock_ARGV = ['--action', 'pull', '--container', "test:#{file}", '--local_path', "#{filepath}", '--config_file', 'test/cfconfig.yml', '-v']
40
+ @backup = CFBackup.new(mock_ARGV)
41
+ end
42
+
43
+ should "return true when file succesfully pulled" do
44
+ assert @backup.run
45
+ end
46
+
47
+ # I don't know why this doesn't work. It's like File is caching results
48
+ # and not updating until the applicaiton exists. Overcoming by
49
+ # asserting the file deletion during teardown at the loss of shoulda
50
+ #
51
+ # should "result in test/tmp/#{file} existing" do
52
+ # assert File.exist?(filepath)
53
+ # end
54
+
55
+ teardown do
56
+ assert File.delete(filepath)
57
+ end
58
+ end
59
+
60
+ context "with a recursive pull" do
61
+ setup do
62
+ mock_ARGV = ['--action', 'pull', '--container', "test", '--local_path', "test/tmp", '--config_file', 'test/cfconfig.yml', '-v']
63
+ @backup = CFBackup.new(mock_ARGV)
64
+ end
65
+
66
+ should "return true when all files pulled" do
67
+ assert @backup.run
68
+ end
69
+
70
+ # Todo compare directories
71
+
72
+ teardown do
73
+ system "rm -rf test/tmp/*"
74
+ # I didn't feel safe with TEST_DIR + '/*'
75
+ # Disaster waiting to happen. rm -rf is already bad enough.
76
+ end
77
+ end # context "A recursive pull"
78
+
6
79
  end
80
+
81
+ context "A deletion" do
82
+
83
+ context "of a single file" do
84
+ setup do
85
+ mock_ARGV = ['--action', 'delete', '--container', "test:folder_1/file1.txt", '--config_file', 'test/cfconfig.yml', '-v']
86
+ @backup = CFBackup.new(mock_ARGV)
87
+ end
88
+
89
+ should "return true when file deleted" do
90
+ assert @backup.run
91
+ end
92
+ end
93
+
94
+ context "of a pseudo directory" do
95
+ setup do
96
+ mock_ARGV = ['--action', 'delete', '-r', '--container', "test:folder_2", '--config_file', 'test/cfconfig.yml', '-v']
97
+ @backup = CFBackup.new(mock_ARGV)
98
+ end
99
+
100
+ should "return true when directory deleted" do
101
+ assert @backup.run
102
+ end
103
+ end
104
+
105
+ end # context "A deletion"
106
+
7
107
  end
data/test/cfconfig.yml ADDED
@@ -0,0 +1,2 @@
1
+ username: xxxxxxxxxxxxx
2
+ api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
File without changes
@@ -0,0 +1,2 @@
1
+ Folder 1
2
+ Test File 1
@@ -0,0 +1,2 @@
1
+ Folder 1
2
+ Test File 1
@@ -0,0 +1,2 @@
1
+ Folder 1/Folder 3
2
+ Test File 1
@@ -0,0 +1,2 @@
1
+ Folder 2
2
+ Test File 1
@@ -0,0 +1,2 @@
1
+ Folder 2
2
+ Test File 1
data/test/test_helper.rb CHANGED
@@ -6,5 +6,5 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
6
  $LOAD_PATH.unshift(File.dirname(__FILE__))
7
7
  require 'cfbackup'
8
8
 
9
- class Test::Unit::TestCase
9
+ class Test::Unit::TestCase
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jmstacey-cfbackup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Stacey
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-19 00:00:00 -07:00
12
+ date: 2009-05-03 00:00:00 -07:00
13
13
  default_executable: cfbackup
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -41,7 +41,13 @@ files:
41
41
  - lib/OptCFBackup.rb
42
42
  - lib/cfbackup.rb
43
43
  - test/cfbackup_test.rb
44
- - test/data.txt
44
+ - test/cfconfig.yml
45
+ - test/data/data.txt
46
+ - test/data/folder_1/file1.txt
47
+ - test/data/folder_1/file2.txt
48
+ - test/data/folder_1/folder_3/file1.txt
49
+ - test/data/folder_2/file1.txt
50
+ - test/data/folder_2/file2.txt
45
51
  - test/test_helper.rb
46
52
  has_rdoc: true
47
53
  homepage: http://github.com/jmstacey/cfbackup