Sunday, March 26, 2023
Learning Code
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#
No Result
View All Result
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#
No Result
View All Result
Learning Code
No Result
View All Result
Home Swift

What’s new in Leaf 4 (Tau)?

learningcode_x1mckf by learningcode_x1mckf
September 19, 2022
in Swift
0
What’s new in Leaf 4 (Tau)?
74
SHARES
1.2k
VIEWS
Share on FacebookShare on Twitter


2020/10/20

The whole lot you must know concerning the upcoming Leaf template engine replace and methods to migrate your Vapor / Swift codebase.

Vapor

Utilizing Leaf 4 Tau

Earlier than we dive in, let’s make a brand new Vapor challenge with the next package deal definition.



import PackageDescription

let package deal = Package deal(
    identify: "myProject",
    platforms: [
       .macOS(.v10_15)
    ],
    dependencies: [
        
        .package(url: "https://github.com/vapor/vapor.git", from: "4.30.0"),
        .package(url: "https://github.com/vapor/leaf", .exact("4.0.0-tau.1")),
        .package(url: "https://github.com/vapor/leaf-kit", .exact("1.0.0-tau.1.1")),
    ],
    targets: [
        .target(name: "App", dependencies: [
            .product(name: "Vapor", package: "vapor"),
            .product(name: "Leaf", package: "leaf"),
        ]),
        .goal(identify: "Run", dependencies: ["App"]),
        .testTarget(identify: "AppTests", dependencies: [
            .target(name: "App"),
            .product(name: "XCTVapor", package: "vapor"),
        ])
    ]
)


The very very first thing I might like to point out you is that we have now a brand new render methodology. Prior to now we had been in a position to make use of the req.view.render perform to render our template information. Contemplate the next actually easy index.leaf file with two context variables that we’ll give show actual quickly.


<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta identify="viewport" content material="width=device-width, initial-scale=1">
        <title>#(title)</title>
    </head>
    <physique>
        #(physique)
    </physique>
</html>

Now in our Vapor codebase we may use one thing like this to render the template.

import Vapor
import Leaf

public func configure(_ app: Utility) throws 

    app.views.use(.leaf)

    app.get()  req -> EventLoopFuture<View> in
        struct Context: Encodable 
            let title: String
            let physique: String
        
        let context = Context(title: "Leaf 4", physique:"Hey Leaf Tau!")
        return req.view.render("index", context)
    

We will use an Encodable object and go it round as a context variable. It is a handy approach of offering values for our Leaf variables. Earlier than we proceed I’ve to inform you that each one of this can proceed to work in Leaf Tau and you do not have to make use of the brand new strategies. 👍



New render strategies

So let me present you the very same factor utilizing the brand new API.

import Vapor
import Leaf

public func configure(_ app: Utility) throws 

    app.views.use(.leaf)

    app.get()  req -> EventLoopFuture<View> in
        let context: LeafRenderer.Context = [
            "title": "Leaf 4",
            "body": "Hello Leaf Tau!",
        ]
        return req.leaf.render(template: "index", context: context)
    

That is not an enormous deal you might say at first sight. Nicely, the factor is that this new methodology supplies type-safe values for our templates and that is simply the tip of the iceberg. It’s best to neglect concerning the view property on the request object, since Leaf began to outgrow the view layer in Vapor.

import Vapor
import Leaf

public func configure(_ app: Utility) throws 

    app.views.use(.leaf)

    app.get()  req -> EventLoopFuture<View> in
        let identify = "Leaf Tau"
        let context: LeafRenderer.Context = [
            "title": "Leaf 4",
            "body": .string("Hello (name)!"),
        ]
        return req.leaf.render(template: "index",
                               from: "default",
                               context: context,
                               choices: [.caching(.bypass)])
    

For those who take a better take a look at this comparable instance, you discover out that the context object and the values are representable by varied sorts, but when we attempt to use an interpolated string, we have now to be a little bit bit extra sort particular. A LeafRenderer.Context object is considerably a [String: LeafData] alias the place LeafData has a number of static strategies to initialize the built-in fundamental Swift sorts for Leaf. That is the place the type-safety function is available in Tau. You need to use the static LeafData helper strategies to ship your values as given sorts. 🔨

The from parameter is usually a LeafSource key, in case you are utilizing a number of template places or file sources then you possibly can render a view utilizing a selected one, ignoring the supply loading order. There’s one other render methodology with out the from parameter that’ll use the default search order of sources.


