erlydtl_runtime.erl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. -module(erlydtl_runtime).
  2. -compile(export_all).
  3. -define(IFCHANGED_CONTEXT_VARIABLE, erlydtl_ifchanged_context).
  4. find_value(Key, Data, Options) when is_atom(Key), is_tuple(Data) ->
  5. Rec = element(1, Data),
  6. Info = proplists:get_value(record_info, Options),
  7. case proplists:get_value(Rec, Info) of
  8. Fields when is_list(Fields), length(Fields) == size(Data) - 1 ->
  9. case proplists:get_value(Key, Fields) of
  10. Idx when is_integer(Idx) -> element(Idx, Data);
  11. _ -> undefined
  12. end;
  13. _ -> find_value(Key, Data)
  14. end;
  15. find_value(Key, Data, _Options) ->
  16. find_value(Key, Data).
  17. find_value(_, undefined) ->
  18. undefined;
  19. find_value(Key, Fun) when is_function(Fun, 1) ->
  20. Fun(Key);
  21. find_value(Key, L) when is_atom(Key), is_list(L) ->
  22. case lists:keyfind(Key, 1, L) of
  23. false -> find_value(atom_to_list(Key), L);
  24. {Key, Value} -> Value
  25. end;
  26. find_value(Key, L) when is_list(Key), is_list(L) ->
  27. case lists:keyfind(Key, 1, L) of
  28. false -> find_value(list_to_binary(Key), L);
  29. {Key, Value} -> Value
  30. end;
  31. find_value(Key, L) when is_binary(Key), is_list(L) ->
  32. case lists:keyfind(Key, 1, L) of
  33. false -> undefined;
  34. {Key, Value} -> Value
  35. end;
  36. find_value(Key, L) when is_integer(Key), is_list(L) ->
  37. if Key < length(L) -> lists:nth(Key, L);
  38. true -> undefined
  39. end;
  40. find_value(Key, {GBSize, GBData}) when is_integer(GBSize) ->
  41. case gb_trees:lookup(Key, {GBSize, GBData}) of
  42. {value, Val} ->
  43. Val;
  44. _ ->
  45. undefined
  46. end;
  47. find_value(Key, Tuple) when is_tuple(Tuple) ->
  48. case element(1, Tuple) of
  49. dict ->
  50. case dict:find(Key, Tuple) of
  51. {ok, Val} ->
  52. Val;
  53. _ ->
  54. undefined
  55. end;
  56. _ when is_integer(Key) ->
  57. if Key < size(Tuple) -> element(Key, Tuple);
  58. true -> undefined
  59. end;
  60. Module ->
  61. case lists:member({Key, 1}, Module:module_info(exports)) of
  62. true ->
  63. case Tuple:Key() of
  64. Val when is_tuple(Val) ->
  65. case element(1, Val) of
  66. 'Elixir.Ecto.Associations.BelongsTo' -> Val:get();
  67. 'Elixir.Ecto.Associations.HasOne' -> Val:get();
  68. _ -> Val
  69. end;
  70. Val -> Val
  71. end;
  72. _ ->
  73. undefined
  74. end
  75. end.
  76. fetch_value(Key, Data, Options) ->
  77. case find_value(Key, Data, Options) of
  78. undefined -> [];
  79. Val -> Val
  80. end.
  81. find_deep_value([Key|Rest],Item) ->
  82. case find_value(Key,Item) of
  83. undefined -> undefined;
  84. NewItem -> find_deep_value(Rest,NewItem)
  85. end;
  86. find_deep_value([],Item) -> Item.
  87. regroup(List, Attribute) ->
  88. regroup(List, Attribute, []).
  89. regroup([], _, []) ->
  90. [];
  91. regroup([], _, [[{grouper, LastGrouper}, {list, LastList}]|Acc]) ->
  92. lists:reverse([[{grouper, LastGrouper}, {list, lists:reverse(LastList)}]|Acc]);
  93. regroup([Item|Rest], Attribute, []) ->
  94. regroup(Rest, Attribute, [[{grouper, find_deep_value(Attribute, Item)}, {list, [Item]}]]);
  95. regroup([Item|Rest], Attribute, [[{grouper, PrevGrouper}, {list, PrevList}]|Acc]) ->
  96. case find_deep_value(Attribute, Item) of
  97. Value when Value =:= PrevGrouper ->
  98. regroup(Rest, Attribute, [[{grouper, PrevGrouper}, {list, [Item|PrevList]}]|Acc]);
  99. Value ->
  100. regroup(Rest, Attribute, [[{grouper, Value}, {list, [Item]}], [{grouper, PrevGrouper}, {list, lists:reverse(PrevList)}]|Acc])
  101. end.
  102. translate(_, none, Default) ->
  103. Default;
  104. translate(String, TranslationFun, Default) when is_function(TranslationFun) ->
  105. case TranslationFun(String) of
  106. undefined -> Default;
  107. <<"">> -> Default;
  108. "" -> Default;
  109. Str -> Str
  110. end.
  111. are_equal(Arg1, Arg2) when Arg1 =:= Arg2 ->
  112. true;
  113. are_equal(Arg1, Arg2) when is_binary(Arg1) ->
  114. are_equal(binary_to_list(Arg1), Arg2);
  115. are_equal(Arg1, Arg2) when is_binary(Arg2) ->
  116. are_equal(Arg1, binary_to_list(Arg2));
  117. are_equal(Arg1, Arg2) when is_integer(Arg1) ->
  118. are_equal(integer_to_list(Arg1), Arg2);
  119. are_equal(Arg1, Arg2) when is_integer(Arg2) ->
  120. are_equal(Arg1, integer_to_list(Arg2));
  121. are_equal(Arg1, Arg2) when is_atom(Arg1), is_list(Arg2) ->
  122. are_equal(atom_to_list(Arg1), Arg2);
  123. are_equal(Arg1, Arg2) when is_list(Arg1), is_atom(Arg2) ->
  124. are_equal(Arg1, atom_to_list(Arg2));
  125. are_equal(_, _) ->
  126. false.
  127. is_false("") -> true;
  128. is_false(false) -> true;
  129. is_false(undefined) -> true;
  130. is_false(0) -> true;
  131. is_false("0") -> true;
  132. is_false(<<"0">>) -> true;
  133. is_false(<<>>) -> true;
  134. is_false(_) -> false.
  135. is_true(V) -> not is_false(V).
  136. 'in'(Sublist, [Sublist|_]) ->
  137. true;
  138. 'in'(Sublist, List) when is_atom(List) ->
  139. 'in'(Sublist, atom_to_list(List));
  140. 'in'(Sublist, List) when is_binary(Sublist) ->
  141. 'in'(binary_to_list(Sublist), List);
  142. 'in'(Sublist, List) when is_binary(List) ->
  143. 'in'(Sublist, binary_to_list(List));
  144. 'in'(Sublist, [C|Rest]) when is_list(Sublist) andalso is_binary(C) ->
  145. 'in'(Sublist, [binary_to_list(C)|Rest]);
  146. 'in'(Sublist, [C|Rest]) when is_list(Sublist) andalso is_list(C) ->
  147. 'in'(Sublist, Rest);
  148. 'in'(Sublist, List) when is_list(Sublist) andalso is_list(List) ->
  149. string:str(List, Sublist) > 0;
  150. 'in'(Element, List) when is_list(List) ->
  151. lists:member(Element, List);
  152. 'in'(_, _) ->
  153. false.
  154. 'not'(Value) ->
  155. not is_true(Value).
  156. 'or'(Value1, Value2) ->
  157. is_true(Value1) or is_true(Value2).
  158. 'and'(Value1, Value2) ->
  159. is_true(Value1) and is_true(Value2).
  160. 'eq'(Value1, Value2) ->
  161. are_equal(Value1, Value2).
  162. 'ne'(Value1, Value2) ->
  163. not are_equal(Value1, Value2).
  164. 'le'(Value1, Value2) ->
  165. not 'gt'(Value1, Value2).
  166. 'ge'(Value1, Value2) ->
  167. not 'lt'(Value1, Value2).
  168. 'gt'(Value1, Value2) when is_list(Value1) ->
  169. 'gt'(list_to_integer(Value1), Value2);
  170. 'gt'(Value1, Value2) when is_list(Value2) ->
  171. 'gt'(Value1, list_to_integer(Value2));
  172. 'gt'(Value1, Value2) when Value1 > Value2 ->
  173. true;
  174. 'gt'(_, _) ->
  175. false.
  176. 'lt'(Value1, Value2) when is_list(Value1) ->
  177. 'lt'(list_to_integer(Value1), Value2);
  178. 'lt'(Value1, Value2) when is_list(Value2) ->
  179. 'lt'(Value1, list_to_integer(Value2));
  180. 'lt'(Value1, Value2) when Value1 < Value2 ->
  181. true;
  182. 'lt'(_, _) ->
  183. false.
  184. stringify_final(In, BinaryStrings) ->
  185. stringify_final(In, [], BinaryStrings).
  186. stringify_final([], Out, _) ->
  187. lists:reverse(Out);
  188. stringify_final([El | Rest], Out, false = BinaryStrings) when is_atom(El) ->
  189. stringify_final(Rest, [atom_to_list(El) | Out], BinaryStrings);
  190. stringify_final([El | Rest], Out, true = BinaryStrings) when is_atom(El) ->
  191. stringify_final(Rest, [atom_to_binary(El, latin1) | Out], BinaryStrings);
  192. stringify_final([El | Rest], Out, BinaryStrings) when is_list(El) ->
  193. stringify_final(Rest, [stringify_final(El, BinaryStrings) | Out], BinaryStrings);
  194. stringify_final([El | Rest], Out, false = BinaryStrings) when is_tuple(El) ->
  195. stringify_final(Rest, [io_lib:print(El) | Out], BinaryStrings);
  196. stringify_final([El | Rest], Out, true = BinaryStrings) when is_tuple(El) ->
  197. stringify_final(Rest, [list_to_binary(io_lib:print(El)) | Out], BinaryStrings);
  198. stringify_final([El | Rest], Out, BinaryStrings) ->
  199. stringify_final(Rest, [El | Out], BinaryStrings).
  200. to_list(Value, true) ->
  201. lists:reverse(to_list(Value, false));
  202. to_list(Value, false) when is_list(Value) ->
  203. Value;
  204. to_list(Value, false) when is_tuple(Value) ->
  205. case element(1, Value) of
  206. 'Elixir.Ecto.Associations.HasMany' ->
  207. Value:to_list();
  208. _ ->
  209. tuple_to_list(Value)
  210. end.
  211. init_counter_stats(List) ->
  212. init_counter_stats(List, undefined).
  213. init_counter_stats(List, Parent) when is_list(List) ->
  214. [{counter, 1},
  215. {counter0, 0},
  216. {revcounter, length(List)},
  217. {revcounter0, length(List) - 1},
  218. {first, true},
  219. {last, length(List) =:= 1},
  220. {parentloop, Parent}].
  221. increment_counter_stats([{counter, Counter}, {counter0, Counter0}, {revcounter, RevCounter},
  222. {revcounter0, RevCounter0}, {first, _}, {last, _}, {parentloop, Parent}]) ->
  223. [{counter, Counter + 1},
  224. {counter0, Counter0 + 1},
  225. {revcounter, RevCounter - 1},
  226. {revcounter0, RevCounter0 - 1},
  227. {first, false}, {last, RevCounter0 =:= 1},
  228. {parentloop, Parent}].
  229. forloop(Fun, Acc0, Values) ->
  230. push_ifchanged_context(),
  231. Result = lists:mapfoldl(Fun, Acc0, Values),
  232. pop_ifchanged_context(),
  233. Result.
  234. push_ifchanged_context() ->
  235. IfChangedContextStack = case get(?IFCHANGED_CONTEXT_VARIABLE) of
  236. undefined -> [];
  237. Stack -> Stack
  238. end,
  239. put(?IFCHANGED_CONTEXT_VARIABLE, [[]|IfChangedContextStack]).
  240. pop_ifchanged_context() ->
  241. [_|Rest] = get(?IFCHANGED_CONTEXT_VARIABLE),
  242. put(?IFCHANGED_CONTEXT_VARIABLE, Rest).
  243. ifchanged(Expressions) ->
  244. [IfChangedContext|Rest] = get(?IFCHANGED_CONTEXT_VARIABLE),
  245. {Result, NewContext} = lists:foldl(fun (Expr, {ProvResult, Context}) when ProvResult == true ->
  246. {_, NContext} = ifchanged2(Expr, Context),
  247. {true, NContext};
  248. (Expr, {_ProvResult, Context}) ->
  249. ifchanged2(Expr, Context)
  250. end, {false, IfChangedContext}, Expressions),
  251. put(?IFCHANGED_CONTEXT_VARIABLE, [NewContext|Rest]),
  252. Result.
  253. ifchanged2({Key, Value}, IfChangedContext) ->
  254. PreviousValue = proplists:get_value(Key, IfChangedContext),
  255. if
  256. PreviousValue =:= Value ->
  257. {false, IfChangedContext};
  258. true ->
  259. NewContext = [{Key, Value}|proplists:delete(Key, IfChangedContext)],
  260. {true, NewContext}
  261. end.
  262. cycle(NamesTuple, Counters) when is_tuple(NamesTuple) ->
  263. element(find_value(counter0, Counters) rem size(NamesTuple) + 1, NamesTuple).
  264. widthratio(Numerator, Denominator, Scale) ->
  265. round(Numerator / Denominator * Scale).
  266. spaceless(Contents) ->
  267. Contents1 = lists:flatten(Contents),
  268. Contents2 = re:replace(Contents1, "^\\s+<", "<", [{return,list}]),
  269. Contents3 = re:replace(Contents2, ">\\s+$", ">", [{return,list}]),
  270. Contents4 = re:replace(Contents3, ">\\s+<", "><", [global, {return,list}]),
  271. Contents4.
  272. read_file(Module, Function, DocRoot, FileName) ->
  273. AbsName = case filename:absname(FileName) of
  274. FileName -> FileName;
  275. _ -> filename:join([DocRoot, FileName])
  276. end,
  277. {ok, Binary} = Module:Function(AbsName),
  278. binary_to_list(Binary).