Skip to content

Instantly share code, notes, and snippets.

@aerosol
Created December 27, 2011 17:25
Show Gist options
  • Select an option

  • Save aerosol/1524457 to your computer and use it in GitHub Desktop.

Select an option

Save aerosol/1524457 to your computer and use it in GitHub Desktop.
-module(rules).
-compile([export_all]).
%% quick n dirty sketch
%%
%% example:
%%
%% rules.txt
%%
%% route foo_backend when recipient matches "[0-9]{4}"
%% route bar_backend when originator matches "^[A-Za-z]+$"
-define(VALID_TOKENS,
lists:map(fun(Pat) ->
{ok, Re} = re:compile(Pat),
Re
end, ["(when|route|recipient|originator|matches)",
"[a-zA-z]+_backend",
"\".+\""
])).
test() ->
{ok, Funs} = load("rules.txt"),
[R1,R2|_] = Funs,
{ok, {route, foo_backend}} = R1(recipient, "1111"),
{ok, skip} = R1(recipient, "111"),
{ok, {route, bar_backend}} = R2(originator, "adam"),
{ok, skip} = R2(originator, "This will not match!"),
{ok, skip} = R2(whatever, "abc"),
{ok, Funs}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
validate([]) -> ok;
validate([T|Rest]) ->
case lists:any(fun(P) -> re_matches(T,P) end, ?VALID_TOKENS) of
true -> validate(Rest);
false -> {error, {invalid_token, T}}
end;
validate(Bin) when is_binary(Bin) ->
Tokens = binary:split(Bin, [<<" ">>, <<"\n">>], [trim, global]),
validate(Tokens).
load(F) ->
{ok, Bin} = file:read_file(F),
case validate(Bin) of
ok -> build_funs(binary:split(Bin, [<<"\n">>], [trim, global]));
Error -> throw(Error)
end.
build_funs([]) -> {ok, []};
build_funs(Rules) when is_list(Rules) ->
{ok, [build_fun(Rule) || Rule <- Rules]}.
build_fun(Rule) ->
{ok, Scanned, _} = erl_scan:string(binary_to_list(Rule)),
[{_,_,Action},{_,_,Backend},{'when',1}|Rest] = Scanned,
[{_,_,Subject}, {_,_,Pred}, {_,_,Pattern}] = Rest,
Cond = list_to_existing_atom(
atom_to_list(Subject)++"_"++atom_to_list(Pred)),
{ok, Pat} = re:compile(Pattern),
fun(S, Data) ->
case S of
Subject ->
case ?MODULE:Cond(Data, Pat) of
true -> {ok, {Action, Backend}};
false -> {ok, skip}
end;
_ -> {ok, skip}
end
end.
re_matches(Data, Pattern) ->
case re:run(Data, Pattern) of
{match, _} -> true;
nomatch -> false
end.
originator_matches(Data, Pattern) ->
re_matches(Data, Pattern).
recipient_matches(Data, Pattern) ->
re_matches(Data, Pattern).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment