https://kotlinlang.org logo
Title
o

oshai

11/20/2018, 5:22 PM
Hi, I have a styling question related to data classes and copy method.
val connectionUri = "jdbc:postgresql://$host:$port/$database?user=$username&password=$password"

val connection = PostgreSQLConnectionBuilder.createConnectionPool(connectionUri) { it.copy(
        connectionCreateTimeout = 1
)}
I am writing a lib and I want to configure a connection. Configuration is a data class and all are vals. Can I avoid the
it.copy
in the code above (call the copy somehow in the lib), or is there an apply method?
Connection configuration is something like this:
data class ConnectionPoolConfiguration @JvmOverloads constructor(
        val host: String = "localhost",
        val port: Int = 5432,
        val database: String? = null,
        val username: String = "dbuser",
        val password: String? = null,
       val connectionCreateTimeout: Long? = null
)
so I cannot mutate it, that is why I am using the
it.copy
do you think of another alternative?
d

diesieben07

11/20/2018, 5:30 PM
copy
is the right tool here. Why don't you want to use it?
o

oshai

11/20/2018, 5:32 PM
just to make it a nicer DSL
d

diesieben07

11/20/2018, 5:32 PM
If you want a nice DSL you'd have to write something with actual `var`s if you want regular assignments.
o

oshai

11/20/2018, 5:34 PM
I like the configuration to be immutable
I wish to somehow call copy from the lib, on parameters I get from the user 🤔
d

diesieben07

11/20/2018, 5:36 PM
Can you show what kind of syntax you want?
o

oshai

11/20/2018, 5:36 PM
PostgreSQLConnectionBuilder.createConnectionPool(connectionUri) { 
        connectionCreateTimeout = 1
}
to override specific values
d

diesieben07

11/20/2018, 5:46 PM
You must have a
var
somewhere to achieve that syntax. It doesn't have to be in the actual connection properties, you can have a temporary mutable object
But what's wrong with
PostgreSQLConnectionBuilder.createConnectionPool(connectionUri, ConnectionProperties(createTimeout = 1))
?
o

oshai

11/20/2018, 5:53 PM
the first part
PostgreSQLConnectionBuilder.createConnectionPool(connectionUri)
is building a default configuration and I would like to be able to override some of the properties later, so I need to use that object
n

Nikky

11/20/2018, 7:15 PM
you could use a mutable variant of the ConnectionPool in the lambda, but then make it immutable and return that
o

oshai

11/20/2018, 7:26 PM
is there a simple way to do that without cloning the class?
d

diegog

11/20/2018, 7:47 PM
Could you please post the signature of
createConnectionPool
?
(I totally agree copying doesn’t feel great)
o

oshai

11/20/2018, 7:57 PM
it looks like that:
@JvmStatic
    fun createConnectionPool(url: String,
                             configurator: (ConnectionPoolConfiguration) -> ConnectionPoolConfiguration = { it }): ConnectionPool<MySQLConnection> {
        val configuration = URLParser.parseOrDie(url)
        val connectionPoolConfiguration = configurator(with(configuration) {
            ConnectionPoolConfiguration(
                    username = username,
                    host = host,
                    port = port,
                    password = password,
                    database = database,
                    ssl = ssl,
                    charset = charset,
                    maximumMessageSize = maximumMessageSize,
                    allocator = allocator,
                    queryTimeout = queryTimeout?.toMillis()
            )})
        return ConnectionPool(
                MySQLConnectionFactory(connectionPoolConfiguration.connectionConfiguration),
                connectionPoolConfiguration.poolConfiguration,
                connectionPoolConfiguration.executionContext
        )
    }
d

diegog

11/20/2018, 8:40 PM
Even if it may feel a bit more fiddly I would pass just ConnectionPoolConfiguration as a parameter forcing the caller to build the object as it needs. I’ve seen may times using it.copy in a way that feels dealing with “mutable” objects. As an end user of the library I would like to be in control and pass the configuration in 😀 It would be great if someone else can come up with a better solution
1
d

dave08

11/26/2018, 8:51 AM
I think provide overloads of createConnectionPool function with the most common things a user might want to set, and if they need more flexibility, just pass in the configuration object. If it wouldn't need java interop you could always have defaults for all the function params and let the user choose what to provide... @oshai
Behind the scenes, the overloads create the immutable configuration onject with sensible defaults
o

oshai

11/26/2018, 6:25 PM
Can you give an example?
d

dave08

11/26/2018, 8:42 PM
Another possibility is to have a builder class like: https://github.com/ktorio/ktor/blob/master/ktor-server/ktor-server-host-common/src/io/ktor/server/engine/ApplicationEngineEnvironment.kt But what I meant previously was something like this: https://github.com/ktorio/ktor/blob/master/ktor-server/ktor-server-host-common/src/io/ktor/server/engine/EmbeddedServer.kt (except that you can have an overload to just provide the whole config object, or you could make it mutable like Ktor..) I think the second is better, because most of your configs are going to be supplied in most cases (database, user, password, host), port can maybe be guessed by implementation 3306 mysql, 5431 postgres... but should optionally be provided, the rest could have reasonable defaults or be provided with config object. @oshai
o

oshai

11/26/2018, 9:39 PM
a builder is a valid option altough I thought we don't need that anymore with kotlin 😉