Skip to content

Commit

Permalink
Extend 'LeafRenderer' so that it can render 'Encodable' contexts (#188)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
glbrntt authored Mar 2, 2021
1 parent 2f02ba9 commit 9e86828
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 3 deletions.
21 changes: 18 additions & 3 deletions Sources/Leaf/LeafRenderer+ViewRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,30 @@ extension LeafRenderer: ViewRenderer {

public func render<E>(_ name: String, _ context: E) -> EventLoopFuture<View>
where E: Encodable
{
return self.render(path: name, context: context).map { buffer in
View(data: buffer)
}
}
}

extension LeafRenderer {
/// Populate the template at `path` with the data from `context`.
///
/// - Parameters:
/// - path: The name of the template to render.
/// - context: Contextual data to render the template with.
/// - Returns: The serialized bytes of the rendered template.
public func render<Context>(path: String, context: Context) -> EventLoopFuture<ByteBuffer>
where Context: Encodable
{
let data: [String: LeafData]
do {
data = try LeafEncoder().encode(context)
} catch {
return self.eventLoop.makeFailedFuture(error)
}
return self.render(path: name, context: data).map { buffer in
return View(data: buffer)
}

return self.render(path: path, context: data)
}
}
38 changes: 38 additions & 0 deletions Tests/LeafTests/LeafTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,44 @@ class LeafTests: XCTestCase {

XCTAssertTrue(renderer.cache.isEnabled)
}

func testLeafRendererWithEncodableContext() throws {
var test = TestFiles()
test.files["/foo.leaf"] = """
Hello #(name)!
"""

let app = Application(.testing)
defer { app.shutdown() }
app.views.use(.leaf)
app.leaf.sources = .singleSource(test)

struct NotHTML: ResponseEncodable {
var data: ByteBuffer

func encodeResponse(for request: Request) -> EventLoopFuture<Response> {
let response = Response(headers: ["content-type": "application/not-html"],
body: .init(buffer: self.data))
return request.eventLoop.makeSucceededFuture(response)
}
}

struct Foo: Encodable {
var name: String
}

app.get("foo") { req in
return req.application.leaf.renderer.render(path: "foo", context: Foo(name: "World")).map {
return NotHTML(data: $0)
}
}

try app.test(.GET, "foo") { res in
XCTAssertEqual(res.status, .ok)
XCTAssertEqual(res.headers.first(name: "content-type"), "application/not-html")
XCTAssertEqual(res.body.string, "Hello World!")
}
}
}

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

0 comments on commit 9e86828

Please sign in to comment.