tiny_backup 0.0.0 → 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2562bb50b04adc3aaf11fe40065fb8a02b89978b
4
+ data.tar.gz: 1572b687adee86d841f166122a5eb830bdef3ffc
5
+ SHA512:
6
+ metadata.gz: 82dacfa4785f06dffc09e96845cf747ca83b9adac230870675ec3896054b2d2f8f0d49883802edd45607e4498196f598b89a2ba05959b165eb7179eb21257557
7
+ data.tar.gz: 3f9e1e98fcf364397c379bee209b4377a2eb267e9186d0255ba284ee2a7dfa5a3f536abb3445c34206a433967c1ae01cb49af0999f222409ea32c045bc547217
@@ -0,0 +1,74 @@
1
+ module TinyBackup
2
+ # Set global settings for TinyBackup like TinyBackup.configure {|config| config.max_versions = 100 }
3
+ def self.configure &block
4
+ yield @config ||= Config.new
5
+ end
6
+
7
+ # Global settings for TinyBackup
8
+ def config
9
+ @config ||= Config.new
10
+ end
11
+
12
+ class Config
13
+ # The folder name that is used to keep the backup files and temporary files.
14
+ # @return [String] defaults to "db/backup"
15
+ attr_accessor :backup_folder
16
+
17
+ # The date format that will be appended and used to time-stamp the backup files. Can be any valid date format similar with the ISO C and POSIX directives.
18
+ # @return [String] defaults to "%d-%m-%Y_%H:%M"
19
+ attr_accessor :date_format
20
+
21
+ # The prefix used to name each .diff file that contains changes from the last version. Automatically generated +version_number+ will be appended to the +version_prefix+.
22
+ # @return [String] defaults to "ver_"
23
+ attr_accessor :version_prefix
24
+
25
+ # The prefix used to name the starting .zip file that contains a schema.rb file and a .csv file for each non-empty table.
26
+ # @return [String] defaults to "origin"
27
+ attr_accessor :zip_prefix
28
+
29
+ # Maximum number of versions that can exist. If this number is exceeded, the first .diff version is merged in the starting .zip file and cannot be reversed.
30
+ # @return [Integer] defaults to 32 (if backup is done daily, keep the last month versions)
31
+ attr_accessor :max_versions
32
+
33
+ # Maximum number of rows fetched by a single query to avoid memory crashes. If your table has more rows it will be used multiple query to fetch all the data.
34
+ # @return [Integer] defaults to 100000
35
+ attr_accessor :per_page
36
+
37
+ # true if the debug info will be provided in the process.
38
+ # @return [Boolean] defaults to false
39
+ attr_accessor :silent
40
+
41
+ def initialize
42
+ @backup_folder = "db/backup"
43
+ @date_format = "%d-%m-%Y_%H:%M"
44
+ @version_prefix = "ver_"
45
+ @zip_prefix = "origin"
46
+ @max_versions = 32
47
+ @per_page = 100000
48
+ @silent = false
49
+ end
50
+
51
+ def backup_folder= val
52
+ @backup_folder = val.to_s[0..-2] if val.to_s.last == "/"
53
+ end
54
+
55
+ def backup_folder
56
+ unless @backup_folder_exist ||= File.directory?(@backup_folder)
57
+ FileUtils.mkdir(@backup_folder)
58
+ @backup_folder_exist = true
59
+ end
60
+
61
+ @backup_folder
62
+ end
63
+
64
+ def date_format= val
65
+ begin
66
+ Time.now.strftime(val).to_datetime
67
+ rescue
68
+ raise ArgumentError, "invalid date format"
69
+ end
70
+
71
+ @date_format = val
72
+ end
73
+ end
74
+ end
@@ -10,96 +10,18 @@ end
10
10
  require 'rake'
11
11
  require 'csv'
12
12
 