There’s a new argument that you should utilize to set predefined choices. You may disable the cache mechanism with the .caching(.bypass) worth or the built-in warning message by way of .missingVariableThrows(false) if a variable shouldn’t be outlined in your template, however you are attempting to make use of it. You may replace the timeout utilizing .timeout(Double) or the encoding through .encoding(.utf8) and grant entry to some nasty entities by together with the .grantUnsafeEntityAccess(true) worth plus there’s a embeddedASTRawLimit choice. Extra about this in a while.


Additionally it is doable to disable Leaf cache globally by way of the LeafRenderer.Context property:

if !app.atmosphere.isRelease 
    LeafRenderer.Choice.caching = .bypass

If the cache is disabled Leaf will re-parse template information each time you attempt to render one thing. Something that may be configured globally for LeafKit is marked with the @LeafRuntimeGuard property wrapper, you possibly can change any of the settings at utility setup time, however they’re locked as quickly as a LeafRenderer is created. 🔒




Context and knowledge illustration

You may conform to the LeafDataRepresentable protocol to submit a customized sort as a context worth. You simply need to implement one leafData property.

struct Person 
    let id: UUID?
    let e mail: String
    let birthYear: Int?
    let isAdmin: Bool

extension Person: LeafDataRepresentable 
    var leafData: LeafData 
        .dictionary([
            "id": .string(id?.uuidString),
            "email": .string(email),
            "birthYear": .int(birthYear),
            "isAdmin": .bool(isAdmin),
            "permissions": .array(["read", "write"]),
            "empty": .nil(.string),
        ])
    

As you possibly can see there are many LeafData helper strategies to characterize Swift sorts. Each single sort has built-in non-obligatory help, so you possibly can ship nil values with out spending further effort on worth checks or nil coalescing.

app.get()  req -> EventLoopFuture<View> in
    let consumer = Person(id: .init(),
                e mail: "[email protected]",
                birthYear: 1980,
                isAdmin: false)

    return req.leaf.render(template: "profile", context: [
        "user": user.leafData,
    ])

You may assemble a LeafDataRepresentable object, however you continue to have to make use of the LeafRenderer.Context as a context worth. Fortuitously that sort may be expressed utilizing a dictionary the place keys are strings and values are LeafData sorts, so this can scale back the quantity of code that you must sort.



Constants, variables, nil coalescing

Now let’s transfer away a little bit bit from Swift and discuss concerning the new options in Leaf. In Leaf Tau you possibly can outline variables utilizing template information with actual dictionary and array help. 🥳

#var(x = 2)
<p>2 + 2 = #(x + 2)</p>
<hr>
#let(consumer = ["name": "Guest"])
<p>Hey #(consumer.identify)</p>
<hr>
#(non-obligatory ?? "fallback")

Identical to in Swift, we are able to create variables and constants with any of the supported sorts. Whenever you inline a template variables may be accessed in each templates, that is fairly useful as a result of you do not have to repeat the identical code time and again, however you should utilize variables and reuse chunks of Leaf code in a clear and environment friendly approach. Let me present you the way this works.

Additionally it is doable to make use of the coalescing operator to offer fallback values for nil variables.




Outline, Consider, Inline

One of many greatest debate in Leaf is the entire template hierarchy system. In Tau, the whole strategy is rebuilt underneath the hood (the entire thing is extra highly effective now), however from the end-user perspective just a few key phrases have modified.



Inline

Lengthen is now changed with the brand new inline block. The inline methodology actually places the content material of a template into one other. You may even use uncooked values in case you do not need to carry out different operations (corresponding to evaluating Leaf variables and tags) on the inlined template.


<!-- index.leaf -->
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta identify="viewport" content material="width=device-width, initial-scale=1">
        <title>Leaf 4</title>
    </head>
    <physique>
        #inline("dwelling", as: uncooked)
    </physique>
</html>

<!-- dwelling.leaf -->
<h1>Hey Leaf Tau!</h1>

As you possibly can see we’re merely placing the content material of the house template into the physique part of the index template.

Now it is extra fascinating once we skip the uncooked half and we inline a daily template that accommodates different expressions. We’re going to flip issues just a bit bit and render the house template as a substitute of the index.


app.get()  req -> EventLoopFuture<View> in
    req.leaf.render(template: "dwelling", context: [
        "title": "Leaf 4",
        "body": "Hello Leaf Tau!",
    ])


So how can I reuse my index template? Ought to I merely print the physique variable and see what occurs? Nicely, we are able to strive that…


<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta identify="viewport" content material="width=device-width, initial-scale=1">
        <title>#(title)</title>
    </head>
    <physique>
        #(physique)
    </physique>
