mysql_truck 0.1.1 → 0.2.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/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: ''