File 0001-Remove-ktn_fsm-it-was-superseeded-by-gen_statem.patch of Package katana
From 095aee1b6287f72b1df3bab7433aba6b32a9621e Mon Sep 17 00:00:00 2001
From: Brujo Benavides <elbrujohalcon@inaka.net>
Date: Sun, 6 May 2018 13:37:14 -0300
Subject: [PATCH] Remove ktn_fsm, it was superseeded by gen_statem
---
README.md | 11 ++--
src/ktn_fsm.erl | 67 ------------------------
test/cover.spec | 1 -
test/ktn_fsm_SUITE.erl | 109 ---------------------------------------
test/secure_vault.erl | 136 -------------------------------------------------
5 files changed, 5 insertions(+), 319 deletions(-)
delete mode 100644 src/ktn_fsm.erl
delete mode 100644 test/ktn_fsm_SUITE.erl
delete mode 100644 test/secure_vault.erl
diff --git a/README.md b/README.md
index 7446a1b..ceabe3e 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,6 @@ Check out as well [katana-code](https://github.com/inaka/katana-code) and [katan
* `ktn_date`: functions useful for handling dates and time values.
* `ktn_debug`: functions useful for debugging.
-* `ktn_fsm`: a _nice_ wrapper on top of `gen_fsm`
* `ktn_json`: functions useful for processing & creating JSON.
* `ktn_maps`: functions useful for handling maps.
* `ktn_numbers`: functions useful for processing numeric values.
@@ -139,12 +138,12 @@ all cases.
A better way to structure the code would be preferable, one that makes the
flow obvious and separates responsibilities into appropriate code segments.
One way is a finite state machine. OTP even has a behaviour for exactly this
-purpose. However, gen_fsm does not exactly fit our needs:
-To begin with, gen_fsms run in their own process, which may not be what is
-needed. Second, logic flow is not immediately obvious because the fsm's
-state depends on the result of each state function. Finally, a gen_fsm
+purpose. However, gen_statem does not exactly fit our needs:
+To begin with, gen_statems run in their own process, which may not be what is
+needed. Second, logic flow is not immediately obvious because the FSM's
+state depends on the result of each state function. Finally, a gen_statem
transitions only on receiving input, whereas we are looking for something
-that runs like normal code, "on its own". So, our fsm will be defined by
+that runs like normal code, "on its own". So, our FSM will be defined by
something like a transition table, a single place you can look at and know
how it executes. This specification will "drive" the recipe.
diff --git a/src/ktn_fsm.erl b/src/ktn_fsm.erl
deleted file mode 100644
index f11ea62..0000000
--- a/src/ktn_fsm.erl
+++ /dev/null
@@ -1,67 +0,0 @@
-%%% @doc "Nicer" interfact to gen_fsm.
-%%% With this module you can:
-%%% - use ktn_fsm:call instead of gen_fsm:sync_send_event
-%%% - use ktn_fsm:call_through instead of gen_fsm:sync_send_all_state_event
-%%% - use ktn_fsm:cast instead of gen_fsm:send_event
-%%% - use ktn_fsm:cast_through instead of gen_fsm:sync_send_event
-%%% - use ktn_fsm:state to retrieve the current gen_fsm state
-%%% @todo implement the inner functional changes:
-%%% - leave some of the callbacks undefined. If a callback function is
-%%% undefined, the caller will throw an exception, but the fsm will keep
-%%% looping. This way you don't have to have catch-all clauses there.
--module(ktn_fsm).
-
--ignore_xref([start/3]).
-
--export(
- [ state/1
- , call/2
- , call/3
- , call_through/2
- , call_through/3
- , cast/2
- , cast_through/2
- ]).
-
--export(['$handle_undefined_function'/2]).
-
--spec state(sys:name()) -> atom().
-state(Fsm) ->
- {StateName, _State} = sys:get_state(Fsm),
- StateName.
-
--spec call(sys:name(), term()) -> term().
-call(Fsm, Call) ->
- gen_fsm:sync_send_event(Fsm, Call).
-
--spec call(sys:name(), term(), timeout()) -> term().
-call(Fsm, Call, Timeout) ->
- gen_fsm:sync_send_event(Fsm, Call, Timeout).
-
--spec call_through(sys:name(), term()) -> term().
-call_through(Fsm, Call) ->
- gen_fsm:sync_send_all_state_event(Fsm, Call).
-
--spec call_through(sys:name(), term(), timeout()) -> term().
-call_through(Fsm, Call, Timeout) ->
- gen_fsm:sync_send_all_state_event(Fsm, Call, Timeout).
-
--spec cast(sys:name(), term()) -> ok.
-cast(Fsm, Cast) ->
- gen_fsm:send_event(Fsm, Cast).
-
--spec cast_through(sys:name(), term()) -> ok.
-cast_through(Fsm, Cast) ->
- gen_fsm:send_all_state_event(Fsm, Cast).
-
--spec '$handle_undefined_function'(atom(), [any()]) -> any().
-'$handle_undefined_function'(Func, Args) ->
- {module, gen_fsm} =
- case code:is_loaded(gen_fsm) of
- false -> code:load_file(gen_fsm);
- _ -> {module, gen_fsm}
- end,
- case erlang:function_exported(gen_fsm, Func, length(Args)) of
- true -> erlang:apply(gen_fsm, Func, Args);
- false -> error_handler:raise_undef_exception(?MODULE, Func, Args)
- end.
diff --git a/test/cover.spec b/test/cover.spec
index 767df06..49614fa 100644
--- a/test/cover.spec
+++ b/test/cover.spec
@@ -4,7 +4,6 @@
[ktn_binary,
ktn_date,
ktn_debug,
- ktn_fsm,
ktn_json,
ktn_lists,
ktn_maps,
diff --git a/test/ktn_fsm_SUITE.erl b/test/ktn_fsm_SUITE.erl
deleted file mode 100644
index 59e4b37..0000000
--- a/test/ktn_fsm_SUITE.erl
+++ /dev/null
@@ -1,109 +0,0 @@
--module(ktn_fsm_SUITE).
-
--export([all/0]).
-
--export([ secure_vault/1
- ]).
-
--type config() :: [{atom(), term()}].
-
-all() -> [secure_vault].
-
--spec secure_vault(config()) -> {comment, []}.
-secure_vault(_Config) ->
- ct:comment("We start the vault with a master password and 3 attempts limit"),
- {ok, V} = secure_vault:start(masterpwd, 3),
-
- ct:comment("While it's unlocked, we can push, pop and check contents"),
- unlocked = secure_vault:state(V),
- [] = secure_vault:contents(V),
- 0 = secure_vault:count(V),
- ok = secure_vault:push(V, a),
- ok = secure_vault:push(V, b),
- [b, a] = secure_vault:contents(V),
- 2 = secure_vault:count(V),
- b = secure_vault:pop(V),
- [a] = secure_vault:contents(V),
- 1 = secure_vault:count(V),
- V ! ignored,
-
- ct:comment("We lock it with password 'pwd'"),
- ok = secure_vault:lock(V, pwd),
- locked = secure_vault:state(V),
- V ! ignored,
-
- ct:comment("While it's locked we can't do anything besides count & unlock"),
- 1 = secure_vault:count(V),
- badstate = fails(fun() -> secure_vault:lock(V, pwd2) end),
- badstate = fails(fun() -> secure_vault:lock(V, pwd) end),
- badstate = fails(fun() -> secure_vault:pop(V) end),
- ok = secure_vault:push(V, notpushed),
- 1 = secure_vault:count(V),
- badstate = fails(fun() -> secure_vault:contents(V) end),
- V ! ignored,
-
- ct:comment("We try to unlock it with a wrong password"),
- badpwd = fails(fun() -> secure_vault:unlock(V, notpwd) end),
- locked = secure_vault:state(V),
- V ! ignored,
-
- ct:comment("We unlock it with the right password"),
- ok = secure_vault:unlock(V, pwd),
- V ! ignored,
-
- ct:comment("While it's unlocked, we can push, pop and check contents again"),
- [a] = secure_vault:contents(V),
- 1 = secure_vault:count(V),
- a = secure_vault:pop(V),
- [] = secure_vault:contents(V),
- 0 = secure_vault:count(V),
- ok = secure_vault:push(V, x),
- V ! ignored,
-
- ct:comment("While it's unlocked, we can't unlock it again"),
- badstate = fails(fun() -> secure_vault:unlock(V, pwd2) end),
- badstate = fails(fun() -> secure_vault:unlock(V, pwd) end),
-
- ct:comment("We lock it again"),
- ok = secure_vault:lock(V, pwd3),
-
- ct:comment("After 3 invalid pwd attempts, the vault is closed"),
- badpwd = fails(fun() -> secure_vault:unlock(V, notpwd3) end),
- badpwd = fails(fun() -> secure_vault:unlock(V, notpwd3) end),
- closed = fails(fun() -> secure_vault:unlock(V, notpwd3) end),
- closed = secure_vault:state(V),
-
- ct:comment("When a vault is closed, there is nothing we can do..."),
- badstate = fails(fun() -> secure_vault:unlock(V, pwd3) end),
- badstate = fails(fun() -> secure_vault:lock(V, pwd3) end),
- badstate = fails(fun() -> secure_vault:lock(V, pwd) end),
- badstate = fails(fun() -> secure_vault:pop(V) end),
- ok = secure_vault:push(V, notpushed),
- 1 = secure_vault:count(V),
- badstate = fails(fun() -> secure_vault:contents(V) end),
-
- ct:comment("...except stopping it with the master password"),
- 1 = secure_vault:count(V),
- ok = secure_vault:stop(V, notmasterpwd),
- 1 = secure_vault:count(V),
- badstate = fails(fun() -> secure_vault:contents(V) end),
- V ! ignored,
-
- ok = secure_vault:stop(V, masterpwd),
- noproc =
- ktn_task:wait_for(
- fun() ->
- fails(fun() -> secure_vault:contents(V) end)
- end, noproc, 500, 10),
-
- {comment, ""}.
-
-fails(Fun) ->
- try Fun() of
- Response ->
- ct:fail("Unexpected response from ~p: ~p", [Fun, Response])
- catch
- _:{noproc, _} -> noproc;
- _:Error -> Error
- end.
-
diff --git a/test/secure_vault.erl b/test/secure_vault.erl
deleted file mode 100644
index bc368fa..0000000
--- a/test/secure_vault.erl
+++ /dev/null
@@ -1,136 +0,0 @@
--module(secure_vault).
-
--behaviour(gen_fsm).
-
--ignore_xref([{ktn_fsm, start, 3}]).
-
--export([ start/2
- , state/1
- , contents/1
- , count/1
- , push/2
- , pop/1
- , lock/2
- , unlock/2
- , stop/2
- ]).
-
--export([ init/1
- , handle_event/3
- , handle_sync_event/4
- , handle_info/3
- , terminate/3
- , code_change/4
- ]).
-
--export([ unlocked/3
- , unlocked/2
- , locked/3
- , locked/2
- , closed/3
- , closed/2
- ]).
-
--dialyzer({no_missing_calls, [start/2]}).
-
-start(MasterPassword, MaxAttempts) ->
- ktn_fsm:start(
- ?MODULE,
- {MasterPassword, MaxAttempts},
- [{debug, [log, statistics, trace]}]).
-
-state(Vault) ->
- ktn_fsm:state(Vault).
-
-contents(Vault) ->
- call(Vault, contents).
-
-count(Vault) ->
- call(Vault, count).
-
-push(Vault, Content) ->
- ktn_fsm:cast(Vault, {push, Content}).
-
-pop(Vault) ->
- call(Vault, pop).
-
-lock(Vault, Password) ->
- call(Vault, {lock, Password}).
-
-unlock(Vault, Password) ->
- call(Vault, {unlock, Password}).
-
-stop(Vault, Password) ->
- ktn_fsm:cast_through(Vault, {stop, Password}).
-
-init({MasterPassword, Attempts}) ->
- StateData =
- #{ masterpwd => MasterPassword
- , max_attempts => Attempts
- , contents => []
- },
- {ok, unlocked, StateData}.
-
-unlocked({lock, Password}, _From, StateData) ->
- #{max_attempts := Attempts} = StateData,
- {reply, ok, locked, StateData#{password => Password, attempts => Attempts}};
-unlocked(pop, _From, #{contents := []} = StateData) ->
- {reply, {ok, undefined}, unlocked, StateData};
-unlocked(pop, _From, StateData) ->
- #{contents := [Content|Contents]} = StateData,
- {reply, {ok, Content}, unlocked, StateData#{contents := Contents}};
-unlocked(contents, _From, StateData) ->
- #{contents := Contents} = StateData,
- {reply, {ok, Contents}, unlocked, StateData};
-unlocked(_, _From, StateData) ->
- {reply, {error, badstate}, unlocked, StateData}.
-
-unlocked({push, Content}, StateData) ->
- #{contents := Contents} = StateData,
- {next_state, unlocked, StateData#{contents := [Content|Contents]}}.
-
-locked({unlock, Password}, _From, #{password := Password} = StateData) ->
- {reply, ok, unlocked, maps:remove(password, StateData)};
-locked({unlock, _NotPassword}, _From, #{attempts := 1} = StateData) ->
- {reply, {error, closed}, closed, StateData};
-locked({unlock, _NotPassword}, _From, StateData) ->
- #{attempts := Attempts} = StateData,
- {reply, {error, badpwd}, locked, StateData#{attempts := Attempts - 1}};
-locked(_, _From, StateData) ->
- {reply, {error, badstate}, locked, StateData}.
-
-locked(_, StateData) ->
- {next_state, locked, StateData}.
-
-closed(_, _From, StateData) ->
- {reply, {error, badstate}, closed, StateData}.
-
-closed(_, StateData) ->
- {next_state, closed, StateData}.
-
-handle_sync_event(count, _From, StateName, StateData) ->
- #{contents := Contents} = StateData,
- {reply, {ok, length(Contents)}, StateName, StateData}.
-
-handle_event({stop, Pwd}, _StateName, #{masterpwd := Pwd} = StateData) ->
- {stop, normal, StateData};
-handle_event(_, StateName, StateData) ->
- {next_state, StateName, StateData}.
-
-handle_info(_, StateName, StateData) ->
- {next_state, StateName, StateData}.
-
-terminate(_Reason, _StateName, _StateData) -> ok.
-
-code_change(_OldVsn, StateName, StateData, _Extra) ->
- {ok, StateName, StateData}.
-
-call(Vault, count) -> call(Vault, count, fun ktn_fsm:call_through/2);
-call(Vault, Call) -> call(Vault, Call, fun ktn_fsm:call/2).
-
-call(Vault, Call, Fun) ->
- case Fun(Vault, Call) of
- ok -> ok;
- {ok, Result} -> Result;
- {error, Error} -> throw(Error)
- end.
--
2.16.4