Skip to content

Commit

Permalink
Change fun newBuilder to operator fun invoke
Browse files Browse the repository at this point in the history
  • Loading branch information
JavierSegoviaCordoba committed Jul 19, 2021
1 parent b18037c commit ef9b3ac
Show file tree
Hide file tree
Showing 12 changed files with 102 additions and 77 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
## 0.2.0

### Changed

- `fun newBuilder` to `operator fun invoke`

### Changed

- Support Kotlin 1.5.0.
- Stately to 1.1.7.

## 0.1.1

### Changed

- Disable IR.

## 0.1.0
Expand Down
64 changes: 42 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ In-memory Cache for Kotlin Multiplatform.

**Work in progress.**

**cache4k** provides a simple in-memory key-value cache for **Kotlin Multiplatform**, with support for time-based (expiration) and size-based evictions.
**cache4k** provides a simple in-memory key-value cache for **Kotlin Multiplatform**, with support
for time-based (expiration) and size-based evictions.

The following targets are currently supported:

Expand All @@ -24,7 +25,8 @@ The following targets are currently supported:

## Download

Dependencies are hosted on [Maven Central](https://search.maven.org/artifact/io.github.reactivecircus.cache4k/cache4k).
Dependencies are hosted
on [Maven Central](https://search.maven.org/artifact/io.github.reactivecircus.cache4k/cache4k).

### Android

Expand Down Expand Up @@ -55,7 +57,7 @@ kotlin {
To create a new `Cache` instance using `Long` for the key and `String` for the value:

```kotlin
val cache = Cache.Builder.newBuilder().build<Long, String>()
val cache = Cache.Builder().build<Long, String>()
```

To start writing entries to the cache:
Expand Down Expand Up @@ -83,11 +85,13 @@ cache.get(1) // returns "bird"

### Cache loader

**Cache** provides an API for getting cached value by key and using the provided `loader: suspend () -> Value` lambda to compute and cache the value automatically if none exists.
**Cache** provides an API for getting cached value by key and using the
provided `loader: suspend () -> Value` lambda to compute and cache the value automatically if none
exists.

```kotlin
runBlockingTest {
val cache = Cache.Builder.newBuilder().build<Long, User>()
val cache = Cache.Builder().build<Long, User>()

val userId = 1L
val user = cache.get(userId) {
Expand All @@ -103,58 +107,68 @@ Any exceptions thrown by the `loader` will be propagated to the caller of this f

### Expirations and evictions

By default, **Cache** has an unlimited number of entries which never expire. But a cache can be configured to support both **time-based expirations** and **size-based evictions**.
By default, **Cache** has an unlimited number of entries which never expire. But a cache can be
configured to support both **time-based expirations** and **size-based evictions**.

#### Time-based expiration

Expiration time can be specified for entries in the cache.

##### Expire after access

To set the maximum time an entry can live in the cache since the last access (also known as **time-to-idle**), where "access" means **reading the cache**, **adding a new cache entry**, or **replacing an existing entry with a new one**:
To set the maximum time an entry can live in the cache since the last access (also known as **
time-to-idle**), where "access" means **reading the cache**, **adding a new cache entry**, or **
replacing an existing entry with a new one**:

```kotlin
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.expireAfterAccess(24.hours)
.build<Long, String>()
```

An entry in this cache will be removed if it has not been read or replaced **after 24 hours** since it's been written into the cache.
An entry in this cache will be removed if it has not been read or replaced **after 24 hours** since
it's been written into the cache.

##### Expire after write

To set the maximum time an entry can live in the cache since the last write (also known as **time-to-live**), where "write" means **adding a new cache entry** or **replacing an existing entry with a new one**:
To set the maximum time an entry can live in the cache since the last write (also known as **
time-to-live**), where "write" means **adding a new cache entry** or **replacing an existing entry
with a new one**:

```kotlin
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.expireAfterWrite(30.minutes)
.build<Long, String>()
```

An entry in this cache will be removed if it has not been replaced **after 30 minutes** since it's been written into the cache.
An entry in this cache will be removed if it has not been replaced **after 30 minutes** since it's
been written into the cache.

_Note that cache entries are **not** removed immediately upon expiration at exact time. Expirations are checked in each interaction with the `cache`._
_Note that cache entries are **not** removed immediately upon expiration at exact time. Expirations
are checked in each interaction with the `cache`._

### Size-based eviction

To set the maximum number of entries to be kept in the cache:

```kotlin
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.maximumCacheSize(100)
.build<Long, String>()
```

Once there are more than **100** entries in this cache, the **least recently used one** will be removed, where "used" means **reading the cache**, **adding a new cache entry**, or **replacing an existing entry with a new one**.
Once there are more than **100** entries in this cache, the **least recently used one** will be
removed, where "used" means **reading the cache**, **adding a new cache entry**, or **replacing an
existing entry with a new one**.

### Getting all cache entries as a Map

To get a copy of the current cache entries as a `Map`:

```kotlin
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.build<Long, String>()

cache.put(1, "dog")
cache.put(2, "cat")

Expand All @@ -171,7 +185,7 @@ Cache entries can also be deleted explicitly.
To delete a cache entry for a given key:

```kotlin
val cache = Cache.Builder.newBuilder().build<Long, String>()
val cache = Cache.Builder().build<Long, String>()
cache.put(1, "dog")

cache.invalidate(1)
Expand All @@ -187,13 +201,14 @@ cache.invalidateAll()

### Unit testing cache expirations

To test logic that depends on cache expiration, pass in a `FakeTimeSource` when building a `Cache` so you can programmatically advance the reading of the time source:
To test logic that depends on cache expiration, pass in a `FakeTimeSource` when building a `Cache`
so you can programmatically advance the reading of the time source:

```kotlin
@Test
fun cacheEntryEvictedAfterExpiration() {
private val fakeTimeSource = FakeTimeSource()
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.fakeTimeSource(fakeTimeSource)
.expireAfterWrite(1.minutes)
.build<Long, String>()
Expand All @@ -216,9 +231,14 @@ fun cacheEntryEvictedAfterExpiration() {

## Credits

The library was ported from a kotlin / JVM cache which I contributed to [dropbox/Store](https://github.com/dropbox/Store) to help unblock Store's multiplatform support (it was reverted before the 1.0 release as multiplatform wasn't a priority). Many thanks to Store's owners and contributors for reviewing and improving the original implementation.
The library was ported from a kotlin / JVM cache which I contributed
to [dropbox/Store](https://github.com/dropbox/Store) to help unblock Store's multiplatform support (
it was reverted before the 1.0 release as multiplatform wasn't a priority). Many thanks to Store's
owners and contributors for reviewing and improving the original implementation.

Native concurrency support of the library is powered by [touchlab/Stately](https://github.com/touchlab/Stately). Many thanks to Stately's authors and contributors for the great library.
Native concurrency support of the library is powered
by [touchlab/Stately](https://github.com/touchlab/Stately). Many thanks to Stately's authors and
contributors for the great library.

## License

Expand Down
2 changes: 1 addition & 1 deletion cache4k/api/cache4k.api
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public abstract interface class io/github/reactivecircus/cache4k/Cache$Builder {
}

public final class io/github/reactivecircus/cache4k/Cache$Builder$Companion {
public final fun newBuilder ()Lio/github/reactivecircus/cache4k/Cache$Builder;
public final fun invoke ()Lio/github/reactivecircus/cache4k/Cache$Builder;
}

public final class io/github/reactivecircus/cache4k/FakeTimeSource : kotlin/time/AbstractLongTimeSource {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public interface Cache<in Key : Any, Value : Any> {
/**
* Returns a new [Cache.Builder] instance.
*/
public fun newBuilder(): Builder = CacheBuilderImpl()
public operator fun invoke(): Builder = CacheBuilderImpl()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.time.Duration
import kotlin.time.TimeSource
import kotlin.time.hours

class CacheBuilderTest {

@Test
fun expireAfterWrite_zeroDuration() {
val exception = assertFailsWith<IllegalArgumentException> {
Cache.Builder.newBuilder()
Cache.Builder()
.expireAfterWrite(Duration.nanoseconds(0))
.build<Any, Any>()
}
Expand All @@ -23,7 +22,7 @@ class CacheBuilderTest {

@Test
fun expireAfterWrite_positiveDuration() {
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.expireAfterWrite(Duration.hours(24))
.build<Any, Any>() as RealCache

Expand All @@ -33,7 +32,7 @@ class CacheBuilderTest {
@Test
fun expireAfterWrite_negativeDuration() {
val exception = assertFailsWith<IllegalArgumentException> {
Cache.Builder.newBuilder()
Cache.Builder()
.expireAfterWrite(Duration.nanoseconds((-1)))
.build<Any, Any>() as RealCache
}
Expand All @@ -44,7 +43,7 @@ class CacheBuilderTest {
@Test
fun expireAfterAccess_zeroDuration() {
val exception = assertFailsWith<IllegalArgumentException> {
Cache.Builder.newBuilder()
Cache.Builder()
.expireAfterAccess(Duration.nanoseconds(0))
.build<Any, Any>() as RealCache
}
Expand All @@ -54,7 +53,7 @@ class CacheBuilderTest {

@Test
fun expireAfterAccess_positiveDuration() {
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.expireAfterAccess(Duration.hours(24))
.build<Any, Any>() as RealCache

Expand All @@ -64,7 +63,7 @@ class CacheBuilderTest {
@Test
fun expireAfterAccess_negativeDuration() {
val exception = assertFailsWith<IllegalArgumentException> {
Cache.Builder.newBuilder()
Cache.Builder()
.expireAfterAccess(Duration.nanoseconds(-1))
.build<Any, Any>() as RealCache
}
Expand All @@ -74,7 +73,7 @@ class CacheBuilderTest {

@Test
fun maximumCacheSize_zero() {
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.maximumCacheSize(0)
.build<Any, Any>() as RealCache

Expand All @@ -83,7 +82,7 @@ class CacheBuilderTest {

@Test
fun maximumCacheSize_positiveValue() {
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.maximumCacheSize(10)
.build<Any, Any>() as RealCache

Expand All @@ -93,7 +92,7 @@ class CacheBuilderTest {
@Test
fun maximumCacheSize_negativeValue() {
val exception = assertFailsWith<IllegalArgumentException> {
Cache.Builder.newBuilder()
Cache.Builder()
.maximumCacheSize(-1)
.build<Any, Any>() as RealCache
}
Expand All @@ -104,7 +103,7 @@ class CacheBuilderTest {
@Test
fun fakeTimeSource() {
val fakeTimeSource = FakeTimeSource()
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.fakeTimeSource(fakeTimeSource)
.build<Any, Any>() as RealCache

Expand All @@ -113,7 +112,7 @@ class CacheBuilderTest {

@Test
fun buildWithDefaults() {
val cache = Cache.Builder.newBuilder().build<Any, Any>() as RealCache
val cache = Cache.Builder().build<Any, Any>() as RealCache

assertEquals(Duration.INFINITE, cache.expireAfterWriteDuration)
assertEquals(Duration.INFINITE, cache.expireAfterAccessDuration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class CacheEvictionTest {

@Test
fun maxSizeLimitReached_addNewEntry_oldEntryEvicted() {
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.maximumCacheSize(2)
.build<Long, String>()

Expand All @@ -33,7 +33,7 @@ class CacheEvictionTest {

@Test
fun maxSizeLimitReached_replaceCacheEntry_doesNotEvict() {
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.maximumCacheSize(2)
.build<Long, String>()

Expand All @@ -50,7 +50,7 @@ class CacheEvictionTest {

@Test
fun readCacheEntry_accessOrderChanged() {
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.maximumCacheSize(3)
.build<Long, String>()

Expand All @@ -73,7 +73,7 @@ class CacheEvictionTest {

@Test
fun replaceCacheValue_accessOrderChanged() {
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.maximumCacheSize(3)
.build<Long, String>()

Expand All @@ -99,7 +99,7 @@ class CacheEvictionTest {

@Test
fun maximumCacheSizeIsOne_addNewEntry_existingEntryEvicted() {
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.maximumCacheSize(1)
.build<Long, String>()

Expand All @@ -121,7 +121,7 @@ class CacheEvictionTest {

@Test
fun maximumCacheSizeIsZero_noValuesCached() {
val cache = Cache.Builder.newBuilder()
val cache = Cache.Builder()
.maximumCacheSize(0)
.build<Long, String>()

Expand Down
Loading

0 comments on commit ef9b3ac

Please sign in to comment.