3

I'm digging the built-in configuration support, and want to use it (instead of just rolling my own alongside Ktor's), but I'm having a hard time figuring out how to do it in a clean way. I've got this, and it's working, but it's really ugly and I feel like there has to be a better way:

val myBatisConfig = MyBatisConfig(
        environment.config.property("mybatis.url").getString(),
        environment.config.property("mybatis.driver").getString(),
        environment.config.property("mybatis.poolSize").getString().toInt())

installKoin(listOf(mybatisModule(myBatisConfig), appModule), logger = SLF4JLogger())

Thanks for any help!

Phil Kulak
  • 6,960
  • 8
  • 45
  • 50

3 Answers3

7

Okay, I think I have a good, clean way of doing this now. The trick is to not bother going through the framework itself. You can get your entire configuration, as these cool HOCON files, extremely easily:

val config = ConfigFactory.load()

And then you can walk the tree yourself and build your objects, or use a project called config4k which will build your model classes for you. So, my setup above has added more configuration, but gotten much simpler and more maintainable:

installKoin(listOf(
            mybatisModule(config.extract("mybatis")),
            zendeskModule(config.extract("zendesk")),
            appModule),
        logger = SLF4JLogger())

Hope someone finds this useful!

Phil Kulak
  • 6,960
  • 8
  • 45
  • 50
6

Adding to the existing accepted answer. An implementation using ConfigFactory.load() could look like this (Without libs):


object Config {
    @KtorExperimentalAPI
    val config = HoconApplicationConfig(ConfigFactory.load())


    @KtorExperimentalAPI
    fun getProperty(key: String): String? = config.propertyOrNull(key)?.getString()


    @KtorExperimentalAPI
    fun requireProperty(key: String): String = getProperty(key)
            ?: throw IllegalStateException("Missing property $key")
}

So, theconfig class would become:

val myBatisConfig = MyBatisConfig(
        requireProperty("mybatis.url"),
        requireProperty("mybatis.driver"),
        requireProperty("mybatis.poolSize").toInt())
dstibbe
  • 1,589
  • 18
  • 33
  • 1
    There's no need to use a Config object. IMO objects without state just add unnecessary boiler plate code. You can just use top level functions for this. – Emanuel Moecklin Mar 26 '21 at 21:02
3

You could also try this solution:

class MyService(val url: String)

fun KoinApplication.loadMyKoins(environment: ApplicationEnvironment): KoinApplication {
    val myFirstModule = module {
        single { MyService(environment.config.property("mybatis.url").getString()) }
    }
    val mySecondModule = module {}
    return modules(listOf(myFirstModule, mySecondModule))
}

fun Application.main() {
    install(DefaultHeaders)
    install(Koin) {
        loadMyKoins(environment)
        SLF4JLogger()
    }
    routing {
        val service by inject<MyService>()
        get("/") {
            call.respond("Hello world! This is my service url: ${service.url}")
        }
    }
}

fun main(args: Array<String>) {
    embeddedServer(Netty, commandLineEnvironment(args)).start()
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
maslick
  • 2,903
  • 3
  • 28
  • 50