Skip to content

Commit bd78a64

Browse files
authored
Merge pull request #26 from aPureBase/v3.1.0
Feature branch for v3.1.0
2 parents 01d129c + a3f2c32 commit bd78a64

File tree

14 files changed

+160
-42
lines changed

14 files changed

+160
-42
lines changed

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,16 @@ abstract class Arkenv(
2323
defaultValue = { programName }
2424
}
2525

26-
internal fun parseArguments(args: Array<out String>) {
27-
if (configuration.clearInputBeforeParse) clear()
26+
internal fun parseArguments(args: Array<out String>) = with(configuration) {
27+
if (clearInputBeforeParse) clear()
2828
argList.addAll(args)
29-
configuration.features.forEach { it.onLoad(this) }
29+
features.forEach { it.onLoad(this@Arkenv) }
30+
features.forEach { it.postLoad(this@Arkenv) }
3031
process()
31-
parse()
32-
configuration.features.forEach { it.finally(this) }
33-
if (configuration.clearInputAfterParse) clear()
32+
parse(delegates)
33+
features.forEach { it.finally(this@Arkenv) }
34+
modules.forEach { parse(it.delegates) }
35+
if (clearInputAfterParse) clear()
3436
}
3537

3638
/**
@@ -98,7 +100,7 @@ abstract class Arkenv(
98100
else names.filterNot(String::isSimpleName).mapNotNull(::getOrNull)
99101
}
100102

101-
private fun parse() = delegates
103+
private fun parse(delegates: Collection<ArgumentDelegate<*>>) = delegates
102104
.sortedBy { it.argument.isMainArg }
103105
.forEach {
104106
configuration.features.forEach { feature ->

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class ArkenvBuilder(installAdvancedFeatures: Boolean = true) {
2121

2222
internal val features: MutableList<ArkenvFeature> = mutableListOf()
2323
internal val processorFeatures: MutableList<ProcessorFeature> = mutableListOf()
24+
internal val modules: MutableList<Arkenv> = mutableListOf()
2425

2526
/**
2627
* Installs the [feature] into [Arkenv].
@@ -73,3 +74,10 @@ class ArkenvBuilder(installAdvancedFeatures: Boolean = true) {
7374
* @param block Arkenv configuration logic.
7475
*/
7576
inline fun configureArkenv(block: (ArkenvBuilder.() -> Unit)) = ArkenvBuilder().apply(block)
77+
78+
/**
79+
* Registers the [module] as a sub module that will be automatically parsed after the super Arkenv.
80+
* It will be parsed using the configuration of its root.
81+
* @param module the sub module to add to this [Arkenv]
82+
*/
83+
fun <T: Arkenv> Arkenv.module(module: T): T = module.also { configuration.modules.add(it) }

arkenv/src/main/kotlin/com/apurebase/arkenv/feature/ArkenvFeature.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ interface ArkenvFeature {
1616
fun onLoad(arkenv: Arkenv) {
1717
}
1818

19+
/**
20+
* Executed after all features have been loaded.
21+
* Can be used for reactive loading, where one feature is enabled by another one.
22+
*/
23+
fun postLoad(arkenv: Arkenv) {
24+
}
25+
1926
/**
2027
* Used to assign a value to the [Argument] represented by [delegate].
2128
*/
@@ -26,6 +33,7 @@ interface ArkenvFeature {
2633
/**
2734
* Applies configuration to every [argument].
2835
*/
36+
@Deprecated("Will be removed in future major version")
2937
fun configure(argument: Argument<*>) {
3038
}
3139

arkenv/src/main/kotlin/com/apurebase/arkenv/feature/EnvironmentVariableFeature.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@ class EnvironmentVariableFeature(
1919
private val dotEnvFilePath: String? = null
2020
) : ArkenvFeature {
2121

22+
private var isLoaded = false
23+
2224
override fun onLoad(arkenv: Arkenv) {
23-
loadEnvironmentVariables(arkenv.getOrNull("ARKENV_DOT_ENV_FILE"))
24-
?.let(arkenv::putAll)
25+
loadEnvironmentVariables(arkenv.getOrNull("ARKENV_DOT_ENV_FILE"))?.let {
26+
arkenv.putAll(it)
27+
isLoaded = true
28+
}
2529
}
2630

2731
override fun onParse(arkenv: Arkenv, delegate: ArgumentDelegate<*>): String? = with(delegate) {
@@ -30,6 +34,10 @@ class EnvironmentVariableFeature(
3034
getEnvValue(argument, envSecrets, setEnvPrefix)
3135
}
3236

37+
override fun postLoad(arkenv: Arkenv) {
38+
if (!isLoaded) onLoad(arkenv)
39+
}
40+
3341
/**
3442
* Loop over all argument names and pick the first one that matches
3543
*/
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.apurebase.arkenv
2+
3+
import com.apurebase.arkenv.feature.ProfileFeature
4+
import com.apurebase.arkenv.feature.PropertyFeature
5+
import com.apurebase.arkenv.test.expectThat
6+
import com.apurebase.arkenv.test.parse
7+
import org.junit.jupiter.api.Test
8+
import strikt.assertions.isEqualTo
9+
10+
internal class ModuleTests {
11+
12+
private inner class DatabaseConfig : Arkenv("Database") {
13+
val port: Int by argument()
14+
}
15+
16+
private inner class Ark : Arkenv("Root", configureArkenv {
17+
uninstall(ProfileFeature())
18+
install(PropertyFeature("application-prod")) // load a specific file to test feature propagation
19+
}) {
20+
val name: String by argument()
21+
val database = module(DatabaseConfig())
22+
}
23+
24+
@Test fun `modules should be parsed by super`() {
25+
val myApp = "production"
26+
val port = 443
27+
Ark().parse()
28+
.expectThat {
29+
get { name }.isEqualTo(myApp)
30+
get { database.port }.isEqualTo(port)
31+
}
32+
}
33+
}

arkenv/src/test/kotlin/com/apurebase/arkenv/feature/EnvFileTests.kt

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@ package com.apurebase.arkenv.feature
33
import com.apurebase.arkenv.Arkenv
44
import com.apurebase.arkenv.argument
55
import com.apurebase.arkenv.configureArkenv
6+
import com.apurebase.arkenv.test.dotEnvPath
67
import com.apurebase.arkenv.test.expectThat
78
import com.apurebase.arkenv.test.getTestResourcePath
89
import com.apurebase.arkenv.test.parse
910
import org.junit.jupiter.api.Test
1011
import org.junit.jupiter.api.assertThrows
12+
import strikt.api.Assertion
1113
import strikt.assertions.isEqualTo
1214
import java.io.FileNotFoundException
1315

1416
class EnvFileTests {
1517

16-
private class EnvFileArk(dotEnvFilePath: String? = null) : Arkenv("Test", configureArkenv {
18+
private inner class EnvFileArk(dotEnvFilePath: String? = null) : Arkenv("Test", configureArkenv {
1719
uninstall(EnvironmentVariableFeature())
1820
install(EnvironmentVariableFeature(dotEnvFilePath = dotEnvFilePath))
1921
}) {
@@ -30,22 +32,32 @@ class EnvFileTests {
3032
}
3133

3234
@Test fun `should load values from dot env file`() {
33-
val path = getTestResourcePath(".env")
34-
EnvFileArk(path).parse().expectThat {
35-
get { mysqlPassword }.isEqualTo("this_is_expected")
36-
get { port }.isEqualTo(5050)
37-
get { connectionString }.isEqualTo("localhost:5050;database=testdb;user=testuser;")
38-
}
35+
EnvFileArk(dotEnvPath).parse().expectThat { verify() }
3936
}
4037

4138
@Test fun `dot env file can be specified via argument`() {
42-
val path = getTestResourcePath(".env-alt")
4339
EnvFileArk()
44-
.parse("ARKENV_DOT_ENV_FILE", path)
45-
.expectThat {
46-
get { mysqlPassword }.isEqualTo("alternative")
47-
get { port }.isEqualTo(8080)
48-
get { connectionString }.isEqualTo("localhost")
49-
}
40+
.parse("ARKENV_DOT_ENV_FILE", altPath)
41+
.expectThat { verifyAlt() }
5042
}
43+
44+
@Test fun `dot env file can be specified via profile`() {
45+
EnvFileArk()
46+
.parse("--arkenv-profile", "placeholder")
47+
.expectThat { verify() }
48+
}
49+
50+
private fun Assertion.Builder<EnvFileArk>.verify() {
51+
get { mysqlPassword }.isEqualTo("this_is_expected")
52+
get { port }.isEqualTo(5050)
53+
get { connectionString }.isEqualTo("localhost:5050;database=testdb;user=testuser;")
54+
}
55+
56+
private fun Assertion.Builder<EnvFileArk>.verifyAlt() {
57+
get { mysqlPassword }.isEqualTo("alternative")
58+
get { port }.isEqualTo(8080)
59+
get { connectionString }.isEqualTo("localhost")
60+
}
61+
62+
private val altPath = getTestResourcePath(".env-alt")
5163
}

arkenv/src/test/kotlin/com/apurebase/arkenv/feature/PlaceholderTests.kt

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

33
import com.apurebase.arkenv.*
4-
import com.apurebase.arkenv.test.MockSystem
5-
import com.apurebase.arkenv.test.expectThat
6-
import com.apurebase.arkenv.test.getTestResourcePath
7-
import com.apurebase.arkenv.test.parse
4+
import com.apurebase.arkenv.test.*
85
import org.junit.jupiter.api.Test
96
import org.junit.jupiter.api.TestInstance
107
import org.junit.jupiter.api.assertThrows
118
import strikt.assertions.isEqualTo
9+
import java.io.File
1210

1311
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
1412
class PlaceholderTests {
@@ -19,8 +17,8 @@ class PlaceholderTests {
1917
}
2018

2119
@Test fun `can refer to previously defined arg in properties`() {
22-
val ark = Ark { install(PropertyFeature("placeholders.properties")) }
23-
ark.parse()
20+
Ark { install(PropertyFeature("placeholders.properties")) }
21+
.parse()
2422
.verify()
2523
}
2624

@@ -82,15 +80,19 @@ class PlaceholderTests {
8280
val testValue = "this_is_expected"
8381
val expected = "$testValue is not declared"
8482

85-
val ark = Ark {
86-
install(EnvironmentVariableFeature(dotEnvFilePath = getTestResourcePath(".env")))
87-
}
88-
ark.parse(appNameArg, appName, appDescArg, "\${mysql_password} is not declared")
83+
Ark { install(EnvironmentVariableFeature(dotEnvFilePath = dotEnvPath)) }
84+
.parse(appNameArg, appName, appDescArg, "\${mysql_password} is not declared")
8985
.expectThat {
9086
get { description }.isEqualTo(expected)
9187
}
9288
}
9389

90+
@Test fun `refer to env from profile`() {
91+
92+
Ark().parse("--arkenv-profile", "placeholder")
93+
.verify()
94+
}
95+
9496
@Test fun `should throw when placeholder is not found`() {
9597
val ark = Ark()
9698
assertThrows<MissingArgumentException> {

arkenv/src/test/kotlin/com/apurebase/arkenv/feature/ProfileFeatureTest.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,11 @@ package com.apurebase.arkenv.feature
33
import com.apurebase.arkenv.Arkenv
44
import com.apurebase.arkenv.argument
55
import com.apurebase.arkenv.configureArkenv
6-
import com.apurebase.arkenv.test.DEPRECATED
76
import com.apurebase.arkenv.test.MockSystem
87
import com.apurebase.arkenv.test.expectThat
98
import com.apurebase.arkenv.test.parse
10-
import org.amshove.kluent.shouldNotBeNull
11-
import org.junit.jupiter.api.Disabled
129
import org.junit.jupiter.api.Test
1310
import org.junit.jupiter.api.TestInstance
14-
import org.junit.jupiter.api.assertThrows
1511
import strikt.api.Assertion
1612
import strikt.assertions.isEqualTo
1713

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ fun <T : Arkenv> T.parse(vararg arguments: String) = apply { parseArguments(argu
1515

1616
fun getTestResource(name: String) = MockSystem::class.java.classLoader.getResource(name)!!.readText()
1717

18-
const val DEPRECATED = "Property files are no longer required, can be null."
18+
val dotEnvPath = getTestResourcePath(".env")

arkenv/src/test/resources/.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ DATABASE_PORT=5050
66
# Everywhere
77

88
CONNECTION_STRING=localhost:${DATABASE_PORT};database=testdb;user=testuser;
9+
10+
APP_NAME=MyApp

0 commit comments

Comments
 (0)