13
- module TinyBackup
14
- # Get configuration settings
15
- #
16
- # Returns a *hash* with the following keys backup_folder, date_format, version_prefix, zip_prefix, max_versions, per_page
17
- def config
18
- {
19
- backup_folder: backup_folder,
20
- date_format: date_format,
21
- version_prefix: version_prefix,
22
- zip_prefix: zip_prefix,
23
- max_versions: max_versions,
24
- per_page: per_page
25
- }
26
- end
27
-
28
- # Set configuration settings
29
- #
30
- # +options+ is a *hash* with the following keys backup_folder, date_format, version_prefix, zip_prefix, max_versions, per_page
31
- def config= options
32
- if options[:date_format].present?
33
- # fall-back if the date_format is invalid
34
- begin
35
- Time.now.strftime(options[:date_format]).to_datetime
36
- rescue
37
- raise ArgumentError, "invalid date format"
38
- end
39
- end
40
-
41
- options.slice(*config.keys).each do |k, v|
42
- instance_variable_set "@#{k}", ([:max_versions, :per_page].include?(k) ? v.to_i : v.to_s)
43
- end
44
- end
45
-
46
- # Returns a *string* of the folder that is used to keep the backup files and temporary files
47
- def backup_folder
48
- this_backup_folder = @backup_folder || "db/backup"
49
-
50
- # the backup_folder will not have any slash at the end
51
- this_backup_folder = this_backup_folder.to_s[0..-2] if this_backup_folder.to_s.last == "/"
52
-
53
- # create the folder if doesn't exist
54
- FileUtils.mkdir(this_backup_folder) unless File.directory?(this_backup_folder)
55
-
56
- return this_backup_folder
57
- end
58
-
59
- # Returns a *string* of the date format that will be appended and used to time-stamp the backup files. Can be any valid date format similar with the ISO C and POSIX directives
60
- #
61
- # *Default* *value*: %d-%m-%Y_%H:%M
62
- def date_format
63
- @date_format || "%d-%m-%Y_%H:%M"
64
- end
13
+ require 'configure'
14
+ require 'version'
65
15
 
66
- # Returns a *string* of the prefix used to name each .diff file that contains changes from the last version. Automatically generated +version_number+ will be appended to the +version_prefix+
67
- #
68
- # *Default* *value*: +ver_+
69
- def version_prefix
70
- @version_prefix || "ver_"
71
- end
72
-
73
- # Returns a *string* of the prefix used to name the starting .zip file that contains a schema.rb file and a .csv file for each non-empty table
74
- #
75
- # *Default* *value*: +origin+
76
- def zip_prefix
77
- @zip_prefix || "origin"
78
- end
79
-
80
- # Returns an *integer* of maximum number of versions that can exist. If this number is exceeded, the first .diff version is merged in the starting .zip file and cannot be reversed
81
- #
82
- # *Default* *value*: +32+ (if backup is done daily, keep the last month versions)
83
- def max_versions
84
- @max_versions || 32
85
- end
86
-
87
- # Returns an *integer* of maximum number of rows fetched by a single query to avoid memory crashes. If your table has more rows it will be used multiple query to fetch all the data
88
- #
89
- # *Default* *value*: +100000+
90
- def per_page
91
- @per_page || 100000
92
- end
16
+ module TinyBackup
93
17
 
94
- # Create a backup of the current database and choose automatically to create a .zip or .diff file
95
- #
96
- # *Lock* *until* *is* *done*
18
+ # Create a backup of the current database and choose automatically to create a .zip or .diff file.
97
19
  def backup_now
98
20
  # if the resource is locked, we skip to ensure block
99
21
  lock
100
22
  locked_by_this_method = true
101
23
 
102
- @tmp_files = []
24
+ tmp_files = []
103
25
  nvf = new_version_filename
104
26
 
105
27
  stream = StringIO.new
@@ -109,21 +31,19 @@ module TinyBackup
109
31
  if nvf.split(".").last == "zip"
110
32
  # there is no .zip file so we must create one and add schema.rb and .csv file for each table
111
33
  # TODO: use a much better compression like Zlib::BEST_COMPRESSION to reduce the zip size, but this will consume processing power
112
- ZIPLIB.open("#{backup_folder}/#{nvf}", ZIPLIB::CREATE) do |f|
113
- puts "-- backup_schema\n"
34
+ ZIPLIB.open("#{config.backup_folder}/#{nvf}", ZIPLIB::CREATE) do |f|
114
35
  t_benchmark = Benchmark.ms do
115
36
  f.get_output_stream("schema.rb") do |ff|
116
37
  ff.write schema_rb
117
- @tmp_files << ff if ZIPOLD
38
+ tmp_files << ff if ZIPOLD
118
39
  end
119
40
  end
120
- puts " -> #{'%.4f' % (t_benchmark/1000)}s\n"
41
+ puts "-- backup_schema\n -> #{'%.4f' % (t_benchmark/1000)}s\n" if !config.silent
121
42
 
122
43
  ActiveRecord::Base.connection.tables.each do |table|
123
44
  next if table == "schema_migrations"
