Skip to content

Commit

Permalink
Merge pull request #106 from ShikaSD/master
Browse files Browse the repository at this point in the history
Better type signatures for model watcher
  • Loading branch information
zsoltk authored Aug 9, 2019
2 parents 6557a15 + 7af2181 commit 2f60113
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 34 deletions.
20 changes: 10 additions & 10 deletions documentation/extras/modelwatcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class View: Consumer<ViewModel> {
private val button: Button = ...

// Specify the fields to observe and actions to execute
private val watcher = modelWatcher {
private val watcher = modelWatcher<ViewModel> {
watch(ViewModel::buttonText) {
button.text = it
}
Expand All @@ -34,7 +34,7 @@ By default, the difference is calculated by value (using `equals`). It is config
The library also includes a couple of commonly used defaults.

```kotlin
val watcher = modelWatcher {
val watcher = modelWatcher<Model> {
watch(Model::field, diffStrategy = byValue()) { } // Compare using equals (default strategy)
watch(Model::field, diffStrategy = byRef()) { } // Compare using referential equality
}
Expand All @@ -44,11 +44,11 @@ The difference can be observed on more than one field with custom diff strategy.
For example, if the click listener should not be set when something is loading, you can do the following:
```kotlin
// Trigger when either loading flag or action changed
val loadingOrAction: DiffStrategy<Model> = { p1, p2 ->
val loadingOrAction: DiffStrategy<ViewModel> = { p1, p2 ->
p1.isLoading != p2.isLoading || p1.buttonAction !== p2.buttonAction
}

val watcher = modelWatcher {
val watcher = modelWatcher<ViewModel> {
watch({ it }, diffStrategy = loadingOrAction) { model ->
// Allow action only when not loading
button.setOnClickListener(
Expand All @@ -60,7 +60,7 @@ val watcher = modelWatcher {

The watcher also provides an optional DSL to add more clarity to the definitions:
```kotlin
val watcher = modelWatcher {
val watcher = modelWatcher<ViewModel> {
// Method call
watch(ViewModel::buttonText) {
button.text = it
Expand All @@ -74,30 +74,30 @@ val watcher = modelWatcher {
```
The same applies to custom strategies.
```kotlin
val watcher = modelWatcher {
val watcher = modelWatcher<ViewModel> {
// Method call
watch(Model::buttonAction, diffStrategy = byRef()) { }

// DSL
val byRef = byRef<() -> Unit>()
Model::buttonAction using byRef {
ViewModel::buttonAction using byRef {

}
}
```
Common strategies on two fields can be defined in a simpler way.
```kotlin
val watcher = modelWatcher {
val watcher = modelWatcher<ViewModel> {
// Method call
val loadingOrAction: DiffStrategy<Model> = { p1, p2 ->
val loadingOrAction: DiffStrategy<ViewModel> = { p1, p2 ->
p1.isLoading != p2.isLoading || p1.buttonAction !== p2.buttonAction
}
watch({ it }, loadingOrAction) {

}

// DSL
(Model::isLoading or Model::buttonAction) {
(ViewModel::isLoading or ViewModel::buttonAction) {

}
}
Expand Down
48 changes: 24 additions & 24 deletions mvicore-diff/src/main/java/com/badoo/mvicore/ModelWatcher.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.badoo.mvicore

class ModelWatcher<T> private constructor(
private val watchers: List<Watcher<T, Any?>>
class ModelWatcher<Model> private constructor(
private val watchers: List<Watcher<Model, Any?>>
) {
private var model: T? = null
private var model: Model? = null

operator fun invoke(newModel: T) {
operator fun invoke(newModel: Model) {
val oldModel = model
watchers.forEach { element ->
val getter = element.accessor
Expand All @@ -18,53 +18,53 @@ class ModelWatcher<T> private constructor(
model = newModel
}

private class Watcher<T, R>(
val accessor: (T) -> R,
val callback: (R) -> Unit,
val diff: DiffStrategy<R>
private class Watcher<Model, Field>(
val accessor: (Model) -> Field,
val callback: (Field) -> Unit,
val diff: DiffStrategy<Field>
)

class Builder<T> @PublishedApi internal constructor() {
private val watchers = mutableListOf<Watcher<T, Any?>>()
class Builder<Model> @PublishedApi internal constructor() {
private val watchers = mutableListOf<Watcher<Model, Any?>>()

fun <R> watch(
accessor: (T) -> R,
diff: DiffStrategy<R> = byValue(),
callback: (R) -> Unit
fun <Field> watch(
accessor: (Model) -> Field,
diff: DiffStrategy<Field> = byValue(),
callback: (Field) -> Unit
) {
watchers += Watcher(
accessor,
callback,
diff
) as Watcher<T, Any?>
) as Watcher<Model, Any?>
}

@PublishedApi
internal fun build(): ModelWatcher<T> =
internal fun build(): ModelWatcher<Model> =
ModelWatcher(watchers)

/*
* Syntactic sugar around watch (scoped inside the builder)
*/

operator fun <R> ((T) -> R).invoke(callback: (R) -> Unit) {
operator fun <Field> ((Model) -> Field).invoke(callback: (Field) -> Unit) {
watch(this, callback = callback)
}

infix fun <R> ((T) -> R).using(pair: Pair<DiffStrategy<R>, (R) -> Unit>) {
infix fun <Field> ((Model) -> Field).using(pair: Pair<DiffStrategy<Field>, (Field) -> Unit>) {
watch(this, pair.first, pair.second)
}

operator fun <R> (DiffStrategy<R>).invoke(callback: (R) -> Unit) =
operator fun <Field> (DiffStrategy<Field>).invoke(callback: (Field) -> Unit) =
this to callback

infix fun <R1, R2> ((T) -> R1).or(f: (T) -> R2): DiffStrategy<T> =
infix fun <Field1, Field2> ((Model) -> Field1).or(f: (Model) -> Field2): DiffStrategy<Model> =
{ old, new -> this(old) != this(new) || f(old) != f(new) }

infix fun <R1, R2> ((T) -> R1).and(f: (T) -> R2): DiffStrategy<T> =
infix fun <Field1, Field2> ((Model) -> Field1).and(f: (Model) -> Field2): DiffStrategy<Model> =
{ old, new -> this(old) != this(new) && f(old) != f(new) }

operator fun DiffStrategy<T>.invoke(callback: (T) -> Unit) {
operator fun DiffStrategy<Model>.invoke(callback: (Model) -> Unit) {
watch(
accessor = { it },
diff = this,
Expand All @@ -74,7 +74,7 @@ class ModelWatcher<T> private constructor(
}
}

inline fun <T> modelWatcher(init: ModelWatcher.Builder<T>.() -> Unit): ModelWatcher<T> =
ModelWatcher.Builder<T>()
inline fun <Model> modelWatcher(init: ModelWatcher.Builder<Model>.() -> Unit): ModelWatcher<Model> =
ModelWatcher.Builder<Model>()
.apply(init)
.build()

0 comments on commit 2f60113

Please sign in to comment.