Skip to content
This repository was archived by the owner on Nov 16, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Sources/TemplateKit/Renderable/AnyRenderable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

/// Type Erased Renderable struct
public struct AnyRenderable {

/// stored renderable
public var renderable: Renderable

/// init
public init(renderable: Renderable) {
self.renderable = renderable
}


}

/// Encodable conformance
extension AnyRenderable: Encodable {

/// Coding keys for Encodable
private enum CodingKeys: CodingKey {
case renderable
}

/// Encode using the `Renderable` super encoder
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try renderable.encode(to: container.superEncoder(forKey: .renderable))
}
}
50 changes: 50 additions & 0 deletions Sources/TemplateKit/Renderable/Renderable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Service

/// Renders the encodable model into a View
public protocol Renderable: Encodable {

/// Renders a view using the information found in the Renderable model.
///
/// - parameters:
/// - renderer: Renderer used for rendering model into `View`
/// - returns: `Future` containing the rendered `View`.
func render(using renderer: TemplateRenderer) throws -> Future<View>
}

/// Provides the model with a template
public protocol Templated {

/// Template
var template: String { get }
}

/// Default implementation of Renderable when model is Templated
extension Renderable where Self: Templated {

/// Uses template to render model into view
public func render(using renderer: TemplateRenderer) throws -> Future<View> {
guard let data = template.data(using: .utf8) else {
throw(TemplateKitError(identifier: "badTemplateString", reason: "Template string could not be converted to data."))
}
return renderer.render(template: data, self)
}
}


/// Provides the model with the path to the template
public protocol TemplatePathed {

/// Path to template
var templatePath: String { get }
}

/// Default implementation of Renderable when model is TemplatePathed
extension Renderable where Self: TemplatePathed {

/// Uses template path to render model into view
public func render(using renderer: TemplateRenderer) throws -> Future<View> {
return renderer.render(templatePath, self)
}
}


3 changes: 2 additions & 1 deletion Sources/TemplateKit/Tag/TagRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public var defaultTags: [String: TagRenderer] {
"count": Count(),
"set": Var(),
"get": Raw(),
"date": DateFormat()
"date": DateFormat(),
"templatedModel": TemplatedModel()
]
}
35 changes: 35 additions & 0 deletions Sources/TemplateKit/Tag/TemplatedModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/// Embeds rendered views for `Templated` or `TemplatePathed` models
///
/// templatedModel(<model>)
///
/// The model must either be `Templated` or `TemplatePathed`
public final class TemplatedModel: TagRenderer {

/// Creates a new `TemplatedModel` tag renderer.
public init() {}

/// See `TagRenderer`.
public func render(tag: TagContext) throws -> EventLoopFuture<TemplateData> {

try tag.requireParameterCount(1)

let context = tag.parameters[0]

let renderer = try tag.container.make(TemplateRenderer.self)

let view: EventLoopFuture<View>
if let templatePath = context.dictionary?["templatePath"]?.string { // `TemplatePathed`
view = renderer.render(templatePath, context)
} else if let template = context.dictionary?["template"]?.data { // `Templateed`
view = renderer.render(template: template, context)
} else {
throw(TemplateKitError(identifier: "notRenderable", reason: "First paramater must be Templated or TemplatePathed. The data supplied does not contain a templatePath or template"))
}

/// Convert into TemplateData
return view.map { view -> TemplateData in
return .data(view.data)
}

}
}