Skip to content

Commit 10b66df

Browse files
committed
[WIP] update & sample
1 parent 9ab06a3 commit 10b66df

39 files changed

+762
-198
lines changed

build.gradle

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
apply from: "$rootDir/gradle/dependencies.gradle"
2+
13
buildscript {
24
apply from: "$rootDir/gradle/dependencies.gradle"
5+
ext {
6+
kotlin_version = '1.3.70'
7+
}
38

49
repositories {
510
google()
@@ -12,9 +17,10 @@ buildscript {
1217
dependencies {
1318
classpath deps.plugins.kotlin
1419
classpath deps.plugins.dokka
20+
// classpath deps.plugins.android
21+
classpath 'com.android.tools.build:gradle:3.6.0'
1522
}
1623
}
17-
apply from: "$rootDir/gradle/dependencies.gradle"
1824

1925
allprojects {
2026
repositories {

gradle/dependencies.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ ext.versions = [
22
kotlin : '1.3.70',
33
dokka : '0.9.17',
44
spek : '2.1.0-alpha.0.9+3d5d865',
5-
atrium : '0.8.0'
5+
atrium : '0.8.0',
6+
androidBuildTools : '3.6.1'
67
]
78

89
ext.deps = [
910
plugins: [
1011
kotlin : "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}",
1112
dokka : "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}",
13+
android : "com.android.tools.build:${versions.androidBuildTools}"
1214
]
1315
]

lib/src/commonMain/kotlin/org/reduxkotlin/Reselect.kt

Lines changed: 36 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ package org.reduxkotlin
22

33
import kotlin.jvm.JvmField
44

5-
private typealias EqualityCheckFn = (a:Any,b:Any)->Boolean
5+
private typealias EqualityCheckFn = (a:Any, b:Any) -> Boolean
6+
67
/**
78
* A rewrite for kotlin of https://github.com/reactjs/reselect library for redux (https://github.com/reactjs/redux)
89
* see also "Computing Derived Data" in redux documentation http://redux.js.org/docs/recipes/ComputingDerivedData.html
@@ -18,13 +19,6 @@ val byRefEqualityCheck: EqualityCheckFn = { a: Any, b: Any -> a === b }
1819
*/
1920
val byValEqualityCheck: EqualityCheckFn = { a: Any, b: Any -> a == b }
2021

21-
/**
22-
* a class for keeping a non null object reference even when actual reference is null
23-
* Needed because selectors do not work for nullable fields: if you have a field in the state that is
24-
* nullable T? the define instead as Opt<T> if you want selectors to work
25-
*/
26-
class Opt<T>(@JvmField val it:T?)
27-
2822
interface Memoizer<T> {
2923
fun memoize(state: Any, vararg inputs: SelectorInput<Any, Any>): T
3024
}
@@ -114,24 +108,6 @@ interface Selector<S, O> : SelectorInput<S, O> {
114108
getIfChangedIn(state)?.let(blockfn)
115109
}
116110

117-
/**
118-
* same as regular [onChangeIn] but don't activate selector unless [condition] is true
119-
* Note that the selector is not run at all if [condition] is false. Therefore
120-
* the selector will be triggered by changes that happened and where ignored, as soon as
121-
* [condition] become true
122-
*/
123-
fun onChangeIn(state: S, condition:Boolean,blockfn: (O) -> Unit) {
124-
if(condition) getIfChangedIn(state)?.let(blockfn)
125-
}
126-
}
127-
128-
/**
129-
* same as [Selector.onChangeIn], but as extension function of state:
130-
* it checks if the specified selector value is changed for the input state and if so, call [blockfn]
131-
* with the updated selector value
132-
*/
133-
fun <S,O> S.whenChangeOf(selector: Selector<S, O>, blockfn: (O) -> Unit) {
134-
selector.getIfChangedIn(this)?.let(blockfn)
135111
}
136112

137113
/**
@@ -164,156 +140,12 @@ abstract class AbstractSelector<S, O> : Selector<S, O> {
164140

165141
}
166142

167-
//use @JvmField annotation for avoiding generation useless getter methods
168-
class SelectorForP5<S:Any, I0 : Any, I1 : Any, I2 : Any, I3 : Any, I4 : Any>(@JvmField val si0: SelectorInput<S, I0>,
169-
@JvmField val si1: SelectorInput<S, I1>,
170-
@JvmField val si2: SelectorInput<S, I2>,
171-
@JvmField val si3: SelectorInput<S, I3>,
172-
@JvmField val si4: SelectorInput<S, I4>
173-
) {
174-
fun<O> compute(equalityCheckForResult: EqualityCheckFn = byRefEqualityCheck, computeFun: (I0, I1, I2, I3, I4) -> O) = object : AbstractSelector<S, O>() {
175-
override val equalityCheck: EqualityCheckFn
176-
get() = equalityCheckForResult
177-
override val computeAndCount = fun(i: Array<out Any>): O {
178-
++_recomputations
179-
@Suppress("UNCHECKED_CAST")
180-
return computeFun(i[0] as I0, i[1] as I1, i[2] as I2, i[3] as I3, i[4] as I4)
181-
}
182-
183-
override operator fun invoke(state: S): O {
184-
@Suppress("UNCHECKED_CAST")
185-
return memoizer.memoize(
186-
state,
187-
si0 as SelectorInput<Any, Any>,
188-
si1 as SelectorInput<Any, Any>,
189-
si2 as SelectorInput<Any, Any>,
190-
si3 as SelectorInput<Any, Any>,
191-
si4 as SelectorInput<Any, Any>
192-
)
193-
}
194-
}
195-
}
196-
197-
//use @JvmField annotation for avoiding generation useless getter methods
198-
class SelectorForP4<S:Any, I0 : Any, I1 : Any, I2 : Any, I3 : Any>(@JvmField val si0: SelectorInput<S, I0>,
199-
@JvmField val si1: SelectorInput<S, I1>,
200-
@JvmField val si2: SelectorInput<S, I2>,
201-
@JvmField val si3: SelectorInput<S, I3>
202-
) {
203-
fun<I4 : Any> withField(fn: S.() -> I4) = SelectorForP5<S, I0, I1, I2, I3, I4>(si0, si1, si2, si3, InputField(fn, byRefEqualityCheck))
204-
fun<I4 : Any> withFieldByValue(fn: S.() -> I4) = SelectorForP5<S, I0, I1, I2, I3, I4>(si0, si1, si2, si3, InputField(fn, byValEqualityCheck))
205-
fun<I4 : Any> withSelector(si: SelectorInput<S, I4>) = SelectorForP5<S, I0, I1, I2, I3, I4>(si0, si1, si2, si3, si)
206-
fun<O> compute(equalityCheckForResult: EqualityCheckFn = byRefEqualityCheck, computeFun: (I0, I1, I2, I3) -> O) = object : AbstractSelector<S, O>() {
207-
override val equalityCheck: EqualityCheckFn
208-
get() = equalityCheckForResult
209-
override val computeAndCount = fun(i: Array<out Any>): O {
210-
++_recomputations
211-
@Suppress("UNCHECKED_CAST")
212-
return computeFun(i[0] as I0, i[1] as I1, i[2] as I2, i[3] as I3)
213-
}
214-
215-
override operator fun invoke(state: S): O {
216-
@Suppress("UNCHECKED_CAST")
217-
return memoizer.memoize(
218-
state,
219-
si0 as SelectorInput<Any, Any>,
220-
si1 as SelectorInput<Any, Any>,
221-
si2 as SelectorInput<Any, Any>,
222-
si3 as SelectorInput<Any, Any>
223-
)
224-
}
225-
}
226-
}
227-
228-
//use @JvmField annotation for avoiding generation useless getter methods
229-
class SelectorForP3<S:Any, I0 : Any, I1 : Any, I2 : Any>(@JvmField val si0: SelectorInput<S, I0>,
230-
@JvmField val si1: SelectorInput<S, I1>,
231-
@JvmField val si2: SelectorInput<S, I2>
232-
) {
233-
fun<I3 : Any> withField(fn: S.() -> I3) = SelectorForP4(si0, si1, si2, InputField(fn, byRefEqualityCheck))
234-
fun<I3 : Any> withFieldByValue(fn: S.() -> I3) = SelectorForP4(si0, si1, si2, InputField(fn, byValEqualityCheck))
235-
fun<I3 : Any> withSelector(si: SelectorInput<S, I3>) = SelectorForP4(si0, si1, si2, si)
236-
fun<O> compute(equalityCheckForResult: EqualityCheckFn = byRefEqualityCheck, computeFun: (I0, I1, I2) -> O) = object : AbstractSelector<S, O>() {
237-
override val equalityCheck: EqualityCheckFn
238-
get() = equalityCheckForResult
239-
override val computeAndCount = fun(i: Array<out Any>): O {
240-
++_recomputations
241-
@Suppress("UNCHECKED_CAST")
242-
return computeFun(i[0] as I0, i[1] as I1, i[2] as I2)
243-
}
244-
245-
override operator fun invoke(state: S): O {
246-
@Suppress("UNCHECKED_CAST")
247-
return memoizer.memoize(
248-
state,
249-
si0 as SelectorInput<Any, Any>,
250-
si1 as SelectorInput<Any, Any>,
251-
si2 as SelectorInput<Any, Any>
252-
)
253-
}
254-
}
255-
}
256-
257-
//use @JvmField annotation for avoiding generation useless getter methods
258-
class SelectorForP2<S:Any, I0 : Any, I1 : Any>(@JvmField val si0: SelectorInput<S, I0>,
259-
@JvmField val si1: SelectorInput<S, I1>
260-
) {
261-
fun<I2 : Any> withField(fn: S.() -> I2) = SelectorForP3<S, I0, I1, I2>(si0, si1, InputField(fn, byRefEqualityCheck))
262-
fun<I2 : Any> withFieldByValue(fn: S.() -> I2) = SelectorForP3<S, I0, I1, I2>(si0, si1, InputField(fn, byValEqualityCheck))
263-
fun<I2 : Any> withSelector(si: SelectorInput<S, I2>) = SelectorForP3<S, I0, I1, I2>(si0, si1, si)
264-
fun<O> compute(equalityCheckForResult: EqualityCheckFn = byRefEqualityCheck, computeFun: (I0, I1) -> O) = object : AbstractSelector<S, O>() {
265-
override val equalityCheck: EqualityCheckFn
266-
get() = equalityCheckForResult
267-
override val computeAndCount = fun(i: Array<out Any>): O {
268-
++_recomputations
269-
@Suppress("UNCHECKED_CAST")
270-
return computeFun(i[0] as I0, i[1] as I1)
271-
}
272-
273-
override operator fun invoke(state: S): O {
274-
@Suppress("UNCHECKED_CAST")
275-
return memoizer.memoize(
276-
state,
277-
si0 as SelectorInput<Any, Any>,
278-
si1 as SelectorInput<Any, Any>
279-
)
280-
}
281-
}
282-
}
283-
284-
//use @JvmField annotation for avoiding generation useless getter methods
285-
class SelectorForP1<S:Any, I0 : Any>(@JvmField val si0: SelectorInput<S, I0>) {
286-
fun<I1 : Any> withField(fn: S.() -> I1) = SelectorForP2<S, I0, I1>(si0, InputField(fn, byRefEqualityCheck))
287-
fun<I1 : Any> withFieldByValue(fn: S.() -> I1) = SelectorForP2<S, I0, I1>(si0, InputField(fn, byValEqualityCheck))
288-
fun<I1 : Any> withSelector(si: SelectorInput<S, I1>) = SelectorForP2<S, I0, I1>(si0, si)
289-
fun<O> compute(equalityCheckForResult: EqualityCheckFn = byRefEqualityCheck, computeFun: (I0) -> O) = object : AbstractSelector<S, O>() {
290-
override val equalityCheck: EqualityCheckFn
291-
get() = equalityCheckForResult
292-
override val computeAndCount = fun(i: Array<out Any>): O {
293-
++_recomputations
294-
@Suppress("UNCHECKED_CAST")
295-
return computeFun(i[0] as I0)
296-
}
297-
298-
override operator fun invoke(state: S): O {
299-
@Suppress("UNCHECKED_CAST")
300-
return memoizer.memoize(
301-
state,
302-
si0 as SelectorInput<Any, Any>
303-
)
304-
}
305-
}
306-
}
307143

308144
/**
309145
* wrapper class for Selector factory methods , that basically is used only to capture
310146
* type information for the state parameter
311147
*/
312148
class SelectorBuilder<S:Any> {
313-
fun<I0 : Any> withField(fn: S.() -> I0) = SelectorForP1<S, I0>(InputField(fn, byRefEqualityCheck))
314-
fun<I0 : Any> withFieldByValue(fn: S.() -> I0) = SelectorForP1<S, I0>(InputField(fn, byValEqualityCheck))
315-
fun<I0 : Any> withSelector(si: SelectorInput<S, I0>) = SelectorForP1<S, I0>(si)
316-
317149
/**
318150
* special single input selector that should be used when you just want to retrieve a single field:
319151
* Warning: Don't use this with primitive type fields, use [withSingleFieldByValue] instead!!!
@@ -363,5 +195,38 @@ class SelectorBuilder<S:Any> {
363195
}
364196
}
365197

198+
/**
199+
* Helper function that creates a DSL for subscribing to changes in specific state fields and actions to take.
200+
* Inside the lambda there is access to the current state through the var `state`
201+
*
202+
* ex:
203+
* val sel = selectorSubscriberFn {
204+
* select({it.foo}, { actionWhenFooChanges() }
205+
*
206+
* withAnyChange {
207+
* //called whenever any change happens to state
208+
* view.setMessage(state.barMsg) //state is current state
209+
* }
210+
* }
211+
*/
212+
fun <State: Any>Store<State>.selectors(selectorSubscriberBuilderInit: SelectorSubscriberBuilder<State>.() -> Unit): StoreSubscriber {
213+
val subscriberBuilder: SelectorSubscriberBuilder<State> = SelectorSubscriberBuilder(this)
214+
subscriberBuilder.selectorSubscriberBuilderInit()
215+
val sub = {
216+
subscriberBuilder.selectorList.forEach { entry ->
217+
@Suppress("UNCHECKED_CAST")
218+
(entry.key as Selector<State, *>).onChangeIn(getState()) { entry.value(getState()) }
219+
}
220+
subscriberBuilder.withAnyChangeFun?.invoke()
221+
Unit
222+
}
223+
//call subscriber immediately when subscribing
224+
sub()
225+
return this.subscribe(sub)
226+
}
366227

367-
228+
fun <State: Any>Store<State>.select(selector: (State) -> Any, action: (Any) -> Unit): StoreSubscriber {
229+
return subscribe(this.selectors {
230+
select(selector, action)
231+
})
232+
}
Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.reduxkotlin
22

3-
43
/**
54
* A Selector Subscriber - group of selectors that subscribe to store state changes.
65
*
@@ -22,31 +21,9 @@ class SelectorSubscriberBuilder<State : Any>(val store: Store<State>) {
2221
withAnyChangeFun = f
2322
}
2423

25-
26-
fun withSingleField(selector: (State) -> Any, action: (Any) -> Unit) {
24+
fun select(selector: (State) -> Any, action: (Any) -> Unit) {
2725
val selBuilder = SelectorBuilder<State>()
2826
val sel = selBuilder.withSingleField(selector)
2927
selectorList[sel] = action
3028
}
31-
32-
infix fun SelectorSubscriberBuilder<State>.select(selector: (State) -> Any): AbstractSelector<State, Any> =
33-
SelectorBuilder<State>().withSingleField(selector)
34-
35-
infix fun SelectorSubscriberBuilder<State>.on(selector: (State) -> Any): AbstractSelector<State, Any> =
36-
SelectorBuilder<State>().withSingleField(selector)
37-
38-
operator fun (() -> Any).unaryPlus(): AbstractSelector<State, Any> {
39-
val that = this
40-
return SelectorBuilder<State>().withSingleField { that() }
41-
}
42-
43-
infix fun AbstractSelector<State, Any>.then(action: (Any) -> Unit) {
44-
selectorList[this] = action
45-
}
46-
47-
infix operator fun AbstractSelector<State, Any>.plus(action: (Any) -> Unit) {
48-
selectorList[this] = action
49-
}
5029
}
51-
52-

sample/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

sample/build.gradle

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
apply plugin: 'com.android.application'
2+
apply plugin: 'kotlin-android'
3+
apply plugin: 'kotlin-android-extensions'
4+
5+
android {
6+
compileSdkVersion 29
7+
buildToolsVersion "29.0.2"
8+
9+
defaultConfig {
10+
applicationId "org.reduxkotlin.reselect.sample"
11+
minSdkVersion 21
12+
targetSdkVersion 29
13+
versionCode 1
14+
versionName "1.0"
15+
16+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17+
}
18+
19+
buildTypes {
20+
release {
21+
minifyEnabled false
22+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23+
}
24+
}
25+
26+
// To inline the bytecode built with JVM target 1.8 into
27+
// bytecode that is being built with JVM target 1.6. (e.g. navArgs)
28+
29+
30+
compileOptions {
31+
sourceCompatibility JavaVersion.VERSION_1_8
32+
targetCompatibility JavaVersion.VERSION_1_8
33+
}
34+
kotlinOptions {
35+
jvmTarget = "1.8"
36+
}
37+
38+
}
39+
40+
dependencies {
41+
implementation fileTree(dir: 'libs', include: ['*.jar'])
42+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
43+
implementation 'androidx.appcompat:appcompat:1.1.0'
44+
implementation 'androidx.core:core-ktx:1.2.0'
45+
implementation 'com.google.android.material:material:1.1.0'
46+
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
47+
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
48+
implementation 'androidx.navigation:navigation-ui-ktx:2.2.1'
49+
implementation 'org.reduxkotlin:redux-kotlin:0.4.0'
50+
implementation 'org.reduxkotlin:redux-kotlin-thunk:0.4.0'
51+
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
52+
implementation project(':lib')
53+
testImplementation 'junit:junit:4.13'
54+
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
55+
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
56+
}

0 commit comments

Comments
 (0)