124
45
  query_count = ActiveRecord::Base.connection.execute("SELECT COUNT(*) FROM #{table}").first.first
125
46
 
126
- puts "-- backup_table(\"#{table}\")\n"
127
47
  t_benchmark = Benchmark.ms do
128
48
  if query_count > 0
129
49
  rows = []
@@ -131,34 +51,33 @@ module TinyBackup
131
51
 
132
52
  loop do
133
53
  break if query_index >= query_count
134
- query = ActiveRecord::Base.connection.execute("SELECT * FROM #{table} LIMIT #{per_page} OFFSET #{query_index}")
54
+ query = ActiveRecord::Base.connection.execute("SELECT * FROM #{table} LIMIT #{config.per_page} OFFSET #{query_index}")
135
55
  rows << add_query(query.fields) if query_index == 0
136
56
  query.each { |row| rows << add_query(row) }
137
- query_index += per_page
57
+ query_index += config.per_page
138
58
  end
139
59
 
140
60
  f.get_output_stream("#{table}.csv") do |ff|
141
61
  ff.write rows.join
142
- @tmp_files << ff if ZIPOLD
62
+ tmp_files << ff if ZIPOLD
143
63
  end
144
64
  end
145
65
  end
146
- puts " -> #{'%.4f' % (t_benchmark/1000)}s\n"
66
+ puts "-- backup_table(\"#{table}\")\n -> #{'%.4f' % (t_benchmark/1000)}s\n" if !config.silent
147
67
  end
148
68
  end
149
69
 
150
70
  else
151
71
  # a new .diff file is created with the diff between origin_zip and tmp_origin_zip(made by merging all the versions into origin)
152
- tmp_origin_zip = "#{backup_folder}/#{compact_original(:all)}"
72
+ tmp_origin_zip = "#{config.backup_folder}/#{compact_original(:all)}"
153
73
  tables = ActiveRecord::Base.connection.tables
154
74
  is_empty = true
155
75
 
156
- File.open("#{backup_folder}/#{nvf}", "wb") do |f|
76
+ File.open("#{config.backup_folder}/#{nvf}", "wb") do |f|
157
77
  ZIPLIB.open(tmp_origin_zip) do |zf|
158
78
  zf.entries.each do |zf_entry|
159
79
 
160
80
  if zf_entry.name == "schema.rb"
161
- puts "-- backup_schema\n"
162
81
  t_benchmark = Benchmark.ms do
163
82
  tables.delete "schema_migrations"
164
83
  this_diff = diff_files zf.read(zf_entry.name), schema_rb
@@ -172,7 +91,7 @@ module TinyBackup
172
91
  f.write "\n\n"
173
92
  end
174
93
  end
175
- puts " -> #{'%.4f' % (t_benchmark/1000)}s\n"
94
+ puts "-- backup_schema\n -> #{'%.4f' % (t_benchmark/1000)}s\n" if !config.silent
176
95
  else
177
96
 
178
97
  table = zf_entry.name.split(".").first
@@ -186,14 +105,13 @@ module TinyBackup
186
105
  rows = []
187
106
  query_index = 0
188
107
 
189
- puts "-- backup_table(\"#{table}\")\n"
190
108
  t_benchmark = Benchmark.ms do
191
109
  loop do
192
110
  break if query_index >= query_count
193
- query = ActiveRecord::Base.connection.execute("SELECT * FROM #{table} LIMIT #{per_page} OFFSET #{query_index}")
111
+ query = ActiveRecord::Base.connection.execute("SELECT * FROM #{table} LIMIT #{config.per_page} OFFSET #{query_index}")
194
112
  rows << add_query(query.fields) if query_index == 0
195
113
  query.each { |row| rows << add_query(row) }
196
- query_index += per_page
114
+ query_index += config.per_page
197
115
  end
198
116
 
199
117
  this_diff = diff_files zf.read(zf_entry.name), rows.join
@@ -207,7 +125,7 @@ module TinyBackup
207
125
  f.write "\n\n"
208
126
  end
209
127
  end
210
- puts " -> #{'%.4f' % (t_benchmark/1000)}s\n"
128
+ puts "-- backup_table(\"#{table}\")\n -> #{'%.4f' % (t_benchmark/1000)}s\n" if !config.silent
211
129
  end
212
130
 
213
131
  end
@@ -224,15 +142,14 @@ module TinyBackup
224
142
  rows = []
225
143
  query_index = 0
226
144
 
