Giter Site home page Giter Site logo

erlydtl's Introduction

ErlyDTL Build Status

ErlyDTL compiles Django Template Language to Erlang bytecode.

Project homepage: https://github.com/erlydtl/erlydtl/wiki

ErlyDTL implements the Django Template Language as documented for version 1.6, here: https://django.readthedocs.org/en/1.6.x/ref/templates/builtins.html

Despite our best efforts to be completely compatible with the Django Template Language, there are still a few differences.

Call for maintainers

My current position in life does not give me the time I feel this project deserves. So, if you feel that there are things that ought to be done, and have the skills and time to do them, please get in touch. -- @kaos

The erlydtl branches & tags

We follow a gitflow inspired branching model, where master is the latest released version (a.k.a. stable), and use develop for stuff that may break from time to time.

master branch

Releases are made from the master branch, with dependency versions pinned down and a hard coded version number in the app file.

develop branch

This is were all the action is, and at times may be slightly broken. Suitable for early adopters who aren't afraid of a little debugging and hopefully also reporting issues.

release tags

Whenever master is deemed stable with a nice set of additions/changes, it is tagged for a new release.

As we're still going for the big 1.0 release, breaking changes may be introduced also on minor release bumps; but after that, we'll stick with semver.

Compilation

To compile ErlyDTL, run

make

in this directory.

Do not use Erlang R16B03

The erl syntax tools is broken in Erlang R16B03, use R16B03-1 or any other supported version instead.

Do use a recent version of rebar

In case of compilation issues, make sure your version of rebar is up-to-date.

Older versions of rebar does not support the raw option for dependencies, and may produce an error message like this:

ERROR: Invalid dependency specification {merl,".*",
                                         {git,
                                          "git://github.com/erlydtl/merl.git",
                                          "28e5b3829168199e8475fa91b997e0c03b90d280"},
                                         [raw]} 

Template compilation

Usage:

erlydtl:compile_file("/path/to/template.dtl", my_module_name)

erlydtl:compile_file("/path/to/template.dtl", my_module_name, Options)

erlydtl:compile_template("<html>{{ foo }}</html>", my_module_name)

erlydtl:compile_template("<html>{{ foo }}</html>", my_module_name, Options)

Result:

{ok, Module}
{ok, Module, Warnings}
{ok, Module, Binary}
{ok, Module, Binary, Warnings}

error
{error, Errors, Warnings}

