# Simple database backup rake task # Supports mysql and postgres # Env vars: # BACKUP_DIR - where to store files. Relative to Rails app root. Defaults to '../../shared/backup', # which works nicely with Capistrano's default file structure. # NUM_TO_KEEP - how many backup files to keep. Defaults to 7. # RAILS_ENV - which database to back up. Defaults to development like all Rake tasks.

namespace :backup do

desc "Backup database"
task :db => [:environment] do
  FileUtils.mkdir_p backup_dir

  adapter = settings['adapter']
  host = settings['host'] || '127.0.0.1'
  port = settings['port']
  database = settings['database']
  user = settings['username']
  password = settings['password']

  # Run the appropriate backup utility
  case adapter
  when 'mysql', 'mysql2'
    port ||= '3306'
    password_arg = password.present? ? "-p#{password}" : ''
    system "/usr/bin/env mysqldump -h #{host} -P #{port} -u #{user} #{password_arg} #{database} > #{output_file}"
  when 'postgresql'
    port ||= '5432'

    # pg_dump doesn't take a password arg, so we have to write the password to the ~/.pgpass file.
    pgpass_file = ENV['HOME']+'/.pgpass'
    pgpass_content = "#{host}:#{port}:#{database}:#{user}:#{password}"
    File.open(pgpass_file, 'a+') do |pgpass|
      pgpass.write(pgpass_content) unless pgpass.grep(pgpass_content).present?
    end

    # Ensure that .pgpass has the correct mode (only really needed if we just)
    # created it.
    # Ruby 1.8/1.9 compatibility
    file_util_class = File.respond_to?(:chmod) ? File : FileUtils
    file_util_class.chmod(0600, pgpass_file)

    system "/usr/bin/env pg_dump -Fc -h #{host} -p #{port} -U #{user} --no-password #{database} > #{output_file}"
  else
    raise RuntimeError, "I don't know how to back up #{settings['adapter']} databases!"
  end
  system "/usr/bin/env gzip -f #{output_file}"
end

namespace :db do
  desc "Prune database backups"
  task :prune => [:environment] do
    files = Dir.glob("#{output_file_prefix}-*").sort

    r_index = (-1 * num_to_keep) - 1

    if files.length > num_to_keep
      files[0..r_index].each do |file|
        FileUtils.rm file
      end
    end
  end
end

def backup_dir
  relative_dir = ENV['BACKUP_DIR'] || '../../shared/backup'
  File.expand_path(relative_dir, Rails.root)
end

def output_file
  # Memoized so that it stays the same even if a task runs into the next day
  @output_file ||= File.expand_path("#{output_file_prefix}-#{Time.now.strftime('%Y%m%d')}.dump", Rails.root)
end

def output_file_prefix
  "#{backup_dir}/#{settings['database']}"
end

def num_to_keep
  if ENV['NUM_TO_KEEP'].to_i > 0
    ENV['NUM_TO_KEEP'].to_i
  else
    7
  end
end

def settings
  @settings ||= YAML.load(File.read(Rails.root.join("config", "database.yml")))[Rails.env]
end

end