</html>

<!-- dwelling.leaf -->
<h1>Hey Leaf Tau!</h1>
#inline("index")


Wait a minute… this code shouldn’t be going to work. Within the dwelling template first we print the physique variable, then we inline the index template and print its contents. That is not what we would like. I need to use the contents of the house template and place it in between the physique tags. 💪




Consider

Meet consider, a perform that may consider a Leaf definition. You may consider this as a block variable definition in Swift. You may create a variable with a given identify and in a while name that variable (consider) utilizing parentheses after the identify of the variable. Now you are able to do the identical skinny in Leaf through the use of the consider key phrase or straight calling the block like a perform.


<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta identify="viewport" content material="width=device-width, initial-scale=1">
        <title>#(title)</title>
    </head>
    <physique>
        #consider(bodyBlock) (# or you should utilize the `#bodyBlock()` syntax #)
    </physique>
</html>


On this template we are able to consider the bodyBlock and in a while we’ll be capable of outline it some other place.



Outline

Definitions. Lastly arrived to the final part that we’ll must compose templates. Now we are able to create our physique block within the dwelling template.


#outline(bodyBlock):
<h1>#(physique)</h1>
#enddefine

#inline("index")

Now in case you reload the browser (Leaf cache have to be disabled) all the pieces ought to work as it’s anticipated. Magic… or science, no matter, be happy to decide on one. 💫

Particular thanks goes to tdotclare who labored day and night time to make Leaf higher. 🙏

So what is going on on right here? The #outline(bodyBlock) part is liable for constructing a block variable known as bodyBlock that’s callable and we are able to consider it in a while. We merely print out the physique context variable inside this block, the physique variable is a context variable coming from Swift, that is fairly easy. Subsequent we inline the index template (think about copy-pasting complete content material of the index template into the house template) which is able to print out the title context variable and evaluates the bodyBlock. The bodyBlock can be out there since we have simply outlined it earlier than our inline assertion. Straightforward peasy. 😝


<!-- var, let -->
#var(x = 10)
#let(foo = "bar")

<!-- outline -->
#outline(resultBlock = x + 1)
#outline(bodyBlock):
    <h2>Hey, world!</h2>
    <p>I am a multi-line block definition</p>
#endblock

<!-- consider -->
#consider(resultBlock)
#bodyBlock()


I am actually pleased about these modifications, as a result of Leaf is heading into the proper course, and people individuals who haven’t used the pre-released Leaf 4 variations but these modifications will not trigger that a lot bother. This new strategy follows extra like the unique Leaf 3 conduct.


Goodbye tags. Hey entities!

