<div dir="ltr"><div>Hi,</div><div><br></div><div>Are you trying to do something like this (just an example)? </div><div><br></div><div>HTH,</div><div>/s</div><div><br></div><div>=====</div><div>% -*- mode:erlang -*-</div><div>{application, my_db, [</div><div> {description, "my_db"},</div><div> {vsn, "0.1"},</div><div> {mod, {my_db_app, []}},</div><div> {env, [</div><div> {my_db, my_simple_db}</div><div> ]},</div><div> {modules, [</div><div> my_db, my_db_app, my_db_sup</div><div> ]},</div><div> {applications, [kernel, stdlib]}</div><div>]}.</div><div>=====</div><div>-module(my_db).</div><div><br></div><div>-export([start/0, stop/0]). </div><div>-export([create/1, read/1, update/1, delete/1]).</div><div><br></div><div>start() -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>application:start(my_db).</div><div>stop() -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>application:stop(my_db).</div><div><br></div><div>create({K, V}) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>gen_server:call(?MODULE, {create, K, V}).</div><div>read(K) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>gen_server:call(?MODULE, {read, K}).</div><div>update({K, V}) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>gen_server:call(?MODULE, {update, K, V}).</div><div>delete(K) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>gen_server:call(?MODULE, {delete, K}).</div><div>====</div><div><div>-module(my_db_app).</div><div><br></div><div>-define(TTY(X), io:format("~p~n", [X])).</div><div><br></div><div>-behaviour(application).</div><div>-export([start/2, stop/1]).</div><div><br></div><div>start(_, _) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{ok, DbModule} = application:get_env(my_db),</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>?TTY(DbModule),</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>my_db_sup:start_link([DbModule]).</div><div><br></div><div>stop(State) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>State.</div></div><div>====</div><div><div>-module(my_db_sup).</div><div><br></div><div>-export([start_link/1]).</div><div><br></div><div>-behaviour(supervisor).</div><div>-export([init/1]).</div><div><br></div><div>%%</div><div>start_link(Opts) -></div><div> supervisor:start_link({local, ?MODULE}, ?MODULE, Opts).</div><div><br></div><div>%%</div><div>init([DbModule]) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>Server = {my_db, {DbModule, start_link, [[]]}, permanent, 2000, worker, []}, </div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{ok, {{one_for_all, 0, 1}, [Server]}}.</div></div><div>====</div><div><div>-module(my_simple_db).</div><div><br></div><div>-export([start_link/1]).</div><div><br></div><div>-behaviour(gen_server).</div><div>-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).</div><div><br></div><div>start_link(Opts) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>gen_server:start_link({local, my_db}, ?MODULE, Opts, []).</div><div><br></div><div>init(_) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{ok, dict:new()}.</div><div><br></div><div>handle_call({create, K, V}, _, Db) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>case dict:is_key(K, Db) of</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>false -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{reply, {ok, K}, dict:store(K, V, Db)};</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>true -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{reply, {error, {exists, K}}, Db}</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>end;</div><div>handle_call({read, K}, _, Db) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>case dict:find(K, Db) of</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{ok, V} -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{reply, {ok, {K, V}}, Db};</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>error -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{reply, {error, not_found}, Db}</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>end;</div><div>handle_call({update, K, V}, _, Db) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>case dict:is_key(K, Db) of</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>true -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{reply, {ok, K}, dict:store(K, V, Db)};</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>false -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{reply, {error, not_found}, Db}</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>end;</div><div>handle_call({delete, K}, _, Db) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>case dict:is_key(K, Db) of</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>true -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{reply, {ok, K}, dict:erase(K, Db)};</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>false -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{reply, {error, not_found, K}, Db}</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>end;</div><div>handle_call(_, _, Db) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{reply, error, Db}.</div><div><br></div><div>handle_cast(_, State) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{noreply, State}.</div><div><br></div><div>handle_info(_, State) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{noreply, State}.</div><div><br></div><div>terminate(_, _State) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>ok.</div><div><br></div><div>code_change(_, State, _) -></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{ok, State}.</div></div><div>====</div>On Saturday, August 10, 2013 11:53:10 AM UTC-5, H.C. v. Stockhausen wrote:<blockquote class="gmail_quote" style="margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">Hi,
<br>
<br>thank you for all your replies. I appreciate your help.
<br>
<br>The DB example, even if applicable to my use case, was perhaps a
<br>little too specific. I don't necessarily need to abstract at a CRUD
<br>level - a higher business level would work too. What I wanted to learn
<br>is how to swap out implementations (at whatever level really), so
<br>thank you for your samples, explanations and advise.
<br>
<br>I am happy though to have also learned that my initial ideaI wasn't
<br>too bad. If I wanted to allow third parties to substitute their
<br>implementations for mine (possibly in a closed source product even?)
<br>it could be an option.
<br>
<br>Best regards,
<br>Hans
<br>
<br>On 9 August 2013 19:23, Bohuslav Svancara <<a href="javascript:" target="_blank" gdf-obfuscated-mailto="bAqYHGHEEigJ">bsva...@gmail.com</a>> wrote:
<br>> Hello!
<br>>
<br>> I tried ContextErlang as an exercise:
<br>> <a href="http://www.guidosalvaneschi.com/wp/software/contexterlang/" target="_blank">http://www.guidosalvaneschi.<wbr>com/wp/software/contexterlang/</a>
<br>>
<br>> The "context programming" concept can be applied very well here (I think).
<br>>
<br>> Here is an example what I am doing.
<br>>
<br>> (1)
<br>> The "base" module "mt4_db_interface" implements MySql operations using emysql.
<br>> It also includes a definition for "context switch". (See
<br>> mt4_db_interface_context/0 ):
<br>>
<br>> -module(mt4_db_interface).
<br>>
<br>> -behaviour(context_agent).
<br>>
<br>> % ContextErlang
<br>> -context_call([connect/5,get_<wbr>c2_id_for_mt4_id/2,update_c2_<wbr>id_for_mt4_id/3,delete/2]).
<br>> -context_cast([]).
<br>>
<br>> %% gen_server callbacks
<br>> -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
<br>> terminate/2, code_change/3]).
<br>>
<br>> %% External exports
<br>> -export([connect/5,get_c2_id_<wbr>for_mt4_id/2,delete/2,update_<wbr>c2_id_for_mt4_id/3,start_link/<wbr>0]).
<br>>
<br>> % ContextErlang
<br>> -include("../../ContextErlang/<wbr>include/context_agent_api.hrl"<wbr>).
<br>>
<br>> -define(PoolId,mt4). % a definition for emysql:add_pool(...)
<br>>
<br>> -record(mt4_db_interface_<wbr>state, {dbHandle,
<br>> dummy_database_for_tests=dict:<wbr>new()}). % in the records.hrl in the
<br>> real program
<br>>
<br>> start_link() -> context_agent:start_link({<wbr>local, ?MODULE}, ?MODULE, [], []).
<br>>
<br>> get_c2_id_for_mt4_id(SystemId,<wbr>MT4OrderId) ->
<br>> context_agent:call(?MODULE, {get_c2_id_for_mt4_id,
<br>> SystemId,MT4OrderId}, 60000).
<br>>
<br>> update_c2_id_for_mt4_id(<wbr>SystemId,MT4OrderId,<wbr>C2SignalId) ->
<br>> context_agent:call(?MODULE, {update_c2_id_for_mt4_id,
<br>> SystemId,MT4OrderId,<wbr>C2SignalId}, 60000).
<br>>
<br>> delete(SystemId,MT4OrderId) -> context_agent:call(?MODULE, {delete,
<br>> SystemId,MT4OrderId}, 60000).
<br>>
<br>> connect(Host, Port, User, Password, DatabaseName) ->
<br>> context_agent:call(?MODULE, {connect, Host, Port, User, Password,
<br>> DatabaseName}, 60000).
<br>>
<br>> init([]) ->
<br>> io:format("Starting ~p~n",[?MODULE]),
<br>> {context,mt4_db_interface_<wbr>context(),#mt4_db_interface_<wbr>state{}}.
<br>>
<br>> handle_call({connect, Host, Port, User, Password, DatabaseName},
<br>> _From, State) ->
<br>> Reply = do_connect(Host, Port, User, Password, DatabaseName),
<br>> {reply, Reply, State};
<br>>
<br>> handle_call({get_c2_id_for_<wbr>mt4_id, SystemId,MT4OrderId}, _From, State) ->
<br>> Reply = do_get_c2_id_for_mt4_id(?<wbr>PoolId,SystemId,MT4OrderId),
<br>> {reply, Reply, State};
<br>>
<br>> handle_call({update_c2_id_for_<wbr>mt4_id, SystemId,MT4OrderId,<wbr>C2SignalId},
<br>> _From, State) ->
<br>> Reply = do_replace_c2_id_for_mt4_id(?<wbr>PoolId,SystemId,MT4OrderId,<wbr>C2SignalId),
<br>> {reply, Reply, State};
<br>>
<br>> handle_call({delete, SystemId,MT4OrderId}, _From, State) ->
<br>> Reply = do_delete(?PoolId,SystemId,<wbr>MT4OrderId),
<br>> {reply, Reply, State};
<br>>
<br>> handle_call(_Request, _From, State) ->
<br>> Reply = ok,
<br>> {reply, Reply, State}.
<br>>
<br>> handle_cast(_Msg, State) ->
<br>> {noreply, State}.
<br>>
<br>> handle_info(_Info, State) ->
<br>> {noreply, State}.
<br>>
<br>> terminate(_Reason, _State) ->
<br>> ok.
<br>>
<br>> code_change(_OldVsn, State, _Extra) ->
<br>> {ok, State}.
<br>>
<br>> %% ==============================<wbr>==============================<wbr>=================
<br>> %% MySql implementations: emysql:execute(DbId,Select)
<br>> do_connect(Host, Port, User, Password, DatabaseName)->
<br>> % Connect to MySql here
<br>> emysql:add_pool(?PoolId,...)..<wbr>.
<br>> ok.
<br>>
<br>> do_get_c2_id_for_mt4_id(DbId,<wbr>SystemId,MT4OrderId) ->
<br>> emysql:execute(...), ....
<br>> do_replace_c2_id_for_mt4_id(<wbr>DbId,SystemId,MT4OrderId,<wbr>C2SignalId) ->
<br>> emysql:execute(...), ....
<br>> do_delete(DbId,SystemId,<wbr>MT4OrderId) -> emysql:execute(...), ....
<br>>
<br>> %% ==============================<wbr>==============================<wbr>========
<br>> %% @doc ContextErlang.
<br>> %% Create a context structure for this module.
<br>> %% Defined variation 'use_dummy_db_for_mt4' is not active in the
<br>> production mode. Activated just for unit tests.
<br>> %% @end
<br>> %% ==============================<wbr>==============================<wbr>========
<br>> mt4_db_interface_context() ->
<br>> Spec = [ {what_db_to_use_slot, use_dummy_db_for_mt4} ],
<br>> context_ADT:create(Spec).
<br>>
<br>>
<br>> (2)
<br>> A variation "use_dummy_db_for_mt4" which implements a fake database
<br>> and is used for unit tests:
<br>>
<br>> %% ==============================<wbr>==============================<wbr>===============
<br>> %% Author: Bohuslav Svancara
<br>> %% Created: 2013-07-20
<br>> %% Description: Experimental ContextErlang variation module for
<br>> mt4_db_interface.
<br>> %%
<br>> %% This variation is used for unit tests where MySql is very hard to use.
<br>> %% ==============================<wbr>==============================<wbr>===============
<br>>
<br>> -module(use_dummy_db_for_mt4).
<br>> -export([handle_call/3,on_<wbr>activation/1,on_deactivation/<wbr>1,test/0]).
<br>>
<br>> -context_call([connect/5,get_<wbr>c2_id_for_mt4_id/2,update_c2_<wbr>id_for_mt4_id/3,delete/2]).
<br>> -context_cast([]).
<br>>
<br>> -record(mt4_db_interface_<wbr>state, {dbHandle,
<br>> dummy_database_for_tests=dict:<wbr>new()}). % in records.hrl in a real
<br>> program
<br>>
<br>> handle_call({connect, Host, Port, User, Password, DatabaseName},
<br>> _From, State) ->
<br>> {reply, ok, State#mt4_db_interface_state{ dummy_database_for_tests
<br>> = dict:new()}};
<br>>
<br>> handle_call({get_c2_id_for_<wbr>mt4_id, SystemId,MT4OrderId}, _From, State) ->
<br>> Reply = case dict:find({SystemId,<wbr>MT4OrderId},
<br>> State#mt4_db_interface_state.<wbr>dummy_database_for_tests) of
<br>> <snip>
<br>> end,
<br>> {reply, Reply, State};
<br>>
<br>> handle_call({update_c2_id_for_<wbr>mt4_id, SystemId,MT4OrderId,<wbr>C2SignalId},
<br>> _From, State) ->
<br>> NewDict = dict:update({SystemId,<wbr>MT4OrderId}, fun(_) -> C2SignalId
<br>> end, C2SignalId,
<br>> State#mt4_db_interface_state.<wbr>dummy_database_for_tests),
<br>> {reply, ok, State#mt4_db_interface_state{<wbr>dummy_database_for_tests
<br>> = NewDict}};
<br>>
<br>> handle_call({delete, SystemId,MT4OrderId}, _From, State) ->
<br>> NewDict = dict:erase({SystemId,<wbr>MT4OrderId},
<br>> State#mt4_db_interface_state.<wbr>dummy_database_for_tests),
<br>> {reply, ok, State#mt4_db_interface_state{<wbr>dummy_database_for_tests
<br>> = NewDict}}.
<br>>
<br>> on_activation(State) -> {ok,State}.
<br>>
<br>> on_deactivation(State) -> {ok,State}.
<br>>
<br>>
<br>> (3)
<br>> I a production code the mt4_db_interface module is used:
<br>>
<br>> {ok, Host, Port, User, Password, DatabaseName} = get_db_config(),
<br>> mt4_db_interface:start_link()<wbr>,
<br>> mt4_db_interface:connect(<wbr>Host, Port, User, Password, DatabaseName),...
<br>>
<br>> mt4_db_interface:get_c2_id_<wbr>for_mt4_id(...)
<br>> mt4_db_interface:update_c2_<wbr>id(...)
<br>> mt4_db_interface:delete(...)
<br>>
<br>> (4)
<br>> In unit tests mt4_db_interface is switched (dynamic switch - the most
<br>> interesting thing in context programming) to "use_dummy_db_for_mt4"
<br>> implementation:
<br>>
<br>> mt4_db_interface:start_link()<wbr>,
<br>> mt4_db_interface:in_cur_<wbr>context_activate(mt4_db_<wbr>interface,
<br>> use_dummy_db_for_mt4, what_db_to_use_slot),
<br>> mt4_db_interface:connect("<wbr>DummyHost", 0, "DummyUser",
<br>> "DummyPassword", "DummyDatabaseName"),
<br>>
<br>> mt4_db_interface:get_c2_id_<wbr>for_mt4_id(...)
<br>> mt4_db_interface:update_c2_<wbr>id(...)
<br>> mt4_db_interface:delete(...)
<br>>
<br>>
<br>> Is there any other person trying context programming in Erlang?
<br>>
<br>> Sincerely,
<br>> Bohuslav Svancara
<br>>
<br>>
<br>> 2013/8/9 H.C. v. Stockhausen <<a href="javascript:" target="_blank" gdf-obfuscated-mailto="bAqYHGHEEigJ">h...@vst.io</a>>
<br>>>
<br>>> Hello,
<br>>>
<br>>> I need a DB backend for my application but I'd like to be able to swap
<br>>> it out for different DBs if I choose so later on.
<br>>>
<br>>> I would like to code against an interface and tell the application
<br>>> what specific backend to use through config rather than code changes.
<br>>>
<br>>> Is there a pattern for doing that? Hot code upgrades and multi-node
<br>>> are not a real concern at this time - mostly, since I have no
<br>>> experience yet with either - however if doing it right means taking
<br>>> that into account too I'd also like to learn more about that.
<br>>>
<br>>> I am thinking of defining a custom behaviour ("my_crud" perhaps), then
<br>>> to implement it for various DBs and to also let a config driven
<br>>> adapter implement it that I then use to throughout the code to talk to
<br>>> the DB layer.
<br>>>
<br>>> For example, using Mnesia and AWS DynamoDB:
<br>>>
<br>>> - my_crud.erl (behaviour)
<br>>> - my_db_mnesia.erl (implements behaviour)
<br>>> - my_db_dynamo.erl (implements behaviour)
<br>>> - my_db.erl (configurable adapter that also implements behaviour)
<br>>> - my.config
<br>>>
<br>>> my_db:insert(Key, Value).
<br>>>
<br>>> Is that a reasonable approach that makes proper use of Erlang and
<br>>> behaviours or is this just not how one should do it?
<br>>>
<br>>> Thank you for any help & best regards,
<br>>> Hans
<br>>> ______________________________<wbr>_________________
<br>>> erlang-questions mailing list
<br>>> <a href="javascript:" target="_blank" gdf-obfuscated-mailto="bAqYHGHEEigJ">erlang-q...@erlang.org</a>
<br>>> <a href="http://erlang.org/mailman/listinfo/erlang-questions" target="_blank">http://erlang.org/mailman/<wbr>listinfo/erlang-questions</a>
<br>> ______________________________<wbr>_________________
<br>> erlang-questions mailing list
<br>> <a href="javascript:" target="_blank" gdf-obfuscated-mailto="bAqYHGHEEigJ">erlang-q...@erlang.org</a>
<br>> <a href="http://erlang.org/mailman/listinfo/erlang-questions" target="_blank">http://erlang.org/mailman/<wbr>listinfo/erlang-questions</a>
<br>______________________________<wbr>_________________
<br>erlang-questions mailing list
<br><a href="javascript:" target="_blank" gdf-obfuscated-mailto="bAqYHGHEEigJ">erlang-q...@erlang.org</a>
<br><a href="http://erlang.org/mailman/listinfo/erlang-questions" target="_blank">http://erlang.org/mailman/<wbr>listinfo/erlang-questions</a>
<br></blockquote></div>