Lambda DSL
In Spring you will come across following syntax :
GET("/hello") { _ ->
ok().body(just("Hello World!"), String::class.java)
}
or
beans {
bean("webHandler"){
RouterFunctions.toWebHandler(router) //<- change our routing intpo spring component
}
}
This syntax is built with standard kotlin mechanisms. In this part we are going to better understand how.
FILE : IntroSpring1.kt
Lambda as the last parameter
In Kotlin when lambda is the last parameter you can use special syntax where lambda actually will be placed outside parenthesis. Let me show you :
fun singleRoute(path:String, request:String, handler : (Request) -> Response):String {
Displayer.section("handling call on path",path)
return handler(request)
}
We have a definition of a single route handler. It has 3 parameters where the last one has lambda type : (Request) -> Response
You can call this method as usual :
singleRoute("/hello","requestExample1", {request -> "response for $request"})
but now let's use alternative syntax where you can move lambda outside parantheses
val r2=singleRoute("/hello","requestExample1"){request ->"esponse for $request"}
this is still valid code! And we can make it look like "more native" if we format brackets like in for example while constructions
val r2=singleRoute("/hello","requestExample1"){request ->
"esponse for $request"
}
Implicit receiver
Well, not exactly like while constructions because it is still lambda with parameter and this arrow after it...
As first step we could use implicit function parameter, so arrow will disappear but we have to write it everywhere.
val buildingResult=buildRouterWitIt {(builder) ->
it.route("/hello",{r -> "response for $r"})
it.route("/info",{r -> "response for $r"})
}
//can become this
val buildingResult=buildRouterWitIt {
it.route("/hello",{r -> "response for $r"})
it.route("/info",{r -> "response for $r"})
}
but what if we could write this code like in the following example :
val dslResult= buildRouter {
route("/index.html",{r -> "response for $r"})
route("/register",{"response for $it"})
}
Of course I've posted this example because you actually can :) There is a special form of an argument declaration where you are actually add extension method to the argument in method scope : YourClass.(A) -> B
fun buildRouter(buildLogic : CustomRoutingDsl.() -> Unit):Routes{
val dsl = CustomRoutingDsl()
// buildLogic(dsl)
dsl.buildLogic()
return dsl.toRoutes()
}
Exercises
FILE : IntroSpring1Exercises
You will have to implement following DSL :
val news=news {
article {
titled("My first article")
withContent("First article content")
}
article {
titled("Learning Kotlin DSL")
withContent("To create Dsl...")
}
}
There are actually two DSLs : one for building whole news and the second one for particular article
class NewsBuildingDsl{
fun article(builder: ArticleDsl.() -> Unit){
TODO("provide your DSL here")
}
}