Current section

Files

Jump to
nitrogen_core src handlers query default_query_handler.erl
Raw

src/handlers/query/default_query_handler.erl

% vim: sw=4 ts=4 et ft=erlang
% Nitrogen Web Framework for Erlang
% Copyright (c) 2008-2010 Rusty Klophaus
% See MIT-LICENSE for licensing information.
% Though this is defined as a handler, it is unlikely
% that anyone would want to override the default behaviour.
% It is defined as a handler simply because it fit well
% into the existing handler pattern.
-module (default_query_handler).
-behaviour (query_handler).
-include ("wf.hrl").
-export ([
init/2,
finish/2,
set_websocket_params/3,
get_value/3,
get_values/3,
get_params/2
]).
-record(state, {request=[], websocket=[]}).
%% TODO: It's worth considering reworking this page to use simple_bridge for
%% all the query and post parameter evaluation, rather than reproducing a
%% sub-set of the functionality here. This, however, does more than the
%% typicaly query-string evaluation, as the normalized paths include all
%% possible element ids. Something to consider.
init(_Config, _State) ->
% Get query params and post params
% from the request bridge...
Bridge = wf_context:bridge(),
QueryParams = sbw:query_params(Bridge),
PostParams = sbw:post_params(Bridge),
% Load into state...
Params = QueryParams ++ PostParams,
% Pre-normalize the parameters.
Params1 = normalize_params(Params),
{ok, #state{request=Params1}}.
finish(_Config, _State) ->
% Clear out the state.
{ok, []}.
set_websocket_params(Params, _Config, State) ->
Params1 = normalize_params(Params),
State1 = State#state{websocket=Params1},
{ok, State1}.
%% Given a path, return the value that matches the path.
get_value(Path, Config, State) ->
case get_values(Path, Config, State) of
[] -> undefined;
[One] ->
wf:to_unicode_list(One);
_Many -> throw({?MODULE, too_many_matches, Path})
end.
get_values(Path, _Config, #state{request=Request, websocket=Websocket} = _State) ->
Path1 = normalize_path(Path),
Vs = refine_params(Path1, Websocket ++ Request),
Vs.
get_params(_Config, #state{request=Request, websocket=Websocket} = _State) ->
Params = Websocket ++ Request,
F = fun({KeyPartsReversed, Value}) ->
KeyParts = lists:reverse(KeyPartsReversed),
Key = string:join(KeyParts, "."),
{ Key, wf:to_unicode_list(Value) }
end,
lists:map(F, Params).
%% Next, narrow down the parameters by keeping only the parameters
%% that contain the next element found in path, while shrinking the
%% parameter paths at the same time.
%% For example, if:
%% Path = [a, b, c]
%% Params = [{[x, a, y, b, c], _}]
%% Then after the first round of refine_params/2 we would have:
%% Path = [b, c]
%% Params = [y, b, c]
-spec refine_params(NormalizedPath :: list(), Params :: list()) -> Values :: list().
refine_params([], Params) ->
[wf:to_unicode_list(V) || {_, V} <- Params];
refine_params([H|T], Params) ->
F = fun({Path, Value}, Acc) ->
case split_on(H, Path) of
{ok, RemainingPath} -> [{RemainingPath, Value}|Acc];
false -> Acc
end
end,
Params1 = lists:foldl(F, [], Params),
refine_params(T, lists:reverse(Params1)).
split_on(_, []) -> false;
split_on(El, [El|T]) -> {ok, T};
split_on(El, [_|T]) -> split_on(El, T).
normalize_path(Path) when is_binary(Path) ->
normalize_path(binary_to_list(Path));
normalize_path(Path) when is_atom(Path) ->
normalize_path(atom_to_list(Path));
normalize_path(Path) when ?IS_STRING(Path) ->
Tokens = string:tokens(Path, "."),
Tokens1 = [strip_array_brackets(strip_wfid(X)) || X <- Tokens],
lists:reverse(Tokens1).
normalize_params(Params) ->
%% In typical erlang fashion, this list is being built in reverse, and will
%% need to be reversed when finished to ensure proper parameter order
BackwardNormalizedParams = lists:foldl(fun(Param, Acc) ->
normalize_param(Param) ++ Acc
end, [], Params),
lists:reverse(BackwardNormalizedParams).
normalize_param({undefined, _}) ->
[];
normalize_param({[], _}) ->
[];
normalize_param({<<>>, _}) ->
[];
normalize_param({Path, Value}) when ?IS_STRING(Value);
Value =:= [];
is_binary(Value);
is_integer(Value);
is_atom(Value) ->
[{normalize_path(Path), Value}];
normalize_param({Path, Values}) when is_list(Values) ->
NPath = normalize_path(Path),
%% Because this is running in the middle of a list that's being built in
%% reverse, this will have to be built in reverse also.
lists:reverse([{NPath, V} || V <- Values]).
%% Most tokens will start with "wfid_". Strip this out.
strip_wfid(Path) ->
case Path of
"wfid_" ++ S -> S;
S -> S
end.
%% For multiselect elements, jquery appends [] to the element name if element's
%% data is an array We strip it out, since Nitrogen doesn't care if it's an
%% array or not, it just uses each key individually
strip_array_brackets(Path) ->
case lists:reverse(Path) of
[ $], $[ | Rest ] -> lists:reverse(Rest);
_ -> Path
end.