Browse Source

Merge branch 'master' into stable

Conflicts:
	NEWS.md
	rebar.config
	src/erlydtl.app.src

Resolved conflicts to keep master a development branch, with a clean
NEWS file, while the stable branch has the full NEWS history and
pinned dependencies and a hard coded version in the .app.src file.
Andreas Stenius 11 years ago
parent
commit
282279b0b6

+ 3 - 3
NEWS.md

@@ -7,8 +7,9 @@ Standards](http://www.gnu.org/prep/standards/html_node/NEWS-File.html#NEWS-File)
 
 ## 0.9.3 (2014-03-27)
 
-* Fixed list indexing (#155).
-* Fixed release process to work for non-git installations (#154).
+* Fix release process to work for non-git installations (#154).
+* Fix list indexing (#155).
+* New option for 0-based list/tuple access (#156) (see README for details).
 
 
 ## 0.9.2 (2014-03-22)
@@ -159,4 +160,3 @@ and will be the starting point for a lot of future improvements.
 * New README file.
 * Remove create_parser stuff because "make" handles this.
 * Make `erlydtl:compile/2,3` a wrapper for `erlydtl_compiler:compile/2,3`.
-

+ 23 - 0
README.markdown

@@ -178,6 +178,12 @@ Options is a proplist possibly containing:
   custom tags and filters. `Module` should implement the
   `erlydtl_library` behaviour (see [Custom tags and filters] below).
 
+* `lists_0_based` - **Compatibility warning** Defaults to `false`,
+  giving 1-based list access, as is common practice in Erlang. Set it
+  to `true` to get 1-based access as in Django, or to `defer` to not
+  decide until render time, using the render option
+  `lists_0_based`. See also `tuples_0_based`.
+
 * `locale` **deprecated** - The same as {blocktrans_locales, [Val]}.
 
 * `no_env` - Do not read additional options from the OS environment
@@ -215,6 +221,13 @@ Options is a proplist possibly containing:
 
 * `report_errors` - Print errors as they occur.
 
+* `tuples_0_based` - **Compatibility warning** Defaults to `false`,
+  giving 1-based tuple access, as is common practice in Erlang. Set it
+  to `true` to get 1-based access as in Django, or to `defer` to not
+  decide until render time, using the render option
+  `tuples_0_based`. See also `lists_0_based`.
+
+
 * `vars` - Variables (and their values) to evaluate at compile-time
   rather than render-time. (Currently not strictly true, see
   [#61](https://github.com/erlydtl/erlydtl/issues/61))
@@ -284,9 +297,19 @@ Same as `render/1`, but with the following options:
   for blocktrans variable interpolation should be wrapped to `{{` and
   `}}`.
 
+* `lists_0_based` - If the compile option `lists_0_based` was set to
+  `defer`, pass this option (or set it to true, `{lists_0_based,
+  true}`) to get 0-based list indexing when rendering the
+  template. See also `tuples_0_based`.
+
 * `locale` - A string specifying the current locale, for use with the
   `blocktrans_fun` compile-time option.
 
+* `tuples_0_based` - If the compile option `tuples_0_based` was set to
+  `defer`, pass this option (or set it to true, `{tuples_0_based,
+  true}`) to get 0-based tuple indexing when rendering the
+  template. See also `lists_0_based`.
+
 
 ### translatable_strings/0
 

+ 3 - 1
include/erlydtl_ext.hrl

@@ -32,7 +32,9 @@
           all_options = [],
           errors = #error_info{},
           warnings = #error_info{},
-          bin = undefined
+          bin = undefined,
+          lists_0_based = false,
+          tuples_0_based = false
          }).
 
 -record(ast_info, {

+ 13 - 3
src/erlydtl_beam_compiler.erl

@@ -1118,13 +1118,23 @@ resolve_variable_ast(VarTuple, FinderFunction, TreeWalker) ->
 
 resolve_variable_ast1({attribute, {{_, Pos, Attr}, Variable}}, {Runtime, Finder}=FinderFunction, TreeWalker) ->
     {{VarAst, VarInfo}, TreeWalker1} = resolve_variable_ast(Variable, FinderFunction, TreeWalker),
+    #treewalker{
+       context=#dtl_context{
+                  lists_0_based = Lists0Based,
+                  tuples_0_based = Tuples0Based
+                 }
+      } = TreeWalker,
     FileName = get_current_file(TreeWalker1),
     {{?Q(["'@Runtime@':'@Finder@'(",
           "  _@Attr@, _@VarAst,",
-          "  [{filename, _@FileName@},",
-          "   {pos, _@Pos@},",
+          "  [",
+          "   {lists_0_based, _@Lists0Based@},",
+          "   {tuples_0_based, _@Tuples0Based@},",
+          "   {render_options, RenderOptions},",
           "   {record_info, _RecordInfo},",
-          "   {render_options, RenderOptions}])"]),
+          "   {filename, _@FileName@},",
+          "   {pos, _@Pos@}",
+          "  ])"]),
       VarInfo},
      TreeWalker1};
 

+ 3 - 1
src/erlydtl_compiler.erl

@@ -253,7 +253,9 @@ init_context(ParseTrail, DefDir, Module, Options) ->
            record_info = [{R, lists:zip(I, lists:seq(2, length(I) + 1))}
                           || {R, I} <- proplists:get_value(record_info, Options, Ctx#dtl_context.record_info)],
            errors = init_error_info(errors, Ctx#dtl_context.errors, Options),
-           warnings = init_error_info(warnings, Ctx#dtl_context.warnings, Options)
+           warnings = init_error_info(warnings, Ctx#dtl_context.warnings, Options),
+           lists_0_based = proplists:get_value(lists_0_based, Options, Ctx#dtl_context.lists_0_based),
+           tuples_0_based = proplists:get_value(tuples_0_based, Options, Ctx#dtl_context.tuples_0_based)
           },
     Context = load_libraries(proplists:get_value(default_libraries, Options, []), Context0),
     case call_extension(Context, init_context, [Context]) of

+ 18 - 1
src/erlydtl_runtime.erl

@@ -17,9 +17,26 @@ find_value(Key, Data, Options) when is_atom(Key), is_tuple(Data) ->
             end;
         _ -> find_value(Key, Data)
     end;
+find_value(Key, Data, Options) when is_integer(Key), is_list(Data) ->
+    find_value(adjust_index(Key, 1, lists_0_based, Options), Data);
+find_value(Key, Data, Options) when is_integer(Key), is_tuple(Data) ->
+    find_value(adjust_index(Key, 1, tuples_0_based, Options), Data);
 find_value(Key, Data, _Options) ->
     find_value(Key, Data).
 
+adjust_index(Key, Off, Opt, Options) when is_list(Options) ->
+    case proplists:get_value(Opt, Options) of
+        defer ->
+            adjust_index(
+              Key, Off, Opt,
+              proplists:get_value(render_options, Options));
+        true ->
+            Key + Off;
+        _ ->
+            Key
+    end;
+adjust_index(Key, _Off, _Opt, _Options) -> Key.
+
 find_value(_, undefined) ->
     undefined;
 find_value(Key, Fun) when is_function(Fun, 1) ->
@@ -60,7 +77,7 @@ find_value(Key, Tuple) when is_tuple(Tuple) ->
                     undefined
             end;
         _ when is_integer(Key) ->
-            if Key < size(Tuple) -> element(Key, Tuple);
+            if Key =< size(Tuple) -> element(Key, Tuple);
                true -> undefined
             end;
         Module ->

+ 29 - 1
test/erlydtl_test_defs.erl

@@ -108,9 +108,37 @@ all_test_defs() ->
        {"Index tuple variable",
         <<"{{ var1.2 }}">>, [{var1, {a, b, c}}],
         <<"b">>},
-       {"Index all elements of list",
+       {"Index all elements of list (default, 1-based)",
         <<"{{ var1.1 }},{{ var1.2 }},{{ var1.3 }}.">>,
         [{var1, [a, b, c]}],
+        <<"a,b,c.">>},
+       {"Index all list elements 0-based (selected at compile time)",
+        <<"{{ var1.0 }},{{ var1.1 }},{{ var1.2 }}.">>,
+        [{var1, [a, b, c]}], [], [lists_0_based],
+        <<"a,b,c.">>},
+       {"Index all list elements 0-based (selected at render time)",
+        <<"{{ var1.0 }},{{ var1.1 }},{{ var1.2 }}.">>,
+        [{var1, [a, b, c]}], [lists_0_based], [{lists_0_based, defer}],
+        <<"a,b,c.">>},
+       {"Index all list elements 1-based (selected at render time)",
+        <<"{{ var1.1 }},{{ var1.2 }},{{ var1.3 }}.">>,
+        [{var1, [a, b, c]}], [], [{lists_0_based, defer}],
+        <<"a,b,c.">>},
+       {"Index all elements of tuple (default, 1-based)",
+        <<"{{ var1.1 }},{{ var1.2 }},{{ var1.3 }}.">>,
+        [{var1, {a, b, c}}],
+        <<"a,b,c.">>},
+       {"Index all tuple elements 0-based (selected at compile time)",
+        <<"{{ var1.0 }},{{ var1.1 }},{{ var1.2 }}.">>,
+        [{var1, {a, b, c}}], [], [tuples_0_based],
+        <<"a,b,c.">>},
+       {"Index all tuple elements 0-based (selected at render time)",
+        <<"{{ var1.0 }},{{ var1.1 }},{{ var1.2 }}.">>,
+        [{var1, {a, b, c}}], [tuples_0_based], [{tuples_0_based, defer}],
+        <<"a,b,c.">>},
+       {"Index all tuple elements 1-based (selected at render time)",
+        <<"{{ var1.1 }},{{ var1.2 }},{{ var1.3 }}.">>,
+        [{var1, {a, b, c}}], [], [{tuples_0_based, defer}],
         <<"a,b,c.">>}
       ]},
      {"now",