erlydtl_runtime.erl 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. -module(erlydtl_runtime).
  2. -compile(export_all).
  3. -type text() :: string() | binary().
  4. -type phrase() :: text() | {text(), {PluralPhrase::text(), non_neg_integer()}}.
  5. -type locale() :: term() | {Locale::term(), Context::binary()}.
  6. -type old_translate_fun() :: fun((text()) -> iodata() | default).
  7. -type new_translate_fun() :: fun((phrase(), locale()) -> iodata() | default).
  8. -type translate_fun() :: new_translate_fun() | old_translate_fun().
  9. -type init_translation() :: none
  10. | fun (() -> init_translation())
  11. | {M::atom(), F::atom()}
  12. | {M::atom(), F::atom(), A::list()}
  13. | translate_fun().
  14. -define(IFCHANGED_CONTEXT_VARIABLE, erlydtl_ifchanged_context).
  15. find_value(Key, Data, Options) when is_atom(Key), is_tuple(Data) ->
  16. Rec = element(1, Data),
  17. Info = proplists:get_value(record_info, Options, []),
  18. case proplists:get_value(Rec, Info) of
  19. Fields when is_list(Fields), length(Fields) == size(Data) - 1 ->
  20. case proplists:get_value(Key, Fields) of
  21. Idx when is_integer(Idx) -> element(Idx, Data);
  22. _ -> undefined
  23. end;
  24. _ -> find_value(Key, Data)
  25. end;
  26. find_value(Key, Data, Options) when is_integer(Key), is_list(Data) ->
  27. find_value(adjust_index(Key, 1, lists_0_based, Options), Data);
  28. find_value(Key, Data, Options) when is_integer(Key), is_tuple(Data) ->
  29. find_value(adjust_index(Key, 1, tuples_0_based, Options), Data);
  30. find_value(Key, Data, _Options) ->
  31. find_value(Key, Data).
  32. adjust_index(Key, Off, Opt, Options) when is_list(Options) ->
  33. case proplists:get_value(Opt, Options) of
  34. defer ->
  35. adjust_index(
  36. Key, Off, Opt,
  37. proplists:get_value(render_options, Options));
  38. true ->
  39. Key + Off;
  40. _ ->
  41. Key
  42. end;
  43. adjust_index(Key, _Off, _Opt, _Options) -> Key.
  44. find_value(_, undefined) ->
  45. undefined;
  46. find_value(Key, Fun) when is_function(Fun, 1) ->
  47. Fun(Key);
  48. find_value(Key, L) when is_atom(Key), is_list(L) ->
  49. case lists:keyfind(Key, 1, L) of
  50. false -> find_value(atom_to_list(Key), L);
  51. {Key, Value} -> Value
  52. end;
  53. find_value(Key, L) when is_list(Key), is_list(L) ->
  54. case lists:keyfind(Key, 1, L) of
  55. false -> find_value(list_to_binary(Key), L);
  56. {Key, Value} -> Value
  57. end;
  58. find_value(Key, L) when is_binary(Key), is_list(L) ->
  59. case lists:keyfind(Key, 1, L) of
  60. false -> undefined;
  61. {Key, Value} -> Value
  62. end;
  63. find_value(Key, L) when is_integer(Key), is_list(L) ->
  64. if Key =< length(L) -> lists:nth(Key, L);
  65. true -> undefined
  66. end;
  67. find_value(_, {0, nil}) -> undefined;
  68. find_value(Key, {GBSize, {_, _, _, _}=GBData}) when is_integer(GBSize) ->
  69. case gb_trees:lookup(Key, {GBSize, GBData}) of
  70. {value, Val} ->
  71. Val;
  72. _ ->
  73. undefined
  74. end;
  75. find_value(Key, Tuple) when is_tuple(Tuple) ->
  76. case element(1, Tuple) of
  77. dict ->
  78. case dict:find(Key, Tuple) of
  79. {ok, Val} ->
  80. Val;
  81. _ ->
  82. undefined
  83. end;
  84. _ when is_integer(Key) ->
  85. if Key =< size(Tuple) -> element(Key, Tuple);
  86. true -> undefined
  87. end;
  88. Module ->
  89. case lists:member({Key, 1}, Module:module_info(exports)) of
  90. true ->
  91. case Tuple:Key() of
  92. Val when is_tuple(Val) ->
  93. case element(1, Val) of
  94. 'Elixir.Ecto.Associations.BelongsTo' -> Val:get();
  95. 'Elixir.Ecto.Associations.HasOne' -> Val:get();
  96. _ -> Val
  97. end;
  98. Val -> Val
  99. end;
  100. _ ->
  101. undefined
  102. end
  103. end;
  104. find_value(Key, Map) ->
  105. case erlang:is_builtin(erlang, is_map, 1) andalso erlang:is_map(Map) of
  106. true -> find_map_value(Key, Map);
  107. false -> undefined
  108. end.
  109. find_map_value(Key, Map) when is_atom(Key) ->
  110. case maps:find(Key, Map) of
  111. error -> find_map_value(atom_to_list(Key), Map);
  112. {ok, Value} -> Value
  113. end;
  114. find_map_value(Key, Map) when is_list(Key) ->
  115. case maps:find(Key, Map) of
  116. error -> find_map_value(list_to_binary(Key), Map);
  117. {ok, Value} -> Value
  118. end;
  119. find_map_value(Key, Map) when is_binary(Key) ->
  120. case maps:find(Key, Map) of
  121. error -> undefined;
  122. {ok, Value} -> Value
  123. end;
  124. find_map_value(Key, Map) ->
  125. case maps:find(Key, Map) of
  126. error -> undefined;
  127. {ok, Value} -> Value
  128. end.
  129. fetch_value(Key, Data, Options) ->
  130. fetch_value(Key, Data, Options, []).
  131. fetch_value(Key, Data, Options, Default) ->
  132. case find_value(Key, Data, Options) of
  133. undefined -> Default;
  134. Val -> Val
  135. end.
  136. find_deep_value(Key, Data) ->
  137. find_deep_value(Key, Data, []).
  138. find_deep_value([Key|Rest], Item, Opts) ->
  139. case find_value(Key, Item, Opts) of
  140. undefined -> undefined;
  141. NewItem -> find_deep_value(Rest, NewItem, Opts)
  142. end;
  143. find_deep_value([], Item, _Opts) -> Item.
  144. regroup(List, Attribute) ->
  145. do_regroup(List, Attribute, [], []).
  146. regroup(List, Attribute, Options) ->
  147. do_regroup(List, Attribute, Options, []).
  148. do_regroup([], _, _, []) -> [];
  149. do_regroup([], _, _, [[{grouper, LastGrouper}, {list, LastList}]|Acc]) ->
  150. lists:reverse([[{grouper, LastGrouper}, {list, lists:reverse(LastList)}]|Acc]);
  151. do_regroup([Item|Rest], Attribute, Options, []) ->
  152. do_regroup(Rest, Attribute, Options, [[{grouper, find_deep_value(Attribute, Item, Options)}, {list, [Item]}]]);
  153. do_regroup([Item|Rest], Attribute, Options, [[{grouper, PrevGrouper}, {list, PrevList}]|Acc]) ->
  154. case find_deep_value(Attribute, Item, Options) of
  155. Value when Value =:= PrevGrouper ->
  156. do_regroup(Rest, Attribute, Options, [[{grouper, PrevGrouper}, {list, [Item|PrevList]}]|Acc]);
  157. Value ->
  158. do_regroup(Rest, Attribute, Options, [[{grouper, Value}, {list, [Item]}], [{grouper, PrevGrouper}, {list, lists:reverse(PrevList)}]|Acc])
  159. end.
  160. -spec init_translation(init_translation()) -> none | translate_fun().
  161. init_translation(none) -> none;
  162. init_translation(Fun) when is_function(Fun, 0) ->
  163. init_translation(Fun());
  164. init_translation({M, F}) ->
  165. init_translation({M, F, []});
  166. init_translation({M, F, A}) ->
  167. init_translation(apply(M, F, A));
  168. init_translation(Fun)
  169. when is_function(Fun, 1); is_function(Fun, 2) -> Fun;
  170. init_translation(Other) ->
  171. throw({translation_fun, Other}).
  172. -spec translate(Phrase, Locale, Fun) -> iodata() | default when
  173. Phrase :: phrase(),
  174. Locale :: locale(),
  175. Fun :: none | translate_fun().
  176. translate(Phrase, Locale, TranslationFun) ->
  177. translate(Phrase, Locale, TranslationFun, trans_text(Phrase)).
  178. translate(_Phrase, _Locale, none, Default) -> Default;
  179. translate(Phrase, Locale, TranslationFun, Default) ->
  180. case do_translate(Phrase, Locale, TranslationFun) of
  181. default -> Default;
  182. <<"">> -> Default;
  183. "" -> Default;
  184. Translated ->
  185. Translated
  186. end.
  187. trans_text({Text, _}) -> Text;
  188. trans_text(Text) -> Text.
  189. do_translate(Phrase, _Locale, TranslationFun)
  190. when is_function(TranslationFun, 1) ->
  191. TranslationFun(trans_text(Phrase));
  192. do_translate(Phrase, Locale, TranslationFun)
  193. when is_function(TranslationFun, 2) ->
  194. TranslationFun(Phrase, Locale).
  195. %% @doc Translate and interpolate 'blocktrans' content.
  196. %% Pre-requisites:
  197. %% * `Variables' should be sorted
  198. %% * Each interpolation variable should exist
  199. %% (String="{{a}}", Variables=[{"b", "b-val"}] will fall)
  200. %% * Orddict keys should be string(), not binary()
  201. -spec translate_block(phrase(), locale(), atom(), orddict:orddict(), none | translate_fun()) -> iodata().
  202. translate_block(Phrase, Locale, AutoEscape, Variables, TranslationFun) ->
  203. case translate(Phrase, Locale, TranslationFun, default) of
  204. default -> default;
  205. Translated ->
  206. try interpolate_variables(Translated, Variables, AutoEscape)
  207. catch
  208. {no_close_var, T} ->
  209. io:format(standard_error, "Warning: template translation: variable not closed: \"~s\"~n", [T]),
  210. default;
  211. _:_ -> default
  212. end
  213. end.
  214. interpolate_variables(Tpl, [], _) ->
  215. Tpl;
  216. interpolate_variables(Tpl, Variables, AutoEscape) ->
  217. BTpl = iolist_to_binary(Tpl),
  218. interpolate_variables1(BTpl, Variables, AutoEscape).
  219. interpolate_variables1(Tpl, Vars, AutoEscape) ->
  220. %% pre-compile binary patterns?
  221. case binary:split(Tpl, <<"{{">>) of
  222. [Tpl]=NoVars -> NoVars; %% need to enclose in list due to list tail call below..
  223. [Pre, Post] ->
  224. case binary:split(Post, <<"}}">>) of
  225. [_] -> throw({no_close_var, Tpl});
  226. [Var, Post1] ->
  227. Var1 = string:strip(binary_to_list(Var)),
  228. Value = cast(orddict:fetch(Var1, Vars), AutoEscape),
  229. [Pre, Value | interpolate_variables1(Post1, Vars, AutoEscape)]
  230. end
  231. end.
  232. cast(V, _) when is_integer(V); is_float(V) ->
  233. erlydtl_filters:format_number(V);
  234. cast(V, true) when is_binary(V); is_list(V) ->
  235. erlydtl_filters:force_escape(V);
  236. cast(V, false) when is_binary(V); is_list(V) ->
  237. V;
  238. cast(V, AutoEscape) ->
  239. cast(io_lib:format("~p", [V]), AutoEscape).
  240. are_equal(Arg1, Arg2) when Arg1 =:= Arg2 ->
  241. true;
  242. are_equal(Arg1, Arg2) when is_binary(Arg1) ->
  243. are_equal(binary_to_list(Arg1), Arg2);
  244. are_equal(Arg1, Arg2) when is_binary(Arg2) ->
  245. are_equal(Arg1, binary_to_list(Arg2));
  246. are_equal(Arg1, Arg2) when is_integer(Arg1) ->
  247. are_equal(integer_to_list(Arg1), Arg2);
  248. are_equal(Arg1, Arg2) when is_integer(Arg2) ->
  249. are_equal(Arg1, integer_to_list(Arg2));
  250. are_equal(Arg1, Arg2) when is_atom(Arg1), is_list(Arg2) ->
  251. are_equal(atom_to_list(Arg1), Arg2);
  252. are_equal(Arg1, Arg2) when is_list(Arg1), is_atom(Arg2) ->
  253. are_equal(Arg1, atom_to_list(Arg2));
  254. are_equal(_, _) ->
  255. false.
  256. is_false("") -> true;
  257. is_false(false) -> true;
  258. is_false(undefined) -> true;
  259. is_false(null) -> true;
  260. is_false(0) -> true;
  261. is_false("0") -> true;
  262. is_false(<<"0">>) -> true;
  263. is_false(<<>>) -> true;
  264. is_false(_) -> false.
  265. is_true(V) -> not is_false(V).
  266. 'in'(Sublist, [Sublist|_]) ->
  267. true;
  268. 'in'(Sublist, List) when is_atom(List) ->
  269. 'in'(Sublist, atom_to_list(List));
  270. 'in'(Sublist, List) when is_binary(Sublist) ->
  271. 'in'(binary_to_list(Sublist), List);
  272. 'in'(Sublist, List) when is_binary(List) ->
  273. 'in'(Sublist, binary_to_list(List));
  274. 'in'(Sublist, [C|Rest]) when is_list(Sublist) andalso is_binary(C) ->
  275. 'in'(Sublist, [binary_to_list(C)|Rest]);
  276. 'in'(Sublist, [C|Rest]) when is_list(Sublist) andalso is_list(C) ->
  277. 'in'(Sublist, Rest);
  278. 'in'(Sublist, List) when is_list(Sublist) andalso is_list(List) ->
  279. string:str(List, Sublist) > 0;
  280. 'in'(Element, List) when is_list(List) ->
  281. lists:member(Element, List);
  282. 'in'(_, _) ->
  283. false.
  284. 'not'(Value) ->
  285. not is_true(Value).
  286. 'or'(Value1, Value2) ->
  287. is_true(Value1) or is_true(Value2).
  288. 'and'(Value1, Value2) ->
  289. is_true(Value1) and is_true(Value2).
  290. 'eq'(Value1, Value2) ->
  291. are_equal(Value1, Value2).
  292. 'ne'(Value1, Value2) ->
  293. not are_equal(Value1, Value2).
  294. 'le'(Value1, Value2) ->
  295. not 'gt'(Value1, Value2).
  296. 'ge'(Value1, Value2) ->
  297. not 'lt'(Value1, Value2).
  298. 'gt'(Value1, Value2) when is_list(Value1) ->
  299. 'gt'(list_to_integer(Value1), Value2);
  300. 'gt'(Value1, Value2) when is_list(Value2) ->
  301. 'gt'(Value1, list_to_integer(Value2));
  302. 'gt'(Value1, Value2) when Value1 > Value2 ->
  303. true;
  304. 'gt'(_, _) ->
  305. false.
  306. 'lt'(Value1, Value2) when is_list(Value1) ->
  307. 'lt'(list_to_integer(Value1), Value2);
  308. 'lt'(Value1, Value2) when is_list(Value2) ->
  309. 'lt'(Value1, list_to_integer(Value2));
  310. 'lt'(Value1, Value2) when Value1 < Value2 ->
  311. true;
  312. 'lt'(_, _) ->
  313. false.
  314. stringify_final(In, BinaryStrings) ->
  315. stringify_final(In, [], BinaryStrings).
  316. stringify_final([], Out, _) ->
  317. lists:reverse(Out);
  318. stringify_final([El | Rest], Out, false = BinaryStrings) when is_atom(El) ->
  319. stringify_final(Rest, [atom_to_list(El) | Out], BinaryStrings);
  320. stringify_final([El | Rest], Out, true = BinaryStrings) when is_atom(El) ->
  321. stringify_final(Rest, [atom_to_binary(El, latin1) | Out], BinaryStrings);
  322. stringify_final([El | Rest], Out, BinaryStrings) when is_list(El) ->
  323. stringify_final(Rest, [stringify_final(El, BinaryStrings) | Out], BinaryStrings);
  324. stringify_final([El | Rest], Out, false = BinaryStrings) when is_tuple(El) ->
  325. stringify_final(Rest, [io_lib:print(El) | Out], BinaryStrings);
  326. stringify_final([El | Rest], Out, true = BinaryStrings) when is_tuple(El) ->
  327. stringify_final(Rest, [list_to_binary(io_lib:print(El)) | Out], BinaryStrings);
  328. stringify_final([El | Rest], Out, BinaryStrings) ->
  329. stringify_final(Rest, [El | Out], BinaryStrings).
  330. to_list(Value, true) ->
  331. lists:reverse(to_list(Value, false));
  332. to_list(Value, false) when is_list(Value) ->
  333. Value;
  334. to_list(Value, false) when is_tuple(Value) ->
  335. case element(1, Value) of
  336. 'Elixir.Ecto.Associations.HasMany' ->
  337. Value:to_list();
  338. _ ->
  339. tuple_to_list(Value)
  340. end.
  341. init_counter_stats(List) ->
  342. init_counter_stats(List, undefined).
  343. init_counter_stats(List, Parent) when is_list(List) ->
  344. ListLen = length(List),
  345. [{counter, 1},
  346. {counter0, 0},
  347. {revcounter, ListLen},
  348. {revcounter0, ListLen - 1},
  349. {first, true},
  350. {last, ListLen =:= 1},
  351. {parentloop, Parent}].
  352. increment_counter_stats([{counter, Counter}, {counter0, Counter0}, {revcounter, RevCounter},
  353. {revcounter0, RevCounter0}, {first, _}, {last, _}, {parentloop, Parent}]) ->
  354. [{counter, Counter + 1},
  355. {counter0, Counter0 + 1},
  356. {revcounter, RevCounter - 1},
  357. {revcounter0, RevCounter0 - 1},
  358. {first, false}, {last, RevCounter0 =:= 1},
  359. {parentloop, Parent}].
  360. forloop(_Fun, [], _Parent, Default) -> Default;
  361. forloop(Fun, Values, Parent, _Default) ->
  362. push_ifchanged_context(),
  363. {Result, _Acc} = lists:mapfoldl(Fun, init_counter_stats(Values, Parent), Values),
  364. pop_ifchanged_context(),
  365. Result.
  366. %% keep old version for backwards compatibility..
  367. forloop(_Fun, [], _Parent) -> empty;
  368. forloop(Fun, Values, Parent) ->
  369. {forloop(Fun, Values, Parent, undefined), undefined}.
  370. push_ifchanged_context() ->
  371. IfChangedContextStack = case get(?IFCHANGED_CONTEXT_VARIABLE) of
  372. undefined -> [];
  373. Stack -> Stack
  374. end,
  375. put(?IFCHANGED_CONTEXT_VARIABLE, [[]|IfChangedContextStack]).
  376. pop_ifchanged_context() ->
  377. [_|Rest] = get(?IFCHANGED_CONTEXT_VARIABLE),
  378. put(?IFCHANGED_CONTEXT_VARIABLE, Rest).
  379. ifchanged(Expressions) ->
  380. [IfChangedContext|Rest] = get(?IFCHANGED_CONTEXT_VARIABLE),
  381. {Result, NewContext} = lists:foldl(fun (Expr, {ProvResult, Context}) when ProvResult == true ->
  382. {_, NContext} = ifchanged2(Expr, Context),
  383. {true, NContext};
  384. (Expr, {_ProvResult, Context}) ->
  385. ifchanged2(Expr, Context)
  386. end, {false, IfChangedContext}, Expressions),
  387. put(?IFCHANGED_CONTEXT_VARIABLE, [NewContext|Rest]),
  388. Result.
  389. ifchanged2({Key, Value}, IfChangedContext) ->
  390. PreviousValue = proplists:get_value(Key, IfChangedContext),
  391. if
  392. PreviousValue =:= Value ->
  393. {false, IfChangedContext};
  394. true ->
  395. NewContext = [{Key, Value}|proplists:delete(Key, IfChangedContext)],
  396. {true, NewContext}
  397. end.
  398. cycle(NamesTuple, Counters) when is_tuple(NamesTuple) ->
  399. element(find_value(counter0, Counters) rem size(NamesTuple) + 1, NamesTuple).
  400. widthratio(Numerator, Denominator, Scale) ->
  401. round(Numerator / Denominator * Scale).
  402. spaceless(Contents) ->
  403. Contents1 = lists:flatten(Contents),
  404. Contents2 = re:replace(Contents1, "^\\s+<", "<", [{return,list}]),
  405. Contents3 = re:replace(Contents2, ">\\s+$", ">", [{return,list}]),
  406. Contents4 = re:replace(Contents3, ">\\s+<", "><", [global, {return,list}]),
  407. Contents4.
  408. read_file(Module, Function, DocRoot, FileName, ReaderOptions) ->
  409. AbsName = case filename:absname(FileName) of
  410. FileName -> FileName;
  411. _ -> filename:join([DocRoot, FileName])
  412. end,
  413. case read_file_internal(Module, Function, AbsName, ReaderOptions) of
  414. {ok, Data} -> Data;
  415. {error, Reason} ->
  416. throw({read_file, AbsName, Reason})
  417. end.
  418. read_file_internal(Module, Function, FileName, ReaderOptions) ->
  419. case erlang:function_exported(Module, Function,1) of
  420. true ->
  421. Module:Function(FileName);
  422. false ->
  423. case erlang:function_exported(Module, Function,2) of
  424. true ->
  425. Module:Function(FileName, ReaderOptions);
  426. false ->
  427. {error, "Empty reader"}
  428. end
  429. end.