227
- puts "-- backup_table(\"#{table}\")\n"
228
145
  t_benchmark = Benchmark.ms do
229
146
  loop do
230
147
  break if query_index >= query_count
231
- query = ActiveRecord::Base.connection.execute("SELECT * FROM #{table} LIMIT #{per_page} OFFSET #{query_index}")
148
+ query = ActiveRecord::Base.connection.execute("SELECT * FROM #{table} LIMIT #{config.per_page} OFFSET #{query_index}")
232
149
 
233
150
  rows << add_query(query.fields) if query_index == 0
234
151
  query.each { |row| rows << add_query(row) }
235
- query_index += per_page
152
+ query_index += config.per_page
236
153
  end
237
154
 
238
155
  this_diff = diff_files "", rows.join
@@ -246,22 +163,22 @@ module TinyBackup
246
163
  f.write "\n\n"
247
164
  end
248
165
  end
249
- puts " -> #{'%.4f' % (t_benchmark/1000)}s\n"
166
+ puts "-- backup_table(\"#{table}\")\n -> #{'%.4f' % (t_benchmark/1000)}s\n" if !config.silent
250
167
  end
251
168
  end
252
169
 
253
170
  File.delete tmp_origin_zip
254
- File.delete("#{backup_folder}/#{nvf}") if is_empty
171
+ File.delete("#{config.backup_folder}/#{nvf}") if is_empty
255
172
  end
256
173
 
257
174
  # keep max versions
258
- version_files = Dir.glob("#{backup_folder}/#{version_prefix}*").sort
259
- if max_versions < version_files.length
175
+ version_files = Dir.glob("#{config.backup_folder}/#{config.version_prefix}*").sort
176
+ if config.max_versions < version_files.length
260
177
  # throw files to garbage
261
- @tmp_files << Dir.glob("#{backup_folder}/#{zip_prefix}*").first
262
- @tmp_files << version_files.first
178
+ tmp_files << Dir.glob("#{config.backup_folder}/#{config.zip_prefix}*").first
179
+ tmp_files << version_files.first
263
180
 
264
- File.rename "#{backup_folder}/#{compact_original(1)}", dup_file("#{backup_folder}/#{zip_prefix}_#{Time.now.strftime(date_format)}.zip")
181
+ File.rename "#{config.backup_folder}/#{compact_original(1)}", dup_file("#{config.backup_folder}/#{config.zip_prefix}_#{Time.now.strftime(config.date_format)}.zip")
265
182
  end
266
183
 
267
184
  # delete temporary files before method exit
@@ -269,62 +186,62 @@ module TinyBackup
269
186
  @method_error = e
270
187
  ensure
271
188
  unlock if locked_by_this_method
272
- @tmp_files.each { |i| File.delete(i) rescue nil } if @tmp_files.present?
189
+ tmp_files.each { |i| File.delete(i) rescue nil } if tmp_files.present?
273
190
  raise @method_error if @method_error.present?
274
191
  return true
275
192
  end
276
193
 
277
- # Merge into the .zip file and delete all the .diff files to clear the space
278
- #
279
- # *Lock* *until* *is* *done*
194
+ # Merge into the .zip file and delete all the .diff files to clear the space.
280
195
  def compact_all
281
196
  lock # if the resource is locked, we skip to ensure block
282
197
  locked_by_this_method = true
283
198
 
284
- @tmp_files = []
285
- @tmp_files += Dir.glob("#{backup_folder}/#{zip_prefix}*")
286
- @tmp_files += Dir.glob("#{backup_folder}/#{version_prefix}*")
199
+ tmp_files = []
200
+ tmp_files += Dir.glob("#{config.backup_folder}/#{config.zip_prefix}*")
201
+ tmp_files += Dir.glob("#{config.backup_folder}/#{config.version_prefix}*")
287
202
 
288
203
  # make the temporary zip be the original and apply the updated_at time-stamp
289
- File.rename "#{backup_folder}/#{compact_original(:all)}", dup_file("#{backup_folder}/#{zip_prefix}_#{Time.now.strftime(date_format)}.zip")
204
+ File.rename "#{config.backup_folder}/#{compact_original(:all)}", dup_file("#{config.backup_folder}/#{config.zip_prefix}_#{Time.now.strftime(config.date_format)}.zip")
290
205
 
291
206
  # delete temporary files before method exit
292
207
  rescue => e
293
208
  @method_error = e
294
209
  ensure
295
210
  unlock if locked_by_this_method
