mysql_truck 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/TODO CHANGED
@@ -1,8 +1,5 @@
1
1
  # TODO
2
2
 
3
- * Download/extract/import one table at a time.
4
- * Rework backup so that table schema and indexes are separated.
5
- * Rework restore so that schema is applied, data is imported, then indexes are
6
- applied.
7
3
  * Add ability to manage number of backups on S3
8
4
  * Better error handling messages rather than stack dumps
5
+ * Add ability to import a table as a tmp table, then rename.
data/bin/mysql_truck CHANGED
@@ -58,7 +58,12 @@ parser = OptionParser.new do |opts|
58
58
 
59
59
  opts.on("-t", "--skip-tables TABLES",
60
60
  "List of tables to skip separated by commas.") do |tables|
61
- options[:skip_tables] = tables.split(",")
61
+ options[:skip_data_for_tables] = tables.split(",")
62
+ end
63
+
64
+ opts.on("-e", "--exec-smartly",
65
+ "On dumping, do not dump tables that have already been dumped. On loading, if the files were already downloaded, do not redownload. This option allows for resuming a previous load/dump that failed.") do
66
+ options[:smartly] = true
62
67
  end
63
68
 
64
69
  opts.on_tail("-h", "--help", "Show this message") do
@@ -0,0 +1,175 @@
1
+ module MysqlTruck
2
+ class Dumper
3
+ include FileUtils
4
+ include Helper
5
+
6
+ REGEX = /,?\s*(UNIQUE)?\s*KEY\s`[A-Za-z\d_]+`\s*\([A-Za-z\d_,`]+\),?\s*/m
7
+
8
+ def initialize(config)
9
+ @config = config
10
+ @time = Time.now # Sets the directory for dump
11
+
12
+ initialize_s3
13
+ initialize_directories
14
+ end
15
+
16
+ def dump
17
+ dump_data
18
+ upload
19
+ remove_directories
20
+ end
21
+
22
+ def dump_data
23
+ tables.each do |table|
24
+ puts "Dumping #{table}..."
25
+ next if gzip_files_exist?(table) && smartly?
26
+
27
+ if dump_table?(table)
28
+
29
+ # This command creates a table_name.sql and a table_name.txt file
30
+ cmd = "mysqldump --quick -T #{tmp_path} "
31
+ cmd += csv_options
32
+ cmd += "#{db_connection_options} #{table}"
33
+ puts cmd
34
+ `#{cmd}`
35
+
36
+ # `mysqldump` creates files with .txt extensions, so we rename it.
37
+ mv filename(table)[:txt_file], filename(table)[:csv_file]
38
+ end
39
+
40
+ if split_schema_file?(table)
41
+ schema_contents = filename(table)[:schema_file].read
42
+
43
+ # Create schema with no indexes
44
+ File.open(filename(table)[:no_index_sql_file], 'w') do |f|
45
+ f.write(schema_contents.gsub(REGEX, ''))
46
+ end
47
+
48
+ # Create an alter table
49
+ indices = []
50
+ File.open(filename(table)[:index_sql_file], 'w') do |f|
51
+ f.write("ALTER TABLE #{table}\n")
52
+
53
+ schema_contents.gsub(/^,?\s*((UNIQUE)?\s*KEY\s`[A-Za-z\d_]+`\s*\([A-Za-z\d_,`]+\)),?\s*$/) do |part|
54
+ indices << $1
55
+ end
56
+ f.write(indices.collect {|i| "ADD #{i}"}.join(",\n"))
57
+ end
58
+ end
59
+
60
+ if gzip_files?(table)
61
+ puts "gzipping #{filename(table)[:no_index_sql_file]}."
62
+ `gzip #{filename(table)[:no_index_sql_file]}`
63
+
64
+ puts "gzipping #{filename(table)[:index_sql_file]}."
65
+ `gzip #{filename(table)[:index_sql_file]}`
66
+
67
+ puts "gziping #{filename(table)[:csv_file]}."
68
+ `gzip #{filename(table)[:csv_file]}`
69
+ end
70
+
71
+ puts "#{table} dumped.\n\n"
72
+ end
73
+ end
74
+
75
+ def upload
76
+ Dir["#{tmp_path}/*"].each do |file|
77
+ next if File.extname(file) != ".gz"
78
+ puts "Uploading #{file} ..."
79
+ upload_file file
80
+ end
81
+ puts "Finished uploading backups."
82
+ end
83
+
84
+ private
85
+
86
+ def smartly?
87
+ config[:smartly]
88
+ end
89
+
90
+ def upload_file(local_file)
91
+ path = Pathname.new(local_file)
92
+ s3_path = bucket_path.join(path.basename)
93
+ @bucket.put(s3_path, open(path), {}, nil, {
94
+ 'x-amz-storage-class' => 'REDUCED_REDUNDANCY'
95
+ })
96
+ end
97
+
98
+ def tables
99
+ return config[:only_tables] if config[:only_tables]
100
+ unless @tables
101
+ res = `mysql #{db_connection_options} -e "SHOW TABLES"`
102
+ @tables = res.split[1..-1]
103
+ end
104
+ @tables
105
+ end
106
+
107
+ def bucket_path
108
+ @bucket_path ||= Pathname.new(bucket_dir).join(@time.strftime("%Y-%m-%d-%H-%M"))
109
+ end
110
+
111
+ def filename(table)
112
+ @table_filenames ||= {}
113
+ @table_filenames[table] ||= {
114
+ :schema_file => tmp_path.join("#{table}.sql"),
115
+ :no_index_sql_file => tmp_path.join("#{table}.no_index.sql"),
116
+ :index_sql_file => tmp_path.join("#{table}.indices.sql"),
117
+ :txt_file => tmp_path.join("#{table}.txt"),
118
+ :csv_file => tmp_path.join("#{table}.csv"),
119
+ :gz_no_index_sql_file => tmp_path.join("#{table}.no_index.sql.gz"),
120
+ :gz_index_sql_file => tmp_path.join("#{table}.indices.sql.gz"),
121
+ :gz_csv_file => tmp_path.join("#{table}.csv.gz"),
122
+ }
123
+ end
124
+
125
+ def gzip_files_exist?(table)
126
+ tmp_path.join("#{table}.sql.gz").file? &&
127
+ tmp_path.join("#{table}.no_index.sql.gz") &&
128
+ tmp_path.join("#{table}.indexes.sql.gz") &&
129
+ tmp_path.join("#{table}.csv.gz")
130
+ end
131
+
132
+
133
+ def dump_table?(table)
134
+ !smartly? ||
135
+ (
136
+ smartly? &&
137
+ !filename(table)[:schema_file].exist? &&
138
+ !filename(table)[:csv_file].exist? &&
139
+ !filename(table)[:no_index_sql_file].exist? &&
140
+ !filename(table)[:index_sql_file].exist? &&
141
+ !filename(table)[:gz_no_index_sql_file].exist? &&
142
+ !filename(table)[:gz_index_sql_file].exist? &&
143
+ !filename(table)[:gz_csv_file].exist?
144
+ )
145
+ end
146
+
147
+ def split_schema_file?(table)
148
+ !smartly? ||
149
+ (
150
+ smartly? &&
151
+ filename(table)[:schema_file].exist? &&
152
+ filename(table)[:csv_file].exist? &&
153
+ !filename(table)[:no_index_sql_file].exist? &&
154
+ !filename(table)[:index_sql_file].exist? &&
155
+ !filename(table)[:gz_no_index_sql_file].exist? &&
156
+ !filename(table)[:gz_index_sql_file].exist? &&
157
+ !filename(table)[:gz_csv_file].exist?
158
+ )
159
+ end
160
+
161
+ def gzip_files?(table)
162
+ !smartly? ||
163
+ (
164
+ smartly? &&
165
+ filename(table)[:schema_file].exist? &&
166
+ filename(table)[:csv_file].exist? &&
167
+ filename(table)[:no_index_sql_file].exist? &&
168
+ filename(table)[:index_sql_file].exist? &&
169
+ !filename(table)[:gz_no_index_sql_file].exist? &&
170
+ !filename(table)[:gz_index_sql_file].exist? &&
171
+ !filename(table)[:gz_csv_file].exist?
172
+ )
173
+ end
174
+ end # class Dumper
175
+ end
@@ -0,0 +1,63 @@
1
+ module MysqlTruck
2
+ module Helper
3
+ include FileUtils
4
+
5
+ def config
6
+ @config
7
+ end
8
+
9
+ def initialize_s3
10
+ @s3 = RightAws::S3.new(
11
+ config[:s3_access_key],
12
+ config[:s3_secret_access_key])
13
+ @bucket = @s3.bucket(config[:bucket])
14
+ end
15
+
16
+ def db_connection_options
17
+ opts = %Q[ -u #{config[:username]} ]
18
+ opts += %Q[ -p"#{config[:password]}" ] unless config[:password].nil?
19
+ opts += %Q[ -h #{config[:host]} --default-character-set=utf8 ]
20
+ opts += %Q[ #{config[:database]} ]
21
+ opts
22
+ end
23
+
24
+ def local_host?
25
+ config[:host] == '127.0.0.1' || config[:host] == 'localhost'
26
+ end
27
+
28
+ def remote_host?
29
+ !local_host?
30
+ end
31
+
32
+ def csv_options
33
+ " --fields-enclosed-by=\\\" --fields-terminated-by=, "
34
+ end
35
+
36
+ def initialize_directories
37
+ mkdir_p base_path
38
+ mkdir_p tmp_path
39
+ chmod 0777, tmp_path
40
+ end
41
+
42
+ def remove_directories
43
+ rm_r tmp_path, :force => true
44
+ end
45
+
46
+ def tmp_path
47
+ raise "@time not initialized" unless @time
48
+ base_path.join(@time.strftime("%Y-%m-%d"))
49
+ end
50
+
51
+ def base_path
52
+ if config[:dump_dir]
53
+ config[:dump_dir].is_a?(Pathname) ? config[:dump_dir].join("mysqltruck") : Pathname.new(config[:dump_dir]).join("mysqltruck")
54
+ else
55
+ Pathname.new("/tmp/mysqltruck")
56
+ end
57
+ end
58
+
59
+ def bucket_dir
60
+ "mysql/#{config[:bucket_dir] || config[:database]}/"
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,179 @@
1
+ module MysqlTruck
2
+ class Loader
3
+ include Helper
4
+ include FileUtils
5
+
6
+ def initialize(config)
7
+ @config = config
8
+ initialize_s3
9
+ end
10
+
11
+ # only import schema for these tables
12
+ def skip_data_for_tables
13
+ config[:skip_data_for_tables] || []
14
+ end
15
+
16
+ # only import these tables schema+data
17
+ def only_tables
18
+ config[:only_tables] || []
19
+ end
20
+
21
+ def load_latest
22
+ prefix = backups.first
23
+
24
+ # Set directory where backup is downloaded to
25
+ @time = Time.new(*prefix.split("/").last.split("-"))
26
+ initialize_directories
27
+
28
+ puts "Downloading backups"
29
+ puts "-------------------"
30
+ @bucket.keys(:prefix => prefix).each do |key|
31
+ puts "\n#{key}"
32
+ next unless (filename = download_file(key))
33
+
34
+ # gunzip file
35
+ if tmp_path.join(filename).exist?
36
+ print " - Inflating #{filename} ... "
37
+ `gunzip -f #{tmp_path.join(filename)}`
38
+ print "complete.\n"
39
+ end
40
+ end
41
+
42
+ # Load data
43
+ puts "\nLoading schema and data by table"
44
+ puts "--------------------------------"
45
+ if remote_host?
46
+ import_cmd = "mysqlimport --local --compress #{db_connection_options}"
47
+ else
48
+ import_cmd = "mysqlimport #{db_connection_options}"
49
+ end
50
+ import_cmd += csv_options
51
+
52
+ # Find all .no_index.sql files and process
53
+ Dir["#{tmp_path}/*.no_index.sql"].each do |file|
54
+ table = File.basename(file, ".no_index.sql")
55
+ puts "\nProcessing #{table}"
56
+
57
+ schema_file = Pathname.new(file)
58
+ index_file = tmp_path.join("#{table}.indices.sql")
59
+ csv_file = tmp_path.join("#{table}.csv")
60
+
61
+
62
+ print " - Loading schema for #{table} ... "
63
+ cmd = "cat #{schema_file} | mysql #{db_connection_options}"
64
+ `#{cmd}`
65
+ print "complete.\n"
66
+
67
+ if csv_file.exist?
68
+ print " - Importing #{schema_file.basename(".sql")} ... "
69
+ `#{import_cmd} #{csv_file}`
70
+ print "complete.\n"
71
+ end
72
+
73
+ if index_file.exist?
74
+ print " - Adding indices for #{schema_file.basename(".no_index.sql")} ... "
75
+ cmd = "cat #{index_file} | mysql #{db_connection_options}"
76
+ `#{cmd}`
77
+ print "complete.\n"
78
+ end
79
+
80
+ schema_file.delete if schema_file.exist?
81
+ index_file.delete if index_file.exist?
82
+ csv_file.delete if csv_file.exist?
83
+ end
84
+
85
+ puts "Backup loaded."
86
+
87
+ # This isn't in an ensure block because we want to keep around
88
+ # downloads if there's a failure importing a table.
89
+ # remove_directories
90
+
91
+ rescue Exception => e
92
+ puts e.message
93
+ puts e.backtrace.join("\n")
94
+ end
95
+
96
+ def download_file(key)
97
+ filename = File.basename(key.name)
98
+
99
+ unless should_download_file?(filename)
100
+ puts " [ SKIP ]"
101
+ return
102
+ end
103
+
104
+ file = tmp_path.join(filename)
105
+ unzipped_file = tmp_path.join(file.basename(".gz"))
106
+ if !smartly? || (smartly? && !unzipped_file.exist?)
107
+ print " - Downloading... "
108
+
109
+ file.open("wb") do |f|
110
+ @bucket.s3.interface.get(@bucket.name, key.name) do |chunk|
111
+ f.write chunk
112
+ end
113
+ end
114
+
115
+ puts "complete."
116
+ else
117
+ puts " already downloaded."
118
+ end
119
+
120
+ filename
121
+ end
122
+
123
+ def should_download_file?(filename)
124
+ table_name = filename.gsub(/\..*\..*$/, '')
125
+
126
+ if only_tables.empty? and skip_data_for_tables.empty?
127
+ return true
128
+ end
129
+
130
+ # If we're targetting specific tables, then we always want both
131
+ # schema and csv files.
132
+ if !only_tables.empty?
133
+ return only_tables.include?(table_name)
134
+ end
135
+
136
+ if filename.match(/\.csv\.gz$/)
137
+ is_data = true
138
+ is_schema = false
139
+ else
140
+ is_data = false
141
+ is_schema = true
142
+ end
143
+
144
+ if !skip_data_for_tables.empty?
145
+ if is_schema or (is_data and !skip_data_for_tables.include?(table_name))
146
+ return true
147
+ end
148
+ end
149
+
150
+ false
151
+ end
152
+
153
+ # Get a list of backups stored on S3.
154
+ #
155
+ # Returns an array of s3 paths that look like:
156
+ #
157
+ # mysql/YYYY-MM-DD
158
+ #
159
+ # Array elements are sorted with the latest date first.
160
+ def backups
161
+ unless @backups
162
+ @backups = []
163
+ # Backups are stored in the mysql/ directory
164
+ @bucket.s3.interface.incrementally_list_bucket(@bucket.name, {
165
+ :prefix => "#{bucket_dir}", :delimiter => "/"
166
+ }) do |item|
167
+ @backups += item[:common_prefixes]
168
+ end
169
+ @backups = @backups.sort { |a,b| b <=> a }
170
+ end
171
+ @backups
172
+ end
173
+
174
+ def smartly?
175
+ config[:smartly]
176
+ end
177
+
178
+ end # class Loader
179
+ end
@@ -1,3 +1,3 @@
1
1
  module MysqlTruck
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/mysql_truck.rb CHANGED
@@ -1,7 +1,12 @@
1
- require "mysql_truck/version"
2
1
  require "right_aws"
3
- require 'fileutils'
4
- require 'pathname'
2
+ require "fileutils"
3
+ require "pathname"
4
+
5
+ require "mysql_truck/version"
6
+ require "mysql_truck/helper"
7
+ require "mysql_truck/dumper"
8
+ require "mysql_truck/loader"
9
+
5
10
 
6
11
  # MysqlTruck
7
12
  #
@@ -21,270 +26,4 @@ module MysqlTruck
21
26
  puts "Unknown action #{action}"
22
27
  end
23
28
  end
24
-
25
- module Helper
26
- include FileUtils
27
-
28
- def config
29
- @config
30
- end
31
-
32
- def initialize_s3
33
- @s3 = RightAws::S3.new(
34
- config[:s3_access_key],
35
- config[:s3_secret_access_key])
36
- @bucket = @s3.bucket(config[:bucket])
37
- end
38
-
39
- def db_connection_options
40
- opts = %Q[ -u #{config[:username]} ]
41
- opts += %Q[ -p"#{config[:password]}" ] unless config[:password].nil?
42
- opts += %Q[ -h #{config[:host]} --default-character-set=utf8 ]
43
- opts += %Q[ #{config[:database]} ]
44
- opts
45
- end
46
-
47
- def csv_options
48
- " --fields-enclosed-by=\\\" --fields-terminated-by=, "
49
- end
50
-
51
- def initialize_directories
52
- mkdir_p base_path
53
- mkdir_p tmp_path
54
- chmod 0777, tmp_path
55
- end
56
-
57
- def remove_directories
58
- rm_r tmp_path, :force => true
59
- end
60
-
61
- def tmp_path
62
- raise "@time not initialized yet" unless @time
63
- base_path.join(@time.strftime("%Y-%m-%d-%H-%M"))
64
- end
65
-
66
- def base_path
67
- if config[:dump_dir]
68
- config[:dump_dir].is_a?(Pathname) ? config[:dump_dir].join("mysqltruck") : Pathname.new(config[:dump_dir]).join("mysqltruck")
69
- else
70
- Pathname.new("/tmp/mysqltruck")
71
- end
72
- end
73
-
74
- def bucket_dir
75
- "mysql/#{config[:bucket_dir] || config[:database]}/"
76
- end
77
- end
78
-
79
- class Dumper
80
- include FileUtils
81
- include Helper
82
-
83
- def initialize(config)
84
- @config = config
85
- @time = Time.now # Sets the directory for dump
86
-
87
- initialize_s3
88
- initialize_directories
89
- end
90
-
91
- def dump
92
- dump_data
93
- upload
94
-
95
- ensure
96
- remove_directories
97
- end
98
-
99
- def dump_data
100
- tables.each do |table|
101
- schema_file = tmp_path.join("#{table}.sql")
102
- csv_file = tmp_path.join("#{table}.txt")
103
- puts "Dumping #{table}."
104
-
105
- # This command creates a table_name.sql and a table_name.txt file
106
- cmd = "mysqldump --quick -T #{tmp_path} "
107
- cmd += csv_options
108
- cmd += "#{db_connection_options} #{table}"
109
- puts cmd
110
- `#{cmd}`
111
-
112
- # `mysqldump` creates files with .txt extensions, so we rename it.
113
- path, file = csv_file.split
114
- csv_file = path.join("#{file.basename(".txt")}.csv")
115
- mv path.join(file), csv_file
116
-
117
- puts "gziping #{schema_file}."
118
- `gzip #{schema_file}`
119
-
120
- puts "gziping #{csv_file}."
121
- `gzip #{csv_file}`
122
-
123
- puts "#{table} dumped.\n\n"
124
- end
125
- end
126
-
127
- def upload
128
- Dir["#{tmp_path}/*"].each do |file|
129
- upload_file file
130
- end
131
- puts "Finished uploading backups."
132
- end
133
-
134
- private
135
-
136
-
137
- def upload_file(local_file)
138
- path = Pathname.new(local_file)
139
- s3_path = bucket_path.join(path.basename)
140
- @bucket.put(s3_path, open(path), {}, nil, {
141
- 'x-amz-storage-class' => 'REDUCED_REDUNDANCY'
142
- })
143
- end
144
-
145
- def tables
146
- unless @tables
147
- res = `mysql #{db_connection_options} -e "SHOW TABLES"`
148
- @tables = res.split[1..-1]
149
- end
150
- @tables
151
- end
152
-
153
- def bucket_path
154
- @bucket_path ||= Pathname.new(bucket_dir).join(@time.strftime("%Y-%m-%d-%H-%M"))
155
- end
156
- end # class Dumper
157
-
158
-
159
- class Loader
160
- include Helper
161
- include FileUtils
162
-
163
- def initialize(config)
164
- @config = config
165
- initialize_s3
166
- end
167
-
168
- # only import schema for these tables
169
- def skip_data_for_tables
170
- config[:skip_data_for_tables] || []
171
- end
172
-
173
- # only import these tables schema+data
174
- def only_tables
175
- config[:only_tables] || []
176
- end
177
-
178
- def load_latest
179
- prefix = backups.first
180
-
181
- # Set directory where backup is downloaded to
182
- @time = Time.new(*prefix.split("/").last.split("-"))
183
- initialize_directories
184
-
185
- puts "Downloading backups ..."
186
- @bucket.keys(:prefix => prefix).each do |key|
187
- next unless (filename = download_file(key))
188
-
189
- # gunzip file
190
- print " -- Inflating #{filename} ... "
191
- `gunzip #{tmp_path.join(filename)}`
192
- print "complete.\n"
193
- end
194
-
195
- # Load data
196
- puts "Loading schema and data by table"
197
- import_cmd = "mysqlimport #{db_connection_options}"
198
- import_cmd += csv_options
199
- Dir["#{tmp_path}/*.sql"].each do |file|
200
- print " - Loading schema for #{File.basename(file, ".sql")} ... "
201
- cmd = "cat #{file} | mysql #{db_connection_options}"
202
- `#{cmd}`
203
- print "complete.\n"
204
-
205
- csv_file = "#{tmp_path}/#{File.basename(file, ".sql")}.csv"
206
- if File.exists?(csv_file)
207
- print " - Importing #{File.basename(csv_file, ".csv")} ... "
208
- `#{import_cmd} #{csv_file}`
209
- print "complete.\n"
210
- end
211
- end
212
-
213
- puts "Backup loaded."
214
-
215
- rescue Exception => e
216
- puts e.message
217
- puts e.backtrace.join("\n")
218
- ensure
219
- remove_directories
220
- end
221
-
222
- def download_file(key)
223
- filename = File.basename(key.name)
224
- print "#{filename}... "
225
-
226
- unless should_download_file?(filename)
227
- puts " [ SKIP ]"
228
- return
229
- end
230
-
231
- print " Downloading... "
232
-
233
- File.open(tmp_path.join(filename), "wb") do |f|
234
- @bucket.s3.interface.get(@bucket.name, key.name) do |chunk|
235
- f.write chunk
236
- end
237
- end
238
-
239
- puts "complete."
240
- filename
241
- end
242
-
243
- def should_download_file?(filename)
244
- table_name = filename.gsub(/\..*\..*$/, '')
245
-
246
- if only_tables.empty? and skip_data_for_tables.empty?
247
- return true
248
- end
249
-
250
- if filename.match(/\.csv\.gz$/)
251
- is_data = true
252
- is_schema = false
253
- else
254
- is_data = false
255
- is_schema = true
256
- end
257
-
258
- if !only_tables.empty?
259
- return only_tables.include?(table_name)
260
- end
261
-
262
- if !skip_data_for_tables.empty?
263
- if is_schema or (is_data and !skip_data_for_tables.include?(table_name))
264
- return true
265
- end
266
- end
267
- end
268
-
269
- # Get a list of backups stored on S3.
270
- #
271
- # Returns an array of s3 paths that look like:
272
- #
273
- # mysql/YYYY-MM-DD-HH-MM
274
- #
275
- # Array elements are sorted with the latest date first.
276
- def backups
277
- unless @backups
278
- @backups = []
279
- # Backups are stored in the mysql/ directory
280
- @bucket.s3.interface.incrementally_list_bucket(@bucket.name, {
281
- :prefix => "#{bucket_dir}", :delimiter => "/"
282
- }) do |item|
283
- @backups += item[:common_prefixes]
284
- end
285
- @backups = @backups.sort { |a,b| b <=> a }
286
- end
287
- @backups
288
- end
289
- end # class Loader
290
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mysql_truck
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,11 +11,11 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2011-12-29 00:00:00.000000000Z
14
+ date: 2012-05-30 00:00:00.000000000Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: right_aws
18
- requirement: &70123421522780 !ruby/object:Gem::Requirement
18
+ requirement: &70339584428840 !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ! '>='
@@ -23,7 +23,7 @@ dependencies:
23
23
  version: '0'
24
24
  type: :runtime
25
25
  prerelease: false
26
- version_requirements: *70123421522780
26
+ version_requirements: *70339584428840
27
27
  description: Mysql database backup tool. Dumps/Loads to/from S3.
28
28
  email:
29
29
  - peter@paydrotalks.com
@@ -39,6 +39,9 @@ files:
39
39
  - TODO
40
40
  - bin/mysql_truck
41
41
  - lib/mysql_truck.rb
42
+ - lib/mysql_truck/dumper.rb
43
+ - lib/mysql_truck/helper.rb
44
+ - lib/mysql_truck/loader.rb
42
45
  - lib/mysql_truck/version.rb
43
46
  - mysql_truck.gemspec
44
47
  homepage: ''