Skip to content

Commit

Permalink
update documentation for IdBasedViolationStore, cleanup freeze-logic (#2
Browse files Browse the repository at this point in the history
)
  • Loading branch information
klu2 authored Jul 17, 2022
1 parent c6e1882 commit 439f77b
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 12 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
kotlin.code.style=official

version=0.0.2
version=0.0.3
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,27 @@ package io.cloudflight.cleancode.archunit

import com.tngtech.archunit.ArchConfiguration
import com.tngtech.archunit.lang.ArchRule
import com.tngtech.archunit.library.freeze.FreezingArchRule
import com.tngtech.archunit.library.freeze.FreezingArchRule.freeze

internal class ArchRuleWithId(val id: String, rule: ArchRule) :
ArchRule by rule.because(idToUrl(id)) {

companion object {
fun archRuleWithId(id: String, delegate: ArchRule): ArchRule {
return if (FREEZE_ENABLED) {
FreezingArchRule.freeze(ArchRuleWithId(id, delegate)).persistIn(IdBasedViolationStore)
freeze(ArchRuleWithId(id, delegate)).persistIn(IdBasedViolationStore)
} else {
ArchRuleWithId(id, delegate)
}
}

internal fun idToUrl(id:String):String {
internal fun idToUrl(id: String): String {
return "$ROOT_URL/${id.substringBefore(".")}.md#user-content-${id.substringAfter(".")}"
}

private val VERSION = "v${ArchRuleWithId::class.java.`package`.implementationVersion}"
private val ROOT_URL = "https://github.com/cloudflightio/archunit-cleancode-verifier/blob/$VERSION/rules"
private val FREEZE_ENABLED = true.toString() == ArchConfiguration.get().getPropertyOrDefault("cleancode.activate-freeze", false.toString())

internal var FREEZE_ENABLED = true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,39 @@ import java.nio.file.StandardOpenOption
import java.util.*
import kotlin.io.path.*

/**
* Our [ArchRuleWithId] adds a URL to GitHub which contains the current version of this library
* (i.e. 0.0.2) to the [ArchRule.because] declaration and that URL changes on every release of this
* library, therefore also the unique identifier of the rule changes with every release and the
* default [ViolationStore] would blow up over time. Inspired by the discussion on
* [this issue](https://github.com/TNG/ArchUnit/issues/751) we created this alternative implementation to the
* `TextFileBasedViolationStore`
*
* The key differences are:
*
* 1. we are using [ArchRuleWithId.id] as identifier for rules and take that as the file name to
* store violations per rule
* 2. the method [ViolationStore.contains] returns `true` for all [ArchRuleWithId] instances. That will
* also help us later if we add additional rules to this library. In case they find violations,
* those violations will fail and not automatically and silently end up in the store
* 3. therefore, there is no need to set `freeze.store.default.allowStoreCreation` to `true`
* 4. no `stored.rules` file is being created
* 5. we don't store empty violation files and in turn delete them when they are empty
*
* Just like in the default implementation,
*
* * you can set the property
* `freeze.store.default.allowStoreUpdate` to `false` in order to avoid that the store is being
* updated. It makes sense to set that property especially in CI environments
* * the folder for the violation files comes from `freeze.store.default.allowStoreUpdate`, the
* default being `archunit_store`
*/
internal object IdBasedViolationStore : ViolationStore {

private var storeCreationAllowed: Boolean = false
private var storeUpdateAllowed: Boolean = false
private lateinit var storeFolder: Path

override fun initialize(properties: Properties) {
storeCreationAllowed = properties.getProperty("default.allowStoreCreation", false.toString()).toBooleanStrict()
storeUpdateAllowed = properties.getProperty("default.allowStoreUpdate", true.toString()).toBooleanStrict()
storeFolder = Paths.get(properties.getProperty("default.path", "archunit_store")).also {
it.createDirectories()
Expand All @@ -24,7 +49,13 @@ internal object IdBasedViolationStore : ViolationStore {

override fun contains(rule: ArchRule): Boolean {
if (rule is ArchRuleWithId) {
return getViolationsFile(rule).exists()
// We don't store empty violation files and compared to the TextFileBasedViolationStore
// we also don't a keep stored.rules file, so we consider that every ArchRuleWithId
// is in the store.
// That will also help us later if we add additional rules to this library. In case
// they find violations, those violations will fail and not automatically and
// silently end up in the store
return true
} else {
throw IllegalArgumentException("Expected ArchRuleWithId")
}
Expand Down Expand Up @@ -57,7 +88,12 @@ internal object IdBasedViolationStore : ViolationStore {

override fun getViolations(rule: ArchRule): List<String> {
if (rule is ArchRuleWithId) {
return getViolationsFile(rule).readLines()
val violationsFile = getViolationsFile(rule)
if (violationsFile.exists()) {
return violationsFile.readLines()
} else {
return emptyList()
}
} else {
throw IllegalArgumentException("Expected ArchRuleWithId")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ package io.cloudflight.cleancode.archunit

import com.tngtech.archunit.junit.ArchTest
import com.tngtech.archunit.junit.ArchTests
import io.cloudflight.cleancode.archunit.rules.jdk.JdkRuleSet
import io.cloudflight.cleancode.archunit.rules.jpa.JpaRuleSet
import io.cloudflight.cleancode.archunit.rules.logging.LoggingRuleSet
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
Expand All @@ -20,13 +17,17 @@ class DocumentationTest {
@ParameterizedTest
@MethodSource("classes")
fun `all rules are explained in the documentation`(clazz: KClass<*>) {
// we need to deactivate freezing here, otherwise we can't access the ArchRuleWithId
// anymore, which will be a private delegate of the FreezingArchRule then
ArchRuleWithId.FREEZE_ENABLED = false
val file = clazz.simpleName!!.lowercase().removeSuffix("ruleset")
val ids = getArchTests(clazz)
.map {
(it.call(clazz.createInstance()) as ArchRuleWithId).id.removeSuffix("$file-")
}
val headers = DocParser("rules/${file}.md").getHeaders().map { "${file}.${it}" }
assertThat(ids).containsExactlyInAnyOrderElementsOf(headers)
ArchRuleWithId.FREEZE_ENABLED = true
}

companion object {
Expand Down

0 comments on commit 439f77b

Please sign in to comment.