vapor / leaf

🍃 An expressive, performant, and extensible templating language built for Swift.

Home Page:https://docs.vapor.codes/4.0/leaf/getting-started

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Consider adding a built-in view context

fwgreen opened this issue · comments

I apologize if this has been bought up before; I've searched and found no reference to it. While it's good to have the flexibility to create one's own contexts, this can become unwieldy as the number of routes grows. After porting a pet project over to Vapor, I found myself with several contexts like the one below:

struct TelephoneContext : Encodable {
    var assignedPhones: [Telephone]
    var outForServicePhones: [Telephone]
    var availablePhones: [Telephone]

    enum CodingKeys: String, CodingKey {
        case assignedPhones = "assigned_phones"
        case outForServicePhones = "out_for_service_phones"
        case availablePhones = "available_phones"
    }
}
//...
let phoneCtx = TelephoneContext(
    assignedPhones:      try Telephone.listBy(status: .assigned), 
    outForServicePhones: try Telephone.listBy(status: .outForService), 
    availablePhones:     try Telephone.listBy(status: .available)
)

Maybe something result builder based?:

let phoneCtx = Context {
    ("assigned_phones",        try Telephone.listBy(status: .assigned))
    ("out_for_service_phones", try Telephone.listBy(status: .outForService)) 
    ("available_phones",       try Telephone.listBy(status: .available))
}

While this does the trick, its reliance on AnyCodable is worrisome:

import AnyCodable
import Vapor

@resultBuilder 
struct ContextBuilder {
    static func buildBlock(_ items: (String, Encodable)...) -> [String: AnyEncodable] {
        items.reduce(into: [:]) { $0[$1.0] = AnyEncodable($1.1) }
    }
}

public struct Context {
    var data: [String: AnyEncodable]

    init(@ContextBuilder items: () throws -> [String: AnyEncodable]) {
        data = try! items()
    }
}

extension ViewRenderer {
    public func render(_ name: String, _ context: Context) async throws -> View {
        return try await self.render(name, context.data)
    }
}

Yeah I think this is probably out of scope for Leaf itself but would make a good community package!

This approach is a bit too involved when all I need is the ability to use a dictionary