mirror of
https://github.com/UzixLS/migresia.git
synced 2025-07-18 23:01:21 +03:00
Massive overhaul to allow for multinode scenarios, and predictable return types.
This commit is contained in:
@ -24,41 +24,46 @@
|
||||
|
||||
-module(migresia).
|
||||
|
||||
-export([create_new_migration/1, create_new_migration/2, check/0, check/1, migrate/0, migrate/1, list_nodes/0]).
|
||||
-export([create_new_migration/2, check/1, migrate/1, list_disc_copy_nodes/0]).
|
||||
|
||||
-spec create_new_migration(string()) -> any().
|
||||
create_new_migration(Description) ->
|
||||
create_new_migration(undefined, Description).
|
||||
-define(TABLE, schema_migrations).
|
||||
|
||||
-spec create_new_migration(atom(), string()) -> any().
|
||||
create_new_migration(App, Description) ->
|
||||
{{Year, Month, Day}, {Hour, Minute, Second}} = calendar:local_time(),
|
||||
Filename = lists:flatten(io_lib:format("~w~2.2.0w~2.2.0w~2.2.0w~2.2.0w~2.2.0w_", [Year, Month, Day, Hour, Minute, Second]) ++ Description),
|
||||
FullPathAndExtension = filename:join(migresia_migrations:get_priv_dir(App), Filename ++ ".erl"),
|
||||
Path = migresia_migrations:get_priv_dir(App),
|
||||
FullPathAndExtension = filename:join(Path, Filename ++ ".erl"),
|
||||
io:format("Creating new migration: ~p~n", [FullPathAndExtension]),
|
||||
file:write_file(FullPathAndExtension, io_lib:fwrite("-module(~p).~n-behavior(db_migration).~n-export([up/0, down/0]). ~n~nup() -> ok.~n~ndown() -> throw(<<\"Downgraders not implemented.\">>).", [list_to_atom(Filename)])),
|
||||
ok = filelib:ensure_dir(<<Path/binary, <<"/">>/binary>>),
|
||||
ok = file:write_file(FullPathAndExtension, io_lib:fwrite("-module(~p).~n-behavior(db_migration).~n-export([up/0, down/0]). ~n~nup() -> ok.~n~ndown() -> throw(<<\"Downgraders not implemented.\">>).", [list_to_atom(Filename)])),
|
||||
io:format("Migration written.~n~n").
|
||||
|
||||
-spec check(atom()) -> any().
|
||||
-spec check(atom()) -> ok | {error, any()}.
|
||||
check(App) ->
|
||||
case start_mnesia(false) of
|
||||
ok ->
|
||||
Loaded = migresia_migrations:list_unapplied_ups(App),
|
||||
if Loaded == [] ->
|
||||
io:format("No migrations to apply.~n", []);
|
||||
[];
|
||||
true ->
|
||||
io:format("Migrations to apply: ~p~n", [ [X||{X,_} <- Loaded] ])
|
||||
[ [X||{X,_} <- Loaded] ]
|
||||
end;
|
||||
{error, Error} -> {error, Error}
|
||||
end.
|
||||
|
||||
-spec check() -> any().
|
||||
check() -> check(undefined).
|
||||
|
||||
-spec migrate(atom()) -> any().
|
||||
-spec migrate(atom()) -> ok | {error, any()}.
|
||||
migrate(App) ->
|
||||
case start_mnesia(true) of
|
||||
ok ->
|
||||
case migresia_migrations:ensure_schema_table_exists() of
|
||||
ok ->
|
||||
Loaded = migresia_migrations:list_unapplied_ups(App),
|
||||
lists:foreach(fun execute_up/1, Loaded).
|
||||
|
||||
-spec migrate() -> any().
|
||||
migrate() ->
|
||||
migrate(undefined).
|
||||
lists:foreach(fun execute_up/1, Loaded);
|
||||
{error, Error} -> Error
|
||||
end;
|
||||
{error, Error} -> {error, Error}
|
||||
end.
|
||||
|
||||
execute_up({Module, Short}) ->
|
||||
io:format("Executing up in ~s...~n", [Module]),
|
||||
@ -66,6 +71,27 @@ execute_up({Module, Short}) ->
|
||||
mnesia:dirty_write(schema_migrations, {schema_migrations, Short, true}),
|
||||
io:format(" => done~n", []).
|
||||
|
||||
list_nodes() ->
|
||||
mnesia:table_info(schema, disc_copies).
|
||||
|
||||
|
||||
-spec start_mnesia(boolean()) -> ok | {error, any()}.
|
||||
start_mnesia(RemoteToo) ->
|
||||
io:format("Starting Mnesia...~n", []),
|
||||
case application:ensure_started(mnesia) of
|
||||
Other when Other /= ok -> io:format(" => Error:~p~n", [Other]), Other;
|
||||
ok ->
|
||||
case RemoteToo of
|
||||
false -> ok;
|
||||
true -> {ResultList, BadNodes} = rpc:multicall(list_all_nodes(), application, ensure_started, [mnesia]),
|
||||
BadStatuses = [X || X <- ResultList, X /= ok],
|
||||
if BadNodes /= [] -> io:format(" => Error:~p~n", [not_all_nodes_running]), {error, not_all_nodes_running};
|
||||
BadStatuses /= [] -> io:format(" => Error:~p~n", [BadStatuses]), {error, BadStatuses};
|
||||
true -> io:format(" => started~n", []), ok
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
list_all_nodes() ->
|
||||
mnesia:table_info(schema, all_nodes).
|
||||
|
||||
list_disc_copy_nodes() ->
|
||||
mnesia:table_info(schema, disc_copies).
|
@ -24,37 +24,47 @@
|
||||
|
||||
-module(migresia_migrations).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-export([list_unapplied_ups/1, get_priv_dir/1, ensure_schema_table_exists/0]).
|
||||
|
||||
-export([list_unapplied_ups/1, get_priv_dir/1]).
|
||||
|
||||
-define(APP, migresia).
|
||||
-define(DIR, <<"migrate">>).
|
||||
-define(TABLE, schema_migrations).
|
||||
|
||||
-spec list_unapplied_ups(atom()) -> [{module(), binary()}].
|
||||
list_unapplied_ups(App) when App == undefined ->
|
||||
list_unapplied_ups(?APP);
|
||||
list_unapplied_ups(App) ->
|
||||
get_migrations(get_priv_dir(App)).
|
||||
|
||||
|
||||
get_priv_dir(App) when App == undefined ->
|
||||
get_priv_dir(?APP);
|
||||
-spec get_priv_dir(atom()) -> binary().
|
||||
get_priv_dir(App) ->
|
||||
application:load(App),
|
||||
filename:join(code:priv_dir(App), ?DIR).
|
||||
case application:load(App) of
|
||||
ok -> filename:join(code:priv_dir(App), ?DIR);
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
|
||||
-spec ensure_schema_table_exists() -> ok | {error, any()}.
|
||||
ensure_schema_table_exists() ->
|
||||
case lists:member(?TABLE, mnesia:system_info(tables)) of
|
||||
true -> ok;
|
||||
false -> io:format("Table schema_migration not found, creating...~n", []),
|
||||
Attr = [{type, ordered_set}, {disc_copies, migresia:list_disc_copy_nodes()}],
|
||||
case mnesia:create_table(?TABLE, Attr) of
|
||||
{atomic, ok} -> io:format(" => created~n", []), ok;
|
||||
{aborted, Reason} -> {error, Reason}
|
||||
end
|
||||
end.
|
||||
|
||||
-spec get_migrations({error, any()} | binary()) -> list().
|
||||
get_migrations({error, _} = Err) ->
|
||||
exit(Err);
|
||||
{error, Err};
|
||||
get_migrations(Dir) ->
|
||||
io:format("DIR: ~p~n~n", [Dir]),
|
||||
ToApply = check_dir(file:list_dir(Dir)),
|
||||
start_mnesia(),
|
||||
Applied = check_table(),
|
||||
case check_table() of
|
||||
{error, Error} -> {error, Error};
|
||||
Applied when is_list(Applied) ->
|
||||
ToExecute = compile_unapplied(Dir, ToApply, Applied, []),
|
||||
Fun = fun({Module, Short, Binary}) -> load_migration(Module, Short, Binary) end,
|
||||
lists:map(Fun, ToExecute).
|
||||
lists:map(Fun, ToExecute)
|
||||
end.
|
||||
|
||||
check_dir({error, Reason}) ->
|
||||
exit({file, list_dir, Reason});
|
||||
@ -74,33 +84,23 @@ normalize_names([Name|_], _Acc) ->
|
||||
normalize_names([], Acc) ->
|
||||
lists:sort(Acc).
|
||||
|
||||
start_mnesia() ->
|
||||
io:format("Starting Mnesia...~n", []),
|
||||
case mnesia:start() of
|
||||
ok -> io:format(" => started~n", []);
|
||||
Other -> io:format(" => Error:~p~nExiting...~n", [Other]), exit(Other)
|
||||
end.
|
||||
|
||||
check_table() ->
|
||||
case lists:member(?TABLE, mnesia:system_info(tables)) of
|
||||
false ->
|
||||
create_migration_table();
|
||||
[];
|
||||
true ->
|
||||
io:format("Waiting for tables...~n", []),
|
||||
ok = mnesia:wait_for_tables([?TABLE], 5000),
|
||||
case mnesia:wait_for_tables([?TABLE], 5000) of
|
||||
ok ->
|
||||
io:format(" => done~n", []),
|
||||
Select = [{{?TABLE,'_','_'},[],['$_']}],
|
||||
List = mnesia:dirty_select(?TABLE, Select),
|
||||
[ X || {schema_migrations, X, true} <- List ]
|
||||
[ X || {schema_migrations, X, true} <- List ];
|
||||
Error -> {error, Error}
|
||||
end
|
||||
end.
|
||||
|
||||
create_migration_table() ->
|
||||
io:format("Table schema_migration not found, creating...~n", []),
|
||||
Attr = [{type, ordered_set}, {disc_copies, migresia:list_nodes()}],
|
||||
case mnesia:create_table(?TABLE, Attr) of
|
||||
{atomic, ok} -> io:format(" => created~n", []), [];
|
||||
{aborted, Reason} -> exit({mnesia, create_table, Reason})
|
||||
end.
|
||||
|
||||
compile_unapplied(Dir, [{Short, Module}|TN], [] = Old, Acc) ->
|
||||
compile_unapplied(Dir, TN, Old, [compile_file(Dir, Short, Module)|Acc]);
|
||||
@ -142,13 +142,3 @@ load_migration(Module, Short, Binary) ->
|
||||
exit({code, load_binary, What})
|
||||
end.
|
||||
|
||||
%%% Some unit tests
|
||||
|
||||
normalize_test() ->
|
||||
[{<<"20130401120000">>, <<"20130401120000">>},
|
||||
{<<"20130402000000">>, <<"20130402000000_">>},
|
||||
{<<"20130403080910">>, <<"20130403080910_test">>}] =
|
||||
normalize_names(
|
||||
[<<"20130402000000_.erl">>,
|
||||
"20130403080910_test.erl",
|
||||
<<"20130401120000.erl">>], []).
|
||||
|
Reference in New Issue
Block a user