Habitat: cfg variables don't expand inside Handlebars each block helper

Created on 19 Jul 2016  路  13Comments  路  Source: habitat-sh/habitat

# cat default.toml
max_connections = 10000

[["ports"]]
port = 8080

[["ports"]]
port = 8081

# cat config/foo.conf
{{~#each cfg.ports}}
    <Connector port="{{port}}"
        maxConnections="{{../cfg.max_connections}}"
    />
{{~/each}}

The rendered output has maxConnections="" instead of maxConnections="10000". It doesn't appear to be a Handlebars problem because when I use http://tryhandlebarsjs.com with ../max_connections inside the #each block, it works. So it would appear to be a problem with the way cfg variables are made available inside the block?

Bug

All 13 comments

I'm not clear on your use of ../ -- why wouldn't {{cfg.max_connections}} work?

The ../ is how Handlebars refers to the parent scope of the block. {{cfg.max_connections}} doesn't work either, btw (how would you differentiate between a field in the table and a variable in the outer block? One would have to win in case of a conflict). And in http://tryhandlebarsjs.com/ {{max_connections}} doesn't work either (empty expansion), yet {{../max_connections}} works and gives the expected value for the expansion.

聽yet聽{{../max_connections}}聽works and gives the expected value for the expansion.

Am I misunderstanding something here? Aren't ../max_connections and cfg.max_connections the same value? Why not just use ../max_connections if it's working?

If you visualize this chunk of TOML as JSON it would be:

{
  "cfg": {
    "max_connections": 10000,
    "ports": [
      { "port": 8000 },
      { "port": 8081 }
    ]
  }
}

When I say "working", I mean {{../max_connections}}or {{../cfg.max_connections}} (with the JSON you suggest) works in http://tryhandlebarsjs.com/ but not in Habitat supervisor template expansion.

Ah ok, that makes much more sense. :) Sorry I wasn't understanding. It's definitely a bug.

The next step might be to create a minimal reproduction case with https://github.com/sunng87/handlebars-rust. At this point I'm not sure if the problem is in our usage of the library or the library itself. Whether it's in Habitat or upstream, we should be able to make it work correctly.

@smith I'll try to create a minimal reproduction case as you suggest.

I'll try and wire the first example into a new unit test in service_config.rg

Ok, I was able to reproduce this with a test. The test pulls out the code from Habitat that parses TOML, converts TOML to JSON, and then renders a Handlebars template. I wanted the bare minimum to see if the issue is with Habitat or any of the deps we use.

I was able to access values from cfg by specifying ../../max_connections like this:

{{#each cfg.ports}} port="{{port}}" maxConnections="{{../../max_connections}}" {{/each}}

Note the ../../. ../cfg.max_connections didn't work as mentioned in the first message of this issue.

This seems odd to me, but http://handlebarsjs.com mentions:

The exact value that ../ will resolve to varies based on the helper that is calling the block. Using ../ is only necessary when context changes, so children of helpers such as each would require the use of ../ while children of helpers such as if do not."

Additionally, handlebars-rust mentions:

This implementation is not fully compatible with the original javascript version. Specifically, mustache list iteration and null check doesn't work. But you can use #each and #if for same behavior.

I'm not sure how many helpers are involved while rendering the #each, but if it's > 1, it may be causing the odd behavior. Commenting out the registration of json_helper, toml_help, and
never_escape_fn achieves the same results of ../ vs ../.../.

At this point, I think the issue is in the Handlebars crate we use. I'm going to submit an issue + test case to handlebars-rust, and I'll attach a link to this issue.


If you'd like to run the test:

git checkout dp_handlebars_each_bug
make shell
cd /src/components/sup
cargo test -- handlebars_each_test --nocapture

Thanks for digging into this, @metadave !

ok, this is definitely a handlebars-rust bug. I have a work in progress branch which I might submit tomorrow: https://github.com/metadave/handlebars-rust/tree/dp_each_parent_bug

The problem is that the array index is included in path navigation in the each. In the example above, the path for every item in the each block is cfg.ports.[0], which is why ../../cfg works.

A fix is released handlebars-rust 0.19.0. Please test if it works and feel free to let me know if you have any question with it. Thanks!

This should be fixed by #1106

Thanks for fixing this!

Was this page helpful?
0 / 5 - 0 ratings