Skip to content

Commit

Permalink
leave only Dsl way to write state reducer
Browse files Browse the repository at this point in the history
  • Loading branch information
Dmitriy Berdnikov committed Feb 19, 2024
1 parent fec3f6f commit 4553dcd
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 170 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package money.vivid.elmslie.core.store
/**
* Reducer that doesn't change state, and doesn't emit commands or effects
*/
class NoOpReducer<Event : Any, State : Any, Effect : Any, Command : Any> : StateReducer<Event, State, Effect, Command> {
class NoOpReducer<Event : Any, State : Any, Effect : Any, Command : Any> :
StateReducer<Event, State, Effect, Command>() {

override fun reduce(event: Event, state: State) = Result<State, Effect, Command>(state)
override fun Result.reduce(event: Event) = Unit
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package money.vivid.elmslie.core.store

import kotlin.reflect.KClass

abstract class ScreenReducer<Event : Any, Ui : Any, Internal : Any, State : Any, Effect : Any, Command : Any>(
private val uiEventClass: KClass<Ui>,
private val internalEventClass: KClass<Internal>
) : StateReducer<Event, State, Effect, Command>() {


protected abstract fun Result.ui(event: Ui): Any?

protected abstract fun Result.internal(event: Internal): Any?

override fun Result.reduce(event: Event) {
@Suppress("UNCHECKED_CAST")
when {
uiEventClass.isInstance(event) -> ui(event as Ui)
internalEventClass.isInstance(event) -> internal(event as Internal)
else -> error("Event ${event::class} is neither UI nor Internal")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package money.vivid.elmslie.core.store

fun interface StateReducer<Event : Any, State : Any, Effect : Any, Command : Any> {
import money.vivid.elmslie.core.store.dsl.ResultBuilder

fun reduce(event: Event, state: State): Result<State, Effect, Command>
abstract class StateReducer<Event : Any, State : Any, Effect : Any, Command : Any> {

// Needed to type less code
protected inner class Result(state: State) : ResultBuilder<State, Effect, Command>(state)

protected abstract fun Result.reduce(event: Event)

fun reduce(event: Event, state: State) = Result(state).apply { reduce(event) }.build()
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,13 @@ class EffectCachingElmStoreTest {
fun `Should collect effects which are emitted before collecting flow`() = runTest {
val store =
store(
state = State(),
reducer = { event, state ->
Result(
state = state,
effect = Effect(event.value),
)
},
)
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
effects { +Effect(value = event.value) }
}
},
)
.toCachedStore()

store.start()
Expand Down Expand Up @@ -76,14 +75,13 @@ class EffectCachingElmStoreTest {
fun `Should collect effects which are emitted before collecting flow and after`() = runTest {
val store =
store(
state = State(),
reducer = { event, state ->
Result(
state = state,
effect = Effect(event.value),
)
},
)
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
effects { +Effect(value = event.value) }
}
},
)
.toCachedStore()

store.start()
Expand Down Expand Up @@ -114,14 +112,13 @@ class EffectCachingElmStoreTest {
fun `Should emit effects from cache only for the first subscriber`() = runTest {
val store =
store(
state = State(),
reducer = { event, state ->
Result(
state = state,
effect = Effect(event.value),
)
},
)
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
effects { +Effect(value = event.value) }
}
},
)
.toCachedStore()

store.start()
Expand Down Expand Up @@ -152,14 +149,13 @@ class EffectCachingElmStoreTest {
fun `Should cache effects if there is no left collectors`() = runTest {
val store =
store(
state = State(),
reducer = { event, state ->
Result(
state = state,
effect = Effect(event.value),
)
},
)
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
effects { +Effect(value = event.value) }
}
},
)
.toCachedStore()

store.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,18 @@ class ElmStoreTest {
override fun execute(command: Command): Flow<Event> =
flow { emit(Event()) }.onEach { delay(1000) }
}

val store =
store(
state = State(),
reducer = { _, state ->
Result(state = state.copy(value = state.value + 1), command = Command())
},
actor = actor,
)
.start()
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
state { copy(value = state.value + 1) }
commands { +Command() }
}
},
actor = actor,
).start()

