I am trying to refactor some code I have for software that collects current status of agents in a call queue. Currently, for each of the 6 or so events that I listen to, I check in a mnesia table if an agent exists and change some values in the row depending on the event or add it as new if the agent doesn't exist. Currently I have this mnesia transaction in each event and of course that is a bunch of repeated code for checking the existance of agents and so on.

I'm trying to change it so that there is one function like *change_agent/2* that I call from the events that handles this for me.

My problems are of course records.... I find no way of dynamically creating them or merging 2 of them together or anything. Preferably there would be a function I could call like:

change_agent("001", #agent(id = "001", name = "Steve")).
change_agent("001", #agent(id = "001", paused = 0, talking_to = "None")).

Any ideas?

+3  A: 

It is difficult to write generic access functions for records. One workaround for this is the 'exprecs' library, which will generate code for low-level record access functions.

The thing you need to do is to add the following lines to a module:

-compile({parse_transform, exprecs}).
-export_records([...]).  % name the records that you want to 'export'

The naming convention for the access functions may look strange, but was inspired by a proposal from Richard O'Keefe. It is, at least, consistent, and unlikely to clash with existing functions. (:

Quick look at this might not be exactly what I was looking for. But interesting nevertheless and something I will check on later.
Jon Gretar
+2  A: 

I wrote some code a while ago that merges two records. Is not entirely dynamic, but whith macros you could easily use it for several records.

It works like this: The merge/2 function takes two records and converts them to lists together with the empty record for reference (the record type is defined at compile time, and must be. This is the "undynamic" part). These are then run through the generic function merge/4 which works with lists and takes elements from A if they are defined, otherwise from B if they are defined, or lastly from Default (which is always defined).

Here's the code (please excuse StackOverflow's poor Erlang syntax highlighting):

%%% @spec merge(RecordA, RecordB) -> #my_record{}
%%%     RecordA = #my_record{}
%%%     RecordB = #my_record{}
%%% @doc Merges two #my_record{} instances. The first takes precedence.
%%% @end
merge(RecordA, RecordB) when is_record(RecordA, my_record),
                             is_record(RecordB, my_record) ->

%%% @spec merge(A, B, Default, []) -> [term()]
%%%     A = [term()]
%%%     B = [term()]
%%%     Default = [term()]
%%% @doc Merges the lists `A' and `B' into to a new list taking
%%% default values from `Default'.
%%% Each element of `A' and `B' are compared against the elements in
%%% `Default'. If they match the default, the default is used. If one
%%% of them differs from the other and the default value, that element is
%%% chosen. If both differs, the element from `A' is chosen.
%%% @end
merge([D|ATail], [D|BTail], [D|DTail], To) ->
    merge(ATail, BTail, DTail, [D|To]); % If default, take from D
merge([D|ATail], [B|BTail], [D|DTail], To) ->
    merge(ATail, BTail, DTail, [B|To]); % If only A default, take from B
merge([A|ATail], [_|BTail], [_|DTail], To) ->
    merge(ATail, BTail, DTail, [A|To]); % Otherwise take from A
merge([],        [],        [],        To) ->

Feel free to use it in any way you want.

Adam Lindberg
Ahh.. This just might work. Thanks.
Jon Gretar