# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE # Version 2, December 2004 # # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION # # 0. You just DO WHAT THE FUCK YOU WANT TO. defmodule Amnesia.Backup do @moduledoc """ This module provides a behavior to implement a backup module and helpers to use backups. """ alias Amnesia.Helper.Options @type o :: { :ok, any } | { :error, any } @type i :: { :module, atom } | { :scope, :global | :local } | { :directory, String.t } @type r :: [{ :module, atom } | { :keep | :skip | :clear | :recreate, atom | [atom] } | { :default, :keep | :skip | :clear | :recreate }] @doc """ Open the backup for writing. """ @callback open_write(any) :: o @doc """ Write the given terms to the backup. """ @callback write(any, [any]) :: o @doc """ Commit the write to the backup. """ @callback commit_write(any) :: o @doc """ Close the backup if the backup is interrupted. """ @callback abort_write(any) :: o @doc """ Open the backup for reading. """ @callback open_read(any) :: o @doc """ Read terms from the backup. """ @callback read(any) :: { :ok, any, [any] } | { :error, any } @doc """ Close the backup. """ @callback close_read(any) :: o @doc """ Create a checkpoint, see `mnesia:activate_checkpoint`. It's important to backup the metadata table, to avoid bugs when restoring the database. Example to backup on a file called "backup" the table Account as well the metadata table: ``` Amnesia.Backup.checkpoint(%{ name: 'backup', max: [Database.Account, Database] }) ``` """ @spec checkpoint(Keyword.t) :: { :ok, any, [node] } | { :error, any } def checkpoint(options) do args = Keyword.new |> Options.update(:name, options[:name]) |> Options.update(:max, options[:max]) |> Options.update(:min, options[:min]) |> Options.update(:allow_remote, options[:remote]) |> Options.update(:ram_overrides_dump, options[:override]) :mnesia.activate_checkpoint(args) end @doc """ Create a checkpoint with the given name, see `mnesia:activate_checkpoint`. """ @spec checkpoint(any, Keyword.t) :: { :ok, any, [node] } | { :error, any } def checkpoint(name, options) do checkpoint(Keyword.put_new(options, :name, name)) end @doc """ Start a backup with the default backup module, see `mnesia:backup`. """ @spec start(any) :: :ok | { :error, any } def start(data) do :mnesia.backup(data) end @doc """ Start a backup of a given checkpoint, see `mnesia:backup_checkpoint`. """ @spec start(any, any) :: :ok | { :error, any } def start(name, data) do :mnesia.backup_checkpoint(name, data) end @doc """ Traverse a backup, see `mnesia:traverse_backup`. """ @spec traverse(any, any, any, ([tuple], any -> any)) :: { :ok, any } | { :error, any } def traverse(source, target, acc, fun) do :mnesia.traverse_backup(source, target, fun, acc) end @doc """ Traverse a backup with custom backup modules, see `mnesia:traverse_backup`. """ @spec traverse(atom, any, atom, any, any, ([tuple], any -> any)) :: { :ok, any } | { :error, any } def traverse(source, source_data, target, target_data, acc, fun) do :mnesia.traverse_backup(source_data, source, target_data, target, fun, acc) end @doc """ Restore a backup, see `mnesia:restore`. """ @spec restore(any, r) :: [atom] | { :error, any } def restore(data, options) do args = Keyword.new |> Options.update(:module, options[:module]) |> Options.update(:keep_tables, options[:keep] |> Options.normalize) |> Options.update(:clear_tables, options[:clear] |> Options.normalize) |> Options.update(:recreate_tables, options[:recreate] |> Options.normalize) |> Options.update(:skip_tables, options[:skip] |> Options.normalize) args = Options.update(args, :default, case options[:default] do :keep -> :keep_tables :skip -> :skip_tables :clear -> :clear_tables :recreate -> :recreate_tables nil -> nil end) :mnesia.restore(data, args) end @doc """ Restore a backup with the given module, see `mnesia:restore`. """ @spec restore(atom, any, r) :: { :atomic, [atom] } | { :aborted, any } def restore(module, data, options) do restore(data, Keyword.put(options, :module, module)) end @doc """ Install a fallback with the default backup module, see `mnesia:install_fallback`. """ @spec install(any) :: :ok | { :error, any } def install(data) do :mnesia.install_fallback(data) end @doc """ Install a fallback with the given backup module, see `mnesia:install_fallback`. """ @spec install(atom | any) :: :ok | { :error, any } def install(module, data) do :mnesia.install_fallback(data, module) end @doc """ Install a fallback with the given backup module and options, see `mnesia:install_fallback`. """ @spec install(atom, any, i) :: :ok | { :error, any } def install(module, data, options) do args = [module: module] |> Options.update(:scope, options[:module]) |> Options.update(:mnesia_dir, options[:directory]) :mnesia.install_fallback(data, args) end @doc """ Uninstall a fallback, see `mnesia:uninstall_fallback`. """ @spec uninstall :: :ok | { :error, any } def uninstall do :mnesia.uninstall_fallback end @doc """ Uninstall a fallback, see `mnesia:uninstall_fallback`. """ @spec uninstall(i) :: :ok | { :error, any } def uninstall(options) do args = Keyword.new |> Options.update(:module, options[:module]) |> Options.update(:scope, options[:module]) |> Options.update(:mnesia_dir, options[:directory]) :mnesia.uninstall_fallback(args) end @doc """ Uninstall a fallback, see `mnesia:uninstall_fallback`. """ @spec uninstall(atom, i) :: :ok | { :error, any } def uninstall(module, options) do uninstall(Keyword.put(options, :module, module)) end @doc false defmacro __using__(_opts) do quote do @behaviour Amnesia.Backup @doc """ Start a backup, see `mnesia:backup`. """ @spec start(any) :: :ok | { :error, any } def start(data) do :mnesia.backup(data, __MODULE__) end @doc """ Start a backup of the given checkpoint, see `mnesia:backup_checkpoint`. """ @spec start(any, any) :: :ok | { :error, any } def start(name, data) do :mnesia.backup_checkpoint(name, data, __MODULE__) end @doc """ Traverse a backup, see `mnesia:traverse_backup`. """ @spec traverse(any, any, any, ([tuple], any -> any)) :: { :ok, any } | { :error, any } def traverse(data, target, acc, fun) do :mnesia.traverse_backup(data, __MODULE__, target, fun, acc) end @doc """ Traverse a backup targeting a custom backup module, see `mnesia:traverse_backup`. """ @spec traverse(any, atom, any, any, ([tuple], any -> any)) :: { :ok, any } | { :error, any } def traverse(data, target, target_data, acc, fun) do :mnesia.traverse_backup(data, target_data, target, fun, acc) end @doc """ Restore a backup, see `mnesia:restore`. """ @spec restore(any, Amnesia.Backup.r) :: { :atomic, [atom] } | { :aborted, any } def restore(data, options) do Amnesia.Backup.restore(__MODULE__, data, options) end @doc """ Install a fallback, see `mnesia:install_fallback`. """ @spec install(any) :: :ok | { :error, any } def install(data) do Amnesia.Backup.install(__MODULE__, data, [scope: :global]) end @doc """ Install a fallback, see `mnesia:install_fallback`. """ @spec install(any, Amnesia.Backup.i) :: :ok | { :error, any } def install(data, options) do Amnesia.Backup.install(__MODULE__, data, options) end @doc """ Uninstall a fallback, see `mnesia:uninstall_fallback`. """ @spec uninstall :: :ok | { :error, any } def uninstall do Amnesia.Backup.uninstall(__MODULE__, [scope: :global]) end @doc """ Uninstall a fallback, see `mnesia:uninstall_fallback`. """ @spec uninstall(atom, Amnesia.Backup.i) :: :ok | { :error, any } def uninstall(options) do Amnesia.Backup.uninstall(__MODULE__, options) end end end end