296
- @tmp_files.each { |i| File.delete(i) rescue nil } if @tmp_files.present?
211
+ tmp_files.each { |i| File.delete(i) rescue nil } if tmp_files.present?
297
212
  raise @method_error if @method_error.present?
298
213
  return true
299
214
  end
300
215
 
301
216
  # Change the database to match the selected integer +version_number+.
302
- # If the +just_temporary+ is false, the unused version files will be deleted and the latest backup version will be synchronized with the database data.
303
- # It's dangerous to start a new backup if the database is not synchronized with the latest backup version(call restore_db(:all) to restore all data)
304
- #
305
- # *Lock* *until* *is* *done*
306
- def restore_db version_number, just_temporary=true
217
+ # @param version_number [Integer or Symbol] can be :all to restore all backup data or 0 to restore only the data collected in the .zip file
218
+ # @param just_temporary [Boolean] if is false, the unused version files will be deleted and the latest backup version will be synchronized with the database data.
219
+ # @param with_backup [Boolean] if is false, the attempt to backup not saved data is canceled
220
+ def restore_db version_number, just_temporary=true, with_backup=true
221
+ # do this before deleting the database and lose data
222
+ backup_now if just_temporary && with_backup
223
+
307
224
  lock # if the resource is locked, we skip to ensure block
308
225
  locked_by_this_method = true
309
226
 
310
- @tmp_files = []
227
+ tmp_files = []
311
228
 
312
229
  if just_temporary && version_number != :all
313
- puts "you want to restore just temporary: DO NOT start a backup BEFORE calling TinyBackup.restore_db(:all)\n"
230
+ puts "you want to restore just temporary: DO NOT start a backup BEFORE calling TinyBackup.restore_db(:all)\n" if !config.silent
314
231
  end
315
232
 
316
- version_files = Dir.glob("#{backup_folder}/#{version_prefix}*")
233
+ version_files = Dir.glob("#{config.backup_folder}/#{config.version_prefix}*")
317
234
  if version_number == :all
318
235
  version_count = version_files.length
319
236
  else
320
- good_versions = version_files.find_all { |i| i.gsub("#{backup_folder}/#{version_prefix}", "").split("_").first.to_i <= version_number.to_i }
237
+ good_versions = version_files.find_all { |i| i.gsub("#{config.backup_folder}/#{config.version_prefix}", "").split("_").first.to_i <= version_number.to_i }
321
238
  version_count = good_versions.length
322
- @tmp_files += version_files - good_versions if !just_temporary
239
+ tmp_files += version_files - good_versions if !just_temporary
323
240
  end
324
241
 
325
242
  tmp_origin_zip = compact_original version_count
326
- @tmp_files << "#{backup_folder}/#{tmp_origin_zip}"
327
- @tmp_files << "#{backup_folder}/schema_tmp.rb"
243
+ tmp_files << "#{config.backup_folder}/#{tmp_origin_zip}"
244
+ tmp_files << "#{config.backup_folder}/schema_tmp.rb"
328
245
 
329
246
  db_name = Rails.configuration.database_configuration[Rails.env]["database"]
330
247
  db_collation = ActiveRecord::Base.connection.collation
@@ -333,31 +250,33 @@ module TinyBackup
333
250
  ActiveRecord::Base.connection.reconnect!
334
251
 
335
252
  # prepare the structure
336
- ZIPLIB.open("#{backup_folder}/#{tmp_origin_zip}") do |zf|
253
+ ZIPLIB.open("#{config.backup_folder}/#{tmp_origin_zip}") do |zf|
337
254
  zf.entries.each do |zf_entry|
338
255
  if zf_entry.name == "schema.rb"
339
- File.open("#{backup_folder}/schema_tmp.rb", "wb") { |f| f.write zf.read(zf_entry.name) }
256
+ File.open("#{config.backup_folder}/schema_tmp.rb", "wb") { |f| f.write zf.read(zf_entry.name) }
340
257
  break
341
258
  end
342
259
  end
343
260
  end
344
- ActiveRecord::Schema.load("#{backup_folder}/schema_tmp.rb")
261
+ schema_verbose = ActiveRecord::Schema.verbose
262
+ ActiveRecord::Schema.verbose = !config.silent
263
+ ActiveRecord::Schema.load("#{config.backup_folder}/schema_tmp.rb")
264
+ ActiveRecord::Schema.verbose = schema_verbose
345
265
 
346
266
  # add the data
