var foo : String? = null
fun main(args: Array<String>): Unit {
foo = "Hello World"
io.ktor.server.netty.EngineMain.main(args)
}
#Suppress("unused") // Referenced in application.conf
#kotlin.jvm.JvmOverloads
fun Application.module(testing: Boolean = false) {
// foo is null here
}
How can I access foo in Application.module and why is this an issue to begin with?
You cannot access such variables in your modules since Ktor loads your application (and modules ofc) in a different classloader.
When you call EngineMain.main(), it goes through a long chain of calls, and one of the steps is to create a new classloader in createApplication() method here. The implementation of createClassLoader() is here.
You can pass arbitrary arguments in the following format: -P:<argument> where <argument> is the actual name for your argument. In the Application.module you can access them via config object:
fun Application.main() {
println(environment.config.property("<argument>").getString())
}
Related
I have Kotlin some code that works as a class but when I try and run it as a Kotlin script I am getting " error: 'getNameAndVersion' is a member and an extension at the same time. References to such elements are not allowed"
enum class Env { Test, Sit }
data class ImageVersions(val apiName: String, val versions: Map<Env, String?>)
fun String.getNameAndVersion() = substringBefore(':') to substringAfter(':')
val testList = listOf("api-car-v1:0.0.118", "api-dog-v1:0.0.11", "api-plane-v1:0.0.36")
val sitList = listOf("api-car-v1:0.0.119", "api-plane-v1:0.0.37", "api-dog-v1:0.0.12")
getVersions(
mapOf(
Env.Test to testList,
Env.Sit to sitList
)
).forEach(::println)
fun getVersions(envMap: Map<Env, List<String>>): List<ImageVersions> {
val envApiNameMap = envMap.mapValues { it.value.associate(String::getNameAndVersion) }
val allApiNames = envApiNameMap.flatMap { it.value.keys }.distinct()
return allApiNames.map { apiName ->
ImageVersions(apiName, envApiNameMap.mapValues { it.value[apiName] })
}
}
I don't think I'm doing anything wrong with the way I'm using the method reference but according to my compiler I'm wrong. Appreciate some help with this. thanks
kotlinc -script .\myscript.kts
error: 'getNameAndVersion' is a member and an extension at the same time. References to such elements are not allowed
I don't have any experience with scripts but this error occurs when you try to reference a function inside a class that is also an extension function. (Here it is pointing to String::getNameAndVersion). Maybe when you run a script, the entire code is wrapped inside a class and then executed.
To fix this you can do one of the following:
Convert the function to a normal function which accepts a String parameter.
fun getNameAndVersion(s: String) = s.substringBefore(':') to s.substringAfter(':')
And replace String::getNameAndVersion with just ::getNameAndVersion in associate function
Other option is to directly call the function in the associate's lambda instead of passing a reference of this function.
.associate { it.getNameAndVersion() }
I´m trying to use a Configuration in an Annotation, like so:
#ConfigurationProperties("scheduling")
interface SchedulingConfiguration {
val initialDelay: String
val fixedDelay: String
}
#Singleton
class Worker(
private val configuration: SchedulingConfiguration,
) {
private val log = LoggerFactory.getLogger(javaClass)
#Scheduled(initialDelay = configuration.initialDelay, fixedDelay = configuration.fixedDelay)
fun fetchQueueEntry() {
log.info("Fetching entry")
}
}
I´m getting the warning An annotation argument must be a compile-time constant.
Is there some way to get this working with Micronaut?
I managed to get it running by browsing the Micronaut documentation and accidentally stumbling across Property Placeholders. This will work fine, even though not feeling 'optimal'.
#Singleton
class Worker {
private val log = LoggerFactory.getLogger(javaClass)
#Scheduled(
initialDelay = "\${scheduling.initialDelay}",
fixedDelay = "\${scheduling.fixedDelay}"
)
fun fetchQueueEntry() {
log.info("Fetching entry")
}
}
It´s also possible to define default values that will be used if the keys are not present in config files or env variables:
#Scheduled(
initialDelay = "\${scheduling.initialDelay:0s}",
fixedDelay = "\${scheduling.fixedDelay:10s}"
)
With no default values and absent configuration for used property placeholders, an Exception will be thrown at runtime and the application will shut down.
I have some testcases that share a common setup. They all need two fields which can be initialized in the same way. So I thought I can extract them into lateinit var fields and create them in an test-case-interceptor.
But when I try to access them in my testcases they always throw an exception because they are not initialized.
Is there a way to create the fields before every testcase?
Here is my code so far:
class ElasticsearchFieldImplTest : WordSpec() {
// These 2 are needed for every test
lateinit var mockDocument: ElasticsearchDocument
lateinit var mockProperty: KProperty<*>
override fun interceptTestCase(context: TestCaseContext, test: () -> Unit) {
// Before Each
mockDocument = mock()
mockProperty = mock {
on {name} doReturn Gen.string().generate()
}
// Execute Test
test()
// After Each
}
init {
"ElasticsearchFields" should {
"behave like normal var properties" {
val target = ElasticsearchFieldImpl<Any>()
// Here the exception is thrown
target.getValue(mockDocument, mockProperty) shouldBe null
val testValue = Gen.string().generate()
target.setValue(mockDocument, mockProperty, testValue)
target.getValue(mockDocument, mockProperty) shouldBe testValue
}
}
}
}
When I step through it with a debugger and set a breakpoint in the interceptTestCase methods I see that it is executed before the test and that the properties are initialized. Then I step forward to the test and in it the properties are not initialized anymore.
Клаус Шварц's answer is incorrect. This is not how kotlintest works - in init lambdas are only created, not run. So you are not accessing your lateinit vars in init block. They just never have any value assigned.
This doesn't work because of bug in kotlintest, described (and actually almost resolved) here: https://github.com/kotlintest/kotlintest/issues/174
In short - interceptTestCase is called on different instance of class than real tests. So it has no influence on your tests at all.
Workaround is to override property:
override val oneInstancePerTest = false
Then there is only one instance and interceptTestCase works correctly, but you have to remember - there is only one instance for all tests.
Kotlintest 3.0 will be free of this bug. (But possibly might have one instance for all tests by default.)
You should not access lateinit vars before they were initialized.
The problem is that you are accessing your lateinit vars inside init {} block, which is default constructor and it is called before interceptTestCase().
The easiest way here is just to make mockDocument and mockProperty nullable.
var mockDocument: ElasticsearchDocument? = null
var mockProperty: KProperty<*>? = null
and if you want you test to crash if these fields were not initialized add !! modifier:
init {
"ElasticsearchFields" should {
"behave like normal var properties" {
val target = ElasticsearchFieldImpl<Any>()
// Here the exception is thrown
target.getValue(mockDocument!!, mockProperty!!) shouldBe null
val testValue = Gen.string().generate()
target.setValue(mockDocument!!, mockProperty!!, testValue)
target.getValue(mockDocument!!, mockProperty!!) shouldBe testValue
}
}
}
A previous question shows how to put a static initializer inside a class using its companion object. I'm trying to find a way to add a static initializer at the package level, but it seems packages have no companion object.
// compiler error: Modifier 'companion' is not applicable inside 'file'
companion object { init { println("Loaded!") } }
fun main(args: Array<String>) { println("run!") }
I've tried other variations that might've made sense (init on its own, static), and I know as a workaround I can use a throwaway val as in
val static_init = {
println("ugly workaround")
}()
but is there a clean, official way to achieve the same result?
Edit: As #mfulton26's answer mentions, there is no such thing as a package-level function really in the JVM. Behind the scenes, the kotlin compiler is wrapping any free functions, including main in a class. I'm trying to add a static initializer to that class -- the class being generated by kotlin for the free functions declared in the file.
Currently there is no way to add code to the static constructor generated for Kotlin file classes, only top-level property initializers are getting there. This sounds like a feature request, so now there is an issue to track this: KT-13486 Package-level 'init' blocks
Another workaround is to place initialization in top-level private/internal object and reference that object in those functions that depend on the effect of that initialization. Objects are initialized lazily, when they are referenced first time.
fun dependsOnState(arg: Int) = State.run {
arg + value
}
private object State {
val value: Int
init {
value = 42
println("State was initialized")
}
}
As you mentioned, you need a property with something that would run on initialisation:
val x = run {
println("The package class has loaded")
}
I got around it by using a Backing Property on the top-level, under the Kotlin file. Kotlin Docs: Backing Properties
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null) {
_table = HashMap() // Type parameters are inferred
// .... some other initialising code here
}
return _table ?: throw AssertionError("Set to null by another thread")
}
I'm trying to keep this minimal, but let me know if I'm being too minimal.
Suppose you have a class hierarchy like this, designed for generating HTML (inspired by the Kotlin tutorial; semi-pseudocode follows):
class Tag {
protected val children = arrayListOf<Tag>()
operator fun String.unaryPlus() = children.add(Text(this))
}
class TagWithChildren : Tag() {
fun head(init: Head.() -> Unit) = initializeTag(Head(), init)
fun script(init: Script.() -> Unit) = initializeTag(Script(), init)
fun <T : Tag> initializeTag(tag: T, init: T.() -> Unit): T {
tag.init()
children.add(tag)
return tag
}
}
class Head : TagWithChildren()
class Script : Tag()
class Text(val str: Text) : Tag()
Notice that Head has head and script methods while Script doesn't.
Now you can construct a template that looks like this:
head {
script {
+"alert('hi');"
}
}
Which works great! However, if the block passed to script tries to call methods that aren't available on Script, it can call the method on Head instead. For example,
head {
script {
script {
+"alert('hi');"
}
}
}
not only isn't a compile error, it's actually equivalent to
head {
script {
}
script {
+"alert('hi');"
}
}
which is super confusing, from a template author's perspective.
Is there any way to prevent method lookups from traveling up the scope like that? I only want it to look at the innermost scope.
UPDATE 11/24/2016:
Kotlin 1.1-M03 has introduced scope control, which I believe solves exactly this problem. https://blog.jetbrains.com/kotlin/2016/11/kotlin-1-1-m03-is-here/
The current behavior is intentional. Code in a lambda has access to receivers of all enclosing scopes. It is possible that a future version of Kotlin will add a modifier that will restrict a lambda with receiver to calling methods on that receiver only and not the enclosing scopes, but in the current version there's no way to change that behavior.
As a workaround, I can have it fail at runtime if I change the classes to look like this:
open class Tag {
operator fun String.unaryPlus()
// pulled up from TagWithChildren, call protected method
fun head(init: Head.() -> Unit) = addChild(Head())
fun script(init: Script.() -> Unit) = addChild(Head())
// throws in Tag
open protected fun addChild(t: Tag) = throw IllegalArgumentException()
}
class TagWithChildren : Tag() {
// overridden to not throw in subclass
protected override fun addChild(t: Tag) = children.add(t)
}
This way, every Tag has the builder methods (solving the scoping problem), but actually calling them may result in a runtime failure.