Merge pull request #23 from InsanusMokrassar/0.6.5

0.6.5
This commit is contained in:
InsanusMokrassar 2021-09-26 14:08:01 +06:00 committed by GitHub
commit 89a6a305f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 204 additions and 43 deletions

View File

@ -1,5 +1,11 @@
# Changelog # Changelog
## 0.6.5
* Flows now use `doWhile` functions
* `doWhile` now use additional delay (for 1 ms) for cases when `block` executing too fast
* New extensions for `KronScheduler`: `do(Once/While/Infinity)Local`/`do(Once/While/Infinity)Tz`
## 0.6.4 ## 0.6.4
* Versions * Versions

View File

@ -7,16 +7,6 @@
Library was created to give oppotunity to launch some things from time to time according to some schedule in Library was created to give oppotunity to launch some things from time to time according to some schedule in
runtime of applications. runtime of applications.
| Table of content |
|---|
| [ How to use ](#how-to-use) |
| [ How to use: Including in project ](#including-in-project) |
| [ How to use: Config from string ](#config-from-string) |
| [ How to use: Config via builder (DSL preview) ](#config-via-builder) |
| [ How to use: KronScheduler as a Flow ](#KronScheduler-as-a-Flow) |
| [ How to use: Offsets ](#Offsets) |
| [ How to use: Note about week days ](#Note-about-week-days) |
## How to use ## How to use
There are several ways to configure and use this library: There are several ways to configure and use this library:
@ -133,6 +123,38 @@ kronScheduler.doInfinity {
All of these examples will do the same things: print `Called` message every five seconds. All of these examples will do the same things: print `Called` message every five seconds.
### do\* functions
With regular `doOnce`/`doWhile`/`doInfinity` there are two types of their variations: **local** and **timezoned**. Local
variations (`doOnceLocal`/`doWhileLocal`/`doInfinityLocal`) will pass `DateTime` as an argument into the block:
```kotlin
doInfinityLocal("/5 * * * *") {
println(it) // will print current date time
}
```
Timezoned variations (`doOnceTz`/`doWhileTz`/`doInfinityTz`) will do the same thing but pass as an argument `DateTimeTz`:
```kotlin
doInfinityTz("/5 * * * * 0o") {
println(it) // will print current date time in UTC
}
```
It is useful in cases when you need to get the time of calling and avoid extra calls to system time.
#### Helpful table for
| | No args | Local `DateTime` | Local `DateTimeTz` with offset of `KronScheduler` |
|---| ------- | ---------------- | ------------------------------------------------- |
| **Call only near time** | doOnce | doOnceLocal | doOnceTz |
| **Call while condition is true** | doWhile | doWhileLocal | doWhileTz |
| **Work infinity*** | doInfinity | doInfinityLocal | doInfinityTz |
*Here there is an important notice, that `Work infinity` is not exactly `infinity`. Actually, that means that `do while
coroutine is alive` and in fact executing will be stopped when coroutine became cancelled.
### KronScheduler as a Flow ### KronScheduler as a Flow
Any `KronScheduler`can e converted to a `Flow<DateTime` using extension `asFlow`: Any `KronScheduler`can e converted to a `Flow<DateTime` using extension `asFlow`:
@ -163,7 +185,7 @@ flow.takeWhile {
Offsets in this library works via passing parameter ending with `o` in any place after `month` config. Currently Offsets in this library works via passing parameter ending with `o` in any place after `month` config. Currently
there is only one format supported for offsets: minutes of offsets. To use time zones you will need to call `next` there is only one format supported for offsets: minutes of offsets. To use time zones you will need to call `next`
method with `DateTimeTz` argument or `nextTimeZoned` method with any `KronScheduler` instance, but in case if this method with `DateTimeTz` argument or `nextTimeZoned` method with any `KronScheduler` instance, but in case if this
scheduler is not instance of `KronSchedulerTz` it will works like you passed just `DateTime`. scheduler is not instance of `KronSchedulerTz` it will work like you passed just `DateTime`.
Besides, in case you wish to use time zones explicitly, you will need to get `KronSchedulerTz`. It is possible by: Besides, in case you wish to use time zones explicitly, you will need to get `KronSchedulerTz`. It is possible by:

View File

@ -33,5 +33,5 @@ androidx_work_version=2.6.0
## Common ## Common
version=0.6.4 version=0.6.5
android_code_version=8 android_code_version=9

View File

@ -1,7 +1,10 @@
package dev.inmo.krontab package dev.inmo.krontab
import com.soywiz.klock.DateTime import com.soywiz.klock.DateTime
import com.soywiz.klock.DateTimeTz
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlin.coroutines.coroutineContext
/** /**
* Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation. * Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
@ -10,29 +13,109 @@ import kotlinx.coroutines.delay
* *
* WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately * WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately
*/ */
suspend inline fun <T> KronScheduler.doOnce(noinline block: suspend () -> T): T { suspend inline fun <T> KronScheduler.doOnceLocal(noinline block: suspend (DateTime) -> T): T {
next() ?.let { val time = nextOrNow().also {
delay((it - DateTime.now()).millisecondsLong) delay((it - DateTime.now()).millisecondsLong)
} }
return block() return block(time)
} }
/** /**
* Will [buildSchedule] using [scheduleConfig] and call [doOnce] on it * Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
*
* WARNING!!! If you want to launch it in parallel, you must do this explicitly.
*
* WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediatelly
*/
suspend inline fun <T> KronScheduler.doOnceTz(noinline block: suspend (DateTimeTz) -> T): T {
val time = when (this) {
is KronSchedulerTz -> nextOrNowWithOffset()
else -> nextOrNow().local
}
delay((time - DateTimeTz.nowLocal()).millisecondsLong)
return block(time)
}
/**
* Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
*
* WARNING!!! If you want to launch it in parallel, you must do this explicitly.
*
* WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately
*/
suspend inline fun <T> KronScheduler.doOnce(noinline block: suspend () -> T): T = doOnceLocal { _ -> block() }
/**
* Will [buildSchedule] using [scheduleConfig] and call [doOnceLocal] on it
* @see buildSchedule
*/
suspend inline fun <T> doOnce(
scheduleConfig: String,
noinline block: suspend (DateTime) -> T
) = buildSchedule(scheduleConfig).doOnceLocal(block)
/**
* Will [buildSchedule] using [scheduleConfig] and call [doOnceLocal] on it
* @see buildSchedule
*/
suspend inline fun <T> doOnceTz(
scheduleConfig: String,
noinline block: suspend (DateTimeTz) -> T
) = buildSchedule(scheduleConfig).doOnceTz(block)
/**
* Will [buildSchedule] using [scheduleConfig] and call [doOnceLocal] on it
* @see buildSchedule * @see buildSchedule
*/ */
suspend inline fun <T> doOnce( suspend inline fun <T> doOnce(
scheduleConfig: String, scheduleConfig: String,
noinline block: suspend () -> T noinline block: suspend () -> T
) = buildSchedule(scheduleConfig).doOnce(block) ) = doOnce(scheduleConfig) { _ -> block() }
/** /**
* Will execute [block] while it will return true as a result of its calculation * Will execute [block] while it will return true as a result of its calculation
*/ */
suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean) { suspend inline fun KronScheduler.doWhileLocal(noinline block: suspend (DateTime) -> Boolean) {
do { val doNext = doOnce(block) } while (doNext) do {
delay(1L)
} while (doOnceLocal(block))
} }
/**
* Will execute [block] while it will return true as a result of its calculation
*/
suspend inline fun KronScheduler.doWhileTz(noinline block: suspend (DateTimeTz) -> Boolean) {
do {
delay(1L)
} while (doOnceTz(block))
}
/**
* Will execute [block] while it will return true as a result of its calculation
*/
suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean) = doWhileLocal { block() }
/**
* Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block]
*
* @see buildSchedule
*/
suspend inline fun doWhileLocal(
scheduleConfig: String,
noinline block: suspend (DateTime) -> Boolean
) = buildSchedule(scheduleConfig).doWhileLocal(block)
/**
* Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block]
*
* @see buildSchedule
*/
suspend inline fun doWhileTz(
scheduleConfig: String,
noinline block: suspend (DateTimeTz) -> Boolean
) = buildSchedule(scheduleConfig).doWhileTz(block)
/** /**
* Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block] * Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block]
* *
@ -41,15 +124,53 @@ suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean)
suspend inline fun doWhile( suspend inline fun doWhile(
scheduleConfig: String, scheduleConfig: String,
noinline block: suspend () -> Boolean noinline block: suspend () -> Boolean
) = buildSchedule(scheduleConfig).doWhile(block) ) = doWhileLocal(scheduleConfig) { block() }
/**
* Will execute [block] without any checking of result
*/
suspend inline fun KronScheduler.doInfinityLocal(noinline block: suspend (DateTime) -> Unit) = doWhileLocal {
block(it)
coroutineContext.isActive
}
/**
* Will execute [block] without any checking of result
*/
suspend inline fun KronScheduler.doInfinityTz(noinline block: suspend (DateTimeTz) -> Unit) = doWhileTz {
block(it)
coroutineContext.isActive
}
/** /**
* Will execute [block] without any checking of result * Will execute [block] without any checking of result
*/ */
suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) = doWhile { suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) = doWhile {
block() block()
true coroutineContext.isActive
} }
/**
* Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block]
*
* @see buildSchedule
*/
suspend inline fun doInfinityLocal(
scheduleConfig: String,
noinline block: suspend (DateTime) -> Unit
) = buildSchedule(scheduleConfig).doInfinityLocal(block)
/**
* Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block]
*
* @see buildSchedule
*/
suspend inline fun doInfinityTz(
scheduleConfig: String,
noinline block: suspend (DateTimeTz) -> Unit
) = buildSchedule(scheduleConfig).doInfinityTz(block)
/** /**
* Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block] * Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block]
* *

View File

@ -1,8 +1,8 @@
package dev.inmo.krontab.internal package dev.inmo.krontab.internal
import com.soywiz.klock.* import com.soywiz.klock.DateTime
import com.soywiz.klock.TimezoneOffset
import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.KronScheduler
import dev.inmo.krontab.utils.copy
/** /**
* @param daysOfWeek 0-6 * @param daysOfWeek 0-6

View File

@ -1,6 +1,7 @@
package dev.inmo.krontab.utils package dev.inmo.krontab.utils
import com.soywiz.klock.* import com.soywiz.klock.DateTime
import com.soywiz.klock.Month
import kotlin.math.min import kotlin.math.min
fun DateTime.copy( fun DateTime.copy(

View File

@ -2,9 +2,9 @@ package dev.inmo.krontab.utils
import com.soywiz.klock.DateTime import com.soywiz.klock.DateTime
import com.soywiz.klock.DateTimeTz import com.soywiz.klock.DateTimeTz
import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.*
import dev.inmo.krontab.next import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.* import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
/** /**
@ -12,30 +12,41 @@ import kotlinx.coroutines.flow.*
* time zones * time zones
* *
* @see channelFlow * @see channelFlow
* @see KronSchedulerTz.doWhile
*/ */
@FlowPreview @FlowPreview
fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = channelFlow { fun KronSchedulerTz.asTzFlow(): Flow<DateTimeTz> = channelFlow {
var previousTime = DateTime.nowLocal() doInfinity {
while (isActive) { send(DateTime.nowLocal())
val now = DateTime.nowLocal()
val nextTime = next(now) ?: break
if (previousTime == nextTime) {
delay(1L) // skip 1ms
continue
} else {
previousTime = nextTime
}
val sleepDelay = (nextTime - DateTime.now().local).millisecondsLong
delay(sleepDelay)
send(nextTime)
} }
} }
/** /**
* This method is a map for [asTzFlow] and will works the same but return flow with [DateTime]s * This method is a map for [asTzFlow] and will works the same but return flow with [DateTime]s
*
* @see channelFlow
* @see KronScheduler.doWhile
*/ */
@FlowPreview @FlowPreview
fun KronScheduler.asFlow(): Flow<DateTime> = asTzFlow().map { it.local } fun KronScheduler.asFlow(): Flow<DateTime> = channelFlow {
doInfinity {
send(DateTime.now())
}
}
/**
* This [Flow] will trigger emitting each near time which will be returned from [this] [KronScheduler] with attention to
* time zones
*
* @see channelFlow
* @see KronScheduler.asFlow
* @see KronSchedulerTz.asTzFlow
*/
@FlowPreview
fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = when (this) {
is KronSchedulerTz -> asTzFlow()
else -> asFlow().map { it.local }
}
@Deprecated( @Deprecated(
"It is not recommended to use this class in future. This functionality will be removed soon", "It is not recommended to use this class in future. This functionality will be removed soon",