Current section

Files

Jump to
ephp src ephp_output.erl
Raw

src/ephp_output.erl

-module(ephp_output).
-author('manuel@altenwald.com').
-include("ephp.hrl").
-type flush_handler() :: stdout | {io, io:device()} | function().
-type output_handler() :: undefined | function().
-record(state,
{output = <<>> :: binary(),
flush = true :: boolean(),
flush_handler = stdout :: flush_handler(),
output_handler :: output_handler(),
global_context :: reference()}).
%% ------------------------------------------------------------------
%% API Function Exports
%% ------------------------------------------------------------------
-export([start_link/1, start_link/2, start_link/3, get/1, push/2, pop/1, size/1, flush/1,
set_flush/2, set_output_handler/2, get_output_handler/1, set_flush_handler/2,
get_flush_handler/1, destroy/1]).
%% ------------------------------------------------------------------
%% API Function Definitions
%% ------------------------------------------------------------------
start_link(Ctx) ->
start_link(Ctx, true, stdout).
start_link(Ctx, Flush) ->
start_link(Ctx, Flush, stdout).
start_link(Ctx, Flush, FlushHandler) ->
Ref = make_ref(),
erlang:put(Ref,
#state{flush = Flush,
flush_handler = FlushHandler,
global_context = Ctx}),
{ok, Ref}.
get_output_handler(Ref) ->
#state{output_handler = Handler} = erlang:get(Ref),
Handler.
set_output_handler(Ref, Handler) ->
State = erlang:get(Ref),
erlang:put(Ref, State#state{output_handler = Handler}),
ok.
get_flush_handler(Ref) ->
#state{flush_handler = Handler} = erlang:get(Ref),
Handler.
set_flush_handler(Ref, Handler) ->
State = erlang:get(Ref),
erlang:put(Ref, State#state{flush_handler = Handler}),
ok.
pop(Ref) ->
#state{output = Output} = State = erlang:get(Ref),
erlang:put(Ref, State#state{output = <<>>}),
Output.
push(Ref, RawText) ->
case erlang:get(Ref) of
#state{flush = true, global_context = Ctx} = State ->
Text = output_handler(Ctx, RawText, State#state.output_handler),
flush_handler(Text, State#state.flush_handler);
% TODO: implement 'output_buffering' (from config)
#state{flush = false, output = Output} = State ->
erlang:put(Ref, State#state{output = <<Output/binary, RawText/binary>>})
end,
ok.
get(Ref) ->
#state{output = Output} = erlang:get(Ref),
Output.
set_flush(Ref, Flush) ->
State = erlang:get(Ref),
erlang:put(Ref, State#state{flush = Flush}),
ok.
size(Ref) ->
#state{output = Output} = erlang:get(Ref),
byte_size(Output).
flush(Ref) ->
State = do_flush(Ref),
erlang:put(Ref, State),
ok.
destroy(Ref) ->
do_flush(Ref),
erlang:erase(Ref),
ok.
%% ------------------------------------------------------------------
%% Internal Function Definitions
%% ------------------------------------------------------------------
do_flush(Ref) ->
#state{output = RawOutput, global_context = Ctx} = State = erlang:get(Ref),
Output = output_handler(Ctx, RawOutput, State#state.output_handler),
if State#state.flush ->
flush_handler(Output, State#state.flush_handler),
State#state{output = <<>>};
true ->
State#state{output = Output}
end.
output_handler(_Ctx, Text, undefined) ->
Text;
output_handler(Ctx, Text, OH) when is_function(OH, 2) ->
OH(Ctx, Text).
flush_handler(Text, stdout) ->
io:fwrite("~s", [Text]),
ok;
flush_handler(Text, {io, FH}) ->
io:fwrite(FH, "~s", [Text]),
ok;
flush_handler(Text, FH) when is_function(FH, 1) ->
FH(Text),
ok.