Class: Backupper::Adapter

Inherits:
Object
  • Object
show all
Defined in:
lib/backupper/adapter.rb

Overview

Base class for dump adapters. A subclass must implement #extension and #command; it is automatically registered under its underscored class name (use register 'name' to pick a different one).

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(database:, username: 'root', password: nil, dump_options: nil, basename:, tmp_dir: '/tmp') ⇒ Adapter

All values arrive raw; escaping happens in the accessors below.



61
62
63
64
65
66
67
68
# File 'lib/backupper/adapter.rb', line 61

def initialize(database:, username: 'root', password: nil, dump_options: nil, basename:, tmp_dir: '/tmp')
  @raw_database = database
  @raw_username = username
  @raw_password = password
  @raw_dump_options = dump_options
  @raw_basename = basename
  @raw_tmp_dir = tmp_dir
end

Class Method Details

.adapter_nameObject



20
21
22
23
24
25
26
27
28
# File 'lib/backupper/adapter.rb', line 20

def adapter_name
  return @adapter_name if defined?(@adapter_name) && @adapter_name
  # On Ruby < 3.0 `inherited` fires before the constant is assigned, so
  # the name must be derived lazily, at lookup time.
  return nil if name.nil?

  basename = name.split('::').last
  return basename.gsub(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
end

.find(name) ⇒ Object



30
31
32
33
# File 'lib/backupper/adapter.rb', line 30

def find(name)
  # Reverse scan: a later-loaded custom adapter can shadow a built-in name.
  return Backupper::Adapter.subclasses.reverse_each.find { |klass| klass.adapter_name == name.to_s }
end

.inherited(subclass) ⇒ Object



11
12
13
14
# File 'lib/backupper/adapter.rb', line 11

def inherited(subclass)
  super
  Backupper::Adapter.subclasses << subclass
end

.namesObject



35
36
37
# File 'lib/backupper/adapter.rb', line 35

def names
  return Backupper::Adapter.subclasses.map(&:adapter_name).compact.uniq.sort
end

.register(name) ⇒ Object



16
17
18
# File 'lib/backupper/adapter.rb', line 16

def register(name)
  @adapter_name = name.to_s
end

.shellescape_dump_options(dump_options) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/backupper/adapter.rb', line 43

def shellescape_dump_options(dump_options)
  return nil if dump_options.nil?

  options =
    if dump_options.is_a?(Array)
      dump_options.map(&:to_s)
    else
      Shellwords.split(dump_options.to_s)
    end
  return nil if options.empty?

  return options.map { |option| Shellwords.escape(option) }.join(' ')
rescue ArgumentError => e
  raise ArgumentError, "Invalid dump_options: #{e.message}"
end

.subclassesObject



39
40
41
# File 'lib/backupper/adapter.rb', line 39

def subclasses
  return @subclasses ||= []
end

Instance Method Details

#commandObject

Shell command that writes the dump to outfile.

Raises:

  • (NotImplementedError)


76
77
78
# File 'lib/backupper/adapter.rb', line 76

def command
  raise NotImplementedError, "#{self.class} must implement #command"
end

#databaseObject

Shell-escaped accessors: interpolate them directly in #command.



94
95
96
# File 'lib/backupper/adapter.rb', line 94

def database
  return Shellwords.escape(@raw_database)
end

#dump_optionsObject



106
107
108
# File 'lib/backupper/adapter.rb', line 106

def dump_options
  return Backupper::Adapter.shellescape_dump_options(@raw_dump_options)
end

#extensionObject

Extension of the file produced by #command (e.g. '.sql.bz2').

Raises:

  • (NotImplementedError)


71
72
73
# File 'lib/backupper/adapter.rb', line 71

def extension
  raise NotImplementedError, "#{self.class} must implement #extension"
end

#filenameObject

Raw paths, for the orchestrator (SSHKit download!, report).



81
82
83
# File 'lib/backupper/adapter.rb', line 81

def filename
  return "#{@raw_basename}#{extension}"
end

#outfileObject



110
111
112
# File 'lib/backupper/adapter.rb', line 110

def outfile
  return Shellwords.escape(outfile_path)
end

#outfile_pathObject



85
86
87
# File 'lib/backupper/adapter.rb', line 85

def outfile_path
  return File.join(@raw_tmp_dir, filename)
end

#passwordObject



102
103
104
# File 'lib/backupper/adapter.rb', line 102

def password
  return @raw_password && Shellwords.escape(@raw_password)
end

#usernameObject



98
99
100
# File 'lib/backupper/adapter.rb', line 98

def username
  return Shellwords.escape(@raw_username)
end

#working_dirObject



114
115
116
# File 'lib/backupper/adapter.rb', line 114

def working_dir
  return Shellwords.escape(working_dir_path)
end

#working_dir_pathObject



89
90
91
# File 'lib/backupper/adapter.rb', line 89

def working_dir_path
  return File.join(@raw_tmp_dir, @raw_basename)
end