Why doesn't Kotlin let me use writeText extension? - kotlin

I have a Kotlin class with a method, which creates some text and then I want to write it to a file:
import java.io.File
import java.util.*
import kotlin.io.*
class MyClass {
fun run() {
val result = html {
head {
title { +"Entry page" }
}
body {
h1 {
+"Map"
}
+"Some HTML code"
}
}
File("target/wdef/index.html").writeText(result)
}
}
I get an error - the writeText(result) is highlighted red and I get the error message Error:(26, 40) Kotlin: Unresolved reference: writeText.
How can I fix it?

A problem might be that you pass a wrong type to writeText. It requires String, but you pass an html building object HTML. Try to convert it with toString:
File("target/wdef/index.html").writeText(result.toString())

Related

ktor - is there a way to serialize sub-template Template<FlowContent> to txt/html?

I am using HTML DSL as the templating engine in my ktor project.
I am trying to send one of the sub-templates Template as a text response (I do not want to send the full HtmlTemplate).
So far I have got below - to make it work I have to wrap my TestTemplate in another div:
fun Route.test() {
get("/test") {
call.respondText(
buildString {
appendHTML().div {
insert(TestTemplate(), TemplatePlaceholder())
}
}
)
}
}
This gives me following response (the inner div is my TestTemplate):
<div>
<div>this is the element I want to get, without the outer element</div>
</div>
What I would like to get is just the TestTemplate:
<div>this is the element I want to get, without the outer element</div>
Is there a way to achieve it in ktor using the HTML DSL?
You can make a class for the template with an HTMLStreamBuilder<StringBuilder> type parameter instead of FlowContent to build a final HTML string without any tags (FlowContent inherits Tag interface).
import io.ktor.application.*
import io.ktor.html.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import kotlinx.html.*
import kotlinx.html.stream.HTMLStreamBuilder
suspend fun main() {
embeddedServer(Netty, port = 3333) {
routing {
get("/test") {
val builder = StringBuilder()
val html: HTMLStreamBuilder<StringBuilder> = HTMLStreamBuilder(builder, prettyPrint = true, xhtmlCompatible = false)
html.insert(TestTemplate(), TemplatePlaceholder())
call.respondText(html.finalize().toString())
}
}
}.start()
}
class TestTemplate : Template<HTMLStreamBuilder<StringBuilder>> {
val articleTitle = Placeholder<FlowContent>()
val articleText = Placeholder<FlowContent>()
override fun HTMLStreamBuilder<StringBuilder>.apply() {
article {
h2 {
insert(articleTitle)
}
p {
insert(articleText)
}
}
}
}

How can I check Koin modules while using injection parameters?

I would like to check my configuration by using the checkModules() method provided by koin-test as explained here.
However, I am using injection parameters and my test fails with an exception:
org.koin.core.error.NoParameterFoundException: Can't get parameter value #0 from org.koin.core.parameter.DefinitionParameters#3804648a
Here is a simple test to demonstrate the issue:
import org.junit.Test
import org.koin.dsl.koinApplication
import org.koin.dsl.module
import org.koin.test.KoinTest
import org.koin.test.check.checkModules
class TestCase : KoinTest {
#Test
fun checkModules() {
koinApplication {
modules(
module { factory { (msg: String) -> Message(msg) } }
)
}.checkModules()
}
data class Message(val message: String)
}
Is there a way to make this work? How could I provide the missing parameter?
You need to pass this parameter to your test, like this:
class TestCase : KoinTest {
#Test
fun checkModules() {
koinApplication {
modules(module { factory { (msg: String) -> Message(msg) } })
}.checkModules {
create<Message> { parametersOf("testMessage") }
}
}
data class Message(val message: String)
}
Example from Koin repository: CheckModulesTest.kt#L156
My issue with the same question: Issue

How to import type extension function in Kotlin

