Skip to content

Commit 8ccfc2e

Browse files
Merge pull request #68 from aPureBase/v3.3.3
Support plain class module prefix
2 parents a1e58a0 + a6793f9 commit 8ccfc2e

File tree

11 files changed

+100
-31
lines changed

11 files changed

+100
-31
lines changed

arkenv/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
plugins {
22
base
3-
kotlin("jvm") version "1.5.10"
3+
kotlin("jvm") version "1.5.21"
44
id("org.jetbrains.dokka") version "1.5.0"
55
id("java-test-fixtures")
66
signing

arkenv/src/main/kotlin/com/apurebase/arkenv/ArkenvBuilder.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import com.apurebase.arkenv.util.key
99
* [Arkenv] configuration builder which controls features and other settings.
1010
* @param installAdvancedFeatures whether to install the profile and placeholder feature.
1111
*/
12-
class ArkenvBuilder(installAdvancedFeatures: Boolean = true) {
12+
class ArkenvBuilder(installAdvancedFeatures: Boolean = true) : ArkenvConfiguration {
1313

1414
/**
1515
* A common prefix that is applied to all argument names.
1616
*/
17-
var prefix: String? = null
17+
override var prefix: String? = null
1818

1919
/**
2020
* Whether data should be cleared before parsing.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.apurebase.arkenv
2+
3+
internal interface ArkenvConfiguration {
4+
var prefix: String?
5+
}

arkenv/src/main/kotlin/com/apurebase/arkenv/argument/ArgumentNameProcessor.kt

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.apurebase.arkenv.argument
22

3+
import com.apurebase.arkenv.ArkenvBuilder
4+
import com.apurebase.arkenv.ArkenvConfiguration
35
import com.apurebase.arkenv.argument.ArkenvArgumentNamingStrategy.*
46
import com.apurebase.arkenv.util.isAdvancedName
57
import com.apurebase.arkenv.util.mapRelaxed
@@ -10,6 +12,13 @@ internal class ArgumentNameProcessor(
1012
private val prefix: String?,
1113
private val namingStrategy: ArkenvArgumentNamingStrategy
1214
) {
15+
internal companion object {
16+
fun get(moduleConfiguration: ArkenvConfiguration?, configuration: ArkenvBuilder): ArgumentNameProcessor {
17+
val prefix = moduleConfiguration?.prefix ?: configuration.prefix
18+
val namingStrategy = configuration.namingStrategy
19+
return ArgumentNameProcessor(prefix, namingStrategy)
20+
}
21+
}
1322

1423
/**
1524
* Processes the [argument]'s names, potentially updating and adding.
@@ -20,8 +29,18 @@ internal class ArgumentNameProcessor(
2029
argument.names = getNames(argument.names, property.name)
2130
}
2231

32+
/**
33+
* Processes a single name.
34+
* @param name the name to process.
35+
* @return the processed name.
36+
*/
37+
fun process(name: String) = name
38+
.ensureStartsWithDash()
39+
.prefix(prefix ?: "")
40+
.mapRelaxed()
41+
2342
private fun getNames(argumentNames: List<String>, propName: String): List<String> =
24-
getNameList(argumentNames, propName).map(::processName)
43+
getNameList(argumentNames, propName).map(::process)
2544

2645
private fun getNameList(argumentNames: List<String>, propName: String): List<String> {
2746
return when (namingStrategy) {
@@ -32,11 +51,6 @@ internal class ArgumentNameProcessor(
3251
}
3352
}
3453

35-
private fun processName(name: String) = name
36-
.ensureStartsWithDash()
37-
.prefix(prefix ?: "")
38-
.mapRelaxed()
39-
4054
private fun String.ensureStartsWithDash() =
4155
if (!startsWith("-")) "--$this" else this
4256

arkenv/src/main/kotlin/com/apurebase/arkenv/argument/ArkenvDelegateLoader.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ class ArkenvDelegateLoader<T : Any>(
88
private val argument: Argument<T>,
99
private val arkenv: Arkenv
1010
) {
11-
private val argumentNameProcessor = ArgumentNameProcessor(
12-
arkenv.configuration.prefix, arkenv.configuration.namingStrategy)
11+
private val argumentNameProcessor = ArgumentNameProcessor.get(null, arkenv.configuration)
1312

1413
operator fun provideDelegate(thisRef: Arkenv, prop: KProperty<*>): ReadOnlyProperty<Arkenv, T> {
1514
argumentNameProcessor.processArgumentNames(argument, prop)

arkenv/src/main/kotlin/com/apurebase/arkenv/argument/ArkenvSimpleArgument.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,8 @@ internal class ArkenvSimpleArgument<T : Any?>(
3434
this.arkenv = arkenv
3535
this.property = property
3636
if (!isInitialized) {
37-
val prefix = moduleConfiguration?.prefix ?: arkenv.configuration.prefix
38-
val namingStrategy = arkenv.configuration.namingStrategy
39-
val argumentNameProcessor = ArgumentNameProcessor(prefix, namingStrategy)
40-
argumentNameProcessor.processArgumentNames(argument, property)
37+
val nameProcessor = ArgumentNameProcessor.get(moduleConfiguration, arkenv.configuration)
38+
nameProcessor.processArgumentNames(argument, property)
4139
isInitialized = true
4240
}
4341
}

arkenv/src/main/kotlin/com/apurebase/arkenv/module/ArkenvModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ interface ArkenvModule<T : Any> : ReadOnlyProperty<Any, T> {
1616

1717
fun initialize(arkenvParser: ArkenvParser<*>) {
1818
if (module == null) {
19-
module = arkenvParser.createInstance(kClass)
19+
module = arkenvParser.createInstance(kClass, configuration)
2020
}
2121
arkenvParser.parse(module!!, configuration)
2222
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.apurebase.arkenv.module
22

3+
import com.apurebase.arkenv.ArkenvConfiguration
4+
35
/**
46
* Module configuration value class.
57
*/
68
class ArkenvModuleConfiguration(
7-
var prefix: String? = null
8-
)
9+
override var prefix: String? = null
10+
) : ArkenvConfiguration

arkenv/src/main/kotlin/com/apurebase/arkenv/parse/ArkenvParser.kt

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package com.apurebase.arkenv.parse
22

3-
import com.apurebase.arkenv.Arkenv
4-
import com.apurebase.arkenv.ArkenvBuilder
3+
import com.apurebase.arkenv.*
4+
import com.apurebase.arkenv.ArkenvConfiguration
55
import com.apurebase.arkenv.ArkenvMapper
66
import com.apurebase.arkenv.ParsingException
7+
import com.apurebase.arkenv.argument.ArgumentNameProcessor
78
import com.apurebase.arkenv.argument.ArkenvArgument
89
import com.apurebase.arkenv.module.ArkenvModule
910
import com.apurebase.arkenv.module.ArkenvModuleConfiguration
10-
import com.apurebase.arkenv.util.toSnakeCase
1111
import kotlin.reflect.KClass
1212
import kotlin.reflect.KFunction
1313
import kotlin.reflect.KParameter
@@ -43,7 +43,7 @@ class ArkenvParser<T : Any>(
4343
* @throws ParsingException when parsing was unsuccessful.
4444
*/
4545
fun parseClass(): T {
46-
val instance = createInstance(kClass)
46+
val instance = createInstance(kClass, null)
4747
parse(instance)
4848
return instance
4949
}
@@ -54,28 +54,29 @@ class ArkenvParser<T : Any>(
5454
arkenv.parsePostLoad()
5555
}
5656

57-
internal fun <R : Any> createInstance(kClass: KClass<R>): R {
57+
internal fun <R : Any> createInstance(kClass: KClass<R>, configuration: ArkenvConfiguration?): R {
5858
val constructor = kClass.constructors.firstOrNull()
5959
?: throw ParsingException(className, IllegalStateException("No valid constructor found"))
60-
return parseConstructor(constructor)
60+
return parseConstructor(constructor, configuration)
6161
}
6262

63-
private fun <R> parseConstructor(constructor: KFunction<R>): R = try {
64-
val constructorArgs = parseConstructorArgs(constructor.parameters)
63+
private fun <R> parseConstructor(constructor: KFunction<R>, configuration: ArkenvConfiguration?): R = try {
64+
val constructorArgs = parseConstructorArgs(constructor.parameters, configuration)
6565
constructor.callBy(constructorArgs)
6666
} catch (ex: IllegalArgumentException) {
6767
throw ParsingException(className, ex)
6868
}
6969

70-
private fun parseConstructorArgs(parameters: Collection<KParameter>): Map<KParameter, Any?> {
70+
private fun parseConstructorArgs(parameters: Collection<KParameter>, configuration: ArkenvConfiguration?): Map<KParameter, Any?> {
71+
val nameProcessor = ArgumentNameProcessor.get(configuration, arkenv.configuration)
7172
return parameters
7273
.filterNot { it.name.isNullOrBlank() }
73-
.mapNotNull(::parseConstructorParameter)
74+
.mapNotNull { parseConstructorParameter(it, nameProcessor) }
7475
.toMap()
7576
}
7677

77-
private fun parseConstructorParameter(parameter: KParameter): Pair<KParameter, Any?>? {
78-
val name = parameter.name!!.toSnakeCase()
78+
private fun parseConstructorParameter(parameter: KParameter, nameProcessor: ArgumentNameProcessor): Pair<KParameter, Any?>? {
79+
val name = nameProcessor.process(parameter.name!!)
7980
val value = arkenv.getOrNull(name)
8081
return if (parameter.isOptional && value == null) null
8182
else {

arkenv/src/test/kotlin/com/apurebase/arkenv/ParseClassTests.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.apurebase.arkenv
22

33
import com.apurebase.arkenv.feature.ProfileFeature
4+
import com.apurebase.arkenv.module.module
45
import com.apurebase.arkenv.test.Expected
56
import com.apurebase.arkenv.util.argument
67
import com.apurebase.arkenv.util.parse
@@ -124,4 +125,53 @@ class ParseClassTests {
124125
// Assert
125126
expectThat(config) { get { headless }.isFalse() }
126127
}
128+
129+
@Test fun `module by class is parsed`() {
130+
// Arrange
131+
val expectedCountry = "DK"
132+
class SubConfig(val country: String) {
133+
val port: Int by argument()
134+
}
135+
class Config {
136+
val subModule by module<SubConfig>()
137+
}
138+
139+
// Act
140+
val config = Arkenv.parse<Config>(arrayOf("--country", expectedCountry))
141+
142+
// Assert
143+
expectThat(config.subModule) {
144+
get { port } isEqualTo Expected.port
145+
get { country } isEqualTo expectedCountry
146+
}
147+
}
148+
149+
@Test fun `prefix applied to constructor`() {
150+
// Arrange
151+
val expectedPort = 199
152+
class PrefixConstructor(val port: Int)
153+
154+
// Act
155+
val config = Arkenv.parse<PrefixConstructor>(arrayOf("--database-port", expectedPort.toString())) { prefix = "database" }
156+
157+
// Assert
158+
expectThat(config).get { port } isEqualTo expectedPort
159+
}
160+
161+
@Test fun `common prefix per module`() {
162+
// Arrange
163+
val prefix = "database"
164+
val expectedPort = 199
165+
class Nested(val port: Int)
166+
167+
class Config {
168+
val database: Nested by module { this.prefix = "database" }
169+
}
170+
171+
// Act
172+
val config = Arkenv.parse<Config>(arrayOf("--$prefix-port", expectedPort.toString()))
173+
174+
// Assert
175+
expectThat(config.database).get { port } isEqualTo expectedPort
176+
}
127177
}

0 commit comments

Comments
 (0)