Skip to content

Commit

Permalink
Review API usage to improve UX (#3)
Browse files Browse the repository at this point in the history
* Add variants of `*All` methods with varargs, to avoid wrapping
  everything in Sets
* Move the mapping function to a separate parameter list, to improve
  cases where you’d want to have a more complex function as a block
  expression

Before:

```
cache.getAll(Set("key", "key2"))(keys => {
  val foo = ???
  ZIO
    .attempt(foo)
    .orElse(ZIO.attempt(???))
    .orElseSucceed(???)
})
```

After:

```
cache.getAll("key", "key2") { keys =>
  val foo = ???
  ZIO
    .attempt(foo)
    .orElse(ZIO.attempt(???))
    .orElseSucceed(???)
}
```
  • Loading branch information
Pierre Dal-Pra authored Apr 27, 2022
1 parent a0696a1 commit 454db23
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 25 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ You can also find additional examples from [zcaffeine’s tests](src/test/scala/
// get or compute
cache.get("key")
// get all or compute all
cache.getAll(Set("key1","key2"), keys => ZIO.succeed(keys.map(key => (key, key + key)).toMap))
cache.getAll("key1","key2"))(keys => ZIO.succeed(keys.map(key => (key, key + key)).toMap))
// get only if cached
cache.getIfPresent("key3")
// insert or replace
Expand All @@ -97,7 +97,7 @@ cache.invalidate("key")
// invalidate all
cache.invalidateAll
// invalidate all specified keys
cache.invalidateAll(Set("key1","key2"))
cache.invalidateAll("key1","key2")


/*****************************************************/
Expand All @@ -112,7 +112,7 @@ cache.getAll(Set("key1","key2"))
// refresh using the functions set while building the cache and get
cache.refresh("key1")
// refresh all using the functions set while building the cache and get all
cache.refreshAll(Set("key1","key2"))
cache.refreshAll("key1","key2")
```
## License

Expand Down
84 changes: 71 additions & 13 deletions src/main/scala/com/stuart/zcaffeine/Cache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ sealed class Cache[R, Key, Value] private[zcaffeine] (runtime: Runtime[R], under
* @return
* the current or newly computed value for the given key
*/
def get(key: Key, mappingFunction: Key => RIO[R, Value]): Task[Value] =
def get(key: Key)(mappingFunction: Key => RIO[R, Value]): Task[Value] =
ZIO.fromCompletableFuture(
underlying.get(key, (key, _) => zioToCompletableFuture(runtime)(mappingFunction(key)))
)
Expand All @@ -32,16 +32,21 @@ sealed class Cache[R, Key, Value] private[zcaffeine] (runtime: Runtime[R], under
* @return
* the current or newly computed values for the given keys
*/
def getAll(keys: Set[Key], mappingFunction: Set[Key] => RIO[R, Map[Key, Value]]): Task[Map[Key, Value]] =
ZIO
.fromCompletableFuture(
underlying
.getAll(
keys.asJava,
(keys, _) => zioToCompletableFuture(runtime)(mappingFunction(keys.asScala.toSet).map(_.asJava))
)
)
.map(_.asScala.toMap)
def getAll(keys: Set[Key])(mappingFunction: Set[Key] => RIO[R, Map[Key, Value]]): Task[Map[Key, Value]] =
getAllInternal(keys)(keys => mappingFunction(keys.toSet))

/**
* Returns the values associated with the given keys in this cache, or asynchronously compute them if missing. Keys
* that are missing from the `mappingFunction` result won’t be saved in the cache.
* @param keys
* the keys for which the associated values are to be returned
* @param mappingFunction
* the function defining how to compute values for keys that are missing from the cache
* @return
* the current or newly computed values for the given keys
*/
def getAll(keys: Key*)(mappingFunction: Seq[Key] => RIO[R, Map[Key, Value]]): Task[Map[Key, Value]] =
getAllInternal(keys)(keys => mappingFunction(keys.toSeq))

/**
* Returns the value associated with the given key if present in this cache, otherwise returns None.
Expand Down Expand Up @@ -90,7 +95,16 @@ sealed class Cache[R, Key, Value] private[zcaffeine] (runtime: Runtime[R], under
* @return
*/
def invalidateAll(keys: Set[Key]): Task[Unit] =
ZIO.attemptBlocking(underlying.synchronous().invalidateAll(keys.asJava))
invalidateAllInternal(keys)

/**
* Remove the cached values for the given keys in this cache if present.
* @param keys
* the keys whose cached values are to be removed from the cache
* @return
*/
def invalidateAll(keys: Key*): Task[Unit] =
invalidateAllInternal(keys)

/**
* Returns the estimated size for this cache. It is only an estimation as it depends if there are pending
Expand All @@ -116,6 +130,22 @@ sealed class Cache[R, Key, Value] private[zcaffeine] (runtime: Runtime[R], under
*/
def cleanUp: Task[Unit] =
ZIO.attemptBlocking(underlying.synchronous().cleanUp())

private def getAllInternal(
keys: Iterable[Key]
)(mappingFunction: Iterable[Key] => RIO[R, Map[Key, Value]]): Task[Map[Key, Value]] =
ZIO
.fromCompletableFuture(
underlying
.getAll(
keys.asJava,
(keys, _) => zioToCompletableFuture(runtime)(mappingFunction(keys.asScala).map(_.asJava))
)
)
.map(_.asScala.toMap)

private def invalidateAllInternal(keys: Iterable[Key]): Task[Unit] =
ZIO.attemptBlocking(underlying.synchronous().invalidateAll(keys.asJava))
}

final class LoadingCache[R, Key, Value] private[zcaffeine] (
Expand Down Expand Up @@ -143,7 +173,18 @@ final class LoadingCache[R, Key, Value] private[zcaffeine] (
* the current or newly computed values for the given keys
*/
def getAll(keys: Set[Key]): Task[Map[Key, Value]] =
ZIO.fromCompletableFuture(underlying.getAll(keys.asJava)).map(_.asScala.toMap)
getAllInternal(keys)

/**
* Returns the value associated with the given key in this cache, or asynchronously computes it, based on the
* `loadAll` function specified while building the cache if defined, or falls back to `loadOne`.
* @param keys
* the keys whose associated values are to be returned
* @return
* the current or newly computed values for the given keys
*/
def getAll(keys: Key*): Task[Map[Key, Value]] =
getAllInternal(keys)

/**
* Refreshes the value associated with the given key in this cache, based on the `reloadOne` function specified while
Expand All @@ -165,5 +206,22 @@ final class LoadingCache[R, Key, Value] private[zcaffeine] (
* the refreshed values for the given keys
*/
def refreshAll(keys: Set[Key]): Task[Map[Key, Value]] =
refreshAllInternal(keys)

/**
* Refreshes the values associated with the given keys in this cache, based on the `reloadOne` function specified
* while building the cache if defined, or falls back to `loadOne`.
* @param keys
* the keys whose associated values are to be refreshed
* @return
* the refreshed values for the given keys
*/
def refreshAll(keys: Key*): Task[Map[Key, Value]] =
refreshAllInternal(keys)

private def getAllInternal(keys: Iterable[Key]): Task[Map[Key, Value]] =
ZIO.fromCompletableFuture(underlying.getAll(keys.asJava)).map(_.asScala.toMap)

private def refreshAllInternal(keys: Iterable[Key]): Task[Map[Key, Value]] =
ZIO.fromCompletableFuture(underlying.synchronous().refreshAll(keys.asJava)).map(_.asScala.toMap)
}
16 changes: 7 additions & 9 deletions src/test/scala/com/stuart/zcaffeine/CacheSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ object CacheSpec extends ZIOSpecDefault {
zcaffeine <- ZCaffeine[TestEnvironment, String, Int]()
cache <- zcaffeine.build()
missing <- cache.getIfPresent("key")
created <- cache.get("key", key => ZIO.succeed(key.length))
unchanged <- cache.get("key", key => ZIO.succeed(key.length + 1))
created <- cache.get("key")(key => ZIO.succeed(key.length))
unchanged <- cache.get("key")(key => ZIO.succeed(key.length + 1))
present <- cache.getIfPresent("key")
} yield assert(missing)(isNone) &&
assert(created)(equalTo(3)) &&
Expand All @@ -27,11 +27,9 @@ object CacheSpec extends ZIOSpecDefault {
cache <- zcaffeine.build()
missing1 <- cache.getIfPresent("key")
missing2 <- cache.getIfPresent("key2")
created <- cache.getAll(Set("key", "key2"), keys => ZIO.succeed(keys.map(key => key -> key.length).toMap))
unchanged <- cache.getAll(
Set("key", "key2"),
keys => ZIO.succeed(keys.map(key => key -> (key.length + 1)).toMap)
)
created <- cache.getAll("key", "key2")(keys => ZIO.succeed(keys.map(key => key -> key.length).toMap))
unchanged <-
cache.getAll(Set("key", "key2"))(keys => ZIO.succeed(keys.map(key => key -> (key.length + 1)).toMap))
} yield assert(missing1)(isNone) &&
assert(missing2)(isNone) &&
assert(created)(equalTo(Map("key" -> 3, "key2" -> 4))) &&
Expand All @@ -54,13 +52,13 @@ object CacheSpec extends ZIOSpecDefault {
for {
zcaffeine <- ZCaffeine[TestEnvironment, String, Int]()
cache <- zcaffeine.build()
_ <- cache.getAll(Set("key", "key2"), keys => ZIO.succeed(keys.map(key => key -> key.length).toMap))
_ <- cache.getAll("key", "key2")(keys => ZIO.succeed(keys.map(key => key -> key.length).toMap))
_ <- cache.invalidate("key")
keyMissing <- cache.getIfPresent("key")
key2Present <- cache.getIfPresent("key2")
_ <- cache.invalidateAll
key2Missing <- cache.getIfPresent("key2")
_ <- cache.getAll(Set("key", "key2"), keys => ZIO.succeed(keys.map(key => key -> key.length).toMap))
_ <- cache.getAll(Set("key", "key2"))(keys => ZIO.succeed(keys.map(key => key -> key.length).toMap))
_ <- cache.invalidateAll(Set("key", "key2"))
keyMissingAgain <- cache.getIfPresent("key")
key2MissingAgain <- cache.getIfPresent("key2")
Expand Down

0 comments on commit 454db23

Please sign in to comment.