347
- ZIPLIB.open("#{backup_folder}/#{tmp_origin_zip}") do |zf|
267
+ ZIPLIB.open("#{config.backup_folder}/#{tmp_origin_zip}") do |zf|
348
268
  zf.entries.each do |zf_entry|
349
269
  next if zf_entry.name == "schema.rb"
350
270
  table_rows = zf.read(zf_entry.name).split("\n")
351
271
  table_header = table_rows.shift
352
272
  table_name = zf_entry.name.split(".").first
353
273
 
354
- puts "-- insert_data(\"#{table_name}\")\n"
355
274
  t_benchmark = Benchmark.ms do
356
- table_rows.in_groups_of(per_page, false) do |tr_group|
275
+ table_rows.in_groups_of(config.per_page, false) do |tr_group|
357
276
  ActiveRecord::Base.connection.execute insert_row(table_name, table_header, tr_group)
358
277
  end
359
278
  end
360
- puts " -> #{'%.4f' % (t_benchmark/1000)}s\n"
279
+ puts "-- insert_data(\"#{table_name}\")\n -> #{'%.4f' % (t_benchmark/1000)}s\n" if !config.silent
361
280
  end
362
281
  end
363
282
 
@@ -366,7 +285,7 @@ module TinyBackup
366
285
  @method_error = e
367
286
  ensure
368
287
  unlock if locked_by_this_method
369
- @tmp_files.each { |i| File.delete(i) rescue nil } if @tmp_files.present?
288
+ tmp_files.each { |i| File.delete(i) rescue nil } if tmp_files.present?
370
289
  raise @method_error if @method_error.present?
371
290
  return true
372
291
  end
@@ -374,20 +293,24 @@ module TinyBackup
374
293
  # YOU SHALL NOT PASS -----------------------------------------------------------------
375
294
  private
376
295
 
377
- # Returns a *string* of the query used to insert a row of data
296
+ # Create an SQL INSERT query.
297
+ # @param table_name [String]
298
+ # @param table_header [String] contains column names delimited by " and separated by , (like '"col1", "col2", "col3"')
299
+ # @param table_rows [Array] each row is a *String* with the same format as table_header
300
+ # @return [String] the query used to insert a row of data
378
301
  def insert_row(table_name, table_header, table_rows)
379
302
  "INSERT INTO #{table_name} (#{table_header.gsub("\"", "`")}) VALUES " +
380
303
  table_rows.map { |i| "(#{i})" }.join(",")
381
304
  end
382
305
 
383
- # Merge X versions starting with the lowest +version_number+ into a temporary .zip file that looks like the original zip
384
- #
385
- # Returns a *string* that is the path of the temporary zip file
306
+ # Merge X versions starting with the lowest +version_number+ into a temporary .zip file that looks like the original zip.
307
+ # @param versions [Integer or Symbol] can be :all to compact all the versions and delete all the .diff files
308
+ # @return [String] the path of the temporary zip file
386
309
  def compact_original versions
387
- versions = Dir.glob("#{backup_folder}/*").length if versions == :all
388
- @tmp_files_compact ||= []
389
- tmp_filename = "tmp_#{zip_prefix}_#{Time.now.to_i}.zip"
390
- origin_zip = Dir.glob("#{backup_folder}/#{zip_prefix}*").first
310
+ versions = Dir.glob("#{config.backup_folder}/*").length if versions == :all
311
+ tmp_files = []
312
+ tmp_filename = "tmp_#{config.zip_prefix}_#{Time.now.to_i}.zip"
313
+ origin_zip = Dir.glob("#{config.backup_folder}/#{config.zip_prefix}*").first
391
314
 
392
315
  # add all files from original zip to a big hash
393
316
  zip_files = {}
@@ -396,7 +319,7 @@ module TinyBackup
396
319
  end
397
320
 
398
321
  # modify the hash on every version
399
- Dir.glob("#{backup_folder}/#{version_prefix}*").sort.first(versions).each do |version_file|
322
+ Dir.glob("#{config.backup_folder}/#{config.version_prefix}*").sort.first(versions).each do |version_file|
400
323
  diff_hash = prepare_diff version_file
401
324
 
402
325
  zip_files.each do |k, v|
@@ -422,20 +345,26 @@ module TinyBackup
422
345
  end
423
346
 
424
347
  # save the big hash
425
- ZIPLIB.open("#{backup_folder}/#{tmp_filename}", ZIPLIB::CREATE) do |f|
348
+ ZIPLIB.open("#{config.backup_folder}/#{tmp_filename}", ZIPLIB::CREATE) do |f|
426
349
  zip_files.each do |k, v|
