Skip to content

Instantly share code, notes, and snippets.

@stolen
Created September 27, 2012 09:33
Show Gist options
  • Save stolen/3793122 to your computer and use it in GitHub Desktop.
Save stolen/3793122 to your computer and use it in GitHub Desktop.
gen_server with self-upgrading state prototype
-module(gs_up).
-export([init/1, handle_call/3, terminate/2]).
-record(state, {
own_fields,
field1,
field2,
field3
}).
init([F1, F3]) ->
{ok, #state{own_fields = record_info(fields, state),
field1 = F1, field3 = F3}}.
terminate(_,_) -> ok.
handle_call(Call, From, State) ->
NewState = case element(2, State) == record_info(fields, state) of
true -> State;
false -> migrate(State)
end,
do_handle_call(Call, From, NewState).
do_handle_call(_, _, #state{} = State) ->
{reply, state_to_pl(State), State}.
state_to_pl(State) when is_tuple(State) ->
Fields = element(2, State),
tl(lists:zip(Fields, tl(tuple_to_list(State)))).
state_from_pl(PL) ->
ZeroState = #state{own_fields = record_info(fields, state)},
FieldsToInherit = state_to_pl(ZeroState),
InheritedValues = [proplists:get_value(Field, PL, Default) || {Field, Default} <- FieldsToInherit],
list_to_tuple([element(1, ZeroState), element(2, ZeroState) | InheritedValues]).
migrate(OldState) ->
state_from_pl(state_to_pl(OldState)).
@stolen
Copy link
Author

stolen commented Sep 27, 2012

Unfortunately, Erlang does not allow matching record_info(fields, state).
With this possibility modifications to usual code would be just first case:

handle_call(C, F, State) when element(2, State) /= record_info(fields, state) ->
  handle_call(C, F, migrate(State));

Maybe it can be done in parse_transform, I will see it later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment