├── .gitignore ├── OTP ├── gen_server_template.erl ├── otp-cheatsheet.markdown ├── rebar └── supervisor_template.erl ├── distributed_erlang ├── .create_tables │ ├── cmdln.txt │ └── create_tables.erl ├── create_tables.erl ├── distributed-erlang-cheatsheet.markdown ├── ec_echo.erl └── simple_cache │ ├── doc │ └── .gitignore │ ├── ebin │ ├── .gitignore │ └── simple_cache.app │ ├── etc │ ├── log4erl.conf │ └── sys.config │ ├── rebar.config │ ├── src │ ├── sc_app.erl │ ├── sc_element.erl │ ├── sc_element_sup.erl │ ├── sc_event.erl │ ├── sc_event_logger.erl │ ├── sc_store.erl │ ├── sc_sup.erl │ └── simple_cache.erl │ └── start.sh ├── essentials ├── ec_add.erl ├── ec_concurrent.erl ├── ec_exception.erl ├── ec_lists.erl ├── ec_math.erl ├── ec_multiply.erl ├── ec_recur.erl └── essentials-part1-cheatsheet.markdown ├── events_and_logs ├── README.md ├── custom_error_report.erl ├── die_please.erl ├── die_please2.erl ├── entity_manager │ ├── em.erl │ ├── em_demo.erl │ ├── entity.erl │ ├── health_component.erl │ ├── weapon_component.erl │ ├── weapon_component_finished.erl │ └── xy_component.erl ├── rebar.config └── simple_cache │ ├── rebar │ ├── rebar.config │ └── src │ ├── sc_app.erl │ ├── sc_server.erl │ ├── sc_sup.erl │ └── simple_cache.app.src ├── releases ├── simple_cache │ ├── Makefile │ ├── README │ ├── config │ │ └── sys.config │ ├── include │ │ └── .gitignore │ ├── rebar.config │ ├── relx.config │ └── src │ │ ├── sc_app.erl │ │ ├── sc_element.erl │ │ ├── sc_element_sup.erl │ │ ├── sc_event.erl │ │ ├── sc_event_logger.erl │ │ ├── sc_store.erl │ │ ├── sc_sup.erl │ │ ├── simple_cache.app.src │ │ └── simple_cache.erl └── tester │ ├── rebar.config │ └── src │ ├── tester.app.src │ ├── tester_app.erl │ └── tester_sup.erl └── testing ├── simple_cache ├── Makefile ├── README ├── config │ └── sys.config ├── doc │ └── .gitignore ├── ebin │ ├── .gitignore │ └── simple_cache.app ├── include │ └── .gitignore ├── priv │ └── .gitignore ├── rebar.config ├── relx.config └── src │ ├── sc_app.erl │ ├── sc_element.erl │ ├── sc_element_sup.erl │ ├── sc_event.erl │ ├── sc_event_logger.erl │ ├── sc_store.erl │ ├── sc_sup.erl │ ├── simple_cache.app.src │ └── simple_cache.erl └── templates ├── common_test_tmpl.erl └── eunit_tmpl.erl /.gitignore: -------------------------------------------------------------------------------- 1 | .* -------------------------------------------------------------------------------- /OTP/gen_server_template.erl: -------------------------------------------------------------------------------- 1 | %%% @author Martin Logan 2 | %%% @copyright (C) 2013, Erlware 3 | %%% @doc 4 | -module(gen_server_template). 5 | -behaviour(gen_server). 6 | 7 | %% API 8 | -export([start_link/0]). 9 | 10 | %% gen_server callbacks 11 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 12 | terminate/2, code_change/3]). 13 | 14 | -define(SERVER, ?MODULE). 15 | -define(IMPLEMENTATION_MODULE, ?MODULE). 16 | 17 | %%%=================================================================== 18 | %%% API 19 | %%%=================================================================== 20 | 21 | %% @doc Starts the server 22 | -spec start_link() -> {ok, pid()}. 23 | start_link() -> 24 | gen_server:start_link({local, ?SERVER}, ?IMPLEMENTATION_MODULE, [], []). 25 | 26 | %%%=================================================================== 27 | %%% gen_server callbacks 28 | %%%=================================================================== 29 | 30 | %%-------------------------------------------------------------------- 31 | %% @private 32 | %% @doc 33 | %% Initializes the server 34 | %% 35 | %% @spec init(Args) -> {ok, State} | 36 | %% {ok, State, Timeout} | 37 | %% ignore | 38 | %% {stop, Reason} 39 | %% @end 40 | %%-------------------------------------------------------------------- 41 | init([]) -> 42 | {ok, }. 43 | 44 | %%-------------------------------------------------------------------- 45 | %% @private 46 | %% @doc 47 | %% Handling call messages 48 | %% 49 | %% @spec handle_call(Request, From, State) -> 50 | %% {reply, Reply, State} | 51 | %% {reply, Reply, State, Timeout} | 52 | %% {noreply, State} | 53 | %% {noreply, State, Timeout} | 54 | %% {stop, Reason, Reply, State} | 55 | %% {stop, Reason, State} 56 | %% @end 57 | %%-------------------------------------------------------------------- 58 | handle_call(_Request, _From, State) -> 59 | Reply = ok, 60 | {reply, Reply, State}. 61 | 62 | %%-------------------------------------------------------------------- 63 | %% @private 64 | %% @doc 65 | %% Handling cast messages 66 | %% 67 | %% @spec handle_cast(Msg, State) -> {noreply, State} | 68 | %% {noreply, State, Timeout} | 69 | %% {stop, Reason, State} 70 | %% @end 71 | %%-------------------------------------------------------------------- 72 | handle_cast(_Msg, State) -> 73 | {noreply, State}. 74 | 75 | %%-------------------------------------------------------------------- 76 | %% @private 77 | %% @doc 78 | %% Handling all non call/cast messages 79 | %% 80 | %% @spec handle_info(Info, State) -> {noreply, State} | 81 | %% {noreply, State, Timeout} | 82 | %% {stop, Reason, State} 83 | %% @end 84 | %%-------------------------------------------------------------------- 85 | handle_info(_Info, State) -> 86 | {noreply, State}. 87 | 88 | %%-------------------------------------------------------------------- 89 | %% @private 90 | %% @doc 91 | %% This function is called by a gen_server when it is about to 92 | %% terminate. It should be the opposite of Module:init/1 and do any 93 | %% necessary cleaning up. When it returns, the gen_server terminates 94 | %% with Reason. The return value is ignored. 95 | %% 96 | %% @spec terminate(Reason, State) -> void() 97 | %% @end 98 | %%-------------------------------------------------------------------- 99 | terminate(_Reason, _State) -> 100 | ok. 101 | 102 | %%-------------------------------------------------------------------- 103 | %% @private 104 | %% @doc 105 | %% Convert process state when code is changed 106 | %% 107 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 108 | %% @end 109 | %%-------------------------------------------------------------------- 110 | code_change(_OldVsn, State, _Extra) -> 111 | {ok, State}. 112 | 113 | %%%=================================================================== 114 | %%% Internal functions 115 | %%%=================================================================== 116 | -------------------------------------------------------------------------------- /OTP/otp-cheatsheet.markdown: -------------------------------------------------------------------------------- 1 | OTP Cheatsheet 2 | =========== 3 | 4 | 5 | ## start_link flow 6 | spawn and link a gen server 7 | ``gen_server:start_link({local, Name}, ImplModule, InitArgs, Options).`` 8 | 9 | The callback for start_link/4 is init. 10 | ``` 11 | init(InitArgs) -> 12 | {ok, InitialState}. 13 | ``` 14 | 15 | ## gen_server:cast flow 16 | Cast is the gen_server function used for async messaging. 17 | 18 | gen_server:cast(Server, Msg). 19 | 20 | for example 21 | 22 | gen_server:cast(?SERVER, {lookup, Key}). 23 | 24 | The callback for cast is handle_cast/2. 25 | 26 | handle_cast(Msg, State) -> 27 | {noreply, NewState}. 28 | 29 | ## call flow 30 | Call is the gen server function that is used for syncronous messaging. 31 | 32 | gen_server:call(Server, Msg). 33 | 34 | The callback function for call is handle_call/3. 35 | handle_call(Msg, From, State) -> 36 | {reply, Reply, NewState}. 37 | 38 | ## Stopping a gen_server 39 | if you want to stop the server from either handle call or cast simply 40 | return the tuple 41 | 42 | {stop, Reason, State} 43 | 44 | ## the .app file. 45 | Rebar will generate a .app.src file in your source directory. This is what 46 | you should modify when creating an app. When you compile your code rebar 47 | will turn the .app.src file into a .app file and place it in the 48 | ebin directory. 49 | 50 | ``` 51 | {application, simple_cache, % App name 52 |  [ 53 |   {description, ”kv store"}, 54 |   {vsn, "1"}, 55 |   {registered, []}, 56 |   {applications, [ 57 |                   kernel, 58 |                   stdlib 59 |                  ]}, 60 |   {mod, {sc_app, []}}, % 'mod' is the important tuple. This is what is called 61 | % to start your app. In this case sc_app:start/2 62 | % with no args 63 |   {env, []} 64 |  ]}. 65 | ``` 66 | 67 | ## Misc 68 | To pull key value pairs out of a kv list [{a, b}, {c, d}] use 69 | proplists:get_value/2 70 | -------------------------------------------------------------------------------- /OTP/rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erlware/erlang-camp/a3130412d67ba618f34959f243bc3df2fb00f4c3/OTP/rebar -------------------------------------------------------------------------------- /OTP/supervisor_template.erl: -------------------------------------------------------------------------------- 1 | %%% @author You 2 | %%% @copyright (C) 2013, Erlware 3 | %%% @doc 4 | 5 | -module(supervisor_template). 6 | -behaviour(supervisor). 7 | 8 | %% API 9 | -export([start_link/0]). 10 | 11 | %% Supervisor callbacks 12 | -export([init/1]). 13 | 14 | -define(SERVER, ?MODULE). 15 | -define(IMPLEMENTATION_MODULE, ?MODULE). 16 | 17 | %%%====================================================================== 18 | %%% API 19 | %%%====================================================================== 20 | 21 | %% @doc Starts the supervisor. 22 | -spec start_link() -> {ok, pid()} | ignore | {error, term()} 23 | start_link() -> 24 | supervisor:start_link({local, ?SERVER}, ?IMPLEMENTATION_MODULE, []). 25 | 26 | 27 | %%%====================================================================== 28 | %%% Behaviour Callback Functions 29 | %%%====================================================================== 30 | init([]) -> 31 | RestartStrategy = one_for_one, 32 | MaxRestarts = 1000, 33 | MaxSecondsBetweenRestarts = 3600, 34 | 35 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, 36 | 37 | Restart = permanent, 38 | Shutdown = 2000, 39 | Type = worker, 40 | 41 | AChild = {module_name, {module_name, start_link, []}, 42 | Restart, Shutdown, Type, [module_name]}, 43 | 44 | {ok, {SupFlags, [AChild]}}. 45 | 46 | %%%=================================================================== 47 | %%% Internal functions 48 | %%%=================================================================== 49 | -------------------------------------------------------------------------------- /distributed_erlang/.create_tables/cmdln.txt: -------------------------------------------------------------------------------- 1 | mnesia:start(). 2 | create_tables:init_tables(). 3 | create_tables:insert_project("catcher in the rye", "a really intesting book"). 4 | create_tables:insert_project("enders game", "great scifi story"). 5 | create_tables:insert_member(1, martinjlogan, ["catcher in the rye", "enders game"]). 6 | -------------------------------------------------------------------------------- /distributed_erlang/.create_tables/create_tables.erl: -------------------------------------------------------------------------------- 1 | -module(create_tables). 2 | 3 | -export([init_tables/0, insert_member/3, insert_project/2]). 4 | 5 | -record(member, { 6 | id, 7 | name 8 | }). 9 | 10 | -record(project, { 11 | title, 12 | description 13 | }). 14 | 15 | -record(contributor, { 16 | member_id, 17 | project_title 18 | }). 19 | 20 | init_tables() -> 21 | mnesia:create_table(member, 22 | [{attributes, record_info(fields, member)}]), 23 | mnesia:create_table(project, 24 | [{attributes, record_info(fields, project)}]), 25 | mnesia:create_table(contributor, 26 | [{type, bag}, {attributes, record_info(fields, contributor)}]). 27 | 28 | insert_member(Id, Name, ProjectTitles) when ProjectTitles =/= [] -> 29 | User = #member{id = Id, name = Name}, 30 | Fun = fun() -> 31 | mnesia:write(User), 32 | lists:foreach( 33 | fun(Title) -> 34 | [#project{title = Title}] = mnesia:read(project, Title), 35 | mnesia:write(#contributor{member_id = Id, 36 | project_title = Title}) 37 | end, 38 | ProjectTitles) 39 | end, 40 | mnesia:transaction(Fun). 41 | 42 | insert_project(Title, Description) -> 43 | mnesia:dirty_write(#project{title = Title, 44 | description = Description}). 45 | -------------------------------------------------------------------------------- /distributed_erlang/create_tables.erl: -------------------------------------------------------------------------------- 1 | -module(create_tables). 2 | 3 | -export([init_tables/0, insert_member/3, insert_project/2]). 4 | 5 | -record(member, { 6 | id, 7 | name 8 | }). 9 | 10 | -record(project, { 11 | title, 12 | description 13 | }). 14 | 15 | -record(contributor, { 16 | member_id, 17 | project_title 18 | }). 19 | 20 | init_tables() -> 21 | mnesia:create_table(member, 22 | [{attributes, record_info(fields, member)}]), 23 | mnesia:create_table(project, 24 | [{attributes, record_info(fields, )}]), 25 | mnesia:create_table(contributor, 26 | [{type, bag}, {attributes, record_info(fields, contributor)}]). 27 | 28 | insert_member(Id, Name, ProjectTitles) when ProjectTitles =/= [] -> 29 | User = #member{id = Id, name = Name}, 30 | Fun = fun() -> 31 | mnesia:write(User), 32 | lists:foreach( 33 | fun(Title) -> 34 | [#project{title = Title}] = mnesia:read(project, Title), 35 | mnesia:write(#contributor{member_id = Id, 36 | project_title = Title}) 37 | end, 38 | ProjectTitles) 39 | end, 40 | mnesia:transaction(). 41 | 42 | insert_project(Title, Description) -> 43 | mnesia:dirty_write(#project{title = Title, 44 | description = Description}). 45 | -------------------------------------------------------------------------------- /distributed_erlang/distributed-erlang-cheatsheet.markdown: -------------------------------------------------------------------------------- 1 | Distributed Erlang Cheatsheet 2 | ============= 3 | 4 | Get the process id of the current process 5 | self() 6 | 7 | Receive a message out of a mailbox and do something with it. 8 | receive X -> X * 2 end 9 | 10 | More complex Receive 11 | receive 12 | X when is_integer(X) -> 13 | X * 2; 14 | X -> 15 | {error, not_integer} 16 | after 17 | 1000 -> 18 | {error, timeout} 19 | end. 20 | 21 | Spawn a process that prints its own process id 22 | spawn(fun() -> io:format("process id of spawned process ~p~n", [self]) end). 23 | 24 | Register a process 25 | register(Name::atom(), Pid). 26 | 27 | Start a distributed node called a 28 | erl -name a 29 | erl -sname a 30 | 31 | Ping and connect with a remote node called a@my-machine. 32 | 1> net_adm:ping('a@my-machine'). 33 | 34 | 35 | -------------------------------------------------------------------------------- /distributed_erlang/ec_echo.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------ 2 | %%% @author Martin Logan 3 | %%% @copyright (C) 2013, Martin Logan 4 | %%% @doc 5 | %%% this is a simple echo server 6 | %%% Directions: 7 | %%% Setup and connect 4 nodes a, b, c and d. 8 | %%% on b, c and d run ec_echo:start_link() 9 | %%% on a run lists:foreach( 10 | %%% fun(Node) -> 11 | %%% io:format("got ~p~n", [ec_echo:send_sync(Node, "msg to echo")]) 12 | %%% end, 13 | %%% nodes()). 14 | %%% @end 15 | %%% Created : 31 Aug 2013 by Martin Logan 16 | %%%------------------------------------------------------- 17 | -module(ec_echo). 18 | 19 | -behaviour(gen_server). 20 | 21 | %% API 22 | -export([start_link/0, send/2, send_sync/2]). 23 | 24 | %% gen_server callbacks 25 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 26 | terminate/2, code_change/3]). 27 | 28 | -define(SERVER, ?MODULE). 29 | 30 | -record(state, {}). 31 | 32 | %%%=================================================================== 33 | %%% API 34 | %%%=================================================================== 35 | 36 | %%-------------------------------------------------------------------- 37 | %% @doc 38 | %% Starts the server 39 | %% 40 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error} 41 | %% @end 42 | %%-------------------------------------------------------------------- 43 | start_link() -> 44 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 45 | 46 | %% @doc send a message to the echo server. 47 | -spec send(node(), any()) -> ok. 48 | send(Node, Msg) -> 49 | gen_server:cast({?SERVER, Node}, {send, {Msg, self()}}). 50 | 51 | %% @doc send a message to the echo server. 52 | -spec send_sync(node(), any()) -> ok. 53 | send_sync(Node, Msg) -> 54 | gen_server:call({?SERVER, Node}, {send_sync, Msg}). 55 | 56 | %%%=================================================================== 57 | %%% gen_server callbacks 58 | %%%=================================================================== 59 | 60 | %%-------------------------------------------------------------------- 61 | %% @private 62 | %% @doc 63 | %% Initializes the server 64 | %% 65 | %% @spec init(Args) -> {ok, State} | 66 | %% {ok, State, Timeout} | 67 | %% ignore | 68 | %% {stop, Reason} 69 | %% @end 70 | %%-------------------------------------------------------------------- 71 | init([]) -> 72 | {ok, #state{}}. 73 | 74 | %%-------------------------------------------------------------------- 75 | %% @private 76 | %% @doc 77 | %% Handling call messages 78 | %% 79 | %% @spec handle_call(Request, From, State) -> 80 | %% {reply, Reply, State} | 81 | %% {reply, Reply, State, Timeout} | 82 | %% {noreply, State} | 83 | %% {noreply, State, Timeout} | 84 | %% {stop, Reason, Reply, State} | 85 | %% {stop, Reason, State} 86 | %% @end 87 | %%-------------------------------------------------------------------- 88 | handle_call({send_sync, Msg}, _From, State) -> 89 | io:format("got msg ~p~n", [Msg]), 90 | {reply, {echo, {Msg, node()}}, State}. 91 | 92 | %%-------------------------------------------------------------------- 93 | %% @private 94 | %% @doc 95 | %% Handling cast messages 96 | %% 97 | %% @spec handle_cast(Msg, State) -> {noreply, State} | 98 | %% {noreply, State, Timeout} | 99 | %% {stop, Reason, State} 100 | %% @end 101 | %%-------------------------------------------------------------------- 102 | handle_cast({send, {Msg, From}}, State) -> 103 | io:format("got msg from ~p ~p~n", [Msg, From]), 104 | From ! {echo, {Msg, node()}}, 105 | {noreply, State}. 106 | 107 | %%-------------------------------------------------------------------- 108 | %% @private 109 | %% @doc 110 | %% Handling all non call/cast messages 111 | %% 112 | %% @spec handle_info(Info, State) -> {noreply, State} | 113 | %% {noreply, State, Timeout} | 114 | %% {stop, Reason, State} 115 | %% @end 116 | %%-------------------------------------------------------------------- 117 | handle_info(_Info, State) -> 118 | {noreply, State}. 119 | 120 | %%-------------------------------------------------------------------- 121 | %% @private 122 | %% @doc 123 | %% This function is called by a gen_server when it is about to 124 | %% terminate. It should be the opposite of Module:init/1 and do any 125 | %% necessary cleaning up. When it returns, the gen_server terminates 126 | %% with Reason. The return value is ignored. 127 | %% 128 | %% @spec terminate(Reason, State) -> void() 129 | %% @end 130 | %%-------------------------------------------------------------------- 131 | terminate(_Reason, _State) -> 132 | ok. 133 | 134 | %%-------------------------------------------------------------------- 135 | %% @private 136 | %% @doc 137 | %% Convert process state when code is changed 138 | %% 139 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 140 | %% @end 141 | %%-------------------------------------------------------------------- 142 | code_change(_OldVsn, State, _Extra) -> 143 | {ok, State}. 144 | 145 | %%%=================================================================== 146 | %%% Internal functions 147 | %%%=================================================================== 148 | 149 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/doc/.gitignore: -------------------------------------------------------------------------------- 1 | edoc-info 2 | stylesheet.css 3 | erlang.png 4 | *.html 5 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/ebin/.gitignore: -------------------------------------------------------------------------------- 1 | *.beam 2 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/ebin/simple_cache.app: -------------------------------------------------------------------------------- 1 | {application, simple_cache, 2 | [{description, "A simple caching system"}, 3 | {vsn, "0.4.0"}, 4 | {modules, [simple_cache, 5 | sc_app, 6 | sc_sup, 7 | sc_element_sup, 8 | sc_store, 9 | sc_element, 10 | sc_event, 11 | sc_event_logger]}, 12 | {registered, [sc_sup]}, 13 | {applications, [kernel, sasl, stdlib, mnesia, resource_discovery]}, 14 | {mod, {sc_app, []}} 15 | ]}. 16 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/etc/log4erl.conf: -------------------------------------------------------------------------------- 1 | logger { 2 | file_appender file{ 3 | dir = "log", 4 | level = info, 5 | file = "app_slogs", 6 | type = size, 7 | max = 100000, 8 | suffix = txt, 9 | rotation = 5, 10 | format = '[%L] %I %l%n' 11 | } 12 | 13 | %% Consloe appender with level set to warn 14 | console_appender cmd{ 15 | level = info, 16 | format = '[%L] %I %l%n' 17 | } 18 | } 19 | 20 | %% %% To send logs to email 21 | %% logger email{ 22 | %% smtp_appender email{ 23 | %% level=all, 24 | %% ip = "192.168.1.6", 25 | %% %port = 25, 26 | %% no_auth = true, 27 | %% %username = user, 28 | %% %password = pass, 29 | %% from = "admin@my_server", 30 | %% to = "notification@my_server", 31 | %% title = "System info", 32 | %% msg = "[%T %j] %L:%n%l%n" 33 | %% } 34 | %%} 35 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/etc/sys.config: -------------------------------------------------------------------------------- 1 | %%% -*- mode:erlang -*- 2 | %%% Warning - this config file *must* end with 3 | 4 | [ 5 | {simple_cache, []}, 6 | {resource_discovery, 7 | [ 8 | {contact_nodes, ['rd@127.0.0.1']}, 9 | % {heartbeat_frequency, 60000}, 10 | {log4erl_config, "etc/log4erl.conf"} 11 | ]} 12 | ]. 13 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/rebar.config: -------------------------------------------------------------------------------- 1 | {lib_dirs,["deps"]}. 2 | {src_dirs, ["src", "test"]}. 3 | {excl_archive_filters, [".*"]}. 4 | {cover_enabled, true}. 5 | {erl_opts, [debug_info, fail_on_warning]}. 6 | 7 | {deps, [ 8 | {'resource_discovery', ".*", {git, "git://github.com/erlware/resource_discovery.git", "master"}} 9 | ]}. 10 | 11 | {post_hooks, [{compile, "cp ./etc/sys.config ./ebin/sys.config"}]}. 12 | 13 | 14 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/src/sc_app.erl: -------------------------------------------------------------------------------- 1 | -module(sc_app). 2 | 3 | -behaviour(application). 4 | 5 | -export([start/2, stop/1]). 6 | 7 | -define(WAIT_FOR_RESOURCES, 2500). 8 | 9 | start(_StartType, _StartArgs) -> 10 | resource_discovery:add_local_resource_tuple({simple_cache, node()}), 11 | resource_discovery:add_target_resource_type(simple_cache), 12 | resource_discovery:sync_resources(), 13 | sc_store:init(), 14 | case sc_sup:start_link() of 15 | {ok, Pid} -> 16 | sc_event_logger:add_handler(), 17 | {ok, Pid}; 18 | Other -> 19 | {error, Other} 20 | end. 21 | 22 | stop(_State) -> 23 | ok. 24 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/src/sc_element.erl: -------------------------------------------------------------------------------- 1 | -module(sc_element). 2 | 3 | -behaviour(gen_server). 4 | 5 | -export([ 6 | start_link/2, 7 | create/1, 8 | create/2, 9 | fetch/1, 10 | replace/2, 11 | delete/1 12 | ]). 13 | 14 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 15 | terminate/2, code_change/3]). 16 | 17 | -define(SERVER, ?MODULE). 18 | -define(DEFAULT_LEASE_TIME, (60 * 60 * 24)). 19 | 20 | -record(state, {value, lease_time, start_time}). 21 | 22 | start_link(Value, LeaseTime) -> 23 | gen_server:start_link(?MODULE, [Value, LeaseTime], []). 24 | 25 | create(Value, LeaseTime) -> 26 | sc_element_sup:start_child(Value, LeaseTime). 27 | 28 | create(Value) -> 29 | create(Value, ?DEFAULT_LEASE_TIME). 30 | 31 | fetch(Pid) -> 32 | gen_server:call(Pid, fetch). 33 | 34 | replace(Pid, Value) -> 35 | gen_server:cast(Pid, {replace, Value}). 36 | 37 | delete(Pid) -> 38 | gen_server:cast(Pid, delete). 39 | 40 | init([Value, LeaseTime]) -> 41 | Now = calendar:local_time(), 42 | StartTime = calendar:datetime_to_gregorian_seconds(Now), 43 | {ok, 44 | #state{value = Value, 45 | lease_time = LeaseTime, 46 | start_time = StartTime}, 47 | time_left(StartTime, LeaseTime)}. 48 | 49 | time_left(_StartTime, infinity) -> 50 | infinity; 51 | time_left(StartTime, LeaseTime) -> 52 | Now = calendar:local_time(), 53 | CurrentTime = calendar:datetime_to_gregorian_seconds(Now), 54 | TimeElapsed = CurrentTime - StartTime, 55 | case LeaseTime - TimeElapsed of 56 | Time when Time =< 0 -> 0; 57 | Time -> Time * 1000 58 | end. 59 | 60 | handle_call(fetch, _From, State) -> 61 | #state{value = Value, 62 | lease_time = LeaseTime, 63 | start_time = StartTime} = State, 64 | TimeLeft = time_left(StartTime, LeaseTime), 65 | {reply, {ok, Value}, State, TimeLeft}. 66 | 67 | handle_cast({replace, Value}, State) -> 68 | #state{lease_time = LeaseTime, 69 | start_time = StartTime} = State, 70 | TimeLeft = time_left(StartTime, LeaseTime), 71 | {noreply, State#state{value = Value}, TimeLeft}; 72 | handle_cast(delete, State) -> 73 | {stop, normal, State}. 74 | 75 | handle_info(timeout, State) -> 76 | {stop, normal, State}. 77 | 78 | terminate(_Reason, _State) -> 79 | sc_store:delete(self()), 80 | ok. 81 | 82 | code_change(_OldVsn, State, _Extra) -> 83 | {ok, State}. 84 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/src/sc_element_sup.erl: -------------------------------------------------------------------------------- 1 | -module(sc_element_sup). 2 | 3 | -behaviour(supervisor). 4 | 5 | -export([start_link/0, 6 | start_child/2 7 | ]). 8 | 9 | -export([init/1]). 10 | 11 | -define(SERVER, ?MODULE). 12 | 13 | start_link() -> 14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 15 | 16 | start_child(Value, LeaseTime) -> 17 | supervisor:start_child(?SERVER, [Value, LeaseTime]). 18 | 19 | init([]) -> 20 | Element = {sc_element, {sc_element, start_link, []}, 21 | temporary, brutal_kill, worker, [sc_element]}, 22 | Children = [Element], 23 | RestartStrategy = {simple_one_for_one, 0, 1}, 24 | {ok, {RestartStrategy, Children}}. 25 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/src/sc_event.erl: -------------------------------------------------------------------------------- 1 | -module(sc_event). 2 | 3 | -export([start_link/0, 4 | add_handler/2, 5 | delete_handler/2, 6 | lookup/1, 7 | create/2, 8 | replace/2, 9 | delete/1]). 10 | 11 | -define(SERVER, ?MODULE). 12 | 13 | start_link() -> 14 | gen_event:start_link({local, ?SERVER}). 15 | 16 | add_handler(Handler, Args) -> 17 | gen_event:add_handler(?SERVER, Handler, Args). 18 | 19 | delete_handler(Handler, Args) -> 20 | gen_event:delete_handler(?SERVER, Handler, Args). 21 | 22 | lookup(Key) -> 23 | gen_event:notify(?SERVER, {lookup, Key}). 24 | 25 | create(Key, Value) -> 26 | gen_event:notify(?SERVER, {create, {Key, Value}}). 27 | 28 | replace(Key, Value) -> 29 | gen_event:notify(?SERVER, {replace, {Key, Value}}). 30 | 31 | delete(Key) -> 32 | gen_event:notify(?SERVER, {delete, Key}). 33 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/src/sc_event_logger.erl: -------------------------------------------------------------------------------- 1 | -module(sc_event_logger). 2 | 3 | -behaviour(gen_event). 4 | 5 | -export([add_handler/0, delete_handler/0]). 6 | 7 | -export([init/1, handle_event/2, handle_call/2, 8 | handle_info/2, code_change/3, terminate/2]). 9 | 10 | -record(state, {}). 11 | 12 | add_handler() -> 13 | sc_event:add_handler(?MODULE, []). 14 | 15 | delete_handler() -> 16 | sc_event:delete_handler(?MODULE, []). 17 | 18 | init([]) -> 19 | {ok, #state{}}. 20 | 21 | handle_event({create, {Key, Value}}, State) -> 22 | error_logger:info_msg("create(~w, ~w)~n", [Key, Value]), 23 | {ok, State}; 24 | handle_event({lookup, Key}, State) -> 25 | error_logger:info_msg("lookup(~w)~n", [Key]), 26 | {ok, State}; 27 | handle_event({delete, Key}, State) -> 28 | error_logger:info_msg("delete(~w)~n", [Key]), 29 | {ok, State}; 30 | handle_event({replace, {Key, Value}}, State) -> 31 | error_logger:info_msg("replace(~w, ~w)~n", [Key, Value]), 32 | {ok, State}. 33 | 34 | handle_call(_Request, State) -> 35 | Reply = ok, 36 | {ok, Reply, State}. 37 | 38 | handle_info(_Info, State) -> 39 | {ok, State}. 40 | 41 | terminate(_Reason, _State) -> 42 | ok. 43 | 44 | code_change(_OldVsn, State, _Extra) -> 45 | {ok, State}. 46 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/src/sc_store.erl: -------------------------------------------------------------------------------- 1 | -module(sc_store). 2 | 3 | -export([ 4 | init/0, 5 | insert/2, 6 | delete/1, 7 | lookup/1 8 | ]). 9 | 10 | -define(TABLE_ID, ?MODULE). 11 | -define(WAIT_FOR_TABLES, 5000). 12 | 13 | -record(key_to_pid, {key, pid}). 14 | 15 | init() -> 16 | mnesia:stop(), 17 | mnesia:delete_schema([node()]), 18 | mnesia:start(), 19 | CacheNodes = resource_discovery:get_resources(simple_cache), 20 | dynamic_db_init(lists:delete(node(), CacheNodes)). 21 | 22 | insert(Key, Pid) -> 23 | mnesia:dirty_write(#key_to_pid{key = Key, pid = Pid}). 24 | 25 | lookup(Key) -> 26 | case mnesia:dirty_read(key_to_pid, Key) of 27 | [{key_to_pid, Key, Pid}] -> 28 | case is_pid_alive(Pid) of 29 | true -> {ok, Pid}; 30 | false -> {error, not_found} 31 | end; 32 | [] -> 33 | {error, not_found} 34 | end. 35 | 36 | delete(Pid) -> 37 | case mnesia:dirty_index_read(key_to_pid, Pid, #key_to_pid.pid) of 38 | [#key_to_pid{} = Record] -> 39 | mnesia:dirty_delete_object(Record); 40 | _ -> 41 | ok 42 | end. 43 | 44 | 45 | %% Internal Functions 46 | 47 | dynamic_db_init([]) -> 48 | %% XXX delete bad nodes from old clusters 49 | mnesia:create_table(key_to_pid, 50 | [{index, [pid]}, 51 | {attributes, record_info(fields, key_to_pid)} 52 | ]); 53 | dynamic_db_init(CacheNodes) -> 54 | add_extra_nodes(CacheNodes). 55 | 56 | add_extra_nodes([Node|T]) -> 57 | case mnesia:change_config(extra_db_nodes, [Node]) of 58 | {ok, [Node]} -> 59 | mnesia:add_table_copy(schema, node(), ram_copies), 60 | 61 | mnesia:add_table_copy(key_to_pid, node(), ram_copies), 62 | 63 | Tables = mnesia:system_info(tables), 64 | mnesia:wait_for_tables(Tables, ?WAIT_FOR_TABLES); 65 | _ -> 66 | add_extra_nodes(T) 67 | end. 68 | 69 | is_pid_alive(Pid) when node(Pid) =:= node() -> 70 | is_process_alive(Pid); 71 | is_pid_alive(Pid) -> 72 | case lists:member(node(Pid), nodes()) of 73 | false -> 74 | false; 75 | true -> 76 | case rpc:call(node(Pid), erlang, is_process_alive, [Pid]) of 77 | true -> 78 | true; 79 | false -> 80 | false; 81 | {badrpc, _Reason} -> 82 | false 83 | end 84 | end. 85 | 86 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/src/sc_sup.erl: -------------------------------------------------------------------------------- 1 | -module(sc_sup). 2 | 3 | -behaviour(supervisor). 4 | 5 | %% API 6 | -export([start_link/0]). 7 | 8 | %% Supervisor callbacks 9 | -export([init/1]). 10 | 11 | -define(SERVER, ?MODULE). 12 | 13 | start_link() -> 14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 15 | 16 | init([]) -> 17 | ElementSup = {sc_element_sup, {sc_element_sup, start_link, []}, 18 | permanent, 2000, supervisor, [sc_element]}, 19 | 20 | EventManager = {sc_event, {sc_event, start_link, []}, 21 | permanent, 2000, worker, [sc_event]}, 22 | 23 | Children = [ElementSup, EventManager], 24 | RestartStrategy = {one_for_one, 4, 3600}, 25 | {ok, {RestartStrategy, Children}}. 26 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/src/simple_cache.erl: -------------------------------------------------------------------------------- 1 | -module(simple_cache). 2 | 3 | -export([insert/2, lookup/1, delete/1]). 4 | 5 | insert(Key, Value) -> 6 | case sc_store:lookup(Key) of 7 | {ok, Pid} -> 8 | sc_event:replace(Key, Value), 9 | sc_element:replace(Pid, Value); 10 | {error, _} -> 11 | {ok, Pid} = sc_element:create(Value), 12 | sc_store:insert(Key, Pid), 13 | sc_event:create(Key, Value) 14 | end. 15 | 16 | lookup(Key) -> 17 | sc_event:lookup(Key), 18 | try 19 | {ok, Pid} = sc_store:lookup(Key), 20 | {ok, Value} = sc_element:fetch(Pid), 21 | {ok, Value} 22 | catch 23 | _Class:_Exception -> 24 | {error, not_found} 25 | end. 26 | 27 | delete(Key) -> 28 | sc_event:delete(Key), 29 | case sc_store:lookup(Key) of 30 | {ok, Pid} -> 31 | sc_element:delete(Pid); 32 | {error, _Reason} -> 33 | ok 34 | end. 35 | -------------------------------------------------------------------------------- /distributed_erlang/simple_cache/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd `dirname $0` 3 | 4 | NAME=$1 5 | shift 6 | 7 | exec erl -pa $PWD/ebin -pa $PWD/deps/*/ebin -name $NAME -s resource_discovery start -config ebin/sys $@ 8 | -------------------------------------------------------------------------------- /essentials/ec_add.erl: -------------------------------------------------------------------------------- 1 | %%% @author Billy Erlware 2 | %%% @doc contains add func 3 | %%% @copyright 2013 Erlware 4 | 5 | -module(ec_add). 6 | -export([add/2]). 7 | 8 | %% @doc adds two numbers together. 9 | -spec(add(number(), number()) -> number()). 10 | add(A, B) -> 11 | A + B. 12 | -------------------------------------------------------------------------------- /essentials/ec_concurrent.erl: -------------------------------------------------------------------------------- 1 | %%% @doc essentials process exercises 2 | 3 | -module(ec_concurrent). 4 | -export([bf_seq/1]). 5 | 6 | %% @doc print 2 sequences simultaneously 7 | -spec(bf_seq(list()) -> ok). 8 | bf_seq(L) -> 9 | RL = lists:reverse(L), 10 | spawn(fun() -> print_seq(L) end), 11 | spawn(fun() -> print_seq(RL) end). 12 | 13 | -spec(print_seq(list()) -> ok). 14 | print_seq([H]) -> 15 | io:format("~p", [H]); 16 | print_seq([H|T]) -> 17 | io:format("~p,", [H]), 18 | print_seq(T). 19 | 20 | -------------------------------------------------------------------------------- /essentials/ec_exception.erl: -------------------------------------------------------------------------------- 1 | %%% @doc Bunch of different exception scenerios 2 | 3 | -module(ec_exception). 4 | -export([try_of_fail1/0]). 5 | 6 | try_of_fail1() -> 7 | try_of_fail1(fun() -> ok end). 8 | 9 | try_of_fail1(Fun) -> 10 | try Fun() of 11 | ok -> 12 | try_of_fail1(fun() -> lists:reverse(not_a_list) end) 13 | catch 14 | error:function_clause -> 15 | io:format("~nException: error~nError: function_clause~n"), 16 | try_of_fail1(fun() -> throw(my_throw) end); 17 | throw:my_throw -> 18 | io:format("~nException: throw~nError: my_throw~n"), 19 | try_of_fail1(fun() -> erlang:error(my_erlang_error) end); 20 | error:my_erlang_error -> 21 | io:format("~nException: error~nError: my_erlang_error~n"), 22 | try_of_fail1(fun() -> exit(my_exit) end); 23 | exit:my_exit -> 24 | io:format("~nException: exit~nError: my_exit~n") 25 | end. 26 | -------------------------------------------------------------------------------- /essentials/ec_lists.erl: -------------------------------------------------------------------------------- 1 | %%% @doc a bit of higher order functional goodness 2 | 3 | -module(ec_lists). 4 | -export([remove_odd/1, twomult/1, yourmap/2]). 5 | 6 | %% @doc remove odd elements from a list of integers 7 | -spec remove_odd(list()) -> list(). 8 | remove_odd(List) -> 9 | remove_odd(List, []). 10 | 11 | -spec remove_odd(list(), list()) -> list(). 12 | remove_odd([], Acc) -> 13 | Acc; 14 | remove_odd([H|T], Acc) when H rem 2 == 0 -> 15 | remove_odd(T, [H|Acc]); 16 | remove_odd([_H|T], Acc) -> 17 | remove_odd(T, Acc). 18 | 19 | %% @doc multiply each integer in the given list by 2 20 | -spec twomult(list()) -> list(). 21 | twomult([H|T]) -> 22 | [H * 2|twomult(T)]; 23 | twomult([]) -> 24 | []. 25 | 26 | %% @doc multiply each element by 2 27 | -spec yourmap(fun(), list()) -> list(). 28 | yourmap(Fun, [H|T]) -> 29 | [Fun(H)|yourmap(Fun, T)]; 30 | yourmap(_Fun, []) -> 31 | []. 32 | 33 | -------------------------------------------------------------------------------- /essentials/ec_math.erl: -------------------------------------------------------------------------------- 1 | %%% @author Sammy Lambda 2 | %%% @doc some mathematical functions. 3 | %%% @copyright 2013 Erlware 4 | -module(ec_math). 5 | -export([op/3, op_w_guards/3]). 6 | 7 | %% @doc perform a mathematical operation. 8 | -spec(op(atom(), number(), number()) -> number()). 9 | op(add, A, B) -> 10 | A + B; 11 | op(sub, A, B) -> 12 | A - B. 13 | 14 | %% @doc perform a mathematical operation but use guards. 15 | -spec(op_w_guards(atom(), number(), number()) -> number()). 16 | op_w_guards(add, A, B) -> 17 | A + B; 18 | op_w_guards(sub, A, B) when A < B -> 19 | error; 20 | op_w_guards(sub, A, B) -> 21 | A - B. 22 | 23 | -------------------------------------------------------------------------------- /essentials/ec_multiply.erl: -------------------------------------------------------------------------------- 1 | %%% @author Sammey Lambda 2 | %%% @doc contains add func 3 | %%% @copyright 2013 Erlware 4 | 5 | -module(ec_multiply). 6 | -export([mult/3]). 7 | 8 | %% @doc multiplies three numbers together. 9 | -spec(mult(number(), number(), number()) -> number()). 10 | mult(A, B, C) -> 11 | A * B * C. 12 | 13 | -------------------------------------------------------------------------------- /essentials/ec_recur.erl: -------------------------------------------------------------------------------- 1 | %%% @author Billy Erlware 2 | %%% @doc a function that sums a sequence. 3 | %%% @copyright Erlware 2013 4 | -module(ec_recur). 5 | -export([sum_seq/1, sum_seq_tail/1, hr/1]). 6 | 7 | %% @doc sum a sequence of a number to 1. 8 | -spec sum_seq(number()) -> number(). 9 | sum_seq(1) -> 10 | 1; 11 | sum_seq(N) -> 12 | N + sum_seq(N - 1). 13 | 14 | %% @doc sum a sequence of a number to 1. 15 | -spec sum_seq_tail(number()) -> number(). 16 | sum_seq_tail(N) -> 17 | sum_seq(N, 0). 18 | 19 | sum_seq(0, Acc) -> 20 | Acc; 21 | sum_seq(N, Acc) -> 22 | sum_seq(N - 1, N + Acc). 23 | 24 | %% Tail recursive 25 | -spec hr(number()) -> number(). 26 | hr(N) -> 27 | hr(N, ""). 28 | 29 | -spec hr(number(), string()) -> number(). 30 | hr(0, Acc) -> 31 | Acc; 32 | hr(N, Acc) -> 33 | hr(N - 1, Acc ++ "-"). 34 | -------------------------------------------------------------------------------- /essentials/essentials-part1-cheatsheet.markdown: -------------------------------------------------------------------------------- 1 | Essentials Part 1 Cheatsheet 2 | =========== 3 | 4 | ## Modules and Functions 5 | 6 | The file name for the following module must match the 7 | module name and so must be "foo.erl" 8 | 9 | ``` 10 | %%% @author Billy Erlware 11 | %%% @doc contains add func 12 | %%% @copyright 2013 Erlware 13 | 14 | -module(foo). 15 | -export([func/2]). 16 | 17 | %% @doc this function of arity 2 has 2 clauses. 18 | -spec func(atom() | integer(), integer()) -> atom() | integer(). 19 | func(undefined, _Var2) -> 20 | undefined; 21 | func(Var1, Var2) -> 22 | Var3 = Var1 + Var2, 23 | Var3. 24 | ``` 25 | 26 | ## Pattern Matching 27 | 28 | ``` 29 | a = a. % This matched two atoms successfully 30 | a = "a". % This throws an exception as the string "a" is not the atom a 31 | A = a. % The variable A is now bound to a 32 | A = a. % This matched a with a 33 | A = b. % throws a badmatch exception as a is not b 34 | 35 | 1> foo:func(tag_b, 1). 36 | ``` 37 | 38 | Pattern matching works in functions. The shell call above matches The 39 | second function clause below in the module foo. 40 | 41 | ``` 42 | matchit(tag_a, Var) -> 43 | ...; 44 | matchit(tag_b, Var) -> 45 | ``` 46 | 47 | ## Guards 48 | 49 | A function clause can be guarded in the following way 50 | 51 | ``` 52 | func(Var1, Var2) when Var1 /= undefined -> 53 | ``` 54 | 55 | ## Recursion 56 | 57 | Accumulate in a tail recursive function 58 | ``` 59 | accumulate_it(0, Acc) -> 60 | Acc; 61 | accumulate_it(N, Acc) -> 62 | accumulate_it(N - 1, Acc + N). 63 | ``` 64 | 65 | ## Lists 66 | ``` 67 | 1> [1|[2, 3, 4]]. 68 | [1, 2, 3, 4] 69 | 70 | 2> [H|T] = [1, 2, 3, 4]. 71 | 3> H. 72 | 1 73 | 4> T. 74 | [2, 3, 4] 75 | ``` 76 | 77 | ## Higher order Functions 78 | 79 | A an anonymous function that multiplies its argument by 2 80 | 81 | ``` 82 | fun(N) -> X * 2 end. 83 | ``` 84 | 85 | 86 | -------------------------------------------------------------------------------- /events_and_logs/README.md: -------------------------------------------------------------------------------- 1 | Events and Logs tutorial 2 | ======================== 3 | 4 | Hey Erlangers! 5 | 6 | You will find complete versions of code for simple cache, as well as 7 | the entity manager in here. Details on each are below. 8 | 9 | If you find any errors or have any suggestions, please e-mail us 10 | at organizers@erlangcamp.com 11 | 12 | Thanks! 13 | 14 | 15 | ## simple_cache 16 | 17 | This version of `simple_cache` includes the supervisor, application, and 18 | also has lager (logging) added to it. You will remember the lager 19 | dependency was added by first adding a `rebar.config` 20 | 21 | Here are the steps on the command line to get the code up and running. 22 | 23 | (assuming you are in the `events_and_logs` directory) 24 | 25 | ``` 26 | $ cd simple_cache 27 | $ ./rebar get-deps 28 | $ ./rebar compile 29 | $ erl -pa ebin -env ERL_LIBS deps 30 | > application:start(syntax_tools). 31 | > application:start(compiler). 32 | > application:start(goldrush). 33 | > application:start(lager). 34 | > application:start(simple_cache). 35 | > observer:start(). 36 | > sc_server:insert(jordan, wilberding). 37 | ok 38 | > sc_server:lookup(jordan). 39 | wilberding 40 | > sc_server:stop(). 41 | 07:23:08.015 [error] gen_server sc_server terminated with reason: just_cuz 42 | 07:23:08.016 [error] CRASH REPORT Process sc_server with 0 neighbours exited with reason: just_cuz in gen_server:terminate/6 line 744 43 | 07:23:08.016 [info] Hey, this is starting!! 44 | **(the above message was generated from our call to lager:info in the start_link of sc_server!)** 45 | 07:23:08.016 [error] Supervisor sc_sup had child sc_server started with sc_server:start_link() at <0.57.0> exit with reason just_cuz in context child_terminated 46 | > sc_server:insert(jordan, wilberding). 47 | ok 48 | > sc_server:lookup(jordan). 49 | wilberding 50 | ``` 51 | 52 | Feel free to add your own logging calls to lager elsewhere and 53 | whatever else you want, have some fun! Also, take a look at the logs 54 | directory created by lager to see what interesting things go there. 55 | 56 | 57 | ## entity_manager 58 | 59 | This is a simple example of utilizing the `gen_event` framework to 60 | create an Entity Manager. 61 | 62 | I have created a code to drive the entity manager in `em_demo.erl`, but 63 | you can take those commands and type them yourself directly into the 64 | shell if you wanted to. 65 | 66 | To run the code, doing the following. 67 | 68 | (assuming you are in the `events_and_logs` directory) 69 | 70 | ``` 71 | $ cd entity_manager 72 | $ erlc *.erl 73 | $ erl 74 | > em_demo:go(). 75 | ``` 76 | 77 | Again, please freel free to play around! 78 | -------------------------------------------------------------------------------- /events_and_logs/custom_error_report.erl: -------------------------------------------------------------------------------- 1 | -module(custom_error_report). 2 | 3 | -behaviour(gen_event). 4 | 5 | %% API 6 | -export([register_with_logger/0]). 7 | 8 | -export([init/1, handle_event/2, handle_call/2, 9 | handle_info/2, terminate/2, code_change/3]). 10 | 11 | -record(state, {}). 12 | 13 | register_with_logger() -> 14 | error_logger:add_report_handler(?MODULE). 15 | 16 | init([]) -> 17 | {ok, #state{}}. 18 | 19 | handle_event({error, _Gleader, {Pid,Format,Data}}, State) -> 20 | io:fwrite("CUSTOM ERROR <~p> ~s", [Pid, io_lib:format(Format, Data)]), 21 | {ok, State}; 22 | handle_event({error_report, _Gleader, {Pid, std_error, Report}}, State) -> 23 | io:fwrite("CUSTOM ERROR <~p> ~p", [Pid, Report]), 24 | {ok, State}; 25 | handle_event({error_report, _Gleader, {Pid, Type, Report}}, State) -> 26 | io:fwrite("CUSTOM ERROR <~p> ~p ~p", [Pid, Type, Report]), 27 | {ok, State}; 28 | handle_event({warning_msg, _Gleader, {Pid, Format, Data}}, State) -> 29 | io:fwrite("CUSTOM WARNING <~p> ~s", [Pid, io_lib:format(Format, Data)]), 30 | {ok, State}; 31 | handle_event({warning_report,_Gleader,{Pid,std_warning,Report}}, State) -> 32 | io:fwrite("CUSTOM WARNING <~p> ~p", [Pid, Report]), 33 | {ok, State}; 34 | handle_event({warning_report,_Gleader,{Pid, Type, Report}}, State) -> 35 | io:fwrite("CUSTOM WARNING <~p> ~p ~p", [Pid, Type, Report]), 36 | {ok, State}; 37 | handle_event({info_msg, _Gleader, {Pid, Format, Data}}, State) -> 38 | io:fwrite("CUSTOM INFO <~p> ~s", [Pid, io_lib:format(Format, Data)]), 39 | {ok, State}; 40 | handle_event({info_report, _Gleader, {Pid, std_info, Report}}, State) -> 41 | io:fwrite("CUSTOM INFO <~p> ~p", [Pid, Report]), 42 | {ok, State}; 43 | handle_event({info_report, _Gleader, {Pid, Type, Report}}, State) -> 44 | io:fwrite("CUSTOM INFO <~p> ~p ~p", [Pid, Type, Report]), 45 | {ok, State}; 46 | handle_event(_Event, State) -> 47 | {ok, State}. 48 | 49 | handle_call(_Request, State) -> 50 | Reply = ok, 51 | {ok, Reply, State}. 52 | 53 | handle_info(_Info, State) -> 54 | {ok, State}. 55 | 56 | terminate(_Reason, _State) -> 57 | ok. 58 | 59 | code_change(_OldVsn, State, _Extra) -> 60 | {ok, State}. 61 | -------------------------------------------------------------------------------- /events_and_logs/die_please.erl: -------------------------------------------------------------------------------- 1 | -module(die_please). 2 | 3 | -behaviour(gen_server). 4 | 5 | -export([start_link/0]). 6 | 7 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 8 | terminate/2, code_change/3]). 9 | 10 | -define(SERVER, ?MODULE). 11 | -define(SLEEP_TIME, (2*1000)). 12 | 13 | -record(state, {}). 14 | 15 | start_link() -> 16 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 17 | 18 | init([]) -> 19 | {ok, #state{}, ?SLEEP_TIME}. 20 | 21 | handle_call(_Request, _From, State) -> 22 | Reply = ok, 23 | {reply, Reply, State}. 24 | 25 | handle_cast(_Msg, State) -> 26 | {noreply, State}. 27 | 28 | handle_info(timeout, State) -> 29 | i_want_to_die = right_now, 30 | {noreply, State}. 31 | 32 | terminate(_Reason, _State) -> 33 | ok. 34 | 35 | code_change(_OldVsn, State, _Extra) -> 36 | {ok, State}. 37 | -------------------------------------------------------------------------------- /events_and_logs/die_please2.erl: -------------------------------------------------------------------------------- 1 | -module(die_please2). 2 | 3 | -export([go/0]). 4 | 5 | -define(SLEEP_TIME, 2000). 6 | 7 | go() -> 8 | %% just sleep for a while, then crash 9 | timer:sleep(?SLEEP_TIME), 10 | i_really_want_to_die = right_now. 11 | -------------------------------------------------------------------------------- /events_and_logs/entity_manager/em.erl: -------------------------------------------------------------------------------- 1 | -module (em). 2 | -export ([start_link/0, 3 | add_component/3, 4 | remove_component/2, 5 | components/1, 6 | notify/2, 7 | destroy/1]). 8 | 9 | start_link() -> 10 | gen_event:start_link(). 11 | 12 | add_component(Pid, Component, Args) -> 13 | gen_event:add_handler(Pid, Component, Args). 14 | 15 | remove_component(Pid, Component) -> 16 | gen_event:delete_handler(Pid, Component). 17 | 18 | components(Pid) -> 19 | gen_event:which_handlers(Pid). 20 | 21 | notify(Pid, Event) -> 22 | gen_event:notify(Pid, Event). 23 | 24 | destroy(Pid) -> 25 | gen_event:stop(Pid). 26 | -------------------------------------------------------------------------------- /events_and_logs/entity_manager/em_demo.erl: -------------------------------------------------------------------------------- 1 | -module(em_demo). 2 | -export([go/0]). 3 | 4 | %% Entity Manager demonstration 5 | 6 | go() -> 7 | %% Create Entity, add health component, then kill it! 8 | {ok, Entity} = em:start_link(), 9 | em:add_component(Entity, health_component, 100), 10 | em:notify(Entity, {hit, 50}), 11 | em:notify(Entity, {hit, 20}), 12 | em:notify(Entity, {hit, 30}), 13 | em:notify(Entity, {hit, 30}), 14 | 15 | %% Our Entity is technically dead.. but let's give it a coordinate 16 | %% component, and some weapons! 17 | em:add_component(Entity, xy_component, {50, 50}), 18 | em:notify(Entity, {change, {y, 35}}), 19 | em:add_component(Entity, weapon_component, ["chair"]), 20 | em:notify(Entity, {add_weapon, "bat"}), 21 | em:notify(Entity, {add_weapon, "chainsaw"}), 22 | em:notify(Entity, {list_weapons}), 23 | em:notify(Entity, {remove_weapon, "chainsaw"}), 24 | em:notify(Entity, list_weapons), 25 | 26 | %% Let's make a new entity and have it find a surprise.. 27 | %% (shows two separate components reacting to same event 28 | {ok, Entity2} = em:start_link(), 29 | em:add_component(Entity2, xy_component, {50, 50}), 30 | em:add_component(Entity2, weapon_component, ["chair"]), 31 | em:notify(Entity2, {change, {{x, y}, {489, 937}}}), 32 | em:notify(Entity2, list_weapons). 33 | -------------------------------------------------------------------------------- /events_and_logs/entity_manager/entity.erl: -------------------------------------------------------------------------------- 1 | -module (entity). 2 | -export ([init/0, 3 | add_component/3, 4 | remove_component/2, 5 | components/1, 6 | notify/2, 7 | destroy/1]). 8 | 9 | init() -> 10 | {ok, Pid} = gen_event:start_link(), 11 | {ok, Pid}. 12 | 13 | add_component(Pid, Component, Args) -> 14 | gen_event:add_handler(Pid, Component, Args). 15 | 16 | remove_component(Pid, Component) -> 17 | gen_event:delete_handler(Pid, Component). 18 | 19 | components(Pid) -> 20 | gen_event:which_handlers(Pid). 21 | 22 | notify(Pid, Event) -> 23 | gen_event:notify(Pid, Event). 24 | 25 | destroy(Pid) -> 26 | gen_event:stop(Pid). 27 | -------------------------------------------------------------------------------- /events_and_logs/entity_manager/health_component.erl: -------------------------------------------------------------------------------- 1 | -module (health_component). 2 | -behaviour(gen_event). 3 | 4 | -export([init/1, 5 | handle_event/2, 6 | handle_call/2, 7 | handle_info/2, 8 | code_change/3, 9 | terminate/2]). 10 | 11 | init(HP) -> 12 | {ok, HP}. 13 | 14 | handle_event({hit, Damage}, HP) when Damage < HP -> 15 | NewHP = HP - Damage, 16 | io:format("Entity got hit with ~p damage and has ~p HP left.~n", [Damage, NewHP]), 17 | {ok, NewHP}; 18 | 19 | handle_event({hit, _Damage}, 0) -> 20 | io:format("Entity is already DEAD!~n"), 21 | {ok, 0}; 22 | 23 | handle_event({hit, Damage}, _HP) -> 24 | io:format("Entity took ~p damage and DIED :(~n", [Damage]), 25 | {ok, 0}; 26 | 27 | handle_event(_, State) -> 28 | {ok, State}. 29 | 30 | handle_call(_, State) -> 31 | {ok, ok, State}. 32 | 33 | handle_info(_, State) -> 34 | {ok, State}. 35 | 36 | code_change(_OldVsn, State, _Extra) -> 37 | {ok, State}. 38 | 39 | terminate(_Args, _State) -> 40 | ok. 41 | -------------------------------------------------------------------------------- /events_and_logs/entity_manager/weapon_component.erl: -------------------------------------------------------------------------------- 1 | -module (weapon_component). 2 | -behaviour(gen_event). 3 | 4 | -export([init/1, 5 | handle_event/2, 6 | handle_call/2, 7 | handle_info/2, 8 | code_change/3, 9 | terminate/2]). 10 | 11 | init(WeaponsList) -> 12 | {ok, WeaponsList}. 13 | 14 | handle_event({add_weapon, Weapon}, WeaponsList) -> 15 | io:format("Adding weapon ~p to arsenal~n", [Weapon]), 16 | {ok, [Weapon | WeaponsList]}; 17 | 18 | handle_event({remove_weapon, Weapon}, WeaponsList) -> 19 | io:format("Removing weapon ~p from arsenal~n", [Weapon]), 20 | {ok, lists:delete(Weapon, WeaponsList)}; 21 | 22 | handle_event(list_weapons, WeaponsList) -> 23 | io:format("Current weapons: ~p~n", [WeaponsList]), 24 | {ok, WeaponsList}; 25 | 26 | %% Add "WMD" to list of weapons when x is 489, and y is 937 (hint: use guards) 27 | handle_event({change, {{x, y}, {NewX, NewY}}}, WeaponsList) 28 | when NewX =:= 489 andalso 29 | NewY =:= 937 -> 30 | io:format("Found Secret! Adding weapon ~p to arsenal~n", ["WMD"]), 31 | {ok, ["WMD" | WeaponsList]}; 32 | 33 | handle_event(_, State) -> 34 | {ok, State}. 35 | 36 | handle_call(_, State) -> 37 | {ok, ok, State}. 38 | 39 | handle_info(_, State) -> 40 | {ok, State}. 41 | 42 | code_change(_OldVsn, State, _Extra) -> 43 | {ok, State}. 44 | 45 | terminate(_Args, _State) -> 46 | ok. 47 | -------------------------------------------------------------------------------- /events_and_logs/entity_manager/weapon_component_finished.erl: -------------------------------------------------------------------------------- 1 | -module (weapon_component). 2 | -behaviour(gen_event). 3 | 4 | -export([init/1, 5 | handle_event/2, 6 | handle_call/2, 7 | handle_info/2, 8 | code_change/3, 9 | terminate/2]). 10 | 11 | init(WeaponsList) -> 12 | {ok, WeaponsList}. 13 | 14 | handle_event({add_weapon, Weapon}, WeaponsList) -> 15 | io:format("Adding weapon ~p to arsenal~n", [Weapon]), 16 | {ok, [Weapon | WeaponsList]}; 17 | 18 | handle_event({remove_weapon, Weapon}, WeaponsList) -> 19 | io:format("Removing weapon ~p from arsenal~n", [Weapon]), 20 | {ok, lists:delete(Weapon, WeaponsList)}; 21 | 22 | handle_event({list_weapons}, WeaponsList) -> 23 | io:format("Current weapons: ~p~n", [WeaponsList]), 24 | {ok, WeaponsList}; 25 | 26 | %% Add "WMD" to list of weapons when x is 489, and y is 937 27 | %%handle_event({change, {{x, y}, {NewX, NewY}}}, ??) -> 28 | 29 | handle_event(_, State) -> 30 | {ok, State}. 31 | 32 | handle_call(_, State) -> 33 | {ok, ok, State}. 34 | 35 | handle_info(_, State) -> 36 | {ok, State}. 37 | 38 | code_change(_OldVsn, State, _Extra) -> 39 | {ok, State}. 40 | 41 | terminate(_Args, _State) -> 42 | ok. 43 | -------------------------------------------------------------------------------- /events_and_logs/entity_manager/xy_component.erl: -------------------------------------------------------------------------------- 1 | -module (xy_component). 2 | -behaviour(gen_event). 3 | 4 | -export([init/1, 5 | handle_event/2, 6 | handle_call/2, 7 | handle_info/2, 8 | code_change/3, 9 | terminate/2]). 10 | 11 | init({X, Y}) -> 12 | {ok, {X, Y}}. 13 | 14 | handle_event({change, {x, NewX}}, {OldX, OldY}) -> 15 | io:format("Entity moved from (~p, ~p) to (~p, ~p).~n", [OldX, OldY, NewX, OldY]), 16 | {ok, {NewX, OldY}}; 17 | 18 | handle_event({change, {y, NewY}}, {OldX, OldY}) -> 19 | io:format("Entity moved from (~p, ~p) to (~p, ~p).~n", [OldX, OldY, OldX, NewY]), 20 | {ok, {OldX, NewY}}; 21 | 22 | %% Update coordinates for both x and y 23 | handle_event({change, {{x, y}, {NewX, NewY}}}, {OldX, OldY}) -> 24 | io:format("Entity moved from (~p, ~p) to (~p, ~p).~n", [OldX, OldY, NewX, NewY]), 25 | {ok, {NewX, NewY}}; 26 | 27 | handle_event(_, State) -> 28 | {ok, State}. 29 | 30 | handle_call(_, State) -> 31 | {ok, ok, State}. 32 | 33 | handle_info(_, State) -> 34 | {ok, State}. 35 | 36 | code_change(_OldVsn, State, _Extra) -> 37 | {ok, State}. 38 | 39 | terminate(_Args, _State) -> 40 | ok. 41 | -------------------------------------------------------------------------------- /events_and_logs/rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [{parse_transform, lager_transform}]}. 2 | 3 | {deps, [ 4 | {lager, "", {git, "git://github.com/basho/lager.git", "master"}} 5 | ]}. 6 | -------------------------------------------------------------------------------- /events_and_logs/simple_cache/rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erlware/erlang-camp/a3130412d67ba618f34959f243bc3df2fb00f4c3/events_and_logs/simple_cache/rebar -------------------------------------------------------------------------------- /events_and_logs/simple_cache/rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [{parse_transform, lager_transform}]}. 2 | 3 | {deps, [ 4 | {lager, "", {git, "git://github.com/basho/lager.git", "master"}} 5 | ]}. 6 | -------------------------------------------------------------------------------- /events_and_logs/simple_cache/src/sc_app.erl: -------------------------------------------------------------------------------- 1 | -module(sc_app). 2 | 3 | -behaviour(application). 4 | 5 | %% Application callbacks 6 | -export([start/2, stop/1]). 7 | 8 | %% =================================================================== 9 | %% Application callbacks 10 | %% =================================================================== 11 | 12 | start(_StartType, _StartArgs) -> 13 | sc_sup:start_link(). 14 | 15 | stop(_State) -> 16 | ok. 17 | -------------------------------------------------------------------------------- /events_and_logs/simple_cache/src/sc_server.erl: -------------------------------------------------------------------------------- 1 | %%% @author Martin Logan 2 | %%% @copyright (C) 2013, Erlware 3 | %%% @doc 4 | -module(sc_server). 5 | -behaviour(gen_server). 6 | 7 | %% API 8 | -export([start_link/0]). 9 | 10 | %% gen_server callbacks 11 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 12 | insert/2, lookup/1, stop/0, terminate/2, code_change/3]). 13 | 14 | -define(SERVER, ?MODULE). 15 | -define(IMPLEMENTATION_MODULE, ?MODULE). 16 | 17 | %%%=================================================================== 18 | %%% API 19 | %%%=================================================================== 20 | 21 | %% @doc Starts the server 22 | -spec start_link() -> {ok, pid()}. 23 | start_link() -> 24 | gen_server:start_link({local, ?SERVER}, ?IMPLEMENTATION_MODULE, [], []). 25 | 26 | %%%=================================================================== 27 | %%% gen_server callbacks 28 | %%%=================================================================== 29 | 30 | %%-------------------------------------------------------------------- 31 | %% @private 32 | %% @doc 33 | %% Initializes the server 34 | %% 35 | %% @spec init(Args) -> {ok, State} | 36 | %% {ok, State, Timeout} | 37 | %% ignore | 38 | %% {stop, Reason} 39 | %% @end 40 | %%-------------------------------------------------------------------- 41 | init([]) -> 42 | {ok, []}. 43 | 44 | -spec insert(atom(), term()) -> ok. 45 | insert(Key, Value) -> 46 | gen_server:cast(?SERVER, {insert, {Key, Value}}). 47 | 48 | -spec lookup(atom()) -> term() | undefined. 49 | lookup(Key) -> 50 | gen_server:call(?SERVER, {lookup, Key}). 51 | 52 | -spec stop() -> ok. 53 | stop() -> 54 | gen_server:cast(?SERVER, stop). 55 | 56 | 57 | %%-------------------------------------------------------------------- 58 | %% @private 59 | %% @doc 60 | %% Handling call messages 61 | %% 62 | %% @spec handle_call(Request, From, State) -> 63 | %% {reply, Reply, State} | 64 | %% {reply, Reply, State, Timeout} | 65 | %% {noreply, State} | 66 | %% {noreply, State, Timeout} | 67 | %% {stop, Reason, Reply, State} | 68 | %% {stop, Reason, State} 69 | %% @end 70 | %%-------------------------------------------------------------------- 71 | handle_call({lookup, Key}, _From, State) -> 72 | Reply = proplists:get_value(Key, State), 73 | lager:info("A search on Key ~p return value ~p.", [Key, Reply]), 74 | {reply, Reply, State}; 75 | 76 | handle_call(_Request, _From, State) -> 77 | Reply = ok, 78 | {reply, Reply, State}. 79 | 80 | 81 | %%-------------------------------------------------------------------- 82 | %% @private 83 | %% @doc 84 | %% Handling cast messages 85 | %% 86 | %% @spec handle_cast(Msg, State) -> {noreply, State} | 87 | %% {noreply, State, Timeout} | 88 | %% {stop, Reason, State} 89 | %% @end 90 | %%-------------------------------------------------------------------- 91 | handle_cast({insert, {Key, Value}} = _KV, State) -> 92 | NewState = [{Key, Value}|State], 93 | lager:info("Added ~p to the data store.", [{Key, Value}]), 94 | {noreply, NewState}; 95 | 96 | handle_cast(stop, State) -> 97 | {stop, just_cuz, State}; 98 | 99 | handle_cast(_Msg, State) -> 100 | {noreply, State}. 101 | 102 | %%-------------------------------------------------------------------- 103 | %% @private 104 | %% @doc 105 | %% Handling all non call/cast messages 106 | %% 107 | %% @spec handle_info(Info, State) -> {noreply, State} | 108 | %% {noreply, State, Timeout} | 109 | %% {stop, Reason, State} 110 | %% @end 111 | %%-------------------------------------------------------------------- 112 | handle_info(_Info, State) -> 113 | {noreply, State}. 114 | 115 | %%-------------------------------------------------------------------- 116 | %% @private 117 | %% @doc 118 | %% This function is called by a gen_server when it is about to 119 | %% terminate. It should be the opposite of Module:init/1 and do any 120 | %% necessary cleaning up. When it returns, the gen_server terminates 121 | %% with Reason. The return value is ignored. 122 | %% 123 | %% @spec terminate(Reason, State) -> void() 124 | %% @end 125 | %%-------------------------------------------------------------------- 126 | terminate(_Reason, _State) -> 127 | ok. 128 | 129 | %%-------------------------------------------------------------------- 130 | %% @private 131 | %% @doc 132 | %% Convert process state when code is changed 133 | %% 134 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 135 | %% @end 136 | %%-------------------------------------------------------------------- 137 | code_change(_OldVsn, State, _Extra) -> 138 | {ok, State}. 139 | 140 | %%%=================================================================== 141 | %%% Internal functions 142 | %%%=================================================================== 143 | -------------------------------------------------------------------------------- /events_and_logs/simple_cache/src/sc_sup.erl: -------------------------------------------------------------------------------- 1 | %%% @author You 2 | %%% @copyright (C) 2013, Erlware 3 | %%% @doc 4 | 5 | -module(sc_sup). 6 | -behaviour(supervisor). 7 | 8 | %% API 9 | -export([start_link/0]). 10 | 11 | %% Supervisor callbacks 12 | -export([init/1]). 13 | 14 | -define(SERVER, ?MODULE). 15 | -define(IMPLEMENTATION_MODULE, ?MODULE). 16 | 17 | %%%====================================================================== 18 | %%% API 19 | %%%====================================================================== 20 | 21 | %% @doc Starts the supervisor. 22 | -spec start_link() -> {ok, pid()} | ignore | {error, term()}. 23 | start_link() -> 24 | supervisor:start_link({local, ?SERVER}, ?IMPLEMENTATION_MODULE, []). 25 | 26 | 27 | %%%====================================================================== 28 | %%% Behaviour Callback Functions 29 | %%%====================================================================== 30 | init([]) -> 31 | RestartStrategy = one_for_one, 32 | MaxRestarts = 1000, 33 | MaxSecondsBetweenRestarts = 3600, 34 | 35 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, 36 | 37 | Restart = permanent, 38 | Shutdown = 2000, 39 | Type = worker, 40 | 41 | AChild = {sc_server, {sc_server, start_link, []}, 42 | Restart, Shutdown, Type, [sc_server]}, 43 | 44 | {ok, {SupFlags, [AChild]}}. 45 | 46 | %%%=================================================================== 47 | %%% Internal functions 48 | %%%=================================================================== 49 | -------------------------------------------------------------------------------- /events_and_logs/simple_cache/src/simple_cache.app.src: -------------------------------------------------------------------------------- 1 | {application, simple_cache, 2 | [ 3 | {description, ""}, 4 | {vsn, "1"}, 5 | {registered, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib, 9 | lager 10 | ]}, 11 | {mod, { sc_app, []}}, 12 | {env, []} 13 | ]}. 14 | -------------------------------------------------------------------------------- /releases/simple_cache/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2012 Erlware, LLC. All Rights Reserved. 2 | # 3 | # This file is provided to you under the Apache License, 4 | # Version 2.0 (the "License"); you may not use this file 5 | # except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, 11 | # software distributed under the License is distributed on an 12 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | # KIND, either express or implied. See the License for the 14 | # specific language governing permissions and limitations 15 | # under the License. 16 | # 17 | 18 | ERLFLAGS= -pa $(CURDIR)/.eunit -pa $(CURDIR)/ebin -pa $(CURDIR)/deps/*/ebin 19 | 20 | DEPS_PLT=$(CURDIR)/.deps_plt 21 | 22 | # ============================================================================= 23 | # Verify that the programs we need to run are installed on this system 24 | # ============================================================================= 25 | ERL = $(shell which erl) 26 | 27 | ifeq ($(ERL),) 28 | $(error "Erlang not available on this system") 29 | endif 30 | 31 | REBAR=$(shell which rebar) 32 | 33 | ifeq ($(REBAR),) 34 | $(error "Rebar not available on this system") 35 | endif 36 | 37 | .PHONY: all compile doc clean test dialyzer typer shell distclean pdf \ 38 | clean-common-test-data 39 | 40 | all: compile test 41 | 42 | # ============================================================================= 43 | # Rules to build the system 44 | # ============================================================================= 45 | 46 | compile: 47 | $(REBAR) skip_deps=true compile 48 | 49 | 50 | doc: 51 | $(REBAR) skip_deps=true doc 52 | 53 | eunit: compile clean-common-test-data 54 | $(REBAR) skip_deps=true eunit 55 | 56 | ct: compile clean-common-test-data 57 | $(REBAR) skip_deps=true ct 58 | 59 | test: compile eunit ct 60 | 61 | $(DEPS_PLT): 62 | @echo "Building local plt" 63 | @echo 64 | dialyzer --output_plt $(DEPS_PLT) --build_plt \ 65 | --apps erts sasl kernel stdlib 66 | 67 | dialyzer: $(DEPS_PLT) 68 | dialyzer --plt $(DEPS_PLT) --fullpath -Wrace_conditions \ 69 | -I include -pa $(CURDIR)/ebin --src src 70 | 71 | typer: 72 | typer --plt $(DEPS_PLT) -r ./src 73 | 74 | shell: get-deps compile 75 | # You often want *rebuilt* rebar tests to be available to the 76 | # shell you have to call eunit (to get the tests 77 | # rebuilt). However, eunit runs the tests, which probably 78 | # fails (thats probably why You want them in the shell). This 79 | # runs eunit but tells make to ignore the result. 80 | - @$(REBAR) skip_deps=true eunit 81 | @$(ERL) $(ERLFLAGS) 82 | 83 | pdf: 84 | pandoc README.md -o README.pdf 85 | 86 | clean-common-test-data: 87 | # We have to do this because of the unique way we generate test 88 | # data. Without this rebar eunit gets very confused 89 | - rm -rf $(CURDIR)/test/*_SUITE_data 90 | 91 | clean: clean-common-test-data 92 | - rm -rf $(CURDIR)/test/*.beam 93 | - rm -rf $(CURDIR)/logs 94 | $(REBAR) skip_deps=true clean 95 | 96 | distclean: clean 97 | - rm -rf $(DEPS_PLT) 98 | - rm -rvf $(CURDIR)/deps/* 99 | -------------------------------------------------------------------------------- /releases/simple_cache/README: -------------------------------------------------------------------------------- 1 | To build this code, run the following command: 2 | 3 | erlc -o ./ebin ./src/*.erl 4 | 5 | To run the program, first start Erlang like this: 6 | 7 | erl -pa ./ebin 8 | 9 | Then, run the following in the Erlang shell: 10 | 11 | 1> application:start(sasl). 12 | ok 13 | 2> application:start(simple_cache). 14 | ok 15 | 3> 16 | -------------------------------------------------------------------------------- /releases/simple_cache/config/sys.config: -------------------------------------------------------------------------------- 1 | [ 2 | %% write log files to sasl_dir 3 | {sasl, 4 | [ 5 | {sasl_error_logger, 6 | {file, "/tmp/simple_cache.sasl_log"}} 7 | ]} 8 | ]. 9 | -------------------------------------------------------------------------------- /releases/simple_cache/include/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erlware/erlang-camp/a3130412d67ba618f34959f243bc3df2fb00f4c3/releases/simple_cache/include/.gitignore -------------------------------------------------------------------------------- /releases/simple_cache/rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*- 2 | {cover_enabled, true}. 3 | {cover_print_enabled, true}. 4 | 5 | {erl_opts, [debug_info, warnings_as_errors, inline]}. 6 | -------------------------------------------------------------------------------- /releases/simple_cache/relx.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*- 2 | {release, {simple_cache, "0.1.0"}, 3 | [simple_cache]}. 4 | -------------------------------------------------------------------------------- /releases/simple_cache/src/sc_app.erl: -------------------------------------------------------------------------------- 1 | -module(sc_app). 2 | 3 | -behaviour(application). 4 | 5 | -export([start/2, stop/1]). 6 | 7 | start(_StartType, _StartArgs) -> 8 | sc_store:init(), 9 | case sc_sup:start_link() of 10 | {ok, Pid} -> 11 | sc_event_logger:add_handler(), 12 | {ok, Pid}; 13 | Other -> 14 | {error, Other} 15 | end. 16 | 17 | stop(_State) -> 18 | ok. 19 | -------------------------------------------------------------------------------- /releases/simple_cache/src/sc_element.erl: -------------------------------------------------------------------------------- 1 | -module(sc_element). 2 | 3 | -behaviour(gen_server). 4 | 5 | -export([ 6 | start_link/2, 7 | create/1, 8 | create/2, 9 | fetch/1, 10 | replace/2, 11 | delete/1 12 | ]). 13 | 14 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 15 | terminate/2, code_change/3]). 16 | 17 | -define(SERVER, ?MODULE). 18 | -define(DEFAULT_LEASE_TIME, (60 * 60 * 24)). 19 | 20 | -record(state, {value, lease_time, start_time}). 21 | 22 | start_link(Value, LeaseTime) -> 23 | gen_server:start_link(?MODULE, [Value, LeaseTime], []). 24 | 25 | create(Value, LeaseTime) -> 26 | sc_element_sup:start_child(Value, LeaseTime). 27 | 28 | create(Value) -> 29 | create(Value, ?DEFAULT_LEASE_TIME). 30 | 31 | fetch(Pid) -> 32 | gen_server:call(Pid, fetch). 33 | 34 | replace(Pid, Value) -> 35 | gen_server:cast(Pid, {replace, Value}). 36 | 37 | delete(Pid) -> 38 | gen_server:cast(Pid, delete). 39 | 40 | init([Value, LeaseTime]) -> 41 | Now = calendar:local_time(), 42 | StartTime = calendar:datetime_to_gregorian_seconds(Now), 43 | {ok, 44 | #state{value = Value, 45 | lease_time = LeaseTime, 46 | start_time = StartTime}, 47 | time_left(StartTime, LeaseTime)}. 48 | 49 | time_left(_StartTime, infinity) -> 50 | infinity; 51 | time_left(StartTime, LeaseTime) -> 52 | Now = calendar:local_time(), 53 | CurrentTime = calendar:datetime_to_gregorian_seconds(Now), 54 | TimeElapsed = CurrentTime - StartTime, 55 | case LeaseTime - TimeElapsed of 56 | Time when Time =< 0 -> 0; 57 | Time -> Time * 1000 58 | end. 59 | 60 | handle_call(fetch, _From, State) -> 61 | #state{value = Value, 62 | lease_time = LeaseTime, 63 | start_time = StartTime} = State, 64 | TimeLeft = time_left(StartTime, LeaseTime), 65 | {reply, {ok, Value}, State, TimeLeft}. 66 | 67 | handle_cast({replace, Value}, State) -> 68 | #state{lease_time = LeaseTime, 69 | start_time = StartTime} = State, 70 | TimeLeft = time_left(StartTime, LeaseTime), 71 | {noreply, State#state{value = Value}, TimeLeft}; 72 | handle_cast(delete, State) -> 73 | {stop, normal, State}. 74 | 75 | handle_info(timeout, State) -> 76 | {stop, normal, State}. 77 | 78 | terminate(_Reason, _State) -> 79 | sc_store:delete(self()), 80 | ok. 81 | 82 | code_change(_OldVsn, State, _Extra) -> 83 | {ok, State}. 84 | -------------------------------------------------------------------------------- /releases/simple_cache/src/sc_element_sup.erl: -------------------------------------------------------------------------------- 1 | -module(sc_element_sup). 2 | 3 | -behaviour(supervisor). 4 | 5 | -export([start_link/0, 6 | start_child/2 7 | ]). 8 | 9 | -export([init/1]). 10 | 11 | -define(SERVER, ?MODULE). 12 | 13 | start_link() -> 14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 15 | 16 | start_child(Value, LeaseTime) -> 17 | supervisor:start_child(?SERVER, [Value, LeaseTime]). 18 | 19 | init([]) -> 20 | Element = {sc_element, {sc_element, start_link, []}, 21 | temporary, brutal_kill, worker, [sc_element]}, 22 | Children = [Element], 23 | RestartStrategy = {simple_one_for_one, 0, 1}, 24 | {ok, {RestartStrategy, Children}}. 25 | -------------------------------------------------------------------------------- /releases/simple_cache/src/sc_event.erl: -------------------------------------------------------------------------------- 1 | -module(sc_event). 2 | 3 | -export([start_link/0, 4 | add_handler/2, 5 | delete_handler/2, 6 | lookup/1, 7 | create/2, 8 | replace/2, 9 | delete/1]). 10 | 11 | -define(SERVER, ?MODULE). 12 | 13 | start_link() -> 14 | gen_event:start_link({local, ?SERVER}). 15 | 16 | add_handler(Handler, Args) -> 17 | gen_event:add_handler(?SERVER, Handler, Args). 18 | 19 | delete_handler(Handler, Args) -> 20 | gen_event:delete_handler(?SERVER, Handler, Args). 21 | 22 | lookup(Key) -> 23 | gen_event:notify(?SERVER, {lookup, Key}). 24 | 25 | create(Key, Value) -> 26 | gen_event:notify(?SERVER, {create, {Key, Value}}). 27 | 28 | replace(Key, Value) -> 29 | gen_event:notify(?SERVER, {replace, {Key, Value}}). 30 | 31 | delete(Key) -> 32 | gen_event:notify(?SERVER, {delete, Key}). 33 | -------------------------------------------------------------------------------- /releases/simple_cache/src/sc_event_logger.erl: -------------------------------------------------------------------------------- 1 | -module(sc_event_logger). 2 | 3 | -behaviour(gen_event). 4 | 5 | -export([add_handler/0, delete_handler/0]). 6 | 7 | -export([init/1, handle_event/2, handle_call/2, 8 | handle_info/2, code_change/3, terminate/2]). 9 | 10 | -record(state, {}). 11 | 12 | add_handler() -> 13 | sc_event:add_handler(?MODULE, []). 14 | 15 | delete_handler() -> 16 | sc_event:delete_handler(?MODULE, []). 17 | 18 | init([]) -> 19 | {ok, #state{}}. 20 | 21 | handle_event({create, {Key, Value}}, State) -> 22 | error_logger:info_msg("create(~w, ~w)~n", [Key, Value]), 23 | {ok, State}; 24 | handle_event({lookup, Key}, State) -> 25 | error_logger:info_msg("lookup(~w)~n", [Key]), 26 | {ok, State}; 27 | handle_event({delete, Key}, State) -> 28 | error_logger:info_msg("delete(~w)~n", [Key]), 29 | {ok, State}; 30 | handle_event({replace, {Key, Value}}, State) -> 31 | error_logger:info_msg("replace(~w, ~w)~n", [Key, Value]), 32 | {ok, State}. 33 | 34 | handle_call(_Request, State) -> 35 | Reply = ok, 36 | {ok, Reply, State}. 37 | 38 | handle_info(_Info, State) -> 39 | {ok, State}. 40 | 41 | terminate(_Reason, _State) -> 42 | ok. 43 | 44 | code_change(_OldVsn, State, _Extra) -> 45 | {ok, State}. 46 | -------------------------------------------------------------------------------- /releases/simple_cache/src/sc_store.erl: -------------------------------------------------------------------------------- 1 | -module(sc_store). 2 | 3 | -export([ 4 | init/0, 5 | insert/2, 6 | delete/1, 7 | lookup/1 8 | ]). 9 | 10 | -define(TABLE_ID, ?MODULE). 11 | 12 | init() -> 13 | ets:new(?TABLE_ID, [public, named_table]), 14 | ok. 15 | 16 | insert(Key, Pid) -> 17 | ets:insert(?TABLE_ID, {Key, Pid}). 18 | 19 | lookup(Key) -> 20 | case ets:lookup(?TABLE_ID, Key) of 21 | [{Key, Pid}] -> {ok, Pid}; 22 | [] -> {error, not_found} 23 | end. 24 | 25 | delete(Pid) -> 26 | ets:match_delete(?TABLE_ID, {'_', Pid}). 27 | -------------------------------------------------------------------------------- /releases/simple_cache/src/sc_sup.erl: -------------------------------------------------------------------------------- 1 | -module(sc_sup). 2 | 3 | -behaviour(supervisor). 4 | 5 | %% API 6 | -export([start_link/0]). 7 | 8 | %% Supervisor callbacks 9 | -export([init/1]). 10 | 11 | -define(SERVER, ?MODULE). 12 | 13 | start_link() -> 14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 15 | 16 | init([]) -> 17 | ElementSup = {sc_element_sup, {sc_element_sup, start_link, []}, 18 | permanent, 2000, supervisor, [sc_element]}, 19 | 20 | EventManager = {sc_event, {sc_event, start_link, []}, 21 | permanent, 2000, worker, [sc_event]}, 22 | 23 | Children = [ElementSup, EventManager], 24 | RestartStrategy = {one_for_one, 4, 3600}, 25 | {ok, {RestartStrategy, Children}}. 26 | -------------------------------------------------------------------------------- /releases/simple_cache/src/simple_cache.app.src: -------------------------------------------------------------------------------- 1 | {application, simple_cache, 2 | [{description, "A simple caching system"}, 3 | {vsn, "0.1.0"}, 4 | {modules, []}, 5 | {registered, [sc_sup]}, 6 | {applications, [kernel, sasl, stdlib]}, 7 | {mod, {sc_app, []}} 8 | ]}. 9 | -------------------------------------------------------------------------------- /releases/simple_cache/src/simple_cache.erl: -------------------------------------------------------------------------------- 1 | -module(simple_cache). 2 | 3 | -export([insert/2, lookup/1, delete/1]). 4 | 5 | insert(Key, Value) -> 6 | case sc_store:lookup(Key) of 7 | {ok, Pid} -> 8 | sc_event:replace(Key, Value), 9 | sc_element:replace(Pid, Value); 10 | {error, _} -> 11 | {ok, Pid} = sc_element:create(Value), 12 | sc_store:insert(Key, Pid), 13 | sc_event:create(Key, Value) 14 | end. 15 | 16 | lookup(Key) -> 17 | sc_event:lookup(Key), 18 | try 19 | {ok, Pid} = sc_store:lookup(Key), 20 | {ok, Value} = sc_element:fetch(Pid), 21 | {ok, Value} 22 | catch 23 | _Class:_Exception -> 24 | {error, not_found} 25 | end. 26 | 27 | delete(Key) -> 28 | sc_event:delete(Key), 29 | case sc_store:lookup(Key) of 30 | {ok, Pid} -> 31 | sc_element:delete(Pid); 32 | {error, _Reason} -> 33 | ok 34 | end. 35 | -------------------------------------------------------------------------------- /releases/tester/rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*- 2 | {cover_enabled, true}. 3 | {cover_print_enabled, true}. 4 | 5 | {erl_opts, [debug_info, warnings_as_errors, inline]}. 6 | -------------------------------------------------------------------------------- /releases/tester/src/tester.app.src: -------------------------------------------------------------------------------- 1 | {application, tester, 2 | [ 3 | {description, ""}, 4 | {vsn, "0.0.1"}, 5 | {registered, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib, 9 | sasl 10 | ]}, 11 | {mod, { tester_app, []}}, 12 | {env, []} 13 | ]}. 14 | -------------------------------------------------------------------------------- /releases/tester/src/tester_app.erl: -------------------------------------------------------------------------------- 1 | -module(tester_app). 2 | 3 | -behaviour(application). 4 | 5 | %% Application callbacks 6 | -export([start/2, stop/1]). 7 | 8 | %% =================================================================== 9 | %% Application callbacks 10 | %% =================================================================== 11 | 12 | start(_StartType, _StartArgs) -> 13 | tester_sup:start_link(). 14 | 15 | stop(_State) -> 16 | ok. 17 | -------------------------------------------------------------------------------- /releases/tester/src/tester_sup.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(tester_sup). 3 | 4 | -behaviour(supervisor). 5 | 6 | %% API 7 | -export([start_link/0]). 8 | 9 | %% Supervisor callbacks 10 | -export([init/1]). 11 | 12 | %% Helper macro for declaring children of supervisor 13 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 14 | 15 | %% =================================================================== 16 | %% API functions 17 | %% =================================================================== 18 | 19 | start_link() -> 20 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 21 | 22 | %% =================================================================== 23 | %% Supervisor callbacks 24 | %% =================================================================== 25 | 26 | init([]) -> 27 | {ok, { {one_for_one, 5, 10}, []} }. 28 | 29 | -------------------------------------------------------------------------------- /testing/simple_cache/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2012 Erlware, LLC. All Rights Reserved. 2 | # 3 | # This file is provided to you under the Apache License, 4 | # Version 2.0 (the "License"); you may not use this file 5 | # except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, 11 | # software distributed under the License is distributed on an 12 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | # KIND, either express or implied. See the License for the 14 | # specific language governing permissions and limitations 15 | # under the License. 16 | # 17 | 18 | ERLFLAGS= -pa $(CURDIR)/.eunit -pa $(CURDIR)/ebin -pa $(CURDIR)/deps/*/ebin 19 | 20 | DEPS_PLT=$(CURDIR)/.deps_plt 21 | 22 | # ============================================================================= 23 | # Verify that the programs we need to run are installed on this system 24 | # ============================================================================= 25 | ERL = $(shell which erl) 26 | 27 | ifeq ($(ERL),) 28 | $(error "Erlang not available on this system") 29 | endif 30 | 31 | REBAR=$(shell which rebar) 32 | 33 | ifeq ($(REBAR),) 34 | $(error "Rebar not available on this system") 35 | endif 36 | 37 | .PHONY: all compile doc clean test dialyzer typer shell distclean pdf \ 38 | clean-common-test-data 39 | 40 | all: compile test 41 | 42 | # ============================================================================= 43 | # Rules to build the system 44 | # ============================================================================= 45 | 46 | compile: 47 | $(REBAR) skip_deps=true compile 48 | 49 | 50 | doc: 51 | $(REBAR) skip_deps=true doc 52 | 53 | eunit: compile clean-common-test-data 54 | $(REBAR) skip_deps=true eunit 55 | 56 | ct: compile clean-common-test-data 57 | mkdir -p $(CURDIR)/logs 58 | mkdir -p $(CURDIR)/test 59 | ct_run -pa $(CURDIR)/ebin -dir $(CURDIR)/test -logdir $(CURDIR)/logs 60 | 61 | test: compile eunit ct 62 | 63 | $(DEPS_PLT): 64 | @echo "Building local plt" 65 | @echo 66 | dialyzer --output_plt $(DEPS_PLT) --build_plt \ 67 | --apps erts sasl kernel stdlib 68 | 69 | dialyzer: $(DEPS_PLT) 70 | dialyzer --plt $(DEPS_PLT) --fullpath -Wrace_conditions \ 71 | -I include -pa $(CURDIR)/ebin --src src 72 | 73 | typer: 74 | typer --plt $(DEPS_PLT) -r ./src 75 | 76 | shell: compile 77 | # You often want *rebuilt* rebar tests to be available to the 78 | # shell you have to call eunit (to get the tests 79 | # rebuilt). However, eunit runs the tests, which probably 80 | # fails (thats probably why You want them in the shell). This 81 | # runs eunit but tells make to ignore the result. 82 | - @$(REBAR) skip_deps=true eunit 83 | @$(ERL) $(ERLFLAGS) 84 | 85 | pdf: 86 | pandoc README.md -o README.pdf 87 | 88 | clean-common-test-data: 89 | # We have to do this because of the unique way we generate test 90 | # data. Without this rebar eunit gets very confused 91 | - rm -rf $(CURDIR)/test/*_SUITE_data 92 | 93 | clean: clean-common-test-data 94 | - rm -rf $(CURDIR)/test/*.beam 95 | - rm -rf $(CURDIR)/logs 96 | $(REBAR) skip_deps=true clean 97 | 98 | distclean: clean 99 | - rm -rf $(DEPS_PLT) 100 | - rm -rvf $(CURDIR)/deps/* 101 | -------------------------------------------------------------------------------- /testing/simple_cache/README: -------------------------------------------------------------------------------- 1 | To build this code, run the following command: 2 | 3 | erlc -o ./ebin ./src/*.erl 4 | 5 | To run the program, first start Erlang like this: 6 | 7 | erl -pa ./ebin 8 | 9 | Then, run the following in the Erlang shell: 10 | 11 | 1> application:start(sasl). 12 | ok 13 | 2> application:start(simple_cache). 14 | ok 15 | 3> 16 | -------------------------------------------------------------------------------- /testing/simple_cache/config/sys.config: -------------------------------------------------------------------------------- 1 | [ 2 | %% write log files to sasl_dir 3 | {sasl, 4 | [ 5 | {sasl_error_logger, 6 | {file, "/tmp/simple_cache.sasl_log"}} 7 | ]} 8 | ]. 9 | -------------------------------------------------------------------------------- /testing/simple_cache/doc/.gitignore: -------------------------------------------------------------------------------- 1 | edoc-info 2 | stylesheet.css 3 | erlang.png 4 | *.html 5 | -------------------------------------------------------------------------------- /testing/simple_cache/ebin/.gitignore: -------------------------------------------------------------------------------- 1 | *.beam 2 | -------------------------------------------------------------------------------- /testing/simple_cache/ebin/simple_cache.app: -------------------------------------------------------------------------------- 1 | {application,simple_cache, 2 | [{description,"A simple caching system"}, 3 | {vsn,"0.1.0"}, 4 | {modules,[sc_app,sc_element,sc_element_sup,sc_event, 5 | sc_event_logger,sc_store,sc_sup,simple_cache]}, 6 | {registered,[sc_sup]}, 7 | {applications,[kernel,stdlib]}, 8 | {mod,{sc_app,[]}}]}. 9 | -------------------------------------------------------------------------------- /testing/simple_cache/include/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erlware/erlang-camp/a3130412d67ba618f34959f243bc3df2fb00f4c3/testing/simple_cache/include/.gitignore -------------------------------------------------------------------------------- /testing/simple_cache/priv/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erlware/erlang-camp/a3130412d67ba618f34959f243bc3df2fb00f4c3/testing/simple_cache/priv/.gitignore -------------------------------------------------------------------------------- /testing/simple_cache/rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*- 2 | {cover_enabled, true}. 3 | {cover_print_enabled, true}. 4 | 5 | {erl_opts, [debug_info, warnings_as_errors, inline]}. 6 | -------------------------------------------------------------------------------- /testing/simple_cache/relx.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*- 2 | {release, {simple_cache, "0.1.0"}, 3 | [simple_cache]}. 4 | -------------------------------------------------------------------------------- /testing/simple_cache/src/sc_app.erl: -------------------------------------------------------------------------------- 1 | -module(sc_app). 2 | 3 | -behaviour(application). 4 | 5 | -export([start/2, stop/1]). 6 | 7 | start(_StartType, _StartArgs) -> 8 | case sc_sup:start_link() of 9 | {ok, Pid} -> 10 | sc_event_logger:add_handler(), 11 | {ok, Pid}; 12 | Other -> 13 | {error, Other} 14 | end. 15 | 16 | stop(_State) -> 17 | ok. 18 | -------------------------------------------------------------------------------- /testing/simple_cache/src/sc_element.erl: -------------------------------------------------------------------------------- 1 | -module(sc_element). 2 | 3 | -behaviour(gen_server). 4 | 5 | -export([ 6 | start_link/2, 7 | create/1, 8 | create/2, 9 | fetch/1, 10 | replace/2, 11 | delete/1 12 | ]). 13 | 14 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 15 | terminate/2, code_change/3]). 16 | 17 | -define(SERVER, ?MODULE). 18 | -define(DEFAULT_LEASE_TIME, (60 * 60 * 24)). 19 | 20 | -record(state, {value, lease_time, start_time}). 21 | 22 | start_link(Value, LeaseTime) -> 23 | gen_server:start_link(?MODULE, [Value, LeaseTime], []). 24 | 25 | create(Value, LeaseTime) -> 26 | sc_element_sup:start_child(Value, LeaseTime). 27 | 28 | create(Value) -> 29 | create(Value, ?DEFAULT_LEASE_TIME). 30 | 31 | fetch(Pid) -> 32 | gen_server:call(Pid, fetch). 33 | 34 | replace(Pid, Value) -> 35 | gen_server:cast(Pid, {replace, Value}). 36 | 37 | delete(Pid) -> 38 | gen_server:cast(Pid, delete). 39 | 40 | init([Value, LeaseTime]) -> 41 | Now = calendar:local_time(), 42 | StartTime = calendar:datetime_to_gregorian_seconds(Now), 43 | {ok, 44 | #state{value = Value, 45 | lease_time = LeaseTime, 46 | start_time = StartTime}, 47 | time_left(StartTime, LeaseTime)}. 48 | 49 | time_left(_StartTime, infinity) -> 50 | infinity; 51 | time_left(StartTime, LeaseTime) -> 52 | Now = calendar:local_time(), 53 | CurrentTime = calendar:datetime_to_gregorian_seconds(Now), 54 | TimeElapsed = CurrentTime - StartTime, 55 | case LeaseTime - TimeElapsed of 56 | Time when Time =< 0 -> 0; 57 | Time -> Time * 1000 58 | end. 59 | 60 | handle_call(fetch, _From, State) -> 61 | #state{value = Value, 62 | lease_time = LeaseTime, 63 | start_time = StartTime} = State, 64 | TimeLeft = time_left(StartTime, LeaseTime), 65 | {reply, {ok, Value}, State, TimeLeft}. 66 | 67 | handle_cast({replace, Value}, State) -> 68 | #state{lease_time = LeaseTime, 69 | start_time = StartTime} = State, 70 | TimeLeft = time_left(StartTime, LeaseTime), 71 | {noreply, State#state{value = Value}, TimeLeft}; 72 | handle_cast(delete, State) -> 73 | {stop, normal, State}. 74 | 75 | handle_info(timeout, State) -> 76 | {stop, normal, State}. 77 | 78 | terminate(_Reason, _State) -> 79 | sc_store:delete(self()), 80 | ok. 81 | 82 | code_change(_OldVsn, State, _Extra) -> 83 | {ok, State}. 84 | -------------------------------------------------------------------------------- /testing/simple_cache/src/sc_element_sup.erl: -------------------------------------------------------------------------------- 1 | -module(sc_element_sup). 2 | 3 | -behaviour(supervisor). 4 | 5 | -export([start_link/0, 6 | start_child/2 7 | ]). 8 | 9 | -export([init/1]). 10 | 11 | -define(SERVER, ?MODULE). 12 | 13 | start_link() -> 14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 15 | 16 | start_child(Value, LeaseTime) -> 17 | supervisor:start_child(?SERVER, [Value, LeaseTime]). 18 | 19 | init([]) -> 20 | Element = {sc_element, {sc_element, start_link, []}, 21 | temporary, brutal_kill, worker, [sc_element]}, 22 | Children = [Element], 23 | RestartStrategy = {simple_one_for_one, 0, 1}, 24 | {ok, {RestartStrategy, Children}}. 25 | -------------------------------------------------------------------------------- /testing/simple_cache/src/sc_event.erl: -------------------------------------------------------------------------------- 1 | -module(sc_event). 2 | 3 | -export([start_link/0, 4 | add_handler/2, 5 | delete_handler/2, 6 | lookup/1, 7 | create/2, 8 | replace/2, 9 | delete/1]). 10 | 11 | -define(SERVER, ?MODULE). 12 | 13 | start_link() -> 14 | gen_event:start_link({local, ?SERVER}). 15 | 16 | add_handler(Handler, Args) -> 17 | gen_event:add_handler(?SERVER, Handler, Args). 18 | 19 | delete_handler(Handler, Args) -> 20 | gen_event:delete_handler(?SERVER, Handler, Args). 21 | 22 | lookup(Key) -> 23 | gen_event:notify(?SERVER, {lookup, Key}). 24 | 25 | create(Key, Value) -> 26 | gen_event:notify(?SERVER, {create, {Key, Value}}). 27 | 28 | replace(Key, Value) -> 29 | gen_event:notify(?SERVER, {replace, {Key, Value}}). 30 | 31 | delete(Key) -> 32 | gen_event:notify(?SERVER, {delete, Key}). 33 | -------------------------------------------------------------------------------- /testing/simple_cache/src/sc_event_logger.erl: -------------------------------------------------------------------------------- 1 | -module(sc_event_logger). 2 | 3 | -behaviour(gen_event). 4 | 5 | -export([add_handler/0, delete_handler/0]). 6 | 7 | -export([init/1, handle_event/2, handle_call/2, 8 | handle_info/2, code_change/3, terminate/2]). 9 | 10 | -record(state, {}). 11 | 12 | add_handler() -> 13 | sc_event:add_handler(?MODULE, []). 14 | 15 | delete_handler() -> 16 | sc_event:delete_handler(?MODULE, []). 17 | 18 | init([]) -> 19 | {ok, #state{}}. 20 | 21 | handle_event({create, {Key, Value}}, State) -> 22 | error_logger:info_msg("create(~w, ~w)~n", [Key, Value]), 23 | {ok, State}; 24 | handle_event({lookup, Key}, State) -> 25 | error_logger:info_msg("lookup(~w)~n", [Key]), 26 | {ok, State}; 27 | handle_event({delete, Key}, State) -> 28 | error_logger:info_msg("delete(~w)~n", [Key]), 29 | {ok, State}; 30 | handle_event({replace, {Key, Value}}, State) -> 31 | error_logger:info_msg("replace(~w, ~w)~n", [Key, Value]), 32 | {ok, State}. 33 | 34 | handle_call(_Request, State) -> 35 | Reply = ok, 36 | {ok, Reply, State}. 37 | 38 | handle_info(_Info, State) -> 39 | {ok, State}. 40 | 41 | terminate(_Reason, _State) -> 42 | ok. 43 | 44 | code_change(_OldVsn, State, _Extra) -> 45 | {ok, State}. 46 | -------------------------------------------------------------------------------- /testing/simple_cache/src/sc_store.erl: -------------------------------------------------------------------------------- 1 | -module(sc_store). 2 | 3 | -export([ 4 | init/0, 5 | insert/3, 6 | delete/2, 7 | lookup/2 8 | ]). 9 | init() -> 10 | dict:new(). 11 | 12 | insert(Store, Key, Value) -> 13 | dict:store(Key, Value, Store). 14 | 15 | lookup(Store, Key) -> 16 | dict:find(Key, Store). 17 | 18 | delete(Store, Key) -> 19 | dict:erase(Key, Store). 20 | -------------------------------------------------------------------------------- /testing/simple_cache/src/sc_sup.erl: -------------------------------------------------------------------------------- 1 | -module(sc_sup). 2 | 3 | -behaviour(supervisor). 4 | 5 | %% API 6 | -export([start_link/0]). 7 | 8 | %% Supervisor callbacks 9 | -export([init/1]). 10 | 11 | -define(SERVER, ?MODULE). 12 | 13 | start_link() -> 14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 15 | 16 | init([]) -> 17 | ElementSup = {sc_element_sup, {sc_element_sup, start_link, []}, 18 | permanent, 2000, supervisor, [sc_element]}, 19 | 20 | SimpleCache = {simple_cache, {simple_cache, start_link, []}, 21 | permanent, 2000, worker, [simple_cache]}, 22 | 23 | SCEvent = {simple_cache_event, {sc_event, start_link, []}, 24 | permanent, 2000, worker, [sc_event]}, 25 | 26 | 27 | RestartStrategy = {one_for_one, 4, 3600}, 28 | 29 | {ok, {RestartStrategy, [ElementSup, SCEvent, SimpleCache]}}. 30 | -------------------------------------------------------------------------------- /testing/simple_cache/src/simple_cache.app.src: -------------------------------------------------------------------------------- 1 | {application, simple_cache, 2 | [{description, "A simple caching system"}, 3 | {vsn, "0.1.0"}, 4 | {modules, []}, 5 | {registered, [sc_sup]}, 6 | {applications, [kernel, stdlib]}, 7 | {mod, {sc_app, []}} 8 | ]}. 9 | -------------------------------------------------------------------------------- /testing/simple_cache/src/simple_cache.erl: -------------------------------------------------------------------------------- 1 | -module(simple_cache). 2 | 3 | -behaviour(gen_server). 4 | 5 | %% API 6 | -export([start_link/0, insert/2, lookup/1, delete/1]). 7 | 8 | %% gen_server callbacks 9 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 10 | terminate/2, code_change/3]). 11 | 12 | -define(SERVER, ?MODULE). 13 | 14 | -record(state, {store}). 15 | 16 | %%%=================================================================== 17 | %%% API 18 | %%%=================================================================== 19 | 20 | start_link() -> 21 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 22 | 23 | insert(Key, Value) -> 24 | gen_server:cast(?SERVER, {insert, Key, Value}). 25 | 26 | lookup(Key) -> 27 | gen_server:call(?SERVER, {lookup, Key}). 28 | 29 | delete(Key) -> 30 | gen_server:cast(?SERVER, {delete, Key}). 31 | 32 | 33 | %%%=================================================================== 34 | %%% gen_server callbacks 35 | %%%=================================================================== 36 | 37 | %% @private 38 | init([]) -> 39 | {ok, #state{store=sc_store:init()}}. 40 | 41 | %% @private 42 | handle_call({lookup, Key}, _From, State = #state{store=Store}) -> 43 | sc_event:lookup(Key), 44 | Reply = try 45 | {ok, Pid} = sc_store:lookup(Store, Key), 46 | {ok, Value} = sc_element:fetch(Pid), 47 | {ok, Value} 48 | catch 49 | _Class:_Exception -> 50 | {error, not_found} 51 | end, 52 | {reply, Reply, State}. 53 | 54 | %% @private 55 | handle_cast({insert, Key, Value}, State = #state{store=Store0}) -> 56 | Store1 = case sc_store:lookup(Store0, Key) of 57 | {ok, Pid} -> 58 | sc_event:replace(Key, Value), 59 | sc_element:replace(Pid, Value), 60 | Store0; 61 | error -> 62 | {ok, Pid} = sc_element:create(Value), 63 | sc_event:create(Key, Value), 64 | sc_store:insert(Store0, Key, Pid) 65 | end, 66 | {noreply, State#state{store=Store1}}; 67 | handle_cast({delete, Key}, State = #state{store=Store}) -> 68 | sc_event:delete(Key), 69 | case sc_store:lookup(Store, Key) of 70 | {ok, Pid} -> 71 | sc_element:delete(Pid); 72 | error -> 73 | ok 74 | end, 75 | {noreply, State#state{store=sc_store:delete(Store, Key)}}. 76 | 77 | %% @private 78 | handle_info(_Info, State) -> 79 | {noreply, State}. 80 | 81 | %% @private 82 | terminate(_Reason, _State) -> 83 | ok. 84 | 85 | %% @private 86 | code_change(_OldVsn, State, _Extra) -> 87 | {ok, State}. 88 | 89 | %%%=================================================================== 90 | %%% Internal functions 91 | %%%=================================================================== 92 | -------------------------------------------------------------------------------- /testing/templates/common_test_tmpl.erl: -------------------------------------------------------------------------------- 1 | -module(common_test_tmpl). 2 | 3 | -include_lib("common_test/include/ct.hrl"). 4 | 5 | suite() -> 6 | [{timetrap,{seconds,30}}]. 7 | 8 | init_per_suite(Config) -> 9 | Config. 10 | 11 | end_per_suite(_Config) -> 12 | ok. 13 | 14 | all() -> 15 | [my_test_case]. 16 | 17 | my_test_case(_Config) -> 18 | ok. 19 | -------------------------------------------------------------------------------- /testing/templates/eunit_tmpl.erl: -------------------------------------------------------------------------------- 1 | %%%=================================================================== 2 | %%% Test Functions 3 | %%%=================================================================== 4 | 5 | -ifndef(NOTEST). 6 | -include_lib("eunit/include/eunit.hrl"). 7 | 8 | my_test() -> 9 | ok. 10 | 11 | my_generator_test_() -> 12 | {setup, fun setup_fun/0, fun cleanup_fun/1, 13 | [fun my_test_fun/1]}. 14 | 15 | -endif. 16 | --------------------------------------------------------------------------------