val emittedStates = mutableListOf<State>()
val collectJob = launch { store.states.toList(emittedStates) }
Expand All @@ -91,9 +94,13 @@ class ElmStoreTest {
fun `Should update state when event is received`() = runTest {
val store =
store(
state = State(),
reducer = { event, state -> Result(state = state.copy(value = event.value)) },
)
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
state { copy(value = event.value) }
}
},
)
.start()

assertEquals(
Expand All @@ -110,9 +117,13 @@ class ElmStoreTest {
fun `Should not update state when it's equal to previous one`() = runTest {
val store =
store(
state = State(),
reducer = { event, state -> Result(state = state.copy(value = event.value)) },
)
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
state { copy(value = event.value) }
}
},
)
.start()

val emittedStates = mutableListOf<State>()
Expand All @@ -134,11 +145,13 @@ class ElmStoreTest {
fun `Should collect all emitted effects`() = runTest {
val store =
store(
state = State(),
reducer = { event, state ->
Result(state = state, effect = Effect(value = event.value))
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
effects { +Effect(value = event.value) }
}
)
},
)
.start()

val effects = mutableListOf<Effect>()
Expand All @@ -161,11 +174,13 @@ class ElmStoreTest {
fun `Should skip the effect which is emitted before subscribing to effects`() = runTest {
val store =
store(
state = State(),
reducer = { event, state ->
Result(state = state, effect = Effect(value = event.value))
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
effects { +Effect(value = event.value) }
}
)
},
)
.start()

val effects = mutableListOf<Effect>()
Expand All @@ -188,19 +203,16 @@ class ElmStoreTest {
fun `Should collect all effects emitted once per time`() = runTest {
val store =
store(
state = State(),
reducer = { event, state ->
Result(
state = state,
commands = emptyList(),
effects =
listOf(
Effect(value = event.value),
Effect(value = event.value),
),
)
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
effects {
+Effect(value = event.value)
+Effect(value = event.value)
}
}
)
},
)
.start()

val effects = mutableListOf<Effect>()
Expand All @@ -222,11 +234,13 @@ class ElmStoreTest {
fun `Should collect all emitted effects by all collectors`() = runTest {
val store =
store(
state = State(),
reducer = { event, state ->
Result(state = state, effect = Effect(value = event.value))
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
effects { +Effect(value = event.value) }
}
)
},
)
.start()

val effects1 = mutableListOf<Effect>()
Expand Down Expand Up @@ -259,11 +273,13 @@ class ElmStoreTest {
fun `Should collect duplicated effects`() = runTest {
val store =
store(
state = State(),
reducer = { event, state ->
Result(state = state, effect = Effect(value = event.value))
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
effects { +Effect(value = event.value) }
}
)
},
)
.start()

val effects = mutableListOf<Effect>()
Expand All @@ -289,15 +305,17 @@ class ElmStoreTest {
}
val store =
store(
state = State(),
reducer = { event, state ->
Result(
state = state.copy(value = event.value),
command = Command(event.value - 1).takeIf { event.value > 0 }
)
},
actor = actor,
)
state = State(),
reducer = object : StateReducer<Event, State, Effect, Command>() {
override fun Result.reduce(event: Event) {
state { copy(value = event.value) }
commands {
+Command(event.value - 1).takeIf { event.value > 0 }
}
}
},
actor = actor,
)
.start()

val states = mutableListOf<State>()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package money.vivid.elmslie.core.store.dsl

import money.vivid.elmslie.core.store.StateReducer
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

private object BasicDslReducer : DslReducer<TestEvent, TestState, TestEffect, TestCommand>() {
private object BasicDslReducer : StateReducer<TestEvent, TestState, TestEffect, TestCommand>() {

override fun Result.reduce(event: TestEvent) =
when (event) {
Expand Down
Loading

0 comments on commit 4553dcd

Please sign in to comment.