427
350
  f.get_output_stream(k) do |ff|
428
351
  ff.write v
429
- @tmp_files_compact << ff if ZIPOLD
352
+ tmp_files << ff if ZIPOLD
430
353
  end if v != "\n"
431
354
  end
432
355
  end
433
356
 
434
- @tmp_files_compact.each { |i| File.delete i } if ZIPOLD
435
- tmp_filename
357
+ # delete temporary files before method exit
358
+ rescue => e
359
+ @method_error = e
360
+ ensure
361
+ tmp_files.each { |i| File.delete(i) rescue nil } if tmp_files.present?
362
+ raise @method_error if @method_error.present?
363
+ return tmp_filename
436
364
  end
437
365
 
438
- # Returns a *hash* with of the parsed .diff file
366
+ # @param diff_path [String]
367
+ # @return [Hash] of the parsed .diff file where each key is a file to be changed and the value is an *Array* or *String* of the .diff file lines
439
368
  def prepare_diff diff_path
440
369
  diff_hash = {}
441
370
 
@@ -454,9 +383,10 @@ module TinyBackup
454
383
  diff_hash
455
384
  end
456
385
 
457
- # It happens if there are two files with the same date(after using the date_format)
458
- #
459
- # Returns a *string* that will fix the duplicated filename issue by appending an index
386
+ # It happens if there are two files with the same date(after using the date_format).
387
+ # *Does* *not* *create* *a* *file!*
388
+ # @param filename [String]
389
+ # @return [String] a new filename that will fix the duplicated filename issue by appending an index
460
390
  def dup_file filename
461
391
  return filename unless File.exist? filename
462
392
  index = 1
@@ -469,7 +399,9 @@ module TinyBackup
469
399
  end
470
400
  end
471
401
 
472
- # Returns a *string* of the updated file after applying the diff
402
+ # @param file [String] content of the original file
403
+ # @param diff_lines [Array] each item is a line *String* of the diff file
404
+ # @return [String] of the updated file after applying the diff
473
405
  def update_file file, diff_lines
474
406
  file = file.split "\n"
475
407
  current_operation = nil
@@ -524,14 +456,16 @@ module TinyBackup
524
456
  file.join("\n") + "\n"
525
457
  end
526
458
 
527
- # Returns a *pair* *of* *integer* of the range interval in the diff operation
459
+ # @param operation [String] operation as seen in the .diff file
460
+ # @param sign [String] one of these values "a", "c" or "d" meaning add, change or delete operation
461
+ # @return [Array] of two *Integer* values of the range interval in the diff operation
528
462
  def diff_operation operation, sign
529
463
  val1, val2 = operation.split sign
530
464
  return diff_range(val1), diff_range(val2)
531
465
  end
532
466
 
533
-
534
- # Returns an *array* that contains the starting position of the change and how many operations it will take
467
+ # @param value [String] a single line number like "3" or a range of lines separated by , like "3,6"
468
+ # @return [Array] that contains the starting position of the change and how many operations it will take
535
469
  def diff_range value
536
470
  if value.include? ","
537
471
  l, r = value.split(",")
@@ -542,10 +476,11 @@ module TinyBackup
542
476
  end
543
477
 
544
478
  # Two temporary files will be created, but deleted shortly after the operation is finished
545
- #
546
- # Returns an *array* that contains lines of the diff between +file1+ and +file2+
479
+ # @param file1 [String] contents of the file
480
+ # @param file2 [String] contents of the file
481
+ # @eturn [Array] that contains lines of the diff between +file1+ and +file2+
547
482
  def diff_files file1, file2
548
- diff_filename = "#{backup_folder}/diff"
483
+ diff_filename = "#{config.backup_folder}/diff"
549
484
 
550
485
  File.open("#{diff_filename}1", "wb") { |f| f.write file1 }
551
486
  File.open("#{diff_filename}2", "wb") { |f| f.write file2 }
@@ -560,21 +495,22 @@ module TinyBackup
560
495
  diff_lines
561
496
  end
562
497
 
563
- # Returns a *string* of the current file created by backup_now function
498
+ # @return [String] of the current file created by backup_now function
564
499
  def new_version_filename
565
- backup_folder_files = Dir.glob("#{backup_folder}/*").map { |i| i.gsub("#{backup_folder}/", "") }
500
+ backup_folder_files = Dir.glob("#{config.backup_folder}/*").map { |i| i.gsub("#{config.backup_folder}/", "") }
566
501
 
567
- zip_file = backup_folder_files.find { |i| i.starts_with?(zip_prefix) && i.ends_with?("zip") }
568
- return "#{zip_prefix}_#{Time.now.strftime(date_format)}.zip" if zip_file.nil?
502
+ zip_file = backup_folder_files.find { |i| i.starts_with?(config.zip_prefix) && i.ends_with?("zip") }
503
+ return "#{config.zip_prefix}_#{Time.now.strftime(config.date_format)}.zip" if zip_file.nil?
569
504
 
570
- version_files = backup_folder_files.find_all { |i| i.starts_with?(version_prefix) && i.ends_with?("diff") }.sort
571
- return "#{version_prefix}1_#{Time.now.strftime(date_format)}.diff" if version_files.blank?
505
+ version_files = backup_folder_files.find_all { |i| i.starts_with?(config.version_prefix) && i.ends_with?("diff") }.sort
506
+ return "#{config.version_prefix}1_#{Time.now.strftime(config.date_format)}.diff" if version_files.blank?
572
507
 
573
- last_version = version_files.last.gsub(version_prefix, "").split("_").first.to_i
574
- return "#{version_prefix}#{last_version + 1}_#{Time.now.strftime(date_format)}.diff"
508
+ last_version = version_files.last.gsub(config.version_prefix, "").split("_").first.to_i
509
+ return "#{config.version_prefix}#{last_version + 1}_#{Time.now.strftime(config.date_format)}.diff"
575
510
  end
576
511
 
577
- # Returns a *string* of parsed csv row to be inserted into database
512
+ # @param values [Array] csv row
513
+ # @return [String] of parsed csv row to be inserted into database
578
514
  def add_query values
579
515
  values.map do |val|
580
516
  if val.nil?
@@ -594,10 +530,10 @@ module TinyBackup
594
530
  @logger = ActiveRecord::Base.logger
595
531
  ActiveRecord::Base.logger = nil
596
532
 
597
- if File.exist?("#{backup_folder}/.lock")
598
- raise "Another operation is running. More info you can find in the '#{backup_folder}/.lock' file"
533
+ if File.exist?("#{config.backup_folder}/.lock")
534
+ raise "Another operation is running. More info you can find in the '#{config.backup_folder}/.lock' file"
599
535
  else
600
- File.open("#{backup_folder}/.lock", "wb") do |f|
536
+ File.open("#{config.backup_folder}/.lock", "wb") do |f|
601
537
  f.write caller.join("\n") + "\n"
602
538
  end
603
539
  end
@@ -607,7 +543,7 @@ module TinyBackup
607
543
  def unlock
608
544
  ActiveRecord::Base.logger = @logger
609
545
 
610
- File.delete("#{backup_folder}/.lock") if File.exist?("#{backup_folder}/.lock")
546
+ File.delete("#{config.backup_folder}/.lock") if File.exist?("#{config.backup_folder}/.lock")
611
547
  end
612
548
  end
613
549
  include TinyBackup
@@ -0,0 +1,3 @@
1
+ module TinyBackup
2
+ VERSION = '0.1.0'
3
+ end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiny_backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
5
- prerelease:
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Razvan Pavel
@@ -18,30 +17,31 @@ executables: []
18
17
  extensions: []
19
18
  extra_rdoc_files: []
20
19
  files:
20
+ - lib/configure.rb
21
21
  - lib/tiny_backup.rb
22
+ - lib/version.rb
22
23
  homepage: http://rubygems.org/gems/tiny_backup
23
24
  licenses:
24
25
  - MIT
26
+ metadata: {}
25
27
  post_install_message:
26
28
  rdoc_options: []
27
29
  require_paths:
28
30
  - lib
29
31
  required_ruby_version: !ruby/object:Gem::Requirement
30
- none: false
31
32
  requirements:
32
- - - ! '>='
33
+ - - ">="
33
34
  - !ruby/object:Gem::Version
34
35
  version: '0'
35
36
  required_rubygems_version: !ruby/object:Gem::Requirement
36
- none: false
37
37
  requirements:
38
- - - ! '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  requirements: []
42
42
  rubyforge_project:
43
- rubygems_version: 1.8.23
43
+ rubygems_version: 2.2.2
44
44
  signing_key:
45
- specification_version: 3
45
+ specification_version: 4
46
46
  summary: Backup your database on S3 or local, optimized for small disk space
47
47
  test_files: []