diff options
Diffstat (limited to 'learn-you-some-erlang/mafiapp-1.0.0/src')
-rw-r--r-- | learn-you-some-erlang/mafiapp-1.0.0/src/mafiapp.erl | 172 | ||||
-rw-r--r-- | learn-you-some-erlang/mafiapp-1.0.0/src/mafiapp_sup.erl | 12 |
2 files changed, 184 insertions, 0 deletions
diff --git a/learn-you-some-erlang/mafiapp-1.0.0/src/mafiapp.erl b/learn-you-some-erlang/mafiapp-1.0.0/src/mafiapp.erl new file mode 100644 index 0000000..0e6a2b0 --- /dev/null +++ b/learn-you-some-erlang/mafiapp-1.0.0/src/mafiapp.erl @@ -0,0 +1,172 @@ +-module(mafiapp). +-behaviour(application). +-include_lib("stdlib/include/ms_transform.hrl"). +-export([start/2, stop/1]). +-export([install/1]). +-export([add_friend/4, friend_by_name/1, friend_by_expertise/1, + add_service/4, debts/1]). +-export([add_enemy/2, find_enemy/1, enemy_killed/1]). + + +-record(mafiapp_friends, {name, + contact=[], + info=[], + expertise}). +-record(mafiapp_services, {from, + to, + date, + description}). +-record(mafiapp_enemies, {name, + info=[]}). + +start(normal, []) -> + mnesia:wait_for_tables([mafiapp_friends, + mafiapp_services, + mafiapp_enemies], 5000), + mafiapp_sup:start_link(). + + +stop(_) -> ok. + +install(Nodes) -> + ok = mnesia:create_schema(Nodes), + rpc:multicall(Nodes, application, start, [mnesia]), + mnesia:create_table(mafiapp_friends, + [{attributes, record_info(fields, mafiapp_friends)}, + {index, [#mafiapp_friends.expertise]}, + {disc_copies, Nodes}]), + mnesia:create_table(mafiapp_services, + [{attributes, record_info(fields, mafiapp_services)}, + {index, [#mafiapp_services.to]}, + {disc_copies, Nodes}, + {type, bag}]), + mnesia:create_table(mafiapp_enemies, + [{attributes, record_info(fields, mafiapp_enemies)}, + {disc_copies, Nodes}, + {local_content, true}]), + rpc:multicall(Nodes, application, stop, [mnesia]). + +add_friend(Name, Contact, Info, Expertise) -> + F = fun() -> + mnesia:write(#mafiapp_friends{name=Name, + contact=Contact, + info=Info, + expertise=Expertise}) + end, + mnesia:activity(transaction, F). + +friend_by_name(Name) -> + F = fun() -> + case mnesia:read({mafiapp_friends, Name}) of + [#mafiapp_friends{contact=C, info=I, expertise=E}] -> + {Name,C,I,E,find_services(Name)}; + [] -> + undefined + end + end, + mnesia:activity(transaction, F). + +friend_by_expertise(Expertise) -> + %% Alternative form: + %% MatchSpec = [{#mafiapp_friends{name = '$1', + %% contact = '$2', + %% info = '$3', + %% expertise = Expertise}, + %% [], + %% [{{'$1','$2','$3',Expertise}}]}], + %% F = fun() -> + %% [{Name,C,I,E,find_services(Name)} || + %% {Name,C,I,E} <- mnesia:select(mafiapp_friends, MatchSpec)] + %% end, + Pattern = #mafiapp_friends{_ = '_', + expertise = Expertise}, + F = fun() -> + Res = mnesia:match_object(Pattern), + [{Name,C,I,Expertise,find_services(Name)} || + #mafiapp_friends{name=Name, + contact=C, + info=I} <- Res] + end, + mnesia:activity(transaction, F). + +%% Adding validation is left to the reader +add_service(From, To, Date, Description) -> + F = fun() -> + case mnesia:read({mafiapp_friends, From}) =:= [] orelse + mnesia:read({mafiapp_friends, To}) =:= [] of + true -> + {error, unknown_friend}; + false -> + mnesia:write(#mafiapp_services{from=From, + to=To, + date=Date, + description=Description}) + end + end, + mnesia:activity(transaction,F). + +debts(Name) -> + Match = ets:fun2ms( + fun(#mafiapp_services{from=From, to=To}) when From =:= Name -> + {To,-1}; + (#mafiapp_services{from=From, to=To}) when To =:= Name -> + {From,1} + end), + F = fun() -> mnesia:select(mafiapp_services, Match) end, + Dict = lists:foldl(fun({Person,N}, Dict) -> + dict:update(Person, fun(X) -> X + N end, N, Dict) + end, + dict:new(), + mnesia:activity(transaction, F)), + lists:sort([{V,K} || {K,V} <- dict:to_list(Dict)]). +%% The following implementation is a somewhat more efficient +%% alternative on large databases, given it will use indexes +%% rather than traversing the entire table. +% F = fun() -> +% Given = mnesia:read({mafiapp_services, Name}), +% Received = mnesia:match_object(#mafiapp_services{to=Name, _='_'}), +% {Given, Received} +% end, +% {Given, Received} = mnesia:activity(transaction, F), +% Dict1 = lists:foldl(fun(#mafiapp_services{to=N}, Dict) -> +% dict:update(N, fun(X) -> X - 1 end, -1, Dict) +% end, +% dict:new(), +% Given), +% Dict2 = lists:foldl(fun(#mafiapp_services{from=N}, Dict) -> +% dict:update(N, fun(X) -> X + 1 end, 1, Dict) +% end, +% Dict1, +% Received), +% lists:sort([{V,K} || {K,V} <- dict:to_list(Dict2)]). + +add_enemy(Name, Info) -> + F = fun() -> mnesia:write(#mafiapp_enemies{name=Name, info=Info}) end, + mnesia:activity(transaction, F). + +find_enemy(Name) -> + F = fun() -> mnesia:read({mafiapp_enemies, Name}) end, + case mnesia:activity(transaction, F) of + [] -> undefined; + [#mafiapp_enemies{name=N, info=I}] -> {N,I} + end. + +enemy_killed(Name) -> + F = fun() -> mnesia:delete({mafiapp_enemies, Name}) end, + mnesia:activity(transaction, F). + +%%%%%%%%%%%%%%% +%%% PRIVATE %%% +%%%%%%%%%%%%%%% + +find_services(Name) -> + Match = ets:fun2ms( + fun(#mafiapp_services{from=From, to=To, date=D, description=Desc}) + when From =:= Name -> + {to, To, D, Desc}; + (#mafiapp_services{from=From, to=To, date=D, description=Desc}) + when To =:= Name -> + {from, From, D, Desc} + end + ), + mnesia:select(mafiapp_services, Match). diff --git a/learn-you-some-erlang/mafiapp-1.0.0/src/mafiapp_sup.erl b/learn-you-some-erlang/mafiapp-1.0.0/src/mafiapp_sup.erl new file mode 100644 index 0000000..e31b221 --- /dev/null +++ b/learn-you-some-erlang/mafiapp-1.0.0/src/mafiapp_sup.erl @@ -0,0 +1,12 @@ +-module(mafiapp_sup). +-behaviour(supervisor). +-export([start_link/0]). +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +%% This does absolutely nothing, only there to +%% allow to wait for tables. +init([]) -> + {ok, {{one_for_one, 1, 1}, []}}. |