Nothing is a tag anymore, however they’re separated to the next issues:

  • Blocks (e.g. #for, #whereas, #if, #elseif, #else)
  • Features (e.g. #Date, #Timestamp, and so on.)
  • Strategies (e.g. .depend(), .isEmpty, and so on.)

Now you can create your very personal features, strategies and even blocks. 🔥

public struct Hey: LeafFunction, StringReturn, Invariant 
    public static var callSignature: [LeafCallParameter]  [.string] 

    public func consider(_ params: LeafCallValues) -> LeafData 
        guard let identify = params[0].string else 
            return .error("`Hey` have to be known as with a string parameter.")
        
        return .string("Hey (identify)!")
    


public func configure(_ app: Utility) throws 

    LeafConfiguration.entities.use(Hey(), asFunction: "Hey")
    

Now you should utilize this perform in your templates like this:

#Hey("Leaf Tau")

You may occasion overload the identical perform with completely different argument labels


public struct HelloPrefix: LeafFunction, StringReturn, Invariant 

    public static var callSignature: [LeafCallParameter]  [
        .string(labeled: "name"),
        .string(labeled: "prefix", optional: true, defaultValue: "Hello")]
    

    public func consider(_ params: LeafCallValues) -> LeafData 
        guard let identify = params[0].string else 
            return .error("`Hey` have to be known as with a string parameter.")
        
        let prefix = params[1].string!
        return .string("(prefix) (identify)!")
    


public func configure(_ app: Utility) throws 


    LeafConfiguration.entities.use(Hey(), asFunction: "Hey")
    LeafConfiguration.entities.use(HelloPrefix(), asFunction: "Hey")

    

This manner you should utilize a number of variations of the identical performance.

#Hey("Leaf Tau")
#Hey(identify: "Leaf Tau", prefix: "Hello")

Here is one other instance of a customized Leaf methodology:


public struct DropLast: LeafNonMutatingMethod, StringReturn, Invariant 
    public static var callSignature: [LeafCallParameter]  [.string] 

    public func consider(_ params: LeafCallValues) -> LeafData 
        .string(String(params[0].string!.dropLast()))
    


public func configure(_ app: Utility) throws 

    LeafConfiguration.entities.use(DropLast(), asMethod: "dropLast")
    

You may outline your personal Leaf entities (extensions) through protocols. You do not have to recollect all of them, as a result of there may be numerous them, however that is the sample that you must search for Leaf*[Method|Function|Block] for the return sorts: [type]Return. If you do not know invariant is a perform that produces the identical output for a given enter and it has no unwanted side effects.

You may register these entities as[Function|Method|Block] by way of the entities property. It is going to take some time till you get accustomed to them, however luckily Leaf 4 comes with fairly a superb set of built-in entities, hopefully the official documentation will cowl most of them. 😉

public struct Path: LeafUnsafeEntity, LeafFunction, StringReturn 
    public var unsafeObjects: UnsafeObjects? = nil

    public static var callSignature: [LeafCallParameter]  [] 

    public func consider(_ params: LeafCallValues) -> LeafData 
        guard let req = req else  return .error("Wants unsafe entry to Request") 
        return .string(req.url.path)
    



public func configure(_ app: Utility) throws 

    LeafConfiguration.entities.use(Path(), asFunction: "Path")

    

Oh, I nearly forgot to say that in case you want particular entry to the app or req property you must outline an unsafe entity, which can be thought-about as a foul apply, however luckily we have now one thing else to switch the necessity for accessing these items…



Scopes

If you should go particular issues to your Leaf templates it is possible for you to to outline customized scopes.

extension Request 
    var customLeafVars: [String: LeafDataGenerator] 
        [
            "url": .lazy([
                        "isSecure": LeafData.bool(self.url.scheme?.contains("https")),
                        "host": LeafData.string(self.url.host),
                        "port": LeafData.int(self.url.port),
                        "path": LeafData.string(self.url.path),
                        "query": LeafData.string(self.url.query)
                    ]),
        ]
    

extension Utility 
    var customLeafVars: [String: LeafDataGenerator] 
        [
            "isDebug": .lazy(LeafData.bool(!self.environment.isRelease && self.environment != .production))
        ]
    


struct ScopeExtensionMiddleware: Middleware 

    func reply(to req: Request, chainingTo subsequent: Responder) -> EventLoopFuture<Response> 
        do 
            strive req.leaf.context.register(mills: req.customLeafVars, toScope: "req")
            strive req.leaf.context.register(mills: req.utility.customLeafVars, toScope: "app")
        
        catch 
            return req.eventLoop.makeFailedFuture(error)
        
        return subsequent.reply(to: req)
    


public func configure(_ app: Utility) throws 

    app.middleware.use(ScopeExtensionMiddleware())

    

Lengthy story brief, you possibly can put LeafData values right into a customized scope, the good factor about this strategy is that they are often lazy, so Leaf will solely compute the corresponding values if when are getting used. The query is, how can we entry the scope? 🤔

<ul>
    <li><b>ctx:</b>: #($context)</li>
    <li><b>self:</b>: #(self)</li>
    <li><b>req:</b>: #($req)</li>
    <li><b>app:</b>: #($app)</li>
</ul>

It’s best to know that self is an alias to $context, and you’ll entry your personal context variables utilizing the $ signal. It’s also possible to construct your personal LeafContextPublisher object that may use to change the scope.


closing class VersionInfo: LeafContextPublisher 

    let main: Int
    let minor: Int
    let patch: Int
    let flags: String?

    init(main: Int, minor: Int, patch: Int, flags: String? = nil) 
        self.main = main
        self.minor = minor
        self.patch = patch
        self.flags = flags
    

    var versionInfo: String 
        let model = "(main).(minor).(patch)"
        if let flags = flags 
            return model + "-" + flags
        
        return model
    

    lazy var leafVariables: [String: LeafDataGenerator] = [
        "version": .lazy([
            "major": LeafData.int(self.major),
            "minor": LeafData.int(self.minor),
            "patch": LeafData.int(self.patch),
            "flags": LeafData.string(self.flags),
            "string": LeafData.string(self.versionInfo),
        ])
    ]


public func configure(_ app: Utility) throws 

    app.views.use(.leaf)

    app.middleware.use(LeafCacheDropperMiddleware())

    app.get(.catchall)  req -> EventLoopFuture<View> in
        var context: LeafRenderer.Context = [
            "title": .string("Leaf 4"),
            "body": .string("Hello Leaf Tau!"),
        ]
        let versionInfo = VersionInfo(main: 1, minor: 0, patch: 0, flags: "rc.1")
        strive context.register(object: versionInfo, toScope: "api")
        return req.leaf.render(template: "dwelling", context: context)
    

    


What if you wish to prolong a scope? No drawback, you are able to do that by registering a generator

extension VersionInfo 

    var extendedVariables: [String: LeafDataGenerator] [
        "isRelease": .lazy(self.major > 0)
    ]




let versionInfo = VersionInfo(main: 1, minor: 0, patch: 0, flags: "rc.1")
strive context.register(object: versionInfo, toScope: "api")
strive context.register(mills: versionInfo.extendedVariables, toScope: "api")
return req.leaf.render(template: "dwelling", context: context)


There’s an app and req scope out there by default, so you possibly can prolong these by way of an extension that may return a [String: LeafDataGenerator] variable.




Abstract

As you possibly can see Leaf improved quite a bit in comparison with the earlier variations. Even within the beta / rc interval of the 4th main model of this async template engine introduced us so many nice stuff.

Hopefully this text will assist you throughout the migration course of, and I consider that it is possible for you to to make the most of most of those built-in functionalities. The model new render and context mechanism provides us extra flexibility with out the necessity of declaring further native constructions, Leaf variables and the redesigned hierarchy system will help us to design much more highly effective reusable templates. Via entity and the scope API we will carry Leaf to a totally new stage. 🍃




Source link

You might also like

Introducing – AI Utils for macOS

Encoding and decoding data using the Hummingbird framework

Hummingbird routing and requests – The.Swift.Dev.

Share30Tweet19
learningcode_x1mckf

learningcode_x1mckf

Recommended For You

Introducing – AI Utils for macOS

by learningcode_x1mckf
March 24, 2023
0
Introducing – AI Utils for macOS

⚠️ REQUIRES a FREE OpenAI API key. You will get one utilizing this hyperlink. ⚠️ Improve your productiveness with our AI powered utility application. - accessible...

Read more

Encoding and decoding data using the Hummingbird framework

by learningcode_x1mckf
March 22, 2023
0
Encoding and decoding data using the Hummingbird framework

HTTP is all about sending and receiving information over the community. Initially it was solely utilized to switch HTML paperwork, however these days we use HTTP to switch...

Read more

Hummingbird routing and requests – The.Swift.Dev.

by learningcode_x1mckf
March 17, 2023
0
Hummingbird routing and requests – The.Swift.Dev.

Routing on the server facet means the server goes to ship a response primarily based on the URL path that the consumer referred to as when firing up...

Read more

Building a Scrollable Custom Tab Bar in SwiftUI

by learningcode_x1mckf
March 10, 2023
0
Building a Scrollable Custom Tab Bar in SwiftUI

Whether or not you’re making a social media app or a productiveness device, the tab bar interface can improve the consumer expertise by making it extra intuitive and...

Read more

Beginner’s guide to server-side Swift using the Hummingbird framework

by learningcode_x1mckf
March 8, 2023
0
Beginner’s guide to server-side Swift using the Hummingbird framework

Swift on the Server in 2023 Three years in the past I began to focus on Vapor, the preferred web-framework written in Swift, which served me very...

Read more
Next Post
An Introduction to JavaScript Web Workers

An Introduction to JavaScript Web Workers

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Related News

Can Google’s New Programming Language ‘Carbon’ Replace C++ Better Than Rust?

Can Google’s New Programming Language ‘Carbon’ Replace C++ Better Than Rust?

September 13, 2022
How to Transform the Character Case of a String in JavaScript

How to Transform the Character Case of a String in JavaScript

September 15, 2022
Document and Test Your Code at Once – Real Python

Document and Test Your Code at Once – Real Python

October 31, 2022

Browse by Category

  • C#
  • C++
  • Java
  • JavaScript
  • Python
  • Swift

RECENT POSTS

  • 2023 Java roadmap for developers – TheServerSide.com
  • YS Jagan launches Ragi Java in Jagananna Gorumudda, says focused on intellectual development of students – The Hans India
  • Disadvantages of Java – TheServerSide.com

CATEGORIES

  • C#
  • C++
  • Java
  • JavaScript
  • Python
  • Swift

© 2022 Copyright Learning Code

No Result
View All Result
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#

© 2022 Copyright Learning Code

Are you sure want to unlock this post?
Unlock left : 0
Are you sure want to cancel subscription?