I wrote an extension function for Any type, that will retrieve object property value by its name. I want to be able to use it from everywhere in my project. Here is my extension function:
package com.example.core.utils.validation
import java.util.NoSuchElementException
import kotlin.reflect.full.memberProperties
fun Any.getFieldValue(fieldName: String): Any? {
try {
return this.javaClass.kotlin.memberProperties.first { it.name == fieldName }.get(this)
} catch (e: NoSuchElementException) {
throw NoSuchFieldException(fieldName)
}
}
Now I want to use it like this
package com.example.core
import com.example.core.utils.validation.*
class App {
val obj = object {
val field = "value"
}
val fieldValue = obj.getFieldValue("field")
}
But there is Unresolved reference error
How should I make my extension function global and import it anywhere?
You should use import statement instead of second package declaration in your second code snippet.
See documentation: https://kotlinlang.org/docs/reference/extensions.html#scope-of-extensions
And actually I am not sure that it is valid to make extension for Any type. I think in that case you need to call it on object of type Any.

How mock Kotlin extension function in interface?

I have an extension function for interface like the following:
import javax.jms.ConnectionFactory
fun ConnectionFactory.foo() {
println("do some stuff")
}
How can I mock the function foo?
Please note, I have seen approaches for classes and objects in http://mockk.io/#extension-functions, but it does not work. I have tried this one:
import io.mockk.classMockk
import io.mockk.every
import org.junit.Test
import javax.jms.ConnectionFactory
class ExtensionFunctionTest {
#Test
fun mockExtensionFunction() {
val connectionFactory = classMockk(ConnectionFactory::class)
every { connectionFactory.foo() } returns println("do other stuff")
connectionFactory.foo()
}
}
It throws exception:
io.mockk.MockKException: Missing calls inside every { ... } block.
According to the documentation in case of module wide extension functions you need to staticMock "hidden" class created for an extension function.
Here is an example (assuming the file name is com/sample/extmockingtest/SampleTest.kt):
fun <T> Iterable<T>.foo(): String = "do some stuff"
class ExtensionFunctionTest {
#Test
fun mockExtensionFunction() {
val itMock = classMockk(Iterable::class);
staticMockk("com.sample.extmockingtest.SampleTestKt").use {
every {
itMock.foo()
} returns "do other stuff"
assertEquals("do other stuff", itMock.foo())
verify {
itMock.foo()
}
}
}
}

How do I extract parts of code into local variables in Kotlin when using Ktor's HTML builder?

I am trying to understand the HTML builder in Kotlin / Ktor.
The example here uses the HTML builder to build the result:
call.respondHtml {
head {
title { +"HTML Application" }
}
body {
h1 { +"Sample application with HTML builders" }
widget {
+"Widgets are just functions"
}
}
}
I am trying to extract the body into a variable like this:
val block: HTML.() -> Unit = {
head {
title { +"HTML Application" }
}
body {
h1 { +"Sample application with HTML builders" }
}
}
call.respondHtml(block)
Now I get the following compile error:
Error:(37, 22) Kotlin: None of the following functions can be called with the arguments supplied:
public suspend fun ApplicationCall.respondHtml(status: HttpStatusCode = ..., versions: List<Version> = ..., cacheControl: CacheControl? = ..., block: HTML.() -> Unit): Unit defined in org.jetbrains.ktor.html
public suspend fun ApplicationCall.respondHtml(status: HttpStatusCode = ..., block: HTML.() -> Unit): Unit defined in org.jetbrains.ktor.html
When I add the first (optional) argument it works again: call.respondHtml(HttpStatusCode.OK, block).
Why does it not work, when I simply try to extract the body into a variable?
I think the compiler doesn't like having a mandatory after default parameters, unless it is a lambda outside of the braces.
Try to name it:
call.respondHtml(block = block)
BTW, if what you want is to extract logic, I would recommend using functions. For your example:
fun HTML.headAndBody() {
head {
title { +"HTML Application" }
}
body {
h1 { +"Sample application with HTML builders" }
widget {
+"Widgets are just functions"
}
}
}
call.respondHtml {
headAndBody()
}
That way you can even add parameters to your block of html, creating a custom component out of it.