Skip to content

Instantly share code, notes, and snippets.

@niamtokik
Created March 16, 2020 11:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save niamtokik/3db37f0749a3c338f08b80b9bd076d04 to your computer and use it in GitHub Desktop.
Save niamtokik/3db37f0749a3c338f08b80b9bd076d04 to your computer and use it in GitHub Desktop.
Linux Magazine - Erlang/OTP
%%%-------------------------------------------------------------------
%%% @doc le module cache permet de créer un processus permettant de
%%% stockée des clés associés à des valeurs.
%%% @end
%%%-------------------------------------------------------------------
-module(cache).
-behaviour(gen_server).
-export([init/1, terminate/2]).
-export([handle_cast/2, handle_call/3]).
-export([add/3, delete/2, get/2, get_keys/1, get_values/1]).
-export([start_link/0]).
%%--------------------------------------------------------------------
%% @doc start_link/0 permet d'utiliser la fonction
%% gen_server:start_link/3 pour démarrer un processus lié.
%% @end
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link(?MODULE, [], []).
%%--------------------------------------------------------------------
%% @doc ce processus ajout un event handler au démarrage et configure
%% l'état avec une map nulle.
%% @end
%%--------------------------------------------------------------------
init(_Args) ->
Etat = #{},
{ok, Etat}.
%%--------------------------------------------------------------------
%% @doc terminate/2 ne fait rien et n'est pas essentielle dans ce cas
%% là.
%% @end
%%--------------------------------------------------------------------
terminate(_Raison, _Etat) ->
ok.
%%--------------------------------------------------------------------
%% @doc handle_cast/2 est un callback permet, dans ce cas là de:
%% - créé une nouvelle clé/valeur
%% - supprimé une clé et sa valeur associée
%% @end
%%--------------------------------------------------------------------
handle_cast({add, Key, Value}, Etat) ->
{noreply, maps:put(Key, Value, Etat)};
handle_cast({delete, Key}, Etat) ->
{noreply, maps:remove(Key, Etat)}.
%%--------------------------------------------------------------------
%% @doc handle_cast/ est un callback qui permet de:
%% - récupérer la liste des clés
%% - récupérer la liste des valeurs
%% - récupéré la valeur associée à une clé.
%% @end
%%--------------------------------------------------------------------
handle_call(get_keys, _From, Etat) ->
{reply, maps:keys(Etat), Etat};
handle_call(get_values, _From, Etat) ->
{reply, maps:values(Etat), Etat};
handle_call({get, Key}, _From, Etat) ->
{reply, maps:get(Key, Etat, undefined), Etat}.
%%--------------------------------------------------------------------
%% @doc add/3 est une interface permettant de rajouter une clé
%% associée à une valeur.
%% @end
%%--------------------------------------------------------------------
add(Pid, Key, Value) ->
gen_server:cast(Pid, {add, Key, Value}).
%%--------------------------------------------------------------------
%% @doc delete/2 est une interface permettant de supprimer une clé
%% avec sa valeur associée.
%% @end
%%--------------------------------------------------------------------
delete(Pid, Key) ->
gen_server:cast(Pid, {delete, Key}).
%%--------------------------------------------------------------------
%% @doc get_keys/1 permet de récupérer la liste des clés.
%% @end
%%--------------------------------------------------------------------
get_keys(Pid) ->
gen_server:call(Pid, get_keys).
%%--------------------------------------------------------------------
%% @doc get_values/1 permet de récupérer la liste des valeurs.
%% @end
%%--------------------------------------------------------------------
get_values(Pid) ->
gen_server:call(Pid, get_values).
%%--------------------------------------------------------------------
%% @doc get/2 permet de récupéré une valeur associée à un clé.
%% @end
%%--------------------------------------------------------------------
get(Pid, Key) ->
gen_server:call(Pid, {get, Key}).
%%%-------------------------------------------------------------------
%%% @doc cache_event est un module permettant de montrer le
%%% fonctionnement d'un event handler utilisant le behaviour
%%% gen_event.
%%% @end
%%%-------------------------------------------------------------------
-module(cache_event).
-behaviour(gen_event).
-export([init/1]).
-export([handle_event/2]).
%%--------------------------------------------------------------------
%% @doc init/1 est un callback obligatoire. Dans ce cas, il configure
%% simplement l'état avec une liste nulle
%% @end
%%--------------------------------------------------------------------
init(_) ->
{ok, []}.
%%--------------------------------------------------------------------
%% @doc handle_event/2 récupère un évènement et affiche simplement un
%% message sur la sortie standard
%% @end
%%--------------------------------------------------------------------
handle_event(Event, State) ->
io:format("receive: ~p~n", [Event]),
{ok, State}.
-module(cache_SUITE).
-compile(export_all).
-include_lib("common_test/include/ct.hrl").
suite() ->
[].
init_per_suite(_Config) ->
c:c(cache),
c:c(cache_sup),
c:c(cache_event),
[].
end_per_suite(_Config) ->
ok.
init_per_testcase(cache, Config) ->
io:format("ta mere!"),
{ok, Pid} = gen_server:start(cache, [], []),
[{pid, Pid}|Config];
init_per_testcase(cache_event, Config) ->
{ok, Pid} = gen_event:start(),
[{pid, Pid}|Config];
init_per_testcase(cache_sup, Config) ->
{ok, Pid} = supervisor:start_link(cache_sup, []),
[{pid, Pid}|Config].
end_per_testcase(cache, Config) ->
Pid = proplists:get_value(pid, Config),
gen_server:stop(Pid);
end_per_testcase(cache_event, Config) ->
Pid = proplists:get_value(pid, Config),
gen_event:stop(Pid);
end_per_testcase(cache_sup, Config) ->
Pid = proplists:get_value(pid, Config),
gen_server:stop(Pid).
all() ->
[cache, cache_event, cache_sup].
cache(Config) ->
Pid = proplists:get_value(pid, Config),
ok = cache:add(Pid, cle, value),
value = cache:get(Pid, cle),
ok = cache:delete(Pid, cle),
undefined = cache:get(Pid, cle),
ok.
cache_event(Config) ->
Pid = proplists:get_value(pid, Config),
gen_event:add_handler(Pid, cache_event, []),
[cache_event] = gen_event:which_handlers(Pid),
ok.
cache_sup(Config) ->
Pid = proplists:get_value(pid, Config),
Counter = supervisor:count_children(Pid),
2 = proplists:get_value(specs, Counter),
2 = proplists:get_value(active, Counter),
0 = proplists:get_value(supervisors, Counter),
2 = proplists:get_value(workers, Counter),
{ok, #{ id := cache }} = supervisor:get_childspec(Pid, cache),
{ok, #{ id := cache_event }} = supervisor:get_childspec(Pid, cache_event).
%%%-------------------------------------------------------------------
%%% @doc cache_sup permet de superviser le module cache et un
%%% gestionnaire d'évènement.
%%% @end
%%%-------------------------------------------------------------------
-module(cache_sup).
-behaviour(supervisor).
-export([init/1, start_link/0]).
%%--------------------------------------------------------------------
%% @doc start_link/0 se base sur la fonction supervisor:start_link/2
%% pour démarrer un processus basé sur le module cache_sup.
%% @end
%%--------------------------------------------------------------------
start_link() ->
supervisor:start_link(?MODULE, []).
%%--------------------------------------------------------------------
%% @doc init/1 est un callback obligatoire. Dans ce cas là, 2 enfants
%% sont créés, un pour cache_event et un autre pour le module cache.
%% @end
%%--------------------------------------------------------------------
init(_Args) ->
SupervisorConf = #{ strategy => one_for_one,
intensity => 1,
period => 5 },
EventStart = {gen_event, start_link, [{local, cache_event}, []]},
EventSpec = #{ id => cache_event, start => EventStart },
CacheStart = {gen_server, start_link, [{local, cache}, cache, [], []]},
CacheSpec = #{ id => cache, start => CacheStart },
{ok, {SupervisorConf, [EventSpec, CacheSpec]}}.
%%%-------------------------------------------------------------------
%%% @doc
%%% @end
%%%-------------------------------------------------------------------
-module(exemple_server).
-export([start/0, start/1, start/2]).
-export([start_link/0, start_link/1, start_link/2]).
-export([init/1, terminate/2]).
-export([handle_cast/2, handle_call/3, handle_info/2]).
%%--------------------------------------------------------------------
%% @doc start/1
%% @end
%%--------------------------------------------------------------------
start() ->
start([]).
%%--------------------------------------------------------------------
%% @doc start/2
%% @end
%%--------------------------------------------------------------------
start(Arguments) ->
start(Arguments, []).
%%--------------------------------------------------------------------
%% @doc start/3
%% @end
%%--------------------------------------------------------------------
start(Arguments, Options) ->
gen_server:start(?MODULE, Arguments, Options).
%%--------------------------------------------------------------------
%% @doc start_link/1
%% @end
%%--------------------------------------------------------------------
start_link() ->
start_link([]).
%%--------------------------------------------------------------------
%% @doc start_link/1
%% @end
%%--------------------------------------------------------------------
start_link(Arguments) ->
start_link(Arguments, []).
%%--------------------------------------------------------------------
%% @doc start_link/2
%% @end
%%--------------------------------------------------------------------
start_link(Arguments, Options) ->
gen_server:start_link(?MODULE, Arguments, Options).
%%--------------------------------------------------------------------
%% @doc init/1
%% @end
%%--------------------------------------------------------------------
init(Arguments) ->
io:format("Argument: ~p~n", [Arguments]),
{ok, Arguments}.
%%--------------------------------------------------------------------
%% @doc terminate/2
%% @end
%%--------------------------------------------------------------------
terminate(Raison, Etat) ->
io:format("arret raison: ~p~n", [Raison]),
io:format("arret etat: ~p~n", [Etat]),
ok.
%%--------------------------------------------------------------------
%% @doc handle_cast/2
%% @end
%%--------------------------------------------------------------------
handle_cast(Message, Etat) ->
io:format("cast message: ~p~n", [Message]),
io:format("cast etat: ~p~n", [Etat]),
{noreply, Etat}.
%%--------------------------------------------------------------------
%% @doc handle_cast/3
%% @end
%%--------------------------------------------------------------------
handle_call(Message, From, Etat) ->
io:format("call message: ~p~n", [Message]),
io:format("call from: ~p~n", [From]),
io:format("call etat: ~p~n", [Etat]),
{reply, {Message, From, Etat}, Etat}.
%%--------------------------------------------------------------------
%% @doc handle_info/2
%% @end
%%--------------------------------------------------------------------
handle_info(Message, Etat) ->
io:format("info message: ~p~n", [Message]),
io:format("info etat: ~p~n", [Etat]),
{noreply, Etat}.
%%%-------------------------------------------------------------------
%%% @doc exemple_statem permet de montrer l'utilisation du behaviour
%%% gen_statem en implémentant un interrupteur. Une implémentation
%%% similaire est donnée dans la documentation officielle.
%%% @end
%%%-------------------------------------------------------------------
-module(exemple_statem).
-export([init/1, terminate/3]).
-export([callback_mode/0]).
-export([handle_event/4]).
-export([start/1, start/2, start/0, start/3]).
-export([start_link/1, start_link/2, start_link/0, start_link/3]).
-export([appuyer/1, lampe/1]).
%%--------------------------------------------------------------------
%% @doc start/0 est une fonction pour simplifié le démarrage. Elle se
%% base sur start/1.
%% @end
%% --------------------------------------------------------------------
start() ->
start([], []).
%%--------------------------------------------------------------------
%% @doc start/1 est une fonction pour simplifié le démarrage et se
%% base sur start/2 avec des options par défaut.
%% @end
%% --------------------------------------------------------------------
start(Arguments) ->
start(Arguments, []).
%%--------------------------------------------------------------------
%% @doc start/2 est une fonction qui s'appuie sur gen_statem:start/3
%% pour démarrer le module exemple_statem.
%% @end
%% --------------------------------------------------------------------
start(Arguments, Options) ->
gen_statem:start(?MODULE, Arguments, Options).
%%--------------------------------------------------------------------
%% @doc start/3 est une fonction qui s'appuie sur gen_statem:start/4,
%% permettant de démarrer un processus enregistré localement. C'est à
%% dire qu'un autre processus avec le même nom peut-être lancé sur un
%% autre noeud du cluster.
%% @end
%%--------------------------------------------------------------------
start(Nom, Arguments, Options) ->
gen_statem:start({local, Nom}, Arguments, Options).
%%--------------------------------------------------------------------
%% @doc start_link/0 est une fonction pour simplifier le démarrer de
%% exemple_statem en créant un lien. Cette fonction s'appuie sur
%% start_link/1.
%% @end
%% --------------------------------------------------------------------
start_link() ->
start_link([]).
%%--------------------------------------------------------------------
%% @doc start_link/1 est une fonction pour simplifier le démarrer de
%% exemple_statem en créant un lien. Cette fonction s'appuie sur
%% start_link/2.
%% @end
%%--------------------------------------------------------------------
start_link(Arguments) ->
start_link(Arguments, []).
%%--------------------------------------------------------------------
%% @doc start_link/2 s'appuie sur la fonctio gen_statem:start_link/3
%% pour démarrer le module exemple_statem pour créer un lien entre le
%% processus qui le démarre et celui créé.
%% @end
%%--------------------------------------------------------------------
start_link(Arguments, Options) ->
gen_statem:start_link(?MODULE, Arguments, Options).
%%--------------------------------------------------------------------
%% @doc start_link/3 s'appuie sur la fonction gen_statem:start_link/4
%% et permet de démarrer un processus nommé au sein d'un cluster de
%% noeud.
%% @end
%% --------------------------------------------------------------------
start_link(Nom, Arguments, Options) ->
gen_statem:start_link({local, Nom}, Arguments, Options).
%%--------------------------------------------------------------------
%% @doc init/1 permet d'initialiser l'état du processus ainsi que les
%% données associées. Cette fonction est équivalent à un constructeur
%% en orienté objet.
%% @end
%%--------------------------------------------------------------------
init(_Arguments) ->
io:format("interrupteur ouvert~n"),
{ok, ouvert, eteint}.
%%--------------------------------------------------------------------
%% @doc terminate/3 permet de "détruire" le processus. Équivalent à un
%% destructeur en orienté objet.
%% @end
%%--------------------------------------------------------------------
terminate(Raison, Etat, Donnee) ->
io:format("interrupteur détruit.~n"),
io:format("raison: ~p~n", [Raison]),
io:format("état: ~p~n", [Etat]),
io:format("lampe: ~p~n", [Donnee]),
ok.
%%--------------------------------------------------------------------
%% @doc callback_mode/0 est un callback obligatoire permettant de
%% définir le fonction du module basé sur le behaviour gen_statem.
%% @end
%%--------------------------------------------------------------------
callback_mode() -> [handle_event_function].
%%--------------------------------------------------------------------
%% @doc handle_event/4 est une fonction utilisé pour récupéré les
%% différents évènements envoyé sur le processus. Ce callback est
%% défini ici due au mode du callback définis dans callback_mode/0.
%% @end
%%--------------------------------------------------------------------
handle_event(cast, appuyer, ferme, allume) ->
io:format("lampe éteinte.~n"),
{next_state, ouvert, eteint};
handle_event(cast, appuyer, ouvert, eteint) ->
io:format("lampe allumée.~n"),
{next_state, ferme, allume};
handle_event({call, From}, lampe, _Etat, Donnee) ->
{keep_state, Donnee, [{reply, From, Donnee}]};
handle_event(TypeEvenement, Evenement, Etat, Donnee) ->
io:format("Type évènement: ~p~n", [TypeEvenement]),
io:format("Contenu évènement: ~p~n", [Evenement]),
io:format("État: ~p~n", [Etat]),
io:format("État (données) processus: ~p~n", [Donnee]),
{keep_state, Donnee}.
%%--------------------------------------------------------------------
%% @doc appuyer/1 est une fonction exporté servant d'API. Elle se base
%% sur la fonction gen_statem:cast/2 pour envoyer un message au
%% processus.
%% @end
%% --------------------------------------------------------------------
appuyer(Pid) ->
gen_statem:cast(Pid, appuyer).
%%--------------------------------------------------------------------
%% @doc tout comme appuyer/1, cette fonction est exporté et sert d'API
%% pour envoyer un message basé sur gen_statem:call/3.
%% @end
%% --------------------------------------------------------------------
lampe(Pid) ->
gen_statem:call(Pid, lampe, 1000).
-module(exemple_SUITE).
-compile(export_all).
-include_lib("common_test/include/ct.hrl").
suite() ->
[].
init_per_suite(Config) ->
c:c(exemple_server),
c:c(exemple_statem),
c:c(exemple_event),
c:c(exemple_supervisor),
[].
end_per_suite(Config) ->
ok.
init_per_testcase(server, Config) ->
{ok, Pid} = gen_server:start(exemple_server, [], []),
[{pid, Pid}|Config];
init_per_testcase(statem, Config) ->
{ok, Pid} = gen_statem:start(exemple_statem, [], []),
[{pid, Pid}|Config];
init_per_testcase(event, Config) ->
{ok, Pid} = gen_event:start(),
gen_event:add_handler(Pid, exemple_event, test),
[{pid, Pid}|Config];
init_per_testcase(supervisor, Config) ->
{ok, Pid} = supervisor:start_link(exemple_supervisor, []),
[{pid, Pid}|Config].
end_per_testcase(server, Config) ->
Pid = proplists:get_value(pid, Config),
gen_server:stop(Pid);
end_per_testcase(statem, Config) ->
Pid = proplists:get_value(pid, Config),
gen_statem:stop(Pid);
end_per_testcase(event, Config) ->
Pid = proplists:get_value(pid, Config),
gen_event:stop(Pid);
end_per_testcase(supervisor, Config) ->
Pid = proplists:get_value(pid, Config),
gen_server:stop(Pid).
all() ->
[server, statem, event, supervisor].
server(Config) ->
Pid = proplists:get_value(pid, Config),
ok = gen_server:cast(Pid, message),
{message, _, _} = gen_server:call(Pid, message).
event(Config) ->
Pid = proplists:get_value(pid, Config),
ok = gen_event:notify(Pid, test).
statem(Config) ->
Pid = proplists:get_value(pid, Config),
eteint = exemple_statem:lampe(Pid),
ok = exemple_statem:appuyer(Pid),
allume = exemple_statem:lampe(Pid),
ok = exemple_statem:appuyer(Pid),
eteint = exemple_statem:lampe(Pid).
supervisor(Config) ->
Pid = proplists:get_value(pid, Config),
Counter = supervisor:count_children(Pid),
2 = proplists:get_value(specs, Counter),
2 = proplists:get_value(active, Counter),
0 = proplists:get_value(supervisors, Counter),
2 = proplists:get_value(workers, Counter),
{ok, #{ id := exemple_server }} = supervisor:get_childspec(Pid, exemple_server),
{ok, #{ id := exemple_statem }} = supervisor:get_childspec(Pid, exemple_statem).
%%%-------------------------------------------------------------------
%%% @doc exemple_supervisor permet de démarrer 2 processus,
%%% exemple_statem et exemple_server. La stratégie utilisé est
%%% one_for_one, si un des 2 processus crash, il sera alors redémarré
%%% automatiquement.
%%% @end
%%%-------------------------------------------------------------------
-module(exemple_supervisor).
-export([init/1]).
-export([start_link/0]).
%%--------------------------------------------------------------------
%% @doc start_link/0 permet d'offrir une facilité de démarrage. Pour
%% l'exécuter et démarrer ce module, rien de plus simple que de faire
%% exemple_supervisor:start_link().
%% @end
%%--------------------------------------------------------------------
start_link() ->
supervisor:start_link(?MODULE, []).
%%--------------------------------------------------------------------
%% @doc init/1 est un callback utilisé pour configuré le
%% superviseur. Il permet de configuré la spécification du superviseur
%% ainsi que la liste des spécifications des enfants à superviser.
%% @end
%%--------------------------------------------------------------------
init(Arguments) ->
SupervisorConf = #{ strategy => one_for_one,
intensity => 1,
period => 5 },
SpecStatem = #{ id => exemple_statem
, start => {exemple_statem, start_link, []}},
SpecServer = #{ id => exemple_server
, start => {exemple_server, start_link, []}},
{ok, {SupervisorConf, [SpecStatem, SpecServer]}}.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment