<commit_before>% vim: sw=4 ts=4 et ft=erlang
% Nitrogen Web Framework for Erlang
% Copyright (c) 2008-2013 <NAME>
% See MIT-LICENSE for licensing information.

-module (element_template).
-include("wf.hrl").
-export([
    reflect/0,
    render_element/1
]).

% TODO - Revisit parsing in the to_module_callback. This
% will currently fail if we encounter a string like:
% "String with ) will fail"
% or
% "String with ]]] will fail"


-spec reflect() -> [atom()].
reflect() -> record_info(fields, template).

-spec render_element(#template{}) -> body().
render_element(Record) ->
    % Parse the template file...

    File = wf:to_list(Record#template.file),
    Template = get_cached_template(File),

    % Let's figure out the appropriate module aliases
    ModuleAliases = get_module_aliases(Record),

    % Evaluate the template.

    %% erl_eval:exprs/2 expects Bindings to be an orddict, but Nitrogen 
    %% does not have this requirement, so let's fix that.
    %% create the needed Ord-Dict just once instead for every eval call down the chain
    OrdDictBindings = orddict:from_list(Record#template.bindings),
    Fixed_bindings_record = Record#template{bindings=OrdDictBindings},
    Body = eval(Template, Fixed_bindings_record, ModuleAliases),
    Body.

get_cached_template(File) ->
    FileAtom = list_to_atom("template_file_" ++ File),

    LastModAtom = list_to_atom("template_lastmod_" ++ File),
    LastMod = nitro_mochiglobal:get(LastModAtom),

    CacheTimeAtom = list_to_atom("template_cachetime_" ++ File),
    CacheTime = nitro_mochiglobal:get(CacheTimeAtom),

    %% Check for recache if one second has passed since last cache time...
    ReCache = case (CacheTime == undefined) orelse (timer:now_diff(now(), CacheTime) > (1000 * 1000)) of
        true ->
            %% Recache if the file has been modified. Otherwise, reset
            %% the CacheTime timer...
            case LastMod /= filelib:last_modified(File) of
                true ->
                    true;
                false ->
                    nitro_mochiglobal:put(CacheTimeAtom, now()),
                    false
            end;
        false ->
            false
    end,

    case ReCache of
        true ->
            %% Recache the template...
            Template = parse_template(File),
            nitro_mochiglobal:put(FileAtom, Template),
            nitro_mochiglobal:put(LastModAtom, filelib:last_modified(File)),
            nitro_mochiglobal:put(CacheTimeAtom, now()),
            Template;
        false ->
            %% Load template from cache...
            nitro_mochiglobal:get(FileAtom)
    end.

parse_template(File) ->
    % TODO - Templateroot
    % File1 = filename:join(nitrogen:get_templateroot(), File),
    File1 = File,
    case file:read_file(File1) of
        {ok, B} -> parse_template1(B);
        _ ->
            ?LOG("Error reading file: ~s~n", [File1]),
            throw({template_not_found, File1})
    end.

parse_template1(B) ->
    F = fun(Tag) ->
        try
            Tag1 = string:strip(wf:to_list(Tag)),
            to_module_callback(Tag1)
        catch _ : _ ->
            ?LOG("Invalid template tag: ~s~n", [Tag])
        end
    end,
    parse(B, F).


%%% PARSE %%%
<commit_msg>Whoa speed improvement.<commit_after>% vim: sw=4 ts=4 et ft=erlang
% Nitrogen Web Framework for Erlang
% Copyright (c) 2008-2013 <NAME>
% See MIT-LICENSE for licensing information.

-module (element_template).
-include("wf.hrl").
-export([
    reflect/0,
    render_element/1
]).

%% Check the last modified time for templates every one second.
-define(RECACHE_CHECK_INTERVAL, 1000000).

% TODO - Revisit parsing in the to_module_callback. This
% will currently fail if we encounter a string like:
% "String with ) will fail"
% or
% "String with ]]] will fail"


-spec reflect() -> [atom()].
reflect() -> record_info(fields, template).

-spec render_element(#template{}) -> body().
render_element(Record) ->
    % Parse the template file...

    File = wf:to_list(Record#template.file),
    Template = get_cached_template(File),

    % Let's figure out the appropriate module aliases
    ModuleAliases = get_module_aliases(Record),

    % Evaluate the template.

    %% erl_eval:exprs/2 expects Bindings to be an orddict, but Nitrogen 
    %% does not have this requirement, so let's fix that.
    %% create the needed Ord-Dict just once instead for every eval call down the chain
    OrdDictBindings = orddict:from_list(Record#template.bindings),
    Fixed_bindings_record = Record#template{bindings=OrdDictBindings},
    Body = eval(Template, Fixed_bindings_record, ModuleAliases),
    Body.

get_cached_template(File) ->
    FileAtom = list_to_atom("template_file_" ++ File),
   
    case is_time_to_recache(File) of
        true ->
            wf:info("Recaching Template~n"),
            %% Recache the template...
            Template = parse_template(File),
            nitro_mochiglobal:put(FileAtom, Template),
            simple_cache:set(nitrogen, infinity, {template_loaded, File}, true),
            Template;
        false ->
            %% Load template from cache...
            nitro_mochiglobal:get(FileAtom)
    end.

is_time_to_recache(File) ->
    IsLoaded = simple_cache:get(nitrogen, infinity, {template_loaded, File}, fun() -> false end),
    LastModified = simple_cache:get(nitrogen, 1000, {template_last_modified, File}, fun() ->
        filelib:last_modified(File)
    end),
    not(IsLoaded) orelse LastModified > {date(), time()}.
            
parse_template(File) ->
    % TODO - Templateroot
    % File1 = filename:join(nitrogen:get_templateroot(), File),
    File1 = File,
    case file:read_file(File1) of
        {ok, B} -> parse_template1(B);
        _ ->
            ?LOG("Error reading file: ~s~n", [File1]),
            throw({template_not_found, File1})
    end.

parse_template1(B) ->
    F = fun(Tag) ->
        try
            Tag1 = string:strip(wf:to_list(Tag)),
            to_module_callback(Tag1)
        catch _ : _ ->
            ?LOG("Invalid template tag: ~s~n", [Tag])
        end
    end,
    parse(B, F).


%%% PARSE %%%

{'fraction_non_alphanumeric': 0.11834997544606318, 'fraction_numerical': 0.01031265346210509, 'mean_word_length': 2.725609756097561, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 40, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2965820', 'n_tokens_mistral': 1978, 'n_tokens_neox': 1868, 'n_words': 476}