Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom tags with body #86

Open
koenpunt opened this issue Jan 5, 2021 · 2 comments
Open

Custom tags with body #86

koenpunt opened this issue Jan 5, 2021 · 2 comments

Comments

@koenpunt
Copy link

koenpunt commented Jan 5, 2021

I tried to define a custom tag that accepts a body, but the body is provided to the tag as [Syntax], which is not something that converts to the required return type of LeafData.

How can I capture the body, and return if necessary?

For context, I'm trying to create requireRole(role) tag, that returns the body if the role matches the role of the current user;

#requireRole("ADMIN"):
<a href="/projects/#(project.id)/delete">Delete</a>
#endrequireRole

In the tag class I retrieve the body using ctx.requireBody(), which, as mentioned, returns an array of Syntax elements.

final class RequireRoleTag<A>: LeafTag where A: RoleAuthenticatable {
    init(userType: A.Type) {}

    func render(_ ctx: LeafContext) throws -> LeafData {
        try ctx.requireParameterCount(1)
        let body = try ctx.requireBody()

        guard let requiredRole = ctx.parameters[0].string else {
            throw "role is not a string"
        }

        guard let req = ctx.request,
              let role = getRole(req: req)
        else {
            return .trueNil
        }

        if role == requiredRole {
            // This doesn't work
            return body
        }

        return .trueNil
    }

    private func getRole(req: Request) -> String? {
        let a = req.auth.get(A.self)
        return a?.role.description
    }
}
@danpalmer
Copy link

+1, this feels like it should be straightforward as #requireRole is essentially syntactic sugar over #if.

A slightly more complex case that I was hoping to solve with a custom tag is injecting common variables without needing to set them in the view model. For example...

#withUser():
    <img src="#(user.avatarURL)" title="#(user.name) />
#endwithUser

When building re-usable templates such as navigation, there are many use-cases for wanting common data available without needing to add all that common context to the view model/controller. I guess this could all be bundled up into some common view model type, but that's still extra code that's required.

@andreasley
Copy link

andreasley commented Sep 5, 2023

How to return LeafData from [Syntax] (only works correctly if there are no nested Leaf tags, so doesn't solve your issue):

let body = try ctx.requireBody()

guard case let .raw(byteBuffer) = body.first else {
    throw "Body not found"
}
            
return .data(Data(buffer: byteBuffer))

Make sure your custom tag conforms to UnsafeUnescapedLeafTag, because the body may contain HTML tags.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants