Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

M6l1 cor #25

Merged
merged 4 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
**/*.hprof

/.idea/
/.gigaide/
/kotlin-js-store/

*.hprof
2 changes: 1 addition & 1 deletion ok-marketplace-be/ok-marketplace-app-ktor/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ tasks {
// Если ошибка: "Entry application.yaml is a duplicate but no duplicate handling strategy has been set."
// Возникает из-за наличия файлов как в common, так и в jvm платформе
withType(ProcessResources::class) {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

val linkReleaseExecutableLinuxX64 by getting(KotlinNativeLink::class)
Expand Down
1 change: 1 addition & 0 deletions ok-marketplace-libs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ build/
!**/src/test/**/build/

/.idea/
/.gigaide/
/kotlin-js-store/
31 changes: 31 additions & 0 deletions ok-marketplace-libs/ok-marketplace-lib-cor/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
plugins {
id("build-kmp")
}

kotlin {
sourceSets {
commonMain {
dependencies {
implementation(kotlin("stdlib-common"))
implementation(libs.coroutines.core)
}
}
commonTest {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
implementation(libs.coroutines.test)
}
}
jvmMain {
dependencies {
implementation(kotlin("stdlib-jdk8"))
}
}
jvmTest {
dependencies {
implementation(kotlin("test-junit"))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package ru.otus.otuskotlin.marketplace.cor

@DslMarker
annotation class CorDslMarker
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ru.otus.otuskotlin.marketplace.cor

/**
* Блок кода, который обрабатывает контекст. Имеет имя и описание
*/
interface ICorExec<T> {
val title: String
val description: String
suspend fun exec(context: T)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package ru.otus.otuskotlin.marketplace.cor

import ru.otus.otuskotlin.marketplace.cor.handlers.CorChainDsl
import ru.otus.otuskotlin.marketplace.cor.handlers.CorWorkerDsl

/**
* Базовый билдер (dsl)
*/
@CorDslMarker
interface ICorExecDsl<T> {
var title: String
var description: String
fun on(function: suspend T.() -> Boolean)
fun except(function: suspend T.(e: Throwable) -> Unit)

fun build(): ICorExec<T>
}

/**
* Билдер (dsl) для цепочек (chain)
*/
@CorDslMarker
interface ICorChainDsl<T> : ICorExecDsl<T> {
fun add(worker: ICorExecDsl<T>)
}

/**
* Билдер (dsl) для рабочих (worker)
*/
@CorDslMarker
interface ICorWorkerDsl<T> : ICorExecDsl<T> {
fun handle(function: suspend T.() -> Unit)
}

/**
* Точка входа в dsl построения цепочек.
* Элементы исполняются последовательно.
*
* Пример:
* ```
* rootChain<SomeContext> {
* worker {
* }
* chain {
* worker(...) {
* }
* worker(...) {
* }
* }
* }
* ```
*/
fun <T> rootChain(function: ICorChainDsl<T>.() -> Unit): ICorChainDsl<T> = CorChainDsl<T>().apply(function)


/**
* Создает цепочку, элементы которой исполняются последовательно.
*/
fun <T> ICorChainDsl<T>.chain(function: ICorChainDsl<T>.() -> Unit) {
add(CorChainDsl<T>().apply(function))
}

/**
* Создает рабочего
*/
fun <T> ICorChainDsl<T>.worker(function: ICorWorkerDsl<T>.() -> Unit) {
add(CorWorkerDsl<T>().apply(function))
}

/**
* Создает рабочего с on и except по умолчанию
*/
fun <T> ICorChainDsl<T>.worker(
title: String,
description: String = "",
blockHandle: T.() -> Unit
) {
add(CorWorkerDsl<T>().also {
it.title = title
it.description = description
it.handle(blockHandle)
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ru.otus.otuskotlin.marketplace.cor.handlers

import ru.otus.otuskotlin.marketplace.cor.ICorExec
import ru.otus.otuskotlin.marketplace.cor.ICorExecDsl

abstract class AbstractCorExec<T>(
override val title: String,
override val description: String = "",
private val blockOn: suspend T.() -> Boolean = { true },
private val blockExcept: suspend T.(Throwable) -> Unit = {},
): ICorExec<T> {
protected abstract suspend fun handle(context: T)

private suspend fun on(context: T): Boolean = context.blockOn()
private suspend fun except(context: T, e: Throwable) = context.blockExcept(e)

override suspend fun exec(context: T) {
if (on(context)) {
try {
handle(context)
} catch (e: Throwable) {
except(context, e)
}
}
}
}

abstract class CorExecDsl<T> : ICorExecDsl<T> {
protected var blockOn: suspend T.() -> Boolean = { true }
protected var blockExcept: suspend T.(e: Throwable) -> Unit = { e: Throwable -> throw e }

override var title: String = ""
override var description: String = ""

override fun on(function: suspend T.() -> Boolean) {
blockOn = function
}

override fun except(function: suspend T.(e: Throwable) -> Unit) {
blockExcept = function
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package ru.otus.otuskotlin.marketplace.cor.handlers

import ru.otus.otuskotlin.marketplace.cor.CorDslMarker
import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl
import ru.otus.otuskotlin.marketplace.cor.ICorExec
import ru.otus.otuskotlin.marketplace.cor.ICorExecDsl

/**
* Реализация цепочки (chain), которая исполняет свои вложенные цепочки и рабочие
*/
class CorChain<T>(
private val execs: List<ICorExec<T>>,
title: String,
description: String = "",
blockOn: suspend T.() -> Boolean = { true },
blockExcept: suspend T.(Throwable) -> Unit = {},
) : AbstractCorExec<T>(title, description, blockOn, blockExcept) {
override suspend fun handle(context: T) {
execs.forEach {
it.exec(context)
}
}
}

@CorDslMarker
class CorChainDsl<T>(
) : CorExecDsl<T>(), ICorChainDsl<T> {
private val workers: MutableList<ICorExecDsl<T>> = mutableListOf()
override fun add(worker: ICorExecDsl<T>) {
workers.add(worker)
}

override fun build(): ICorExec<T> = CorChain(
title = title,
description = description,
execs = workers.map { it.build() },
blockOn = blockOn,
blockExcept = blockExcept
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ru.otus.otuskotlin.marketplace.cor.handlers

import ru.otus.otuskotlin.marketplace.cor.CorDslMarker
import ru.otus.otuskotlin.marketplace.cor.ICorExec
import ru.otus.otuskotlin.marketplace.cor.ICorWorkerDsl

class CorWorker<T>(
title: String,
description: String = "",
blockOn: suspend T.() -> Boolean = { true },
private val blockHandle: suspend T.() -> Unit = {},
blockExcept: suspend T.(Throwable) -> Unit = {},
) : AbstractCorExec<T>(title, description, blockOn, blockExcept) {
override suspend fun handle(context: T) = blockHandle(context)
}

@CorDslMarker
class CorWorkerDsl<T> : CorExecDsl<T>(), ICorWorkerDsl<T> {
private var blockHandle: suspend T.() -> Unit = {}
override fun handle(function: suspend T.() -> Unit) {
blockHandle = function
}

override fun build(): ICorExec<T> = CorWorker(
title = title,
description = description,
blockOn = blockOn,
blockHandle = blockHandle,
blockExcept = blockExcept
)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ru.otus.otuskotlin.marketplace.cor

import kotlinx.coroutines.test.runTest
import ru.otus.otuskotlin.marketplace.cor.handlers.CorChain
import ru.otus.otuskotlin.marketplace.cor.handlers.CorWorker
import kotlin.test.Test
import kotlin.test.assertEquals

class CorChainTest {
@Test
fun `chain should execute workers`() = runTest {
val createWorker = { title: String ->
CorWorker<TestContext>(
title = title,
blockOn = { status == TestContext.CorStatuses.NONE },
blockHandle = { history += "$title; " }
)
}
val chain = CorChain<TestContext>(
execs = listOf(createWorker("w1"), createWorker("w2")),
title = "chain",
)
val ctx = TestContext()
chain.exec(ctx)
assertEquals("w1; w2; ", ctx.history)
}
}
Loading