diff options
author | Trygve Laugstøl <trygvis@inamo.no> | 2024-02-23 07:08:18 +0100 |
---|---|---|
committer | Trygve Laugstøl <trygvis@inamo.no> | 2024-02-23 07:08:18 +0100 |
commit | 5a9cdd3cc89507d4d74f8bded56ce5e037b3b56e (patch) | |
tree | 982ca2e7f9ac4e8c350dfb5c4f60bcfdfff5afaf /learn-you-some-erlang/mafiapp-1.0.1 | |
parent | 05ae56e5e89abf2993f84e6d52b250131f247c35 (diff) | |
download | erlang-workshop-5a9cdd3cc89507d4d74f8bded56ce5e037b3b56e.tar.gz erlang-workshop-5a9cdd3cc89507d4d74f8bded56ce5e037b3b56e.tar.bz2 erlang-workshop-5a9cdd3cc89507d4d74f8bded56ce5e037b3b56e.tar.xz erlang-workshop-5a9cdd3cc89507d4d74f8bded56ce5e037b3b56e.zip |
wip
Diffstat (limited to 'learn-you-some-erlang/mafiapp-1.0.1')
8 files changed, 295 insertions, 0 deletions
diff --git a/learn-you-some-erlang/mafiapp-1.0.1/Emakefile b/learn-you-some-erlang/mafiapp-1.0.1/Emakefile new file mode 100644 index 0000000..b203ea3 --- /dev/null +++ b/learn-you-some-erlang/mafiapp-1.0.1/Emakefile @@ -0,0 +1,2 @@ +{["src/*", "test/*"], + [{i,"include"}, {outdir, "ebin"}]}. diff --git a/learn-you-some-erlang/mafiapp-1.0.1/ebin/mafiapp.app b/learn-you-some-erlang/mafiapp-1.0.1/ebin/mafiapp.app new file mode 100644 index 0000000..f4268ae --- /dev/null +++ b/learn-you-some-erlang/mafiapp-1.0.1/ebin/mafiapp.app @@ -0,0 +1,5 @@ +{application, mafiapp, + [{description, "Help the boss keep track of his friends"}, + {vsn, "1.0.1"}, + {modules, [mafiapp, mafiapp_sup]}, + {applications, [stdlib, kernel, mnesia]}]}. diff --git a/learn-you-some-erlang/mafiapp-1.0.1/logs/.please-track-this b/learn-you-some-erlang/mafiapp-1.0.1/logs/.please-track-this new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/learn-you-some-erlang/mafiapp-1.0.1/logs/.please-track-this diff --git a/learn-you-some-erlang/mafiapp-1.0.1/mafiapp.spec b/learn-you-some-erlang/mafiapp-1.0.1/mafiapp.spec new file mode 100644 index 0000000..4953e67 --- /dev/null +++ b/learn-you-some-erlang/mafiapp-1.0.1/mafiapp.spec @@ -0,0 +1,3 @@ +{alias, root, "./test/"}. +{logdir, "./logs/"}. +{suites, root, all}. diff --git a/learn-you-some-erlang/mafiapp-1.0.1/src/mafiapp.erl b/learn-you-some-erlang/mafiapp-1.0.1/src/mafiapp.erl new file mode 100644 index 0000000..f40ebce --- /dev/null +++ b/learn-you-some-erlang/mafiapp-1.0.1/src/mafiapp.erl @@ -0,0 +1,142 @@ +-module(mafiapp). +-behaviour(application). +-include_lib("stdlib/include/ms_transform.hrl"). +-include_lib("stdlib/include/qlc.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) -> + F = fun() -> + qlc:eval(qlc:q( + [{Name,C,I,E,find_services(Name)} || + #mafiapp_friends{name=Name, + contact=C, + info=I, + expertise=E} <- mnesia:table(mafiapp_friends), + E =:= Expertise])) + 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) -> + F = fun() -> + QH = qlc:q( + [if Name =:= To -> {From,1}; + Name =:= From -> {To,-1} + end || #mafiapp_services{from=From, to=To} <- + mnesia:table(mafiapp_services), + Name =:= To orelse Name =:= From]), + qlc:fold(fun({Person,N}, Dict) -> + dict:update(Person, fun(X) -> X + N end, N, Dict) + end, + dict:new(), + QH) + end, + lists:sort([{V,K} || {K,V} <- dict:to_list(mnesia:activity(transaction, F))]). + +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.1/src/mafiapp_sup.erl b/learn-you-some-erlang/mafiapp-1.0.1/src/mafiapp_sup.erl new file mode 100644 index 0000000..e31b221 --- /dev/null +++ b/learn-you-some-erlang/mafiapp-1.0.1/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}, []}}. diff --git a/learn-you-some-erlang/mafiapp-1.0.1/test/mafiapp.spec b/learn-you-some-erlang/mafiapp-1.0.1/test/mafiapp.spec new file mode 100644 index 0000000..4953e67 --- /dev/null +++ b/learn-you-some-erlang/mafiapp-1.0.1/test/mafiapp.spec @@ -0,0 +1,3 @@ +{alias, root, "./test/"}. +{logdir, "./logs/"}. +{suites, root, all}. diff --git a/learn-you-some-erlang/mafiapp-1.0.1/test/mafiapp_SUITE.erl b/learn-you-some-erlang/mafiapp-1.0.1/test/mafiapp_SUITE.erl new file mode 100644 index 0000000..6005d29 --- /dev/null +++ b/learn-you-some-erlang/mafiapp-1.0.1/test/mafiapp_SUITE.erl @@ -0,0 +1,128 @@ +-module(mafiapp_SUITE). +-include_lib("common_test/include/ct.hrl"). +-export([init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2, + all/0]). +-export([add_service/1, friend_by_name/1, friend_by_expertise/1, + friend_with_services/1, accounts/1, enemies/1]). + +all() -> [add_service, friend_by_name, friend_by_expertise, + friend_with_services, accounts, enemies]. + +init_per_suite(Config) -> + Priv = ?config(priv_dir, Config), + application:load(mnesia), + application:set_env(mnesia, dir, Priv), + application:load(mafiapp), + mafiapp:install([node()]), + application:start(mnesia), + application:start(mafiapp), + Config. + +end_per_suite(_Config) -> + application:stop(mnesia), + ok. + +init_per_testcase(add_service, Config) -> + Config; +init_per_testcase(accounts, Config) -> + ok = mafiapp:add_friend("Consigliere", [], [you], consigliere), + Config; +init_per_testcase(_, Config) -> + ok = mafiapp:add_friend("Don Corleone", [], [boss], boss), + Config. + +end_per_testcase(_, _Config) -> + ok. + +%% services can go both way: from a friend to the boss, or +%% from the boss to a friend! A boss friend is required! +add_service(_Config) -> + {error, unknown_friend} = mafiapp:add_service("from name", + "to name", + {1946,5,23}, + "a fake service"), + ok = mafiapp:add_friend("Don Corleone", [], [boss], boss), + ok = mafiapp:add_friend("Alan Parsons", + [{twitter,"@ArtScienceSound"}], + [{born, {1948,12,20}}, + musician, 'audio engineer', + producer, "has projects"], + mixing), + ok = mafiapp:add_service("Alan Parsons", "Don Corleone", + {1973,3,1}, + "Helped release a Pink Floyd album"). + +friend_by_name(_Config) -> + ok = mafiapp:add_friend("Pete Cityshend", + [{phone, "418-542-3000"}, + {email, "quadrophonia@example.org"}, + {other, "yell real loud"}], + [{born, {1945,5,19}}, + musician, popular], + music), + {"Pete Cityshend", + _Contact, _Info, music, + _Services} = mafiapp:friend_by_name("Pete Cityshend"), + undefined = mafiapp:friend_by_name(make_ref()). + +friend_by_expertise(_Config) -> + ok = mafiapp:add_friend("A Red Panda", + [{location, "in a zoo"}], + [animal,cute], + climbing), + [{"A Red Panda", + _Contact, _Info, climbing, + _Services}] = mafiapp:friend_by_expertise(climbing), + [] = mafiapp:friend_by_expertise(make_ref()). + +friend_with_services(_Config) -> + ok = mafiapp:add_friend("Someone", [{other, "at the fruit stand"}], + [weird, mysterious], shadiness), + ok = mafiapp:add_service("Don Corleone", "Someone", + {1949,2,14}, "Increased business"), + ok = mafiapp:add_service("Someone", "Don Corleone", + {1949,12,25}, "Gave a Christmas gift"), + %% We don't care about the order. The test was made to fit + %% whatever the functions returned. + {"Someone", + _Contact, _Info, shadiness, + [{to, "Don Corleone", {1949,12,25}, "Gave a Christmas gift"}, + {from, "Don Corleone", {1949,2,14}, "Increased business"}]} = + mafiapp:friend_by_name("Someone"). + +%% It should be possible to find all people who owe us things. +accounts(_Config) -> + ok = mafiapp:add_friend("Gill Bates", [{email, "ceo@macrohard.com"}], + [clever,rich], computers), + ok = mafiapp:add_service("Consigliere", "Gill Bates", + {1985,11,20}, "Bought 15 copies of software"), + ok = mafiapp:add_service("Gill Bates", "Consigliere", + {1986,8,17}, "Made computer faster"), + ok = mafiapp:add_friend("Pierre Gauthier", [{other, "city arena"}], + [{job, "sports team GM"}], sports), + ok = mafiapp:add_service("Pierre Gauthier", "Consigliere", {2009,6,30}, + "Took on a huge, bad contract"), + ok = mafiapp:add_friend("Wayne Gretzky", [{other, "Canada"}], + [{born, {1961,1,26}}, "hockey legend"], + hockey), + ok = mafiapp:add_service("Consigliere", "Wayne Gretzky", {1964,1,26}, + "Gave first pair of ice skates"), + %% Wayne Gretzky owes us something so the debt is negative + %% Gill Bates are equal + %% Gauthier is owed a service. + [{-1,"Wayne Gretzky"}, + {0,"Gill Bates"}, + {1,"Pierre Gauthier"}] = mafiapp:debts("Consigliere"), + [{1, "Consigliere"}] = mafiapp:debts("Wayne Gretzky"). + +enemies(_Config) -> + undefined = mafiapp:find_enemy("Edward"), + ok = mafiapp:add_enemy("Edward", [{bio, "Vampire"}, + {comment, "He sucks (blood)"}]), + {"Edward", [{bio, "Vampire"}, + {comment, "He sucks (blood)"}]} = + mafiapp:find_enemy("Edward"), + ok = mafiapp:enemy_killed("Edward"), + undefined = mafiapp:find_enemy("Edward"). + |