Options is a proplist possibly containing:

  • auto_escape - Control automatic HTML escaping of template values. Enabled by default.

  • binary - Include the compiled template binary code in the result tuple (between the module name and any warning/error lists). Note, this option is named the same as for the Erlang compiler, with similar use, except that this option does NOT affect whether or not a .beam file is saved.

  • binary_strings - Whether to compile strings as binary terms (rather than lists). Defaults to true.

  • compiler_options - Proplist with extra options passed directly to compiler:forms/2. This can prove useful when using extensions to add extra defines etc when compiling the generated code.

  • constants - Replace template variables with a constant value when compiling the template. This can not be overridden when rendering the template. See also default_vars.

  • custom_filters_modules deprecated - A list of modules to be used for handling custom filters. The modules will be searched in order and take precedence over the built-in filters. Each custom filter should correspond to an exported filter, e.g.

    some_filter(Value) -> iolist()

    If the filter takes any arguments (e.g. "foo:2"), those will be added to the call:

    some_filter(Value, Arg) -> iolist()
  • custom_tags_dir - Directory of DTL files (no extension) includable as tags. E.g. if $custom_tags_dir/foo contains <b>{{ bar }}</b>, then {% foo bar=100 %} will evaluate to <b>100</b>.

  • custom_tags_modules deprecated - A list of modules to be used for handling custom tags. The modules will be searched in order and take precedence over custom_tags_dir. Each custom tag should correspond to an exported function with one of the following signatures:

    some_tag(TagVars)          -> iolist()
    some_tag(TagVars, Options) -> iolist()

    The TagVars are variables provided to a custom tag in the template's body (e.g. {% foo bar=100 %} results in TagVars = [{bar, 100}]). The Options are options passed as the second argument to the render/2 call at render-time. (These may include any options, not just locale and translation_fun.)

  • debug_compiler - Enable compiler debug diagnostics. Currently it debug prints the options passed to compile:forms (i.e. if verbosity is >= 2; that is, with two or more verbose options) and enables the saving of the compiled template in source form to a .erl file.

  • debug_info - This option is passed to compile:forms to include debug information in the compiled module.

  • debug_root - Only applies when debug_compiler is true. The root directory for debug source dumps. If set to false, no source dump will be saved. Defaults to undefined, leaving the source dump next to the source template file.

  • default_libraries - A list of libraries that should be loaded by default when compiling a template. Libraries can be specified either by name (when there is a name to module mapping also provided in the libraries option) or by module.

  • default_vars - Provide default values for variables. Any value from the render variables takes precedence. Notice: in case the value is a fun/0, it will be called at compile time. See also constants.

  • doc_root - Included template paths will be relative to this directory; defaults to the compiled template's directory.

  • extension_module experimental - This is work in progress to make erlydtl extensible.

  • force_recompile - Recompile the module even if the source's checksum has not changed. Useful for debugging.

  • libraries - A list of {Name, Module} libraries implementing 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 - Locale to translate to during compile time. May be specified multiple times as well as together with the locales option.

  • locales - A list of locales to be passed to translation_fun. Defaults to [].

  • no_env - Do not read additional options from the OS environment variable ERLYDTL_COMPILER_OPTIONS.

  • no_load - Do not load the compiled template.

  • out_dir - Directory to store generated .beam files. If not specified, no .beam files will be created and a warning is emitted. To silence the warning, use {out_dir, false}.

  • reader - {module, function} tuple that takes a path to a template, may be ReaderOptions and returns a binary with the file contents. Defaults to {file, read_file}. Useful for reading templates from a network resource.

  • reader_options - list of {option_name, Option} that passed as the second parameter to reader.

      extra_reader(FileName, ReaderOptions) ->
       UserID = proplists:get_value(user_id, ReaderOptions, <<"IDUnknown">>),
       UserName = proplists:get_value(user_name, ReaderOptions, <<"NameUnknown">>),
       case file:read_file(FileName) of
        {ok, Data} when UserID == <<"007">>, UserName == <<"Agent">> ->
         {ok, Data};
        {ok, _Data} ->
         {error, "Not Found"};
        Err ->
         Err
       end.
      CompileResult = erlydtl:compile(Body, ModName,
      										[return,
      										  {reader, {?MODULE, extra_reader}},
      										  {reader_options, [{user_id, <<"007">>},
      											    {user_name, <<"Agent">>}]}
      									    ]),
    
  • record_info - List of records to look for when rendering the template. Each record info is a tuple with the fields of the record:

    {my_record, record_info(fields, my_record)}
  • return - Short form for both return_warnings and return_errors.

  • return_warnings - If this flag is set, then an extra field containing warnings is added to the tuple returned on success.

  • return_errors - If this flag is set, then an error-tuple with two extra fields containing errors and warnings is returned when there are errors.

  • report - Short form for both report_warnings and report_errors.

  • report_warnings - Print warnings as they occur.

  • report_errors - Print errors as they occur.

  • translation_fun - A two-argument fun to use for translating blocktrans blocks, trans tags and _(..) expressions at compile time. This will be called once for each pair of translated element and locale specified with locales and locale options. The fun should take the form:

    fun (Block::string(), Locale|{Locale, Context}) ->
        <<"ErlyDTL code">>::binary() | default
      when Locale::string(), Context::string().

    Please, keep in mind, that if your templates where not specially designed, you probably still need render time translations. See description of the translation_fun render option for more details on the translation context.

    Notice, you may instead pass a fun/0, {Module, Function} or {Module, Function, Args} which will be called recursively until it yields a valid translation function, at which time any needed translation setup actions can be carried out prior to returning the next step (either another setup function/tuple, or the translation function).

    %% sample translation setup
    fun () ->
        translation_engine:init(),
        fun translation_engine:translate/2
    end
  • 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 deprecated - Use default_vars instead. Variables (and their values) to evaluate at compile-time rather than render-time.

  • verbose - Enable verbose printing of compilation progress. Add several for even more verbose (e.g. debug) output.

  • w - Enable/Disable compile time checks.

    Available checks:

    • non_block_tag indicated that there is other data than block tags in an extends-template (e.g. a template that begins with the extends tag).
  • warnings_as_errors - Treat warnings as errors.

Helper compilation

Helpers provide additional templating functionality and can be used in conjunction with the custom_tags_module option above. They can be created from a directory of templates thusly:

erlydtl:compile_dir("/path/to/dir", my_helper_module_name)

erlydtl:compile_dir("/path/to/dir", my_helper_module_name, Options)

The resulting module will export a function for each template appearing in the specified directory. Options is the same as for compile/3.

Compiling a helper module can be more efficient than using custom_tags_dir because the helper functions will be compiled only once (rather than once per template).

Notice: The exported template functions return an iolist() on success only, failures are non-local (e.g. as a throw). To get the result in wrapped tuple {ok, iolist()} | {error, Reason} call one of the render functions: render(Tag) | render(Tag, Vars) | render(Tag, Vars, Opts).

Usage (of a compiled template)

render/1

my_compiled_template:render(Variables) -> {ok, IOList} | {error, Err}

Variables is a proplist, dict, gb_tree, or a parameterized module (whose method names correspond to variable names). The variable values can be atoms, strings, binaries, or (nested) variables.

IOList is the rendered template.

render/2

my_compiled_template:render(Variables, Options) -> {ok, IOList} | {error, Err}

Same as render/1, but with the following options:

  • translation_fun - A fun/1 or fun/2 that will be used to translate strings appearing inside {% trans %} and {% blocktrans %} tags at render-time. The simplest TranslationFun would be fun(Val) -> Val end. Placeholders for blocktrans variable interpolation should be wrapped in {{ and }}. In case of fun/2, the extra argument is the current locale, possibly together with a translation context in a tuple:

    fun (Val|{Val, {Plural_Val, Count}}, Locale|{Locale, Context}) ->
        Translated_Val
    end

    The context is present when specified in the translation tag. Example:

    {% trans "Some text to translate" context "app-specific" %}
      or
    {% blocktrans context "another-context" %}
      Translate this for {{ name }}.
    {% endblocktrans %}

    The plural form is present when using count and plural in a blocktrans block:

    {% blocktrans count counter=var|length %}
      There is {{ counter }} element in the list.
    {% plural %}
      There are {{ counter }} elements in the list.
    {% endblocktrans %}

    Render time translation function is also used to translate dates. Date tokens mimics those used in django, so you may reuse django translations. Tokens may appear in a date are:

    1. Full months names, capitalized ("January" .. "December");
    2. 3 letters months names ("jan" .. "dec");
    3. Associated Press style months ("Jan." .. "Dec." with "abbrev.month" context);
    4. Alternative month name, for "E" option ("January" .. "December" with "alt. month" context);
    5. Full week day names, capitalized ("Monday" .. "Sunday");
    6. Abbreviated week day names, capitalized ("Mon" .. "Sun");
    7. day time tokens ("AM", "PM", "a.m.", "p.m.", "noon", "midnight");

    While date token values may be passed as lists, consider using binaries as a default string format. Here is a simple but robust translation function stub:

       translation_placeholder({Val,{Plural, Count}}, {L, C}) when is_list(Val) ->
         translation_placeholder({list_to_binary(Val),{Plural, Count}}, LC);
       translation_placeholder(Val, {L, C}) when is_list(Val) ->
         translation_placeholder(list_to_binary(Val), LC);
       translation_placeholder(Val, {L, C}) when is_list(C) ->
         translation_placeholder(Val, list_to_binary(C));
       translation_placeholder(Val, {L, C}) when is_list(C) ->
         io:format("Translating ~p into ~p with context ~p~n", [Val, L, C]),
         %% do nothing and return original value
         Val.

    The translation fun can also be a fun/0, {Module, Function} or {Module, Function, Args} which will be called recursively until it yields a valid translation function, at which time any needed translation setup actions can be carried out prior to returning the next step (either another setup function/tuple, or the translation function).

  • 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 translation_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

my_compiled_template:translatable_strings() -> [String]

List of strings appearing in {% trans %} and _(..) tags.

translated_blocks/0

my_compiled_template:translated_blocks() -> [String]

List of strings appearing in {% blocktrans %}...{% endblocktrans %} blocks; the translations (which can contain ErlyDTL code) are hard-coded into the module and appear at render-time. To get a list of translatable blocks before compile-time, use the provided blocktrans_extractor module.

source/0

my_compiled_template:source() -> {FileName, CheckSum}

Name and checksum of the original template file.

dependencies/0

my_compiled_template:dependencies() -> [{FileName, CheckSum}]

List of names/checksums of templates included by the original template file. Useful for frameworks that recompile a template only when the template's dependencies change.

variables/0

my_compiled_template:variables() -> [Variable::atom()]

Sorted list of unique variables used in the template's body. The list can be used for determining which variable bindings need to be passed to the render/3 function.

default_variables/0

my_compiled_template:default_variables() -> [Variable::atom()]

Like variables/0, but for any variable which have a default value provided at compile time.

constants/0

my_compiled_template:constants() -> [Variable::atom()]

Like default_variables/0, but these template variables has been replaced with a fixed value at compile time and can not be changed when rendering the template.

Custom tags and filters

Starting with release 0.9.1, the recommended way to add custom tags and filters are to register a module implementing the erlydtl_library behaviour. There are two functions needed to implement a custom library: version/0 and inventory/1.

The version/0 function is to be able to keep backwards compatibility in face of an evolving library behaviour, and should return the version of the behaviour the library supports. The valid range of versions is in the spec for the version/0 callback in erlydtl_library.erl.

Library version 1

The inventory/1 function is called with either filters or tags as argument, and should return a list of all filters or tags available in that library, respectively (see spec in erlydtl_library.erl for details on syntax for this list).

Tag functions must take a list of arguments, and may take a list of render options, and should return an iolist() as result value (see custom_tags_modules compile option).

Filter functions must take an iolist() value to filter, and may take an argument, and should return an iolist() as result value (see custom_filters_modules compile option).

Differences from standard Django Template Language

  • csrf_token The Cross Site Request Forgery tag is not implemented.
  • url The url tag is not implemented. This should be addressed in a future release.
  • List indexing is 1-based in erlydtl, while 0-based in Django (see #156).
  • For an up-to-date list, see all issues marked dtl_compat.
  • Erlang specifics: Template variables may be prefixed with underscore (_) to avoid "unused variable" warnings (see #164).
  • cycle tags do not support independently moving the cycle value from the original loop.

Tests

From a Unix shell, run:

make tests

Note that the tests will create some output in tests/output in case of regressions.

License

ErlyDTL is released under the MIT license.

erlydtl's People

Contributors

a40in avatar alexr avatar archaelus avatar brigadier avatar burbas avatar choptastic avatar danikp avatar davidw avatar dcy avatar dgulino avatar evanmiller avatar garazdawi avatar gardenia avatar gwitmond avatar hdima avatar jflatow avatar kaos avatar lemenkov avatar licenser avatar noss avatar oxpa avatar pablo-meier avatar ricecake avatar rsaccon avatar runejuhl avatar saleyn avatar sendtopms avatar seriyps avatar srstrong avatar ten0s avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

erlydtl's Issues

Compile-time variables don't work as expected

Do compile-time variables work at all? The README documentation states:
vars - Variables (and their values) to evaluate at compile-time rather than render-time.

Here's a test template:

$ cat src/cmp_test.dtl
{{ s.X }}
{{ g.Y }}
{{ Z }}

1> erlydtl:compile("src/cmp_test.dtl", cmp_test,
    [{compiler_options,[verbose,debug_info]}, {doc_root,"src"},
     {force_recompile,true}, {module_ext,[]}, {out_dir,"ebin"}, {recursive,false},
     {verbose,true}, {vars, [{s, [{'X', "TestX"}]}]}]).
Template module: cmp_test -> ebin/cmp_test.beam
ok

After decompiling the resulting "ebin/cmp_test.beam with decompile:run("ebin/cmp_test.beam", [erl]). (https://github.com/saleyn/util/blob/master/src/decompiler.erl), we get "cmp_test.erl" (redacted):

erlydtl_runtime:stringify_final(
    [pre_render0([{s, [{'X', "TestX"}]}]),         
     <<"\n">>,
     erlydtl_filters:format_number(
        erlydtl_runtime:find_value('Y',
            erlydtl_runtime:find_value(g, _Variables))),
     <<"\n">>,
     erlydtl_filters:format_number(
        erlydtl_runtime:find_value('Z', _Variables)),
     <<"\n">>],
    true).

pre_render0(_Variables) ->
    erlydtl_filters:format_number(
        erlydtl_runtime:find_value('X',
            erlydtl_runtime:find_value(s, _Variables))).                                

This is not what I would expect to see in the compiled code. I would expect that the {{ s.X }} variable would get replaced by a constant <<"TestX">> instead of a pre_render0/1 call that evaluates the value at run-time. This contradicts documentation.

2> erlydtl:compile("src/cmp_test.dtl", cmp_test,
    [{compiler_options,[verbose,debug_info]}, {doc_root,"src"},
     {force_recompile,true}, {module_ext,[]}, {out_dir,"ebin"}, {recursive,false},
     {verbose,true}, {vars, [{s, [{'X', "TestX"}]}, {'Z', "CTestZ"}]}]).
Template module: cmp_test -> ebin/cmp_test.beam
ok

When we also pass the value of variable 'Z' at compile time, it has no effect on the compilation result - the source code of cmp_test.erl:render_internal/2 is identical to the above. Yet I would expect it would be:

erlydtl_runtime:stringify_final(
    [<<"TestX">>,         
     <<"\n">>,
     erlydtl_filters:format_number(
        erlydtl_runtime:find_value('Y',
            erlydtl_runtime:find_value(g, _Variables))),
     <<"\n">>,
     <<"CTestZ">>,
     <<"\n">>],
    true).

load tag

There's a load tag in Django, which we don't have.

I suggest that we implement the load tag thus that we can add modules to the filter_modules and custom_tags_modules options in the compilers dtl_context.

Also, in order to support loading only a named subset of the filters/tags from a module, it seems a small refactoring of those options would be in order. Namely, fill in a list for each of filters and tags available from the modules. (hope it makes sense, it's as much for myself so I don't forget it..)

extends has bad error reporting

If I try to extends "some_nonexistent_file" it spits out some random error:

{function_clause,
    [{calendar,datetime_to_gregorian_seconds,
         [0],
         [{file,"calendar.erl"},{line,136}]},
     {boss_load,module_older_than,2,
         [{file,"src/boss/boss_load.erl"},{line,386}]},
     {boss_load,load_view_if_old,5,
         [{file,"src/boss/boss_load.erl"},{line,314}]},
     {boss_load,load_view_if_dev,3,
         [{file,"src/boss/boss_load.erl"},{line,334}]},
     {lists,foldl,3,[{file,"lists.erl"},{line,1197}]},
     {boss_web_controller,render_view,6,
         [{file,"src/boss/boss_web_controller.erl"},{line,901}]},
     {boss_web_controller,execute_action,5,
         [{file,"src/boss/boss_web_controller.erl"},{line,806}]},
     {boss_web_controller,process_not_found,5,
         [{file,"src/boss/boss_web_controller.erl"},{line,556}]}]}

This could obviously be a bit more informative. : ) If there's a way to include the file the user wrote, "some_nonexistent_file" and the file and line number that statement is in, that'd be great.

Bug escape iolist

26> erlydtl_filters:force_escape("...'yyy'...").  
"...&#039;yyy&#039;..."
27> erlydtl_filters:force_escape(["...'yyy'..."]).
["...'yyy'..."]

Include adds newline characters

Including a template adds a newline character after the include statement. This can be problematic when generating certain kinds of output, such as data for javascript consumption. For instance:

var client_id = "{% include "client_id.dtl" %}";

The newline character would be a part of the client_id string. The obvious workaround for this would be to have the quotes as part of the template, but this could limit template reusability.

The DTL documentation makes no reference to this as an expected behavior, but it may very well be what their implementation does as well. If it is I will take it up with them.

escapejs escapes new lines

escapejs escapes new lines. This is bad for textareas, as users may wish to enter some newline characters. Especially combined with linebreaksbr filter, it becomes a big mess.

[GC4] Enhancement request: Multiline {% %} blocks

From google code: https://code.google.com/p/erlydtl/issues/detail?id=4

For review.

Reported by [email protected], Mar 25, 2008

What steps will reproduce the problem?
1.

{% if entry.content 
    %}{% if entry.content.text 
        %}{% if entry.content.text.html 
            %}{{ entry.content.text.html.en_us 
        }}{% endif 
        %}{% if entry.content.text.wikicreole 
            %}{{ entry.content.text.wikicreole.en_us 
        }}{% endif 
    %}{% endif
%}{% endif %}

What is the expected output? What do you see instead?
If entry.content.text.html exists then it's value
Ditto if entry.content.text.wikicreole.

What I actually get is an error on col 21, at the new line character. This
may be a slight change to grammar file, but still haven't had time to ramp
up on yecc.

What version of the product are you using? On what operating system?
r29

Please provide any additional information below.

{{ block.super }} not working

block.super variable in template context should resolve to contents of current block in parent template.
now it is causing parse error.

Handling wrong types and errors in filters

Hi,
Just having recovered from a silly template bug where I applied floatformat to an integer field -- I'm wondering about two issues.

First, in the spirit of django accomodating all-too-human template writers, should the ideal behavior for filters be to try to coerce into the appropriate value if sensible? Thus in this case, coercing an integer to a float would satisfy most people and hurt very few. Might be a bit confusing, but much less so than a page full of stack trace, etc. For the anal programmer types (mysefl included) a warning emitted to a log would be most useful for eventually tracking down little misuses like this.

Secondly, I think that filter errors (wrong type, incorrect syntax, etc.) should result in a short, readable human error.

To quote the django book:

The system fails silently rather than raising an exception because it’s intended to be resilient to human error. In this case, all of the lookups failed because variable names have the wrong case or name. In the real world, it’s unacceptable for a Web site to become inaccessible due to a small template syntax error.

Okay, this is relevant to missing varialbles, but I think the same philosophy applies to variable filters. Only in this case I think that instead of a blank field, there should be a short error message, like ** Bad date format "Marge Simpson" **.

Would this be compatible with the erlydtl style? I know that Erlang means fail fast, fail hard, and that debugging is much easier if things are allowed to just fail in situ. Perhaps there is a need for (or there already exists) an erlydtl run or compile time option for user friendly errors?

dynamic variable substitution

I am planning to use erlydtl for one my application. Thanks for the great work. I like to see support for dynamic variable substitution -- thats L1 = [{a, one}, {b,two}]
L2 = [{one, "This is one"}, {two, "this is two"}]

where
I want to do {% for K, V in L1 %}

{{ L2.V}}
{% endfor %}

Please excuse for bad example but it explains what it should be there.

This is kind of done by zotonic team http://code.google.com/p/zotonic/
Is it possible to support this use case? this is going to be very powerful.

for loop and variable properties issue

I have a situation as below

  1. L1 = [{K1, V1}, {K2, V2}] and L2 = [{V1, some_value}, {V2, other_value}]
    I have forloop to iterate L1 but not know how to access L2 value, is that
    {% for Key, Val in L1 %} {{ L2.Key}}
    {% endfor %}
    or how do you recommend to access it?

Access tuple members and list items by index

In django it is possible to write something like {{ mylist.1 }} to display mylist[1] in template. It would be very convenient to have his in erlydtl for lists and especially tuples (records).

I think about implementing this in erlydtl myself. How hard it will be to do this, to change grammatic? Any caveats with, say, translation engine or whatnot?

[GC3] Enhancement request: attribute of undefined value should not throw error

Old issue from google code site: https://code.google.com/p/erlydtl/issues/detail?id=3

For review.

Reported by [email protected], Mar 25, 2008

What steps will reproduce the problem?

  1. There?{% if foo.bar.baz %}Yes{% endif %} with bindings of [{foo, []}]

What is the expected output? What do you see instead?
I'd like to see output of
There?

What I see instead is a error trace on the console.
I'd like to have it return undefined if any level in nested var path is
undefined, rather than have it continuing on and throwing an error.

What version of the product are you using? On what operating system?
r29

Please provide any additional information below.
Why is this needed?
So I can do things like:
{% if entry.content.text.html.en_us %}
instead of
{% if entry.content %}{% if entry.content.text %}{% if entry.content.text.html %}{% if entry.content.text.html.en_us %}

In the meanwhile I'll investigate doing this with modules pr preprocessing
the vars before I send them to erlydtl.

Mar 25, 2008 #1 [email protected]

Hand in hand with this is the ability to have multi-line {% %} blocks, see issue 4

erlydtl doesn't build anymore

Following the addition of the pmod_transform dependency, make doesn't work anymore, because it doesn't get the dependency.

Also, that dependency is only used for tests, could it be an optional dependency downloaded only when you try to run tests?

Thanks.

First line comment crashes erlydtl

Hi,
Given the following template:
<!-- comment -->
<html>
...
</html>

Then this one:
<!-- other comment -->
{% extend "path/to/first/template.html" %}

Compilation of second template fails with:
=ERROR REPORT==== 14-Jun-2012::13:25:59 ===
{{case_clause,{extends,{string_literal,{2,12},""greeting/list.html""}}},
[{erlydtl_compiler,value_ast,4,
[{file,"src/erlydtl_compiler.erl"},{line,634}]},
{erlydtl_compiler,'-body_ast/3-fun-2-',3,
[{file,"src/erlydtl_compiler.erl"},{line,601}]},
{lists,mapfoldl,3,[{file,"lists.erl"},{line,1278}]},
{lists,mapfoldl,3,[{file,"lists.erl"},{line,1279}]},
{erlydtl_compiler,body_ast,3,[{file,"src/erlydtl_compiler.erl"},{line,509}]},
{erlydtl_compiler,compile_to_binary,4,
[{file,"src/erlydtl_compiler.erl"},{line,189}]},
{erlydtl_compiler,compile,3,[{file,"src/erlydtl_compiler.erl"},{line,104}]},
{boss_load,compile_view_erlydtl,4,
[{file,"src/boss/boss_load.erl"},{line,205}]}]}

If I remove the comment in 2nd template, it compiles fine. Comment in the first template cause no problem. Using DTL style comment for 2nd template also leads to crash.

Regards,
Jean

Title filter differs from Django implementation

I'm not sure how much of an issue this really is, but Django's title filter upcases differently from the erlydtl title filter. Django will upcase all characters after quite a lot of tokens, including ' and !, due to the use of the Python str.title() function.

>>> import re
>>> def title(value):
...     """Converts a string into titlecase."""
...     t = re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
...     return re.sub("\d([A-Z])", lambda m: m.group(0).lower(), t)
... 
>>> title("string-String-ssTring123-STRING-123string-123STRING-!string-a?string")
'String-String-Sstring123-String-123string-123string-!String-A?String'

Compare this with

(runejerl@libertad)5> erlydtl_filters:title("string-String-ssTring123-STRING-123string-123STRING-!string-a?string").
"String-String-ssTring123-STRING-123string-123STRING-!string-a?string"

Any thoughts? Should erlydtl strive to follow Django templates as closely as possible?

sources_parser fails to parse templates with "now" tag

Example:

15> sources_parser:process_content("", <<"{%trans \"\"%} {% now \"Ymd\" %}">>).
** exception error: no function clause matching 
     sources_parser:process_ast([],{string_literal,{1,21},"\"Ymd\""},[{[],{[],1,9}}]) (src/i18n/sources_parser.erl, line 51)
     in function  sources_parser:process_ast/3 (src/i18n/sources_parser.erl, line 53)
     in call from sources_parser:process_ast/2 (src/i18n/sources_parser.erl, line 50)
     in call from sources_parser:process_content/2 (src/i18n/sources_parser.erl, line 43)

Can be easily hotfixed by adding

process_token(_Fname, {apply_filter, _Value, _Filter}, Acc) -> Acc;
+ process_token(_Fname, {date, now, _Format}, Acc) -> Acc;
process_token(Fname, {_Instr, _Cond, Children}, Acc) -> process_ast(Fname, Children, Acc);

to src/i18n/sources_parser.erl

Error while trying to compile

I have downloaded erlydtl and on "make" it gives me this error:
"==> erlydtl_error (compile)
src/filter_lib/erlydtl_slice.erl:none: error in parse transform 'eunit_autoexport': {undef,
[{eunit_autoexport,
parse_transform,
[[{attribute,1,file,
{"src/filter_lib/erlydtl_slice.erl",
1}},
{attribute,1,module,
erlydtl_slice},
{attribute,3, ...
...
...
...
... {compile,fold_comp,3},
{compile,internal_comp,4},
{compile,internal,3}]}
make: *** [compile] Error 1"

What's the problem?

Allow {% blocktrans %} translation in runtime

Like in Django.

In Django {% blocktrans %} can contain only simple {{ var }} substitutions, so, as I understand, they can be replaced relatively easy and fast in runtime.

Also, i18n_manager should extract blocktrans phrases to .po files as well as trans phrases.
As a good point for compatibility with python's .po files, it may replace {{ var }} with %(var)s

sub binary use is non-optimal

After compiling with ERL_COMPILER_OPTIONS=bin_opt_info set for binary use warnings, this was printed.

It might be good to fix these.

src/erlydtl_filters.erl:211: Warning: INFO: using the original binary variable in a guard will prevent delayed sub binary optimization
src/erlydtl_filters.erl:213: Warning: NOT OPTIMIZED: sub binary used by erlang:binary_to_list/1
src/erlydtl_filters.erl:407: Warning: NOT OPTIMIZED: sub binary is used or returned
src/erlydtl_filters.erl:892: Warning: NOT OPTIMIZED: sub binary is used or returned
src/erlydtl_filters.erl:894: Warning: NOT OPTIMIZED: sub binary is used or returned
src/erlydtl_filters.erl:896: Warning: NOT OPTIMIZED: sub binary is used or returned
src/erlydtl_filters.erl:898: Warning: NOT OPTIMIZED: sub binary is used or returned
src/erlydtl_filters.erl:900: Warning: NOT OPTIMIZED: sub binary is used or returned
src/erlydtl_filters.erl:902: Warning: INFO: using a matched out sub binary will prevent delayed sub binary optimization
src/erlydtl_filters.erl:937: Warning: NOT OPTIMIZED: sub binary is used or returned
src/erlydtl_filters.erl:939: Warning: INFO: using a matched out sub binary will prevent delayed sub binary optimization
src/erlydtl_filters.erl:968: Warning: NOT OPTIMIZED: sub binary is used or returned
src/erlydtl_filters.erl:970: Warning: NOT OPTIMIZED: sub binary is used or returned
src/erlydtl_filters.erl:972: Warning: INFO: using a matched out sub binary will prevent delayed sub binary optimization
src/erlydtl_filters.erl:988: Warning: NOT OPTIMIZED: sub binary is used or returned
src/erlydtl_filters.erl:990: Warning: INFO: using a matched out sub binary will prevent delayed sub binary optimization
src/erlydtl_filters.erl:1118: Warning: INFO: using a matched out sub binary will prevent delayed sub binary optimization
src/erlydtl_filters.erl:1120: Warning: NOT OPTIMIZED: sub binary is used or returned
src/erlydtl_filters.erl:1122: Warning: NOT OPTIMIZED: sub binary is used or returned

Support for tagged releases

For the sake of accountability in production projects it can be very useful to use an explicit, frozen release of a software library.

Might it be possible to start using git tag so that a particular version of ErlyDTL can be used in {deps, []} in rebar.config?

This appears to be a fairly common convention with many other GitHub-hosted projects.

Tags to minify js, css and html

Hi,

The spaceless tag is very useful and it will be great to have similar tags:

  • striphtmlcomments - remove html comments.
  • minifyjs - remove comments and spaces from javascript.
  • minifycss - remove comments and spaces from css.
  • minifyhtml - remove comments and spaces from html and embedded js and css sections.

Is there something related to the tags suggested above?

Thanks

urlencode does not conform to Django docs

I have promised to share more details on what we have discussed here:
https://groups.google.com/d/msg/chicagoboss/IQ0d1DBKgAM/j8Asd8WPjTYJ

...well I forgot what was the real use-case where we faced this problem, but the issue is simple:

erlydtl_filters:urlencode ("http://www.example.org/foo?a=b&c=d").
"http%3A%2F%2Fwww.example.org%2Ffoo%3Fa%3Db%26c%3Dd"

And that is not the same result as reported here:
https://docs.djangoproject.com/en/dev/ref/templates/builtins/#urlencode

Also, the optional argument of the list of characters to not escape, is completely missing.

undefined dict attributes

{"Render variable with empty attribute in dict",
  <<"{{ var1.foo }}">>, [{var1, dict:store(attr, "Othello", dict:new())}], <<"Othello">>},

I'm not sure what this should return, but "undefined" doesn't seem like the best choice, either in terms of django compatibility, or in terms of general usefulness.

make test failing

Pulled from master, ran make test:

mplate: "var_preset", ... compiling ... rendering
 Template: "cycle", ... compiling ... rendering
 Template: "custom_tag", ... compiling ... {"init terminating in do_boot",{undef,[{erlydtl_custom_tags,module_info,[exports],[]},{erlydtl_compiler,custom_tags_modules_ast,3,[{file,"src/erlydtl_compiler.erl"},{line,1294}]},{erlydtl_compiler,tag_ast,4,[{file,"src/erlydtl_compiler.erl"},{line,1281}]},{lists,mapfoldl,3,[{file,"lists.erl"},{line,1329}]},{lists,mapfoldl,3,[{file,"lists.erl"},{line,1330}]},{erlydtl_compiler,body_ast,3,[{file,"src/erlydtl_compiler.erl"},{line,544}]},{erlydtl_compiler,compile_to_binary,4,[{file,"src/erlydtl_compiler.erl"},{line,205}]},{erlydtl_compiler,compile,3,[{file,"src/erlydtl_compiler.erl"},{line,105}]}]}}

Crash dump was written to: erl_crash.dump
init terminating in do_boot ()
make: *** [test] Error 1

Required app syntax_tools

Minor and easy to fix: erlydtl uses erl_syntax, which is in the syntax_tools app. This should be included in the applications list in src/erlydtl.app.src, which will catch this dependency when creating releases.

Error parsing .po files

We have a gettext file created by poEdit, which contains a lot of additional data and comments.

The general file looks like this:

msgid ""
msgstr ""
"Project-Id-Version: Project\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-02-10 17:50+0100\n"
"PO-Revision-Date: 2012-02-10 17:50+0100\n"
"Last-Translator: Thomas Schaaf <[email protected]>\n"
"Language-Team: komola UG <[email protected]>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-KeywordsList: _;gettext;gettext_noop;_t\n"
"X-Poedit-Basepath: .\n"
"X-Poedit-Language: German\n"
"X-Poedit-Country: GERMANY\n"
"X-Poedit-SourceCharset: utf-8\n"
"X-Poedit-SearchPath-0: .\n"

#: strings.php:2
msgid "PAGE_NOT_FOUND"
msgstr "Seite nicht gefunden!"

This is a common format for gettext files.

However, ErlyDTL only seems to be able to parse this file, when I deleted all the "X-Poedit" lines and the #: file:line lines.

Syntax error on lookup values

My vars look like this:

[{params, [{"filter", "foo"}]}]

This variable lookup:

{{ params.filter }}

generates this error on compile:

[{{22,63},
erlydtl_parser,
["syntax error before: ",
["filter"]]}]

If I use "filter2", it's okay.

Maybe this is not a bug and I need to somehow escape the use of "filter" to avoid colliding with the macro/filter grammar.

Though it does seem to me a bug in the grammar.

Please tag releases

In src/erlydtl.app.src the vsn (version) field is used, currently set to 0.7.0.

Please also tag releases with git.

Motivation: Easier to handle dependency tracking and locking. E.g. v0.7.0 has understandable meaning, which 604cdda doesn't.

locale

In templates I frequently need access to the CurrentLocale variable that is passed to the template's render/3 function as {locale, CurrentLocale} parameter. However, it's not available for access and can only be made available if one also passes it in the list of bound variables, which is redundant.

I can see three ways to solve it:

  1. Add artificial variable to the Bindings passed to a template behind the scene.
  2. Introduce another tag {% locale %}
  3. Introduce a special variable {{ locale }} which will resolve to current locale if the variable with the same name is not passed by the user.

I believe solution #2 is the best, though it extends the Django grammar, and if that's a problem than #3 is the best.

Upon getting your feedback, I'll submit a patch with this implementation as I believe this feature would be useful for other users of erlydtl.

Thoughts?

erlydtl_compiler:compile_dir FilePath error

Hi.

I write in my projecgt this code:
erlydtl_compiler:compile_dir("templates/tags", Name, [{out_dir, "ebin"},{force_recompile, true}]).

then compile project with rebar, and get the error:
escript: exception error: no match of right hand side value {error,{"templates/tags/templates/tags/style", [{0,view_custom_tags,"Failed to read file"}]}}

If I change next code (erlydtl_compiler.erl line 123):
FilePath = filename:join([Dir, File]),
to code
FilePath = filename:join([Dir, filename:absname(File)]),

the error disappears.

Erl version: V5.9 (R15B).

error reports

The error reporting doesn't follow the Erlang way of returning an error tuple with ErrorModule and ErrorDescription, which you can translate to a descriptive message with ErrorModule:format_error(ErrorDescription).

Go through the code and turn all errors into error descriptions, and implement a format_error/1 function to turn them into strings.

rebar build broken again

ERROR: One or more modules listed in erlydtl.app are not present in ebin/*.beam:
* gettext
* sources_parser_unittests

Evan, can you just build erlydtl with rebar so we don't have these issues? You'll want to do that for CB anyway.

list to atom in README.markdown

Hi,

i found README.markdown has a bit error.

That says custom_tags_modules function will take proplists that have a list as a key.
e.g. TagVars = [{"bar", 100}]

In current version, TagVars uses atom as a key.

Could you please change the example from list to atom?

i have example code, write following.
It will pass if change from "bar" to bar.

-module(rend).
-export([er/0]).
-export([foo/1]).
-include_lib("eunit/include/eunit.hrl").

er() ->
    erlydtl:compile(<<"{% foo bar=100 %}">>,
                    custom_tags_dtl,
                    [{custom_tags_modules, [?MODULE]}]),
    custom_tags_dtl:render([]).

foo(TagVars) ->
    ?debugVal(TagVars),
    Bar = proplists:get_value("bar", TagVars),
    ?assertEqual(100, Bar).

Compiling templates from binary with extends

Hello,

I am wondering if you would accept a patch that would allow for a hook to be done when trying to load a base template (extends tag). This would allow me to specify a function to load said template from a database instead of having to maintain a lot of temporary files on disc. Very useful for me since my users use them in a fashion similar to wikis.

[feature] Erlang packages support

I tried to put the templates in Erlang packages (http://www.erlang.org/doc/man/packages.html).

> erlydtl:compile("templates/test/test_base.html", 'test.base', 
> [{out_dir, "ebin/test"}, force_recompile]).

... and rename ebin/test/test.base.beam to ebin/test/base.beam. After that I'm try to render:

> test.base:render([]). 
** exception error: undefined function 'test.erlydtl_runtime':stringify_final/1
     in function  'test.base':render/2

How do I deal with this?

TEST macro break rebar eunit integration

When the TEST macro is defined within a module, it can cause a redefine error during eunit compilation when using rebar in a project that depends on erlydtl. There are some simple modifications that can prevent the breakage.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.