hummingbird-project / swift-mustache

Mustache templating engine
Apache License 2.0
21 stars 2 forks source link

Dictionary template data renders incorrectly #16

Closed drekka closed 2 years ago

drekka commented 2 years ago

Hi, I was trying to use a dictionary as a template but it would not render correctly. Optional Strings were rendered as `Optional(\"\")" instead of "". Debugging through I found this test (which works fine):

    func testOptionalMirror() throws {
        struct Test {
            let string: String?
        }
        let template = try HBMustacheTemplate(string: "test {{string}}")
        XCTAssertEqual(template.render(Test(string: "string")), "test string")
        XCTAssertEqual(template.render(Test(string: nil)), "test ")
    }

So I modelled off it but changed the data from a struct to a Dictionary like this:

    func testOptionalDictionaryValue() throws {
        let templateData: [String: String?] = [
            "string": "string"
        ]
        let template = try HBMustacheTemplate(string: "test {{string}}")
        XCTAssertEqual(template.render(templateData), "test string")
    }

This test fails with the message: XCTAssertEqual failed: ("test Optional("string")") is not equal to ("test string").

I've been debugging through to try and understand why a dictionary with optional values does not render correctly and am looking at the code in Template+Render. but so far no obvious reason other than when the execution gets to line 54 it appears to have child as an Optional which seems to indicate that even though there is a if let ... on line 48, it's somehow returning an optional. Possibly the value being returned is a double optional (???). Not sure.

Still trying to work this out.

drekka commented 2 years ago

Ok. I think I've figured this out. My first thought was to create a dictionary with a value type of Any?. The thought being that keys with nil values would render as "". However the use of Any? created a double optional situation (Any??) that no amount of messing about (and I tried in a branch and a playground) could resolve well.

Then I sat back and re-assessed and realised that I could used a [String:Any] dictionary and just not include the keys for values I wanted to be treated as nil. This worked just fine.

So the rule is - if you want to set a nil value, leave the key out instead.

I'm not sure if this is worth commenting on in the doco anywhere šŸ¤£

adam-fowler commented 2 years ago

I haven't tested this but I think you would have to use the following template

{{#string}}{{.}}{{/string}}
drekka commented 2 years ago

I'll close this as I think it's a PEBCAK issue :-)