Skip to content

Commit 9e86828

Browse files
authored
Extend 'LeafRenderer' so that it can render 'Encodable' contexts (#188)
Motivation: Out of the box, the 'LeafRenderer' offers a base API which can render Leaf templates to a ByteBuffer. This requires the context to be provided as a dictionary. With 'ViewRenderer' conformance the 'LeafRenderer' can render 'View's using an Encodable context. However, the 'LeafRenderer' can't render templates to a ByteBuffer using an 'Encodable' context. Since, the 'LeafEncoder' is internal users also can't encode their context to use the 'base' API. This functionality is helpful when Leaf is used to render documents which aren't HTML; such as SVG or XML. Modifications: - add an extension to 'LeafRenderer' to render a template at the given path using an 'Encodable' context - add a test Results: The LeafRenderer can render templates to bytes using Encodable contexts.
1 parent 2f02ba9 commit 9e86828

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

Sources/Leaf/LeafRenderer+ViewRenderer.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,30 @@ extension LeafRenderer: ViewRenderer {
88

99
public func render<E>(_ name: String, _ context: E) -> EventLoopFuture<View>
1010
where E: Encodable
11+
{
12+
return self.render(path: name, context: context).map { buffer in
13+
View(data: buffer)
14+
}
15+
}
16+
}
17+
18+
extension LeafRenderer {
19+
/// Populate the template at `path` with the data from `context`.
20+
///
21+
/// - Parameters:
22+
/// - path: The name of the template to render.
23+
/// - context: Contextual data to render the template with.
24+
/// - Returns: The serialized bytes of the rendered template.
25+
public func render<Context>(path: String, context: Context) -> EventLoopFuture<ByteBuffer>
26+
where Context: Encodable
1127
{
1228
let data: [String: LeafData]
1329
do {
1430
data = try LeafEncoder().encode(context)
1531
} catch {
1632
return self.eventLoop.makeFailedFuture(error)
1733
}
18-
return self.render(path: name, context: data).map { buffer in
19-
return View(data: buffer)
20-
}
34+
35+
return self.render(path: path, context: data)
2136
}
2237
}

Tests/LeafTests/LeafTests.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,44 @@ class LeafTests: XCTestCase {
192192

193193
XCTAssertTrue(renderer.cache.isEnabled)
194194
}
195+
196+
func testLeafRendererWithEncodableContext() throws {
197+
var test = TestFiles()
198+
test.files["/foo.leaf"] = """
199+
Hello #(name)!
200+
"""
201+
202+
let app = Application(.testing)
203+
defer { app.shutdown() }
204+
app.views.use(.leaf)
205+
app.leaf.sources = .singleSource(test)
206+
207+
struct NotHTML: ResponseEncodable {
208+
var data: ByteBuffer
209+
210+
func encodeResponse(for request: Request) -> EventLoopFuture<Response> {
211+
let response = Response(headers: ["content-type": "application/not-html"],
212+
body: .init(buffer: self.data))
213+
return request.eventLoop.makeSucceededFuture(response)
214+
}
215+
}
216+
217+
struct Foo: Encodable {
218+
var name: String
219+
}
220+
221+
app.get("foo") { req in
222+
return req.application.leaf.renderer.render(path: "foo", context: Foo(name: "World")).map {
223+
return NotHTML(data: $0)
224+
}
225+
}
226+
227+
try app.test(.GET, "foo") { res in
228+
XCTAssertEqual(res.status, .ok)
229+
XCTAssertEqual(res.headers.first(name: "content-type"), "application/not-html")
230+
XCTAssertEqual(res.body.string, "Hello World!")
231+
}
232+
}
195233
}
196234

197235
/// Helper `LeafFiles` struct providing an in-memory thread-safe map of "file names" to "file data"

0 commit comments

Comments
 (0)