Skip to content

Instantly share code, notes, and snippets.

@joshrotenberg
Last active December 29, 2015 11:49
Show Gist options
  • Select an option

  • Save joshrotenberg/7666492 to your computer and use it in GitHub Desktop.

Select an option

Save joshrotenberg/7666492 to your computer and use it in GitHub Desktop.
-module(function_options).
-export([my_fn/1, my_fn/2]).
-include_lib("eunit/include/eunit.hrl").
%% we'll populate a record with all of our interesting pieces. this also allows the specification of
%% defaults for things the user may not provide. required_arg will hold our required param, so we'll
%% leave them undefined. anything that starts with opt, however may or may not be supplied
-record(myrec, {required_arg=undefined,
integer_opt=0,
list_opt=[],
boolean_opt=false,
atom_opt=undefined
}).
%% single arg since our options are, uh, optional
my_fn(Arg) ->
my_fn(Arg, []).
%% double arg, processing Options with our option processing below
my_fn(Arg, Options) ->
R1 = get_my_options(Options),
R1#myrec{required_arg = Arg}.
%% ------------------------------------------------------------------
%% tests
%% ------------------------------------------------------------------
no_options_test() ->
?assertEqual(my_fn("foo"), #myrec{required_arg="foo",
integer_opt=0,
list_opt=[],
boolean_opt=false,
atom_opt=undefined}),
?assertEqual(my_fn("bar", []), #myrec{required_arg="bar",
integer_opt=0,
list_opt=[],
boolean_opt=false,
atom_opt=undefined}).
integer_opt_test() ->
?assertEqual(my_fn("foo", [{integer_opt, 20}]),
#myrec{required_arg="foo",
integer_opt=20}),
?assertException(throw,
{bad_option,{integer_opt,0}},
my_fn("foo", [{integer_opt, 0}])).
list_opt_test() ->
?assertEqual(my_fn("foo", [{list_opt, [1,2,3]}]),
#myrec{required_arg="foo",
list_opt=[1,2,3]}),
?assertEqual(my_fn("foo", [{list_opt, "doof"}]),
#myrec{required_arg="foo",
list_opt="doof"}),
?assertException(throw,
{bad_option,{list_opt,[]}},
my_fn("foo", [{list_opt, []}])).
boolean_opt_test() ->
?assertEqual(my_fn("foo", [{boolean_opt, true}]),
#myrec{required_arg="foo",
boolean_opt=true}),
?assertEqual(my_fn("foo", [{boolean_opt, false}]),
#myrec{required_arg="foo",
boolean_opt=false}),
?assertException(throw,
{bad_option,{boolean_opt,barf}},
my_fn("foo", [{boolean_opt, barf}])).
atom_opt_test() ->
?assertEqual(my_fn("foo", [foo]),
#myrec{required_arg="foo",
atom_opt=foo}),
?assertEqual(my_fn("foo", [bar]),
#myrec{required_arg="foo",
atom_opt=bar}),
?assertException(throw,
{bad_option, baz},
my_fn("foo", [baz])).
%% ------------------------------------------------------------------
%% internal stuff
%% ------------------------------------------------------------------
%% entry point with a single argument
get_my_options(Options) ->
Opts = #myrec{},
get_my_option(Options, Opts).
%% no more options to process
get_my_option([], Record) ->
Record;
%% check that the integer opt is in fact an integer and is non-zero
get_my_option([{integer_opt, Integer} | Rest], Record) when is_integer(Integer), Integer > 0 ->
get_my_option(Rest, Record#myrec{integer_opt = Integer});
%% check that the list opt is a list and has a length of at least 1
get_my_option([{list_opt, List} | Rest], Record) when is_list(List), length(List) >= 1 ->
get_my_option(Rest, Record#myrec{list_opt = List});
%% check that the boolean opt is true or false
get_my_option([{boolean_opt, Boolean} | Rest], Record) when is_boolean(Boolean) ->
get_my_option(Rest, Record#myrec{boolean_opt = Boolean});
%% this handles a single atom rather than a tuple. we've predefined acceptable options below in thex
%% is_valid_atom_opt function
get_my_option([Atom | Rest], Record) when is_atom(Atom) ->
case is_valid_atom_opt(Atom) of
true -> get_my_option(Rest, Record#myrec{atom_opt = Atom});
false -> throw({bad_option, Atom})
end;
%% anything else results in a bad_option exception being thrown
get_my_option([Unknown | _Rest], _Record) ->
throw({bad_option, Unknown}).
%% the single atom option can be either foo or bar (or not there at all)
is_valid_atom_opt(foo) -> true;
is_valid_atom_opt(bar) -> true;
is_valid_atom_opt(_) -> false.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment