Add support for proper lambdas (#48)
* Add support for proper lambdas * Get rid of recursion Remove renderSectionLambda as I can use renderUnescapedLambda for that.
This commit is contained in:
@@ -63,7 +63,7 @@ final class MustacheSpecTests: XCTestCase {
|
||||
struct Test: Decodable {
|
||||
let name: String
|
||||
let desc: String
|
||||
let data: AnyDecodable
|
||||
var data: AnyDecodable
|
||||
let partials: [String: String]?
|
||||
let template: String
|
||||
let expected: String
|
||||
@@ -155,6 +155,49 @@ final class MustacheSpecTests: XCTestCase {
|
||||
print(-date.timeIntervalSinceNow)
|
||||
}
|
||||
|
||||
func testLambdaSpec() async throws {
|
||||
var g = 0
|
||||
let lambdaMap = [
|
||||
"Interpolation": MustacheLambda { "world" },
|
||||
"Interpolation - Expansion": MustacheLambda { "{{planet}}" },
|
||||
"Interpolation - Alternate Delimiters": MustacheLambda { "|planet| => {{planet}}" },
|
||||
"Interpolation - Multiple Calls": MustacheLambda { return MustacheLambda { g += 1; return g }},
|
||||
"Escaping": MustacheLambda { ">" },
|
||||
"Section": MustacheLambda { text in text == "{{x}}" ? "yes" : "no" },
|
||||
"Section - Expansion": MustacheLambda { text in text + "{{planet}}" + text },
|
||||
// Not going to bother implementing this requires pushing alternate delimiters through the context
|
||||
// "Section - Alternate Delimiters": MustacheLambda { text in return text + "{{planet}} => |planet|" + text },
|
||||
"Section - Multiple Calls": MustacheLambda { text in "__" + text + "__" },
|
||||
"Inverted Section": MustacheLambda { false },
|
||||
]
|
||||
let url = URL(string: "https://raw.githubusercontent.com/mustache/spec/master/specs/~lambdas.json")!
|
||||
#if compiler(>=6.0)
|
||||
let (data, _) = try await URLSession.shared.data(from: url)
|
||||
#else
|
||||
let data = try Data(contentsOf: url)
|
||||
#endif
|
||||
let spec = try JSONDecoder().decode(Spec.self, from: data)
|
||||
// edit spec and replace lambda with Swift lambda
|
||||
let editedSpecTests = spec.tests.compactMap { test -> Spec.Test? in
|
||||
var test = test
|
||||
var newTestData: [String: Any] = [:]
|
||||
guard let dictionary = test.data.value as? [String: Any] else { return nil }
|
||||
for values in dictionary {
|
||||
newTestData[values.key] = values.value
|
||||
}
|
||||
guard let lambda = lambdaMap[test.name] else { return nil }
|
||||
newTestData["lambda"] = lambda
|
||||
test.data = .init(newTestData)
|
||||
return test
|
||||
}
|
||||
|
||||
let date = Date()
|
||||
for test in editedSpecTests {
|
||||
XCTAssertNoThrow(try test.run())
|
||||
}
|
||||
print(-date.timeIntervalSinceNow)
|
||||
}
|
||||
|
||||
func testCommentsSpec() async throws {
|
||||
try await self.testSpec(name: "comments")
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ final class TemplateParserTests: XCTestCase {
|
||||
|
||||
func testSection() throws {
|
||||
let template = try MustacheTemplate(string: "test {{#section}}text{{/section}}")
|
||||
XCTAssertEqual(template.tokens, [.text("test "), .section(name: "section", template: .init([.text("text")]))])
|
||||
XCTAssertEqual(template.tokens, [.text("test "), .section(name: "section", template: .init([.text("text")], text: "text"))])
|
||||
}
|
||||
|
||||
func testInvertedSection() throws {
|
||||
let template = try MustacheTemplate(string: "test {{^section}}text{{/section}}")
|
||||
XCTAssertEqual(template.tokens, [.text("test "), .invertedSection(name: "section", template: .init([.text("text")]))])
|
||||
XCTAssertEqual(template.tokens, [.text("test "), .invertedSection(name: "section", template: .init([.text("text")], text: "text"))])
|
||||
}
|
||||
|
||||
func testComment() throws {
|
||||
|
||||
@@ -140,7 +140,7 @@ final class TemplateRendererTests: XCTestCase {
|
||||
}
|
||||
|
||||
/// variables
|
||||
func testMustacheManualExample1() throws {
|
||||
func testMustacheManualVariables() throws {
|
||||
let template = try MustacheTemplate(string: """
|
||||
Hello {{name}}
|
||||
You have just won {{value}} dollars!
|
||||
@@ -157,8 +157,8 @@ final class TemplateRendererTests: XCTestCase {
|
||||
""")
|
||||
}
|
||||
|
||||
/// test esacped and unescaped text
|
||||
func testMustacheManualExample2() throws {
|
||||
/// test escaped and unescaped text
|
||||
func testMustacheManualEscapedText() throws {
|
||||
let template = try MustacheTemplate(string: """
|
||||
*{{name}}
|
||||
*{{age}}
|
||||
@@ -174,8 +174,71 @@ final class TemplateRendererTests: XCTestCase {
|
||||
""")
|
||||
}
|
||||
|
||||
/// test dotted names
|
||||
func test_MustacheManualDottedNames() throws {
|
||||
let template = try MustacheTemplate(string: """
|
||||
* {{client.name}}
|
||||
* {{age}}
|
||||
* {{client.company.name}}
|
||||
* {{{company.name}}}
|
||||
""")
|
||||
let object: [String: Any] = [
|
||||
"client": (
|
||||
name: "Chris & Friends",
|
||||
age: 50
|
||||
),
|
||||
"company": [
|
||||
"name": "<b>GitHub</b>",
|
||||
],
|
||||
]
|
||||
XCTAssertEqual(template.render(object), """
|
||||
* Chris & Friends
|
||||
*
|
||||
*
|
||||
* <b>GitHub</b>
|
||||
""")
|
||||
}
|
||||
|
||||
/// test implicit operator
|
||||
func testMustacheManualImplicitOperator() throws {
|
||||
let template = try MustacheTemplate(string: """
|
||||
* {{.}}
|
||||
""")
|
||||
let object = "Hello!"
|
||||
XCTAssertEqual(template.render(object), """
|
||||
* Hello!
|
||||
""")
|
||||
}
|
||||
|
||||
/// test lambda
|
||||
func test_MustacheManualLambda() throws {
|
||||
let template = try MustacheTemplate(string: """
|
||||
* {{time.hour}}
|
||||
* {{today}}
|
||||
""")
|
||||
let object: [String: Any] = [
|
||||
"year": 1970,
|
||||
"month": 1,
|
||||
"day": 1,
|
||||
"time": MustacheLambda { _ in
|
||||
return (
|
||||
hour: 0,
|
||||
minute: 0,
|
||||
second: 0
|
||||
)
|
||||
},
|
||||
"today": MustacheLambda { _ in
|
||||
return "{{year}}-{{month}}-{{day}}"
|
||||
},
|
||||
]
|
||||
XCTAssertEqual(template.render(object), """
|
||||
* 0
|
||||
* 1970-1-1
|
||||
""")
|
||||
}
|
||||
|
||||
/// test boolean
|
||||
func testMustacheManualExample3() throws {
|
||||
func testMustacheManualSectionFalse() throws {
|
||||
let template = try MustacheTemplate(string: """
|
||||
Shown.
|
||||
{{#person}}
|
||||
@@ -190,7 +253,7 @@ final class TemplateRendererTests: XCTestCase {
|
||||
}
|
||||
|
||||
/// test non-empty lists
|
||||
func testMustacheManualExample4() throws {
|
||||
func testMustacheManualSectionList() throws {
|
||||
let template = try MustacheTemplate(string: """
|
||||
{{#repo}}
|
||||
<b>{{name}}</b>
|
||||
@@ -205,13 +268,29 @@ final class TemplateRendererTests: XCTestCase {
|
||||
""")
|
||||
}
|
||||
|
||||
/// test non-empty lists
|
||||
func testMustacheManualSectionList2() throws {
|
||||
let template = try MustacheTemplate(string: """
|
||||
{{#repo}}
|
||||
<b>{{.}}</b>
|
||||
{{/repo}}
|
||||
""")
|
||||
let object: [String: Any] = ["repo": ["resque", "hub", "rip"]]
|
||||
XCTAssertEqual(template.render(object), """
|
||||
<b>resque</b>
|
||||
<b>hub</b>
|
||||
<b>rip</b>
|
||||
|
||||
""")
|
||||
}
|
||||
|
||||
/// test lambdas
|
||||
func testMustacheManualExample5() throws {
|
||||
func testMustacheManualSectionLambda() throws {
|
||||
let template = try MustacheTemplate(string: """
|
||||
{{#wrapped}}{{name}} is awesome.{{/wrapped}}
|
||||
""")
|
||||
func wrapped(object: Any, template: MustacheTemplate) -> String {
|
||||
return "<b>\(template.render(object))</b>"
|
||||
func wrapped(_ s: String) -> Any? {
|
||||
return "<b>\(s)</b>"
|
||||
}
|
||||
let object: [String: Any] = ["name": "Willy", "wrapped": MustacheLambda(wrapped)]
|
||||
XCTAssertEqual(template.render(object), """
|
||||
@@ -220,7 +299,7 @@ final class TemplateRendererTests: XCTestCase {
|
||||
}
|
||||
|
||||
/// test setting context object
|
||||
func testMustacheManualExample6() throws {
|
||||
func testMustacheManualContextObject() throws {
|
||||
let template = try MustacheTemplate(string: """
|
||||
{{#person?}}
|
||||
Hi {{name}}!
|
||||
@@ -234,7 +313,7 @@ final class TemplateRendererTests: XCTestCase {
|
||||
}
|
||||
|
||||
/// test inverted sections
|
||||
func testMustacheManualExample7() throws {
|
||||
func testMustacheManualInvertedSection() throws {
|
||||
let template = try MustacheTemplate(string: """
|
||||
{{#repo}}
|
||||
<b>{{name}}</b>
|
||||
@@ -251,7 +330,7 @@ final class TemplateRendererTests: XCTestCase {
|
||||
}
|
||||
|
||||
/// test comments
|
||||
func testMustacheManualExample8() throws {
|
||||
func testMustacheManualComment() throws {
|
||||
let template = try MustacheTemplate(string: """
|
||||
<h1>Today{{! ignore me }}.</h1>
|
||||
""")
|
||||
|
||||
Reference in New Issue
Block a user