diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..6c9861f --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,23 @@ +version=2.4.1 +maxColumn = 80 +align = more +assumeStandardLibraryStripMargin = true +importSelectors = singleLine +indentYieldKeyword = false +newlines.alwaysBeforeTopLevelStatements = true +project.git = true + +rewrite.rules = [ + prefercurlyfors + redundantbraces + redundantparens + sortimports + sortmodifiers +] +rewrite.redundantBraces.stringInterpolation = true + +rewriteTokens = { + "⇒": "=>" + "←": "<-" + "→": "->" +} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index e26448c..17311ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ stages: jobs: include: - stage: test - script: sbt clean coverage +test +doc && sbt coverageReport coveralls + script: sbt clean scalafmtSbtCheck scalafmtCheck coverage +test +doc && sbt coverageReport coveralls - stage: publish script: sbt +publish diff --git a/build.sbt b/build.sbt index 70d3a79..e89983a 100644 --- a/build.sbt +++ b/build.sbt @@ -14,85 +14,92 @@ scalaVersion := "2.11.12" libraryDependencies ++= Seq( - "com.github.ben-manes.caffeine" % "caffeine" % CaffeineVersion.value, - "org.scala-lang.modules" %% "scala-java8-compat" % "0.9.1", - "com.google.code.findbugs" % "jsr305" % "3.0.2" % "provided", - "org.scalactic" %% "scalactic" % "3.2.0-M4" % "test", - "org.scalatest" %% "scalatest" % "3.2.0-M4" % "test" + "com.github.ben-manes.caffeine" % "caffeine" % CaffeineVersion.value, + "org.scala-lang.modules" %% "scala-java8-compat" % "0.9.1", + "com.google.code.findbugs" % "jsr305" % "3.0.2" % "provided", + "org.scalactic" %% "scalactic" % "3.2.0-M4" % "test", + "org.scalatest" %% "scalatest" % "3.2.0-M4" % "test" ) +scalafmtOnCompile := true + scalacOptions ++= Seq("-target:jvm-1.8") scalacOptions ++= (CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, 11)) => - Seq( - "-deprecation", - "-encoding", "UTF-8", - "-feature", - "-language:existentials", - "-language:higherKinds", - "-language:implicitConversions", - "-unchecked", - "-Xfatal-warnings", - "-Xlint", - "-Yno-adapted-args", - "-Ywarn-dead-code", - "-Ywarn-numeric-widen", - "-Ywarn-value-discard", - "-Xfuture", - "-Ywarn-unused-import" - ) - case Some((2, 12)) => - Seq( - "-deprecation", // Emit warning and location for usages of deprecated APIs. - "-encoding", "utf-8", // Specify character encoding used by source files. - "-explaintypes", // Explain type errors in more detail. - "-feature", // Emit warning and location for usages of features that should be imported explicitly. - "-language:existentials", // Existential types (besides wildcard types) can be written and inferred - "-language:experimental.macros", // Allow macro definition (besides implementation and application) - "-language:higherKinds", // Allow higher-kinded types - "-language:implicitConversions", // Allow definition of implicit functions called views - "-unchecked", // Enable additional warnings where generated code depends on assumptions. - "-Xcheckinit", // Wrap field accessors to throw an exception on uninitialized access. - "-Xfatal-warnings", // Fail the compilation if there are any warnings. - "-Xfuture", // Turn on future language features. - "-Xlint:adapted-args", // Warn if an argument list is modified to match the receiver. - "-Xlint:by-name-right-associative", // By-name parameter of right associative operator. - "-Xlint:constant", // Evaluation of a constant arithmetic expression results in an error. - "-Xlint:delayedinit-select", // Selecting member of DelayedInit. - "-Xlint:doc-detached", // A Scaladoc comment appears to be detached from its element. - "-Xlint:inaccessible", // Warn about inaccessible types in method signatures. - "-Xlint:infer-any", // Warn when a type argument is inferred to be `Any`. - "-Xlint:missing-interpolator", // A string literal appears to be missing an interpolator id. - "-Xlint:nullary-override", // Warn when non-nullary `def f()' overrides nullary `def f'. - "-Xlint:nullary-unit", // Warn when nullary methods return Unit. - "-Xlint:option-implicit", // Option.apply used implicit view. - "-Xlint:package-object-classes", // Class or object defined in package object. - "-Xlint:poly-implicit-overload", // Parameterized overloaded implicit methods are not visible as view bounds. - "-Xlint:private-shadow", // A private field (or class parameter) shadows a superclass field. - "-Xlint:stars-align", // Pattern sequence wildcard must align with sequence component. - "-Xlint:type-parameter-shadow", // A local type parameter shadows a type already in scope. - "-Xlint:unsound-match", // Pattern match may not be typesafe. - "-Yno-adapted-args", // Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver. - "-Ypartial-unification", // Enable partial unification in type constructor inference - "-Ywarn-dead-code", // Warn when dead code is identified. - "-Ywarn-extra-implicit", // Warn when more than one implicit parameter section is defined. - "-Ywarn-inaccessible", // Warn about inaccessible types in method signatures. - "-Ywarn-infer-any", // Warn when a type argument is inferred to be `Any`. - "-Ywarn-nullary-override", // Warn when non-nullary `def f()' overrides nullary `def f'. - "-Ywarn-nullary-unit", // Warn when nullary methods return Unit. - "-Ywarn-numeric-widen", // Warn when numerics are widened. - "-Ywarn-unused:implicits", // Warn if an implicit parameter is unused. - "-Ywarn-unused:imports", // Warn if an import selector is not referenced. - "-Ywarn-unused:locals", // Warn if a local definition is unused. - "-Ywarn-unused:params", // Warn if a value parameter is unused. - "-Ywarn-unused:patvars", // Warn if a variable bound in a pattern is unused. - "-Ywarn-unused:privates", // Warn if a private member is unused. - "-Ywarn-value-discard" // Warn when non-Unit expression results are unused. - ) - case _ => - Nil - }) + case Some((2, 11)) => + Seq( + "-deprecation", + "-encoding", + "UTF-8", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-unchecked", + "-Xfatal-warnings", + "-Xlint", + "-Yno-adapted-args", + "-Ywarn-dead-code", + "-Ywarn-numeric-widen", + "-Ywarn-value-discard", + "-Xfuture", + "-Ywarn-unused-import" + ) + case Some((2, 12)) => + Seq( + "-deprecation", // Emit warning and location for usages of deprecated APIs. + "-encoding", + "utf-8", // Specify character encoding used by source files. + "-explaintypes", // Explain type errors in more detail. + "-feature", // Emit warning and location for usages of features that should be imported explicitly. + "-language:existentials", // Existential types (besides wildcard types) can be written and inferred + "-language:experimental.macros", // Allow macro definition (besides implementation and application) + "-language:higherKinds", // Allow higher-kinded types + "-language:implicitConversions", // Allow definition of implicit functions called views + "-unchecked", // Enable additional warnings where generated code depends on assumptions. + "-Xcheckinit", // Wrap field accessors to throw an exception on uninitialized access. + "-Xfatal-warnings", // Fail the compilation if there are any warnings. + "-Xfuture", // Turn on future language features. + "-Xlint:adapted-args", // Warn if an argument list is modified to match the receiver. + "-Xlint:by-name-right-associative", // By-name parameter of right associative operator. + "-Xlint:constant", // Evaluation of a constant arithmetic expression results in an error. + "-Xlint:delayedinit-select", // Selecting member of DelayedInit. + "-Xlint:doc-detached", // A Scaladoc comment appears to be detached from its element. + "-Xlint:inaccessible", // Warn about inaccessible types in method signatures. + "-Xlint:infer-any", // Warn when a type argument is inferred to be `Any`. + "-Xlint:missing-interpolator", // A string literal appears to be missing an interpolator id. + "-Xlint:nullary-override", // Warn when non-nullary `def f()' overrides nullary `def f'. + "-Xlint:nullary-unit", // Warn when nullary methods return Unit. + "-Xlint:option-implicit", // Option.apply used implicit view. + "-Xlint:package-object-classes", // Class or object defined in package object. + "-Xlint:poly-implicit-overload", // Parameterized overloaded implicit methods are not visible as view bounds. + "-Xlint:private-shadow", // A private field (or class parameter) shadows a superclass field. + "-Xlint:stars-align", // Pattern sequence wildcard must align with sequence component. + "-Xlint:type-parameter-shadow", // A local type parameter shadows a type already in scope. + "-Xlint:unsound-match", // Pattern match may not be typesafe. + "-Yno-adapted-args", // Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver. + "-Ypartial-unification", // Enable partial unification in type constructor inference + "-Ywarn-dead-code", // Warn when dead code is identified. + "-Ywarn-extra-implicit", // Warn when more than one implicit parameter section is defined. + "-Ywarn-inaccessible", // Warn about inaccessible types in method signatures. + "-Ywarn-infer-any", // Warn when a type argument is inferred to be `Any`. + "-Ywarn-nullary-override", // Warn when non-nullary `def f()' overrides nullary `def f'. + "-Ywarn-nullary-unit", // Warn when nullary methods return Unit. + "-Ywarn-numeric-widen", // Warn when numerics are widened. + "-Ywarn-unused:implicits", // Warn if an implicit parameter is unused. + "-Ywarn-unused:imports", // Warn if an import selector is not referenced. + "-Ywarn-unused:locals", // Warn if a local definition is unused. + "-Ywarn-unused:params", // Warn if a value parameter is unused. + "-Ywarn-unused:patvars", // Warn if a variable bound in a pattern is unused. + "-Ywarn-unused:privates", // Warn if a private member is unused. + "-Ywarn-value-discard" // Warn when non-Unit expression results are unused. + ) + case _ => + Nil + }) -scalacOptions in (Compile, console) --= Seq("-Ywarn-unused:imports", "-Xfatal-warnings") +scalacOptions in (Compile, console) --= Seq( + "-Ywarn-unused:imports", + "-Xfatal-warnings" +) diff --git a/formatting.sbt b/formatting.sbt deleted file mode 100644 index e7ad1de..0000000 --- a/formatting.sbt +++ /dev/null @@ -1,7 +0,0 @@ -import com.typesafe.sbt.SbtScalariform.ScalariformKeys - -import scalariform.formatter.preferences._ - -ScalariformKeys.preferences := ScalariformKeys.preferences.value - .setPreference(DoubleIndentConstructorArguments, true) - .setPreference(DanglingCloseParenthesis, Force) \ No newline at end of file diff --git a/project/CaffeineVersion.scala b/project/CaffeineVersion.scala index 9feea5f..e9a7ade 100644 --- a/project/CaffeineVersion.scala +++ b/project/CaffeineVersion.scala @@ -1,5 +1,5 @@ object CaffeineVersion { - + val value: String = "2.8.1" } diff --git a/project/TravisCredentials.scala b/project/TravisCredentials.scala index 8108374..40eaf63 100644 --- a/project/TravisCredentials.scala +++ b/project/TravisCredentials.scala @@ -2,14 +2,15 @@ import sbt.Keys._ import sbt._ object TravisCredentials { - def updateCredentials() = (for { - username <- Option(System.getenv().get("SONATYPE_USERNAME")) - password <- Option(System.getenv().get("SONATYPE_PASSWORD")) - } yield - credentials += Credentials( - "Sonatype Nexus Repository Manager", - "oss.sonatype.org", - username, - password) - ).getOrElse(credentials ++= Seq()) -} \ No newline at end of file + + def updateCredentials() = + (for { + username <- Option(System.getenv().get("SONATYPE_USERNAME")) + password <- Option(System.getenv().get("SONATYPE_PASSWORD")) + } yield credentials += Credentials( + "Sonatype Nexus Repository Manager", + "oss.sonatype.org", + username, + password + )).getOrElse(credentials ++= Seq()) +} diff --git a/project/plugins.sbt b/project/plugins.sbt index cb19926..22e4bff 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,7 +2,7 @@ addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1") addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.2.7") -addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.3.2") addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.13") diff --git a/release.sbt b/release.sbt index 720b4c7..0ed3395 100644 --- a/release.sbt +++ b/release.sbt @@ -8,7 +8,10 @@ releaseProcess := Seq[ReleaseStep]( setReleaseVersion, commitReleaseVersion, tagRelease, - ReleaseStep(action = Command.process("publishSigned", _), enableCrossBuild = true), + ReleaseStep( + action = Command.process("publishSigned", _), + enableCrossBuild = true + ), setNextVersion, commitNextVersion, ReleaseStep(action = Command.process("sonatypeReleaseAll", _)), @@ -24,7 +27,7 @@ publishMavenStyle := true TravisCredentials.updateCredentials() pomExtra in Global := { - + scm:git:github.com/blemale/scaffeine.git scm:git:git@github.com:blemale/scaffeine.git github.com/blemale/scaffeine.git diff --git a/scaladoc.sbt b/scaladoc.sbt index fd1e097..0407dd6 100644 --- a/scaladoc.sbt +++ b/scaladoc.sbt @@ -5,21 +5,25 @@ autoAPIMappings := true apiMappings ++= { val cp = (fullClasspath in Compile).value - val bootClasspath = System.getProperty("sun.boot.class.path").split(":").map(file(_)) + val bootClasspath = + System.getProperty("sun.boot.class.path").split(":").map(file(_)) def findManagedDependency(organization: String, name: String): File = (for { - entry <- cp - module <- entry.get(moduleID.key) - if module.organization == organization - if module.name.startsWith(name) - jarFile = entry.data - } yield jarFile - ).head + entry <- cp + module <- entry.get(moduleID.key) + if module.organization == organization + if module.name.startsWith(name) + jarFile = entry.data + } yield jarFile).head Map( - bootClasspath.find(_.getPath.endsWith("rt.jar")).get -> url("http://docs.oracle.com/javase/8/docs/api/"), - findManagedDependency("com.github.ben-manes.caffeine", "caffeine") -> url(s"http://static.javadoc.io/com.github.ben-manes.caffeine/caffeine/${CaffeineVersion.value}/") + bootClasspath.find(_.getPath.endsWith("rt.jar")).get -> url( + "http://docs.oracle.com/javase/8/docs/api/" + ), + findManagedDependency("com.github.ben-manes.caffeine", "caffeine") -> url( + s"http://static.javadoc.io/com.github.ben-manes.caffeine/caffeine/${CaffeineVersion.value}/" + ) ) } @@ -27,28 +31,37 @@ lazy val fixJavaLinksTask = taskKey[Unit]( "Fix Java links in scaladoc" ) -val jdkApiLink = """\"(http://docs\.oracle\.com/javase/8/docs/api/index\.html)#([^"]*)\"""".r -val caffeineApiLink = ("""\"(http://static\.javadoc\.io/com\.github\.ben-manes\.caffeine/caffeine/""" + CaffeineVersion.value.replace(".", "\\.") + """/)index\.html#([^"]*)\"""").r +val jdkApiLink = + """\"(http://docs\.oracle\.com/javase/8/docs/api/index\.html)#([^"]*)\"""".r + +val caffeineApiLink = + ("""\"(http://static\.javadoc\.io/com\.github\.ben-manes\.caffeine/caffeine/""" + CaffeineVersion.value + .replace(".", "\\.") + """/)index\.html#([^"]*)\"""").r def hasJavadocLink(f: File): Boolean = { val content = IO.read(f) (jdkApiLink findFirstIn content).nonEmpty || (caffeineApiLink findFirstIn content).nonEmpty } -val fixJdkLinks: Match => String = m => m.group(1) + "?" + m.group(2).replace(".", "/") + ".html" +val fixJdkLinks: Match => String = m => + m.group(1) + "?" + m.group(2).replace(".", "/") + ".html" val replaceJdkLinks: String => String = jdkApiLink.replaceAllIn(_, fixJdkLinks) -val fixCaffeineLinks: Match => String = m => m.group(1) + m.group(2).replace(".", "/") + ".html" -val replaceCaffeineLinks: String => String = caffeineApiLink.replaceAllIn(_, fixCaffeineLinks) +val fixCaffeineLinks: Match => String = m => + m.group(1) + m.group(2).replace(".", "/") + ".html" + +val replaceCaffeineLinks: String => String = + caffeineApiLink.replaceAllIn(_, fixCaffeineLinks) fixJavaLinksTask := { val log = streams.value.log log.info("Fixing Java links") - val t = (target in(Compile, doc)).value + val t = (target in (Compile, doc)).value (t ** "*.html").get.filter(hasJavadocLink).foreach { f => try { log.info("Fixing " + f) - val fixedContent = replaceCaffeineLinks.andThen(replaceJdkLinks).apply(IO.read(f)) + val fixedContent = + replaceCaffeineLinks.andThen(replaceJdkLinks).apply(IO.read(f)) IO.write(f, fixedContent) } catch { case NonFatal(e) => log.error(s"Failed to replace links in $f") diff --git a/src/main/scala/com/github/blemale/scaffeine/AsyncCache.scala b/src/main/scala/com/github/blemale/scaffeine/AsyncCache.scala index 3a5c9a8..e26d724 100644 --- a/src/main/scala/com/github/blemale/scaffeine/AsyncCache.scala +++ b/src/main/scala/com/github/blemale/scaffeine/AsyncCache.scala @@ -2,124 +2,145 @@ package com.github.blemale.scaffeine import java.util.concurrent.Executor -import com.github.benmanes.caffeine.cache.{ AsyncCache => CaffeineAsyncCache } +import com.github.benmanes.caffeine.cache.{AsyncCache => CaffeineAsyncCache} import scala.collection.JavaConverters._ import scala.compat.java8.FunctionConverters._ import scala.compat.java8.FutureConverters._ -import scala.concurrent.{ ExecutionContext, Future } +import scala.concurrent.{ExecutionContext, Future} object AsyncCache { + def apply[K, V](asyncCache: CaffeineAsyncCache[K, V]): AsyncCache[K, V] = new AsyncCache(asyncCache) } class AsyncCache[K, V](val underlying: CaffeineAsyncCache[K, V]) { - private[this] implicit val ec: ExecutionContext = DirectExecutionContext + implicit private[this] val ec: ExecutionContext = DirectExecutionContext /** - * Returns the future associated with `key` in this cache, or `None` if there is no - * cached future for `key`. - * - * @param key key whose associated value is to be returned - * @return an option containing the current (existing or computed) future value to which the - * specified key is mapped, or `None` if this map contains no mapping for the key - */ + * Returns the future associated with `key` in this cache, or `None` if there is no + * cached future for `key`. + * + * @param key key whose associated value is to be returned + * @return an option containing the current (existing or computed) future value to which the + * specified key is mapped, or `None` if this map contains no mapping for the key + */ def getIfPresent(key: K): Option[Future[V]] = Option(underlying.getIfPresent(key)).map(_.toScala) /** - * Returns the future associated with `key` in this cache, obtaining that value from - * `mappingFunction` if necessary. This method provides a simple substitute for the - * conventional "if cached, return; otherwise create, cache and return" pattern. - * - * @param key key with which the specified value is to be associated - * @param mappingFunction the function to asynchronously compute a value - * @return the current (existing or computed) future value associated with the specified key - */ + * Returns the future associated with `key` in this cache, obtaining that value from + * `mappingFunction` if necessary. This method provides a simple substitute for the + * conventional "if cached, return; otherwise create, cache and return" pattern. + * + * @param key key with which the specified value is to be associated + * @param mappingFunction the function to asynchronously compute a value + * @return the current (existing or computed) future value associated with the specified key + */ def get(key: K, mappingFunction: K => V): Future[V] = underlying.get(key, asJavaFunction(mappingFunction)).toScala /** - * Returns the future associated with `key` in this cache, obtaining that value from - * `mappingFunction` if necessary. This method provides a simple substitute for the - * conventional "if cached, return; otherwise create, cache and return" pattern. - * - * @param key key with which the specified value is to be associated - * @param mappingFunction the function to asynchronously compute a value - * @return the current (existing or computed) future value associated with the specified key - * @throws java.lang.RuntimeException or Error if the mappingFunction does when constructing the future, - * in which case the mapping is left unestablished - */ + * Returns the future associated with `key` in this cache, obtaining that value from + * `mappingFunction` if necessary. This method provides a simple substitute for the + * conventional "if cached, return; otherwise create, cache and return" pattern. + * + * @param key key with which the specified value is to be associated + * @param mappingFunction the function to asynchronously compute a value + * @return the current (existing or computed) future value associated with the specified key + * @throws java.lang.RuntimeException or Error if the mappingFunction does when constructing the future, + * in which case the mapping is left unestablished + */ def getFuture(key: K, mappingFunction: K => Future[V]): Future[V] = - underlying.get( - key, - asJavaBiFunction((k: K, _: Executor) => mappingFunction(k).toJava.toCompletableFuture) - ).toScala + underlying + .get( + key, + asJavaBiFunction((k: K, _: Executor) => + mappingFunction(k).toJava.toCompletableFuture + ) + ) + .toScala /** - * Returns the future of a map of the values associated with `keys`, creating or retrieving - * those values if necessary. The returned map contains entries that were already cached, combined - * with newly loaded entries. If the any of the asynchronous computations fail, those entries will - * be automatically removed from this cache. - * - * A single request to the `mappingFunction` is performed for all keys which are not already - * present in the cache. - * - * @param keys the keys whose associated values are to be returned - * @param mappingFunction the function to asynchronously compute the values - * @return the future containing an unmodifiable mapping of keys to values for the specified keys - * in this cache - * @throws java.lang.RuntimeException or Error if the mappingFunction does when constructing the future, - * in which case the mapping is left unestablished - */ - def getAll(keys: Iterable[K], mappingFunction: Iterable[K] => Map[K, V]): Future[Map[K, V]] = - underlying.getAll( - keys.asJava, - asJavaFunction((ks: java.lang.Iterable[_ <: K]) => mappingFunction(ks.asScala).asJava) - ).toScala.map(_.asScala.toMap) + * Returns the future of a map of the values associated with `keys`, creating or retrieving + * those values if necessary. The returned map contains entries that were already cached, combined + * with newly loaded entries. If the any of the asynchronous computations fail, those entries will + * be automatically removed from this cache. + * + * A single request to the `mappingFunction` is performed for all keys which are not already + * present in the cache. + * + * @param keys the keys whose associated values are to be returned + * @param mappingFunction the function to asynchronously compute the values + * @return the future containing an unmodifiable mapping of keys to values for the specified keys + * in this cache + * @throws java.lang.RuntimeException or Error if the mappingFunction does when constructing the future, + * in which case the mapping is left unestablished + */ + def getAll( + keys: Iterable[K], + mappingFunction: Iterable[K] => Map[K, V] + ): Future[Map[K, V]] = + underlying + .getAll( + keys.asJava, + asJavaFunction((ks: java.lang.Iterable[_ <: K]) => + mappingFunction(ks.asScala).asJava + ) + ) + .toScala + .map(_.asScala.toMap) /** - * Returns the future of a map of the values associated with `keys`, creating or retrieving - * those values if necessary. The returned map contains entries that were already cached, combined - * with newly loaded entries. If the any of the asynchronous computations fail, those entries will - * be automatically removed from this cache. - * - * A single request to the `mappingFunction` is performed for all keys which are not already - * present in the cache. - * - * @param keys the keys whose associated values are to be returned - * @param mappingFunction the function to asynchronously compute the values - * @return the future containing an unmodifiable mapping of keys to values for the specified keys - * in this cache - * @throws java.lang.RuntimeException or Error if the mappingFunction does when constructing the future, - * in which case the mapping is left unestablished - */ - def getAllFuture(keys: Iterable[K], mappingFunction: Iterable[K] => Future[Map[K, V]]): Future[Map[K, V]] = - underlying.getAll( - keys.asJava, - asJavaBiFunction((ks: java.lang.Iterable[_ <: K], _: Executor) => mappingFunction(ks.asScala).map(_.asJava).toJava.toCompletableFuture) - ).toScala.map(_.asScala.toMap) + * Returns the future of a map of the values associated with `keys`, creating or retrieving + * those values if necessary. The returned map contains entries that were already cached, combined + * with newly loaded entries. If the any of the asynchronous computations fail, those entries will + * be automatically removed from this cache. + * + * A single request to the `mappingFunction` is performed for all keys which are not already + * present in the cache. + * + * @param keys the keys whose associated values are to be returned + * @param mappingFunction the function to asynchronously compute the values + * @return the future containing an unmodifiable mapping of keys to values for the specified keys + * in this cache + * @throws java.lang.RuntimeException or Error if the mappingFunction does when constructing the future, + * in which case the mapping is left unestablished + */ + def getAllFuture( + keys: Iterable[K], + mappingFunction: Iterable[K] => Future[Map[K, V]] + ): Future[Map[K, V]] = + underlying + .getAll( + keys.asJava, + asJavaBiFunction((ks: java.lang.Iterable[_ <: K], _: Executor) => + mappingFunction(ks.asScala).map(_.asJava).toJava.toCompletableFuture + ) + ) + .toScala + .map(_.asScala.toMap) /** - * Associates `value` with `key` in this cache. If the cache previously contained a - * value associated with `key`, the old value is replaced by `value`. If the - * asynchronous computation fails, the entry will be automatically removed. - * - * @param key key with which the specified value is to be associated - * @param valueFuture value to be associated with the specified key - */ + * Associates `value` with `key` in this cache. If the cache previously contained a + * value associated with `key`, the old value is replaced by `value`. If the + * asynchronous computation fails, the entry will be automatically removed. + * + * @param key key with which the specified value is to be associated + * @param valueFuture value to be associated with the specified key + */ def put(key: K, valueFuture: Future[V]): Unit = underlying.put(key, valueFuture.toJava.toCompletableFuture) /** - * Returns a view of the entries stored in this cache as a synchronous [[Cache]]. A - * mapping is not present if the value is currently being loaded. Modifications made to the - * synchronous cache directly affect the asynchronous cache. If a modification is made to a - * mapping that is currently loading, the operation blocks until the computation completes. - * - * @return a thread-safe synchronous view of this cache - */ + * Returns a view of the entries stored in this cache as a synchronous [[Cache]]. A + * mapping is not present if the value is currently being loaded. Modifications made to the + * synchronous cache directly affect the asynchronous cache. If a modification is made to a + * mapping that is currently loading, the operation blocks until the computation completes. + * + * @return a thread-safe synchronous view of this cache + */ def synchronous(): Cache[K, V] = Cache(underlying.synchronous()) diff --git a/src/main/scala/com/github/blemale/scaffeine/AsyncLoadingCache.scala b/src/main/scala/com/github/blemale/scaffeine/AsyncLoadingCache.scala index f980eb1..ebb2d13 100644 --- a/src/main/scala/com/github/blemale/scaffeine/AsyncLoadingCache.scala +++ b/src/main/scala/com/github/blemale/scaffeine/AsyncLoadingCache.scala @@ -1,53 +1,58 @@ package com.github.blemale.scaffeine -import com.github.benmanes.caffeine.cache.{ AsyncLoadingCache => CaffeineAsyncLoadingCache } +import com.github.benmanes.caffeine.cache.{AsyncLoadingCache => CaffeineAsyncLoadingCache} import scala.collection.JavaConverters._ import scala.compat.java8.FutureConverters._ -import scala.concurrent.{ ExecutionContext, Future } +import scala.concurrent.{ExecutionContext, Future} object AsyncLoadingCache { - def apply[K, V](asyncLoadingCache: CaffeineAsyncLoadingCache[K, V]): AsyncLoadingCache[K, V] = + + def apply[K, V]( + asyncLoadingCache: CaffeineAsyncLoadingCache[K, V] + ): AsyncLoadingCache[K, V] = new AsyncLoadingCache(asyncLoadingCache) } -class AsyncLoadingCache[K, V](override val underlying: CaffeineAsyncLoadingCache[K, V]) extends AsyncCache[K, V](underlying) { - private[this] implicit val ec: ExecutionContext = DirectExecutionContext +class AsyncLoadingCache[K, V]( + override val underlying: CaffeineAsyncLoadingCache[K, V] +) extends AsyncCache[K, V](underlying) { + implicit private[this] val ec: ExecutionContext = DirectExecutionContext /** - * Returns the future associated with `key` in this cache, obtaining that value from - * `loader` if necessary. If the asynchronous computation fails, the entry - * will be automatically removed. - * - * @param key key with which the specified value is to be associated - * @return the current (existing or computed) future value associated with the specified key - * @throws java.lang.RuntimeException or Error if the `loader` does when constructing the future, - * in which case the mapping is left unestablished - */ + * Returns the future associated with `key` in this cache, obtaining that value from + * `loader` if necessary. If the asynchronous computation fails, the entry + * will be automatically removed. + * + * @param key key with which the specified value is to be associated + * @return the current (existing or computed) future value associated with the specified key + * @throws java.lang.RuntimeException or Error if the `loader` does when constructing the future, + * in which case the mapping is left unestablished + */ def get(key: K): Future[V] = underlying.get(key).toScala /** - * Returns the future of a map of the values associated with `keys`, creating or retrieving - * those values if necessary. The returned map contains entries that were already cached, combined - * with newly loaded entries. If the any of the asynchronous computations fail, those entries will - * be automatically removed. - * - * @param keys the keys whose associated values are to be returned - * @return the future containing an mapping of keys to values for the specified keys in this cache - * @throws java.lang.RuntimeException or Error if the `loader` does so - */ + * Returns the future of a map of the values associated with `keys`, creating or retrieving + * those values if necessary. The returned map contains entries that were already cached, combined + * with newly loaded entries. If the any of the asynchronous computations fail, those entries will + * be automatically removed. + * + * @param keys the keys whose associated values are to be returned + * @return the future containing an mapping of keys to values for the specified keys in this cache + * @throws java.lang.RuntimeException or Error if the `loader` does so + */ def getAll(keys: Iterable[K]): Future[Map[K, V]] = underlying.getAll(keys.asJava).toScala.map(_.asScala.toMap) /** - * Returns a view of the entries stored in this cache as a synchronous [[LoadingCache]]. A - * mapping is not present if the value is currently being loaded. Modifications made to the - * synchronous cache directly affect the asynchronous cache. If a modification is made to a - * mapping that is currently loading, the operation blocks until the computation completes. - * - * @return a thread-safe synchronous view of this cache - */ + * Returns a view of the entries stored in this cache as a synchronous [[LoadingCache]]. A + * mapping is not present if the value is currently being loaded. Modifications made to the + * synchronous cache directly affect the asynchronous cache. If a modification is made to a + * mapping that is currently loading, the operation blocks until the computation completes. + * + * @return a thread-safe synchronous view of this cache + */ override def synchronous(): LoadingCache[K, V] = LoadingCache(underlying.synchronous()) diff --git a/src/main/scala/com/github/blemale/scaffeine/Cache.scala b/src/main/scala/com/github/blemale/scaffeine/Cache.scala index 61b8ec7..2c48b70 100644 --- a/src/main/scala/com/github/blemale/scaffeine/Cache.scala +++ b/src/main/scala/com/github/blemale/scaffeine/Cache.scala @@ -1,154 +1,164 @@ package com.github.blemale.scaffeine import com.github.benmanes.caffeine.cache.stats.CacheStats -import com.github.benmanes.caffeine.cache.{ Policy, Cache => CaffeineCache } +import com.github.benmanes.caffeine.cache.{Policy, Cache => CaffeineCache} import scala.collection.JavaConverters._ import scala.compat.java8.FunctionConverters._ object Cache { + def apply[K, V](cache: CaffeineCache[K, V]) = new Cache(cache) } class Cache[K, V](val underlying: CaffeineCache[K, V]) { + /** - * Returns the value associated with `key` in this cache, or `None` if there is no - * cached value for `key`. - * - * @param key key whose associated value is to be returned - * @return an option value containing the value to which the specified key is mapped, - * or `None` if this map contains no mapping for the key - */ + * Returns the value associated with `key` in this cache, or `None` if there is no + * cached value for `key`. + * + * @param key key whose associated value is to be returned + * @return an option value containing the value to which the specified key is mapped, + * or `None` if this map contains no mapping for the key + */ def getIfPresent(key: K): Option[V] = Option(underlying.getIfPresent(key)) /** - * Returns the value associated with `key` in this cache, obtaining that value from - * `mappingFunction` if necessary. This method provides a simple substitute for the - * conventional "if cached, return; otherwise create, cache and return" pattern. - * - * @param key key with which the specified value is to be associated - * @param mappingFunction the function to compute a value - * @return the current (existing or computed) value associated with the specified key - * @throws java.lang.IllegalStateException if the computation detectably attempts a recursive update to this - * cache that would otherwise never complete - * @throws java.lang.RuntimeException or Error if the mappingFunction does so, in which case the mapping is - * left unestablished - */ + * Returns the value associated with `key` in this cache, obtaining that value from + * `mappingFunction` if necessary. This method provides a simple substitute for the + * conventional "if cached, return; otherwise create, cache and return" pattern. + * + * @param key key with which the specified value is to be associated + * @param mappingFunction the function to compute a value + * @return the current (existing or computed) value associated with the specified key + * @throws java.lang.IllegalStateException if the computation detectably attempts a recursive update to this + * cache that would otherwise never complete + * @throws java.lang.RuntimeException or Error if the mappingFunction does so, in which case the mapping is + * left unestablished + */ def get(key: K, mappingFunction: K => V): V = underlying.get(key, mappingFunction.asJava) /** - * Returns a map of the values associated with `keys` in this cache. The returned map will - * only contain entries which are already present in the cache. - * - * @param keys the keys whose associated values are to be returned - * @return the mapping of keys to values for the specified keys found in this cache - */ + * Returns a map of the values associated with `keys` in this cache. The returned map will + * only contain entries which are already present in the cache. + * + * @param keys the keys whose associated values are to be returned + * @return the mapping of keys to values for the specified keys found in this cache + */ def getAllPresent(keys: Iterable[K]): Map[K, V] = underlying.getAllPresent(keys.asJava).asScala.toMap /** - * Returns the future of a map of the values associated with `keys`, creating or retrieving - * those values if necessary. The returned map contains entries that were already cached, combined - * with newly loaded entries. - * - * A single request to the `mappingFunction` is performed for all keys which are not already - * present in the cache. - * - * @param keys the keys whose associated values are to be returned - * @param mappingFunction the function to compute the values - * @return an unmodifiable mapping of keys to values for the specified keys in this cache - * @throws java.lang.RuntimeException or Error if the mappingFunction does so, in which - * case the mapping is left unestablished - */ - def getAll(keys: Iterable[K], mappingFunction: Iterable[K] => Map[K, V]): Map[K, V] = - underlying.getAll( - keys.asJava, - asJavaFunction((ks: java.lang.Iterable[_ <: K]) => mappingFunction(ks.asScala).asJava) - ).asScala.toMap + * Returns the future of a map of the values associated with `keys`, creating or retrieving + * those values if necessary. The returned map contains entries that were already cached, combined + * with newly loaded entries. + * + * A single request to the `mappingFunction` is performed for all keys which are not already + * present in the cache. + * + * @param keys the keys whose associated values are to be returned + * @param mappingFunction the function to compute the values + * @return an unmodifiable mapping of keys to values for the specified keys in this cache + * @throws java.lang.RuntimeException or Error if the mappingFunction does so, in which + * case the mapping is left unestablished + */ + def getAll( + keys: Iterable[K], + mappingFunction: Iterable[K] => Map[K, V] + ): Map[K, V] = + underlying + .getAll( + keys.asJava, + asJavaFunction((ks: java.lang.Iterable[_ <: K]) => + mappingFunction(ks.asScala).asJava + ) + ) + .asScala + .toMap /** - * Associates `value` with `key` in this cache. If the cache previously contained a - * value associated with `key`, the old value is replaced by `value`. - * - * @param key key with which the specified value is to be associated - * @param value value to be associated with the specified key - */ + * Associates `value` with `key` in this cache. If the cache previously contained a + * value associated with `key`, the old value is replaced by `value`. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + */ def put(key: K, value: V): Unit = underlying.put(key, value) /** - * Copies all of the mappings from the specified map to the cache. - * - * @param map mappings to be stored in this cache - */ + * Copies all of the mappings from the specified map to the cache. + * + * @param map mappings to be stored in this cache + */ def putAll(map: Map[K, V]): Unit = underlying.putAll(map.asJava) /** - * Discards any cached value for key `key`. - * - * @param key key whose mapping is to be removed from the cache - */ + * Discards any cached value for key `key`. + * + * @param key key whose mapping is to be removed from the cache + */ def invalidate(key: K): Unit = underlying.invalidate(key) /** - * Discards any cached values for keys `keys`. - * - * @param keys the keys whose associated values are to be removed - */ + * Discards any cached values for keys `keys`. + * + * @param keys the keys whose associated values are to be removed + */ def invalidateAll(keys: Iterable[K]): Unit = underlying.invalidateAll(keys.asJava) /** - * Discards all entries in the cache. - */ + * Discards all entries in the cache. + */ def invalidateAll(): Unit = underlying.invalidateAll() /** - * Returns the approximate number of entries in this cache. - * - * @return the estimated number of mappings - */ + * Returns the approximate number of entries in this cache. + * + * @return the estimated number of mappings + */ def estimatedSize(): Long = underlying.estimatedSize() /** - * Returns a current snapshot of this cache's cumulative statistics. All statistics are - * initialized to zero, and are monotonically increasing over the lifetime of the cache. - * - * @return the current snapshot of the statistics of this cache - */ + * Returns a current snapshot of this cache's cumulative statistics. All statistics are + * initialized to zero, and are monotonically increasing over the lifetime of the cache. + * + * @return the current snapshot of the statistics of this cache + */ def stats(): CacheStats = underlying.stats() /** - * Returns a view of the entries stored in this cache as a thread-safe map. Modifications made to - * the map directly affect the cache. - * - * @return a thread-safe view of this cache - */ + * Returns a view of the entries stored in this cache as a thread-safe map. Modifications made to + * the map directly affect the cache. + * + * @return a thread-safe view of this cache + */ def asMap(): collection.concurrent.Map[K, V] = underlying.asMap().asScala /** - * Performs any pending maintenance operations needed by the cache. Exactly which activities are - * performed -- if any -- is implementation-dependent. - */ + * Performs any pending maintenance operations needed by the cache. Exactly which activities are + * performed -- if any -- is implementation-dependent. + */ def cleanUp(): Unit = underlying.cleanUp() /** - * Returns access to inspect and perform low-level operations on this cache based on its runtime - * characteristics. These operations are optional and dependent on how the cache was constructed - * and what abilities the implementation exposes. - * - * @return access to inspect and perform advanced operations based on the cache's characteristics - */ + * Returns access to inspect and perform low-level operations on this cache based on its runtime + * characteristics. These operations are optional and dependent on how the cache was constructed + * and what abilities the implementation exposes. + * + * @return access to inspect and perform advanced operations based on the cache's characteristics + */ def policy(): Policy[K, V] = underlying.policy() diff --git a/src/main/scala/com/github/blemale/scaffeine/DirectExecutionContext.scala b/src/main/scala/com/github/blemale/scaffeine/DirectExecutionContext.scala index cca0341..8213d3b 100644 --- a/src/main/scala/com/github/blemale/scaffeine/DirectExecutionContext.scala +++ b/src/main/scala/com/github/blemale/scaffeine/DirectExecutionContext.scala @@ -4,6 +4,10 @@ import scala.concurrent.ExecutionContext private[scaffeine] object DirectExecutionContext extends ExecutionContext { override def execute(command: Runnable): Unit = command.run() + override def reportFailure(cause: Throwable): Unit = - throw new IllegalStateException("problem in scaffeine internal callback", cause) + throw new IllegalStateException( + "problem in scaffeine internal callback", + cause + ) } diff --git a/src/main/scala/com/github/blemale/scaffeine/LoadingCache.scala b/src/main/scala/com/github/blemale/scaffeine/LoadingCache.scala index ee49a3f..3492c2b 100644 --- a/src/main/scala/com/github/blemale/scaffeine/LoadingCache.scala +++ b/src/main/scala/com/github/blemale/scaffeine/LoadingCache.scala @@ -1,53 +1,58 @@ package com.github.blemale.scaffeine -import com.github.benmanes.caffeine.cache.{ LoadingCache => CaffeineLoadingCache } +import com.github.benmanes.caffeine.cache.{LoadingCache => CaffeineLoadingCache} import scala.collection.JavaConverters._ object LoadingCache { - def apply[K, V](loadingCache: CaffeineLoadingCache[K, V]): LoadingCache[K, V] = + + def apply[K, V]( + loadingCache: CaffeineLoadingCache[K, V] + ): LoadingCache[K, V] = new LoadingCache(loadingCache) } -class LoadingCache[K, V](override val underlying: CaffeineLoadingCache[K, V]) extends Cache(underlying) { +class LoadingCache[K, V](override val underlying: CaffeineLoadingCache[K, V]) + extends Cache(underlying) { + /** - * Returns the value associated with `key` in this cache, obtaining that value from - * `loader` if necessary. - *

- * If another call to this method is currently loading the value for `key`, this thread - * simply waits for that thread to finish and returns its loaded value. Note that multiple threads - * can concurrently load values for distinct keys. - * - * @param key key with which the specified value is to be associated - * @return the current (existing or computed) value associated with the specified key - * @throws java.lang.IllegalArgumentException if the computation detectably attempts a recursive update to this - * cache that would otherwise never complete - * @throws java.util.concurrent.CompletionException if a checked exception was thrown while loading the value - * @throws java.lang.RuntimeException or Error if the `CacheLoader` does so, in which case the mapping - * is left unestablished - */ + * Returns the value associated with `key` in this cache, obtaining that value from + * `loader` if necessary. + *

+ * If another call to this method is currently loading the value for `key`, this thread + * simply waits for that thread to finish and returns its loaded value. Note that multiple threads + * can concurrently load values for distinct keys. + * + * @param key key with which the specified value is to be associated + * @return the current (existing or computed) value associated with the specified key + * @throws java.lang.IllegalArgumentException if the computation detectably attempts a recursive update to this + * cache that would otherwise never complete + * @throws java.util.concurrent.CompletionException if a checked exception was thrown while loading the value + * @throws java.lang.RuntimeException or Error if the `CacheLoader` does so, in which case the mapping + * is left unestablished + */ def get(key: K): V = underlying.get(key) /** - * Returns a map of the values associated with `keys`, creating or retrieving those values - * if necessary. The returned map contains entries that were already cached, combined with newly - * loaded entries. - * - * @param keys the keys whose associated values are to be returned - * @return the mapping of keys to values for the specified keys in this cache - * @throws java.util.concurrent.CompletionException if a checked exception was thrown while loading the value - * @throws java.lang.RuntimeException or Error if the `loader` does so - */ + * Returns a map of the values associated with `keys`, creating or retrieving those values + * if necessary. The returned map contains entries that were already cached, combined with newly + * loaded entries. + * + * @param keys the keys whose associated values are to be returned + * @return the mapping of keys to values for the specified keys in this cache + * @throws java.util.concurrent.CompletionException if a checked exception was thrown while loading the value + * @throws java.lang.RuntimeException or Error if the `loader` does so + */ def getAll(keys: Iterable[K]): Map[K, V] = underlying.getAll(keys.asJava).asScala.toMap /** - * Loads a new value for the `key`, asynchronously. While the new value is loading the - * previous value (if any) will continue to be returned by `get(key)` unless it is evicted. - * - * @param key key with which a value may be associated - */ + * Loads a new value for the `key`, asynchronously. While the new value is loading the + * previous value (if any) will continue to be returned by `get(key)` unless it is evicted. + * + * @param key key with which a value may be associated + */ def refresh(key: K): Unit = underlying.refresh(key) diff --git a/src/main/scala/com/github/blemale/scaffeine/Scaffeine.scala b/src/main/scala/com/github/blemale/scaffeine/Scaffeine.scala index 5ad77d4..8c0cda1 100644 --- a/src/main/scala/com/github/blemale/scaffeine/Scaffeine.scala +++ b/src/main/scala/com/github/blemale/scaffeine/Scaffeine.scala @@ -1,7 +1,7 @@ package com.github.blemale.scaffeine -import java.util.concurrent.{ CompletableFuture, Executor } -import java.{ lang, util } +import java.util.concurrent.{CompletableFuture, Executor} +import java.{lang, util} import com.github.benmanes.caffeine import com.github.benmanes.caffeine.cache.Scheduler @@ -15,387 +15,437 @@ import scala.concurrent.Future import scala.concurrent.duration._ object Scaffeine { + /** - * Constructs a new `Scaffeine` instance with default settings, including strong keys, strong - * values, and no automatic eviction of any kind. - * - * @return a new instance with default settings - */ + * Constructs a new `Scaffeine` instance with default settings, including strong keys, strong + * values, and no automatic eviction of any kind. + * + * @return a new instance with default settings + */ def apply(): Scaffeine[Any, Any] = - Scaffeine(caffeine.cache.Caffeine.newBuilder().asInstanceOf[caffeine.cache.Caffeine[Any, Any]]) + Scaffeine( + caffeine.cache.Caffeine + .newBuilder() + .asInstanceOf[caffeine.cache.Caffeine[Any, Any]] + ) /** - * Constructs a new `Scaffeine` instance with the settings specified in `spec`. - * - * @param spec an instance of [[com.github.benmanes.caffeine.cache.CaffeineSpec]] - * @return a new instance with the specification's settings - */ + * Constructs a new `Scaffeine` instance with the settings specified in `spec`. + * + * @param spec an instance of [[com.github.benmanes.caffeine.cache.CaffeineSpec]] + * @return a new instance with the specification's settings + */ def apply(spec: caffeine.cache.CaffeineSpec): Scaffeine[Any, Any] = - Scaffeine(caffeine.cache.Caffeine.from(spec).asInstanceOf[caffeine.cache.Caffeine[Any, Any]]) + Scaffeine( + caffeine.cache.Caffeine + .from(spec) + .asInstanceOf[caffeine.cache.Caffeine[Any, Any]] + ) /** - * Constructs a new `Scaffeine` instance with the settings specified in `spec`. - * - * @param spec a [[java.lang.String]] in the format specified by - * [[com.github.benmanes.caffeine.cache.CaffeineSpec]] - * @return a new instance with the specification's settings - */ + * Constructs a new `Scaffeine` instance with the settings specified in `spec`. + * + * @param spec a [[java.lang.String]] in the format specified by + * [[com.github.benmanes.caffeine.cache.CaffeineSpec]] + * @return a new instance with the specification's settings + */ def apply(spec: String): Scaffeine[Any, Any] = - Scaffeine(caffeine.cache.Caffeine.from(spec).asInstanceOf[caffeine.cache.Caffeine[Any, Any]]) + Scaffeine( + caffeine.cache.Caffeine + .from(spec) + .asInstanceOf[caffeine.cache.Caffeine[Any, Any]] + ) } case class Scaffeine[K, V](underlying: caffeine.cache.Caffeine[K, V]) { + /** - * Sets the minimum total size for the internal hash tables. - * - * @param initialCapacity minimum total size for the internal hash tables - * @return this builder instance - * @throws java.lang.IllegalArgumentException if initialCapacity - * @throws java.lang.IllegalStateException if an initial capacity was already set - */ + * Sets the minimum total size for the internal hash tables. + * + * @param initialCapacity minimum total size for the internal hash tables + * @return this builder instance + * @throws java.lang.IllegalArgumentException if initialCapacity + * @throws java.lang.IllegalStateException if an initial capacity was already set + */ def initialCapacity(initialCapacity: Int): Scaffeine[K, V] = Scaffeine(underlying.initialCapacity(initialCapacity)) /** - * Specifies the executor to use when running asynchronous tasks. - * - * @param executor the executor to use for asynchronous execution - * @return this builder instance - */ + * Specifies the executor to use when running asynchronous tasks. + * + * @param executor the executor to use for asynchronous execution + * @return this builder instance + */ def executor(executor: Executor): Scaffeine[K, V] = Scaffeine(underlying.executor(executor)) /** - * Specifies the maximum number of entries the cache may contain. - * - * @param maximumSize the maximum size of the cache - * @return this builder instance - * @throws java.lang.IllegalArgumentException `size` is negative - * @throws java.lang.IllegalStateException if a maximum size or weight was already set - */ + * Specifies the maximum number of entries the cache may contain. + * + * @param maximumSize the maximum size of the cache + * @return this builder instance + * @throws java.lang.IllegalArgumentException `size` is negative + * @throws java.lang.IllegalStateException if a maximum size or weight was already set + */ def maximumSize(maximumSize: Long): Scaffeine[K, V] = Scaffeine(underlying.maximumSize(maximumSize)) /** - * Specifies the maximum weight of entries the cache may contain. - *

- * This feature cannot be used in conjunction with [[Scaffeine.maximumSize]]. - * - * @param maximumWeight the maximum total weight of entries the cache may contain - * @return this builder instance - * @throws java.lang.IllegalArgumentException if `maximumWeight` is negative - * @throws java.lang.IllegalStateException if a maximum weight or size was already set - */ + * Specifies the maximum weight of entries the cache may contain. + *

+ * This feature cannot be used in conjunction with [[Scaffeine.maximumSize]]. + * + * @param maximumWeight the maximum total weight of entries the cache may contain + * @return this builder instance + * @throws java.lang.IllegalArgumentException if `maximumWeight` is negative + * @throws java.lang.IllegalStateException if a maximum weight or size was already set + */ def maximumWeight(maximumWeight: Long): Scaffeine[K, V] = Scaffeine(underlying.maximumWeight(maximumWeight)) /** - * Specifies the weigher to use in determining the weight of entries. - * - * @param weigher the weigher to use in calculating the weight of cache entries - * @tparam K1 key type of the weigher - * @tparam V1 value type of the weigher - * @return this builder instance - * @throws java.lang.IllegalArgumentException if `size` is negative - * @throws java.lang.IllegalStateException if a maximum size was already set - */ + * Specifies the weigher to use in determining the weight of entries. + * + * @param weigher the weigher to use in calculating the weight of cache entries + * @tparam K1 key type of the weigher + * @tparam V1 value type of the weigher + * @return this builder instance + * @throws java.lang.IllegalArgumentException if `size` is negative + * @throws java.lang.IllegalStateException if a maximum size was already set + */ def weigher[K1 <: K, V1 <: V](weigher: (K1, V1) => Int) = Scaffeine(underlying.weigher(new caffeine.cache.Weigher[K1, V1] { override def weigh(key: K1, value: V1): Int = weigher(key, value) })) /** - * Specifies that each key (not value) stored in the cache should be wrapped in a - * [[java.lang.ref.WeakReference]] (by default, strong references are used). - *

- * This feature cannot be used in conjunction with [[Scaffeine.writer]]. - * - * @return this builder instance - * @throws java.lang.IllegalStateException if the key strength was already set or the writer was set - */ + * Specifies that each key (not value) stored in the cache should be wrapped in a + * [[java.lang.ref.WeakReference]] (by default, strong references are used). + *

+ * This feature cannot be used in conjunction with [[Scaffeine.writer]]. + * + * @return this builder instance + * @throws java.lang.IllegalStateException if the key strength was already set or the writer was set + */ def weakKeys(): Scaffeine[K, V] = Scaffeine(underlying.weakKeys()) /** - * Specifies that each value (not key) stored in the cache should be wrapped in a - * [[java.lang.ref.WeakReference]] (by default, strong references are used). - *

- * This feature cannot be used in conjunction with [[Scaffeine.buildAsync[K1<:K,V1<:V]()*]]. - * - * @return this builder instance - * @throws java.lang.IllegalStateException if the value strength was already set - */ + * Specifies that each value (not key) stored in the cache should be wrapped in a + * [[java.lang.ref.WeakReference]] (by default, strong references are used). + *

+ * This feature cannot be used in conjunction with [[Scaffeine.buildAsync[K1<:K,V1<:V]()*]]. + * + * @return this builder instance + * @throws java.lang.IllegalStateException if the value strength was already set + */ def weakValues(): Scaffeine[K, V] = Scaffeine(underlying.weakValues()) /** - * Specifies that each value (not key) stored in the cache should be wrapped in a - * [[java.lang.ref.SoftReference]] (by default, strong references are used). - *

- * This feature cannot be used in conjunction with [[Scaffeine.buildAsync[K1<:K,V1<:V]()*]]. - * - * @return this builder instance - * @throws java.lang.IllegalStateException if the value strength was already set - */ + * Specifies that each value (not key) stored in the cache should be wrapped in a + * [[java.lang.ref.SoftReference]] (by default, strong references are used). + *

+ * This feature cannot be used in conjunction with [[Scaffeine.buildAsync[K1<:K,V1<:V]()*]]. + * + * @return this builder instance + * @throws java.lang.IllegalStateException if the value strength was already set + */ def softValues(): Scaffeine[K, V] = Scaffeine(underlying.softValues()) /** - * Specifies that each entry should be automatically removed from the cache once a fixed duration - * has elapsed after the entry's creation, or the most recent replacement of its value. - * - * @param duration the length of time after an entry is created that it should be automatically - * removed - * @return this builder instance - * @throws java.lang.IllegalArgumentException if `duration` is negative - * @throws java.lang.IllegalStateException if the time to live or time to idle was already set - */ + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, or the most recent replacement of its value. + * + * @param duration the length of time after an entry is created that it should be automatically + * removed + * @return this builder instance + * @throws java.lang.IllegalArgumentException if `duration` is negative + * @throws java.lang.IllegalStateException if the time to live or time to idle was already set + */ def expireAfterWrite(duration: FiniteDuration): Scaffeine[K, V] = Scaffeine(underlying.expireAfterWrite(duration.toJava)) /** - * Specifies that each entry should be automatically removed from the cache once a fixed duration - * has elapsed after the entry's creation, the most recent replacement of its value, or its last - * read. - * - * @param duration the length of time after an entry is last accessed that it should be - * automatically removed - * @return this builder instance - * @throws java.lang.IllegalArgumentException if `duration` is negative - * @throws java.lang.IllegalStateException if the time to idle or time to live was already set - */ + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, the most recent replacement of its value, or its last + * read. + * + * @param duration the length of time after an entry is last accessed that it should be + * automatically removed + * @return this builder instance + * @throws java.lang.IllegalArgumentException if `duration` is negative + * @throws java.lang.IllegalStateException if the time to idle or time to live was already set + */ def expireAfterAccess(duration: FiniteDuration): Scaffeine[K, V] = Scaffeine(underlying.expireAfterAccess(duration.toJava)) /** - * Specifies that each entry should be automatically removed from the cache once a duration has - * elapsed after the entry's creation, the most recent replacement of its value, or its last - * read. - * - * @param create the length of time an entry should be automatically removed from the cache after the entry's creation. - * @param update the length of time an entry should be automatically removed from the cache after the replacement of it's value. - * @param read the length of time an entry should be automatically removed from the cache after the entry's last read. - * @tparam K1 the key type of the expiry. - * @tparam V1 the value type of the expiry. - * @return this builder instance - * @throws java.lang.IllegalStateException if expiration was already set or used with expiresAfterAccess or expiresAfterWrite. - */ + * Specifies that each entry should be automatically removed from the cache once a duration has + * elapsed after the entry's creation, the most recent replacement of its value, or its last + * read. + * + * @param create the length of time an entry should be automatically removed from the cache after the entry's creation. + * @param update the length of time an entry should be automatically removed from the cache after the replacement of it's value. + * @param read the length of time an entry should be automatically removed from the cache after the entry's last read. + * @tparam K1 the key type of the expiry. + * @tparam V1 the value type of the expiry. + * @return this builder instance + * @throws java.lang.IllegalStateException if expiration was already set or used with expiresAfterAccess or expiresAfterWrite. + */ def expireAfter[K1 <: K, V1 <: V]( - create: (K1, V1) => FiniteDuration, - update: (K1, V1, FiniteDuration) => FiniteDuration, - read: (K1, V1, FiniteDuration) => FiniteDuration + create: (K1, V1) => FiniteDuration, + update: (K1, V1, FiniteDuration) => FiniteDuration, + read: (K1, V1, FiniteDuration) => FiniteDuration ): Scaffeine[K1, V1] = - Scaffeine(underlying.expireAfter(new caffeine.cache.Expiry[K1, V1] { - override def expireAfterCreate(key: K1, value: V1, currentTime: Long): Long = - create(key, value).toNanos - - override def expireAfterUpdate(key: K1, value: V1, currentTime: Long, currentDuration: Long): Long = - update(key, value, currentDuration.nanos).toNanos - - override def expireAfterRead(key: K1, value: V1, currentTime: Long, currentDuration: Long): Long = - read(key, value, currentDuration.nanos).toNanos - }).asInstanceOf[caffeine.cache.Caffeine[K1, V1]]) + Scaffeine( + underlying + .expireAfter(new caffeine.cache.Expiry[K1, V1] { + + override def expireAfterCreate(key: K1, value: V1, currentTime: Long) + : Long = + create(key, value).toNanos + + override def expireAfterUpdate( + key: K1, + value: V1, + currentTime: Long, + currentDuration: Long + ): Long = + update(key, value, currentDuration.nanos).toNanos + + override def expireAfterRead( + key: K1, + value: V1, + currentTime: Long, + currentDuration: Long + ): Long = + read(key, value, currentDuration.nanos).toNanos + }) + .asInstanceOf[caffeine.cache.Caffeine[K1, V1]] + ) /** - * Specifies that active entries are eligible for automatic refresh once a fixed duration has - * elapsed after the entry's creation, or the most recent replacement of its value. - * - * @param duration the length of time after an entry is created that it should be considered - * stale, and thus eligible for refresh - * @return this builder instance - * @throws java.lang.IllegalArgumentException if `duration` is negative - * @throws java.lang.IllegalStateException if the refresh interval was already set - */ + * Specifies that active entries are eligible for automatic refresh once a fixed duration has + * elapsed after the entry's creation, or the most recent replacement of its value. + * + * @param duration the length of time after an entry is created that it should be considered + * stale, and thus eligible for refresh + * @return this builder instance + * @throws java.lang.IllegalArgumentException if `duration` is negative + * @throws java.lang.IllegalStateException if the refresh interval was already set + */ def refreshAfterWrite(duration: FiniteDuration): Scaffeine[K, V] = Scaffeine(underlying.refreshAfterWrite(duration.toJava)) /** - * Specifies a nanosecond-precision time source for use in determining when entries should be - * expired or refreshed. By default, `java.lang.System.nanoTime` is used. - * - * @param ticker a nanosecond-precision time source - * @return this builder instance - * @throws java.lang.IllegalStateException if a ticker was already set - */ + * Specifies a nanosecond-precision time source for use in determining when entries should be + * expired or refreshed. By default, `java.lang.System.nanoTime` is used. + * + * @param ticker a nanosecond-precision time source + * @return this builder instance + * @throws java.lang.IllegalStateException if a ticker was already set + */ def ticker(ticker: caffeine.cache.Ticker): Scaffeine[K, V] = Scaffeine(underlying.ticker(ticker)) /** - * Specifies a listener instance that caches should notify each time an entry is removed for any - * [[com.github.benmanes.caffeine.cache.RemovalCause]]. - * - * @param removalListener a listener that caches should notify each time an entry is - * removed - * @tparam K1 the key type of the listener - * @tparam V1 the value type of the listener - * @return this builder instance - * @throws java.lang.IllegalStateException if a removal listener was already set - */ - def removalListener[K1 <: K, V1 <: V](removalListener: (K1, V1, caffeine.cache.RemovalCause) => Unit): Scaffeine[K1, V1] = - Scaffeine(underlying.removalListener(new caffeine.cache.RemovalListener[K1, V1] { - override def onRemoval(key: K1, value: V1, cause: caffeine.cache.RemovalCause): Unit = removalListener(key, value, cause) - })) + * Specifies a listener instance that caches should notify each time an entry is removed for any + * [[com.github.benmanes.caffeine.cache.RemovalCause]]. + * + * @param removalListener a listener that caches should notify each time an entry is + * removed + * @tparam K1 the key type of the listener + * @tparam V1 the value type of the listener + * @return this builder instance + * @throws java.lang.IllegalStateException if a removal listener was already set + */ + def removalListener[K1 <: K, V1 <: V]( + removalListener: (K1, V1, caffeine.cache.RemovalCause) => Unit + ): Scaffeine[K1, V1] = + Scaffeine( + underlying.removalListener(new caffeine.cache.RemovalListener[K1, V1] { + + override def onRemoval( + key: K1, + value: V1, + cause: caffeine.cache.RemovalCause + ): Unit = removalListener(key, value, cause) + }) + ) /** - * Specifies a writer instance that caches should notify each time an entry is explicitly created - * or modified, or removed for any [[com.github.benmanes.caffeine.cache.RemovalCause]]. - *

- * This feature cannot be used in conjunction with [[Scaffeine.weakKeys]] or [[Scaffeine.buildAsync[K1<:K,V1<:V]()*]]. - * - * @param writer a writer instance that caches should notify each time an entry is explicitly - * created or modified, or removed for any reason - * @tparam K1 the key type of the writer - * @tparam V1 the value type of the writer - * @return this builder instance - * @throws java.lang.IllegalStateException if a writer was already set or if the key strength is weak - */ - def writer[K1 <: K, V1 <: V](writer: caffeine.cache.CacheWriter[K1, V1]): Scaffeine[K1, V1] = + * Specifies a writer instance that caches should notify each time an entry is explicitly created + * or modified, or removed for any [[com.github.benmanes.caffeine.cache.RemovalCause]]. + *

+ * This feature cannot be used in conjunction with [[Scaffeine.weakKeys]] or [[Scaffeine.buildAsync[K1<:K,V1<:V]()*]]. + * + * @param writer a writer instance that caches should notify each time an entry is explicitly + * created or modified, or removed for any reason + * @tparam K1 the key type of the writer + * @tparam V1 the value type of the writer + * @return this builder instance + * @throws java.lang.IllegalStateException if a writer was already set or if the key strength is weak + */ + def writer[K1 <: K, V1 <: V]( + writer: caffeine.cache.CacheWriter[K1, V1] + ): Scaffeine[K1, V1] = Scaffeine(underlying.writer(writer)) /** - * Enables the accumulation of [[com.github.benmanes.caffeine.cache.stats.CacheStats]] - * during the operation of the cache. - * - * @return this builder instance - */ + * Enables the accumulation of [[com.github.benmanes.caffeine.cache.stats.CacheStats]] + * during the operation of the cache. + * + * @return this builder instance + */ def recordStats(): Scaffeine[K, V] = Scaffeine(underlying.recordStats()) /** - * Enables the accumulation of [[com.github.benmanes.caffeine.cache.stats.CacheStats]] during - * the operation of the cache. - * - * @param statsCounterSupplier a supplier that returns a new - * [[com.github.benmanes.caffeine.cache.stats.StatsCounter]] - * @return this builder instance - */ - def recordStats[C <: StatsCounter](statsCounterSupplier: () => C): Scaffeine[K, V] = + * Enables the accumulation of [[com.github.benmanes.caffeine.cache.stats.CacheStats]] during + * the operation of the cache. + * + * @param statsCounterSupplier a supplier that returns a new + * [[com.github.benmanes.caffeine.cache.stats.StatsCounter]] + * @return this builder instance + */ + def recordStats[C <: StatsCounter]( + statsCounterSupplier: () => C + ): Scaffeine[K, V] = Scaffeine(underlying.recordStats(asJavaSupplier(statsCounterSupplier))) /** - * Specifies the scheduler to use when scheduling routine maintenance based on an expiration - * event. This augments the periodic maintenance that occurs during normal cache operations to - * allow for the prompt removal of expired entries regardless of whether any cache activity is - * occurring at that time. By default the scheduler is disabled. - * - * @param scheduler the scheduler that submits a task to the [[Scaffeine.executor*]] after a given delay - * @return this builder instance - */ + * Specifies the scheduler to use when scheduling routine maintenance based on an expiration + * event. This augments the periodic maintenance that occurs during normal cache operations to + * allow for the prompt removal of expired entries regardless of whether any cache activity is + * occurring at that time. By default the scheduler is disabled. + * + * @param scheduler the scheduler that submits a task to the [[Scaffeine.executor*]] after a given delay + * @return this builder instance + */ def scheduler(scheduler: Scheduler) = Scaffeine(underlying.scheduler(scheduler)) /** - * Builds a cache which does not automatically load values when keys are requested. - * - * @tparam K1 the key type of the cache - * @tparam V1 the value type of the cache - * @return a cache having the requested features - */ + * Builds a cache which does not automatically load values when keys are requested. + * + * @tparam K1 the key type of the cache + * @tparam V1 the value type of the cache + * @return a cache having the requested features + */ def build[K1 <: K, V1 <: V](): Cache[K1, V1] = Cache(underlying.build()) /** - * Builds a cache, which either returns an already-loaded value for a given key or atomically - * computes or retrieves it using the supplied `loader`. If another thread is currently - * loading the value for this key, simply waits for that thread to finish and returns its loaded - * value. Note that multiple threads can concurrently load values for distinct keys. - * - * @param loader the loader used to obtain new values - * @param allLoader the loader used to obtain new values in bulk, called by [[LoadingCache.getAll(keys:Iterable[K])*]] - * @param reloadLoader the loader used to obtain already-cached values - * @tparam K1 the key type of the loader - * @tparam V1 the value type of the loader - * @return a cache having the requested features - */ + * Builds a cache, which either returns an already-loaded value for a given key or atomically + * computes or retrieves it using the supplied `loader`. If another thread is currently + * loading the value for this key, simply waits for that thread to finish and returns its loaded + * value. Note that multiple threads can concurrently load values for distinct keys. + * + * @param loader the loader used to obtain new values + * @param allLoader the loader used to obtain new values in bulk, called by [[LoadingCache.getAll(keys:Iterable[K])*]] + * @param reloadLoader the loader used to obtain already-cached values + * @tparam K1 the key type of the loader + * @tparam V1 the value type of the loader + * @return a cache having the requested features + */ def build[K1 <: K, V1 <: V]( - loader: K1 => V1, - allLoader: Option[Iterable[K1] => Map[K1, V1]] = None, - reloadLoader: Option[(K1, V1) => V1] = None + loader: K1 => V1, + allLoader: Option[Iterable[K1] => Map[K1, V1]] = None, + reloadLoader: Option[(K1, V1) => V1] = None ): LoadingCache[K1, V1] = - LoadingCache(underlying.build( - toCacheLoader( - loader, - allLoader, - reloadLoader + LoadingCache( + underlying.build( + toCacheLoader( + loader, + allLoader, + reloadLoader + ) ) - )) + ) /** - * Builds a cache which does not automatically load values when keys are requested unless a - * mapping function is provided. The returned [[scala.concurrent.Future]] may be already loaded or - * currently computing the value for a given key. If the asynchronous computation fails - * value then the entry will be automatically removed. Note that multiple - * threads can concurrently load values for distinct keys. - * - * @tparam K1 the key type of the cache - * @tparam V1 the value type of the cache - * @return a cache having the requested features - */ + * Builds a cache which does not automatically load values when keys are requested unless a + * mapping function is provided. The returned [[scala.concurrent.Future]] may be already loaded or + * currently computing the value for a given key. If the asynchronous computation fails + * value then the entry will be automatically removed. Note that multiple + * threads can concurrently load values for distinct keys. + * + * @tparam K1 the key type of the cache + * @tparam V1 the value type of the cache + * @return a cache having the requested features + */ def buildAsync[K1 <: K, V1 <: V](): AsyncCache[K1, V1] = AsyncCache(underlying.buildAsync[K1, V1]()) /** - * Builds a cache, which either returns a [[scala.concurrent.Future]] already loaded or currently - * computing the value for a given key, or atomically computes the value asynchronously through a - * supplied mapping function or the supplied `loader`. If the asynchronous computation - * fails then the entry will be automatically removed. Note that multiple threads can - * concurrently load values for distinct keys. - * - * @param loader the loader used to obtain new values - * @param allLoader the loader used to obtain new values in bulk, called by [[AsyncLoadingCache.getAll(keys:Iterable[K])*]] - * @param reloadLoader the loader used to obtain already-cached values - * @tparam K1 the key type of the loader - * @tparam V1 the value type of the loader - * @return a cache having the requested features - * @throws java.lang.IllegalStateException if the value strength is weak or soft - */ + * Builds a cache, which either returns a [[scala.concurrent.Future]] already loaded or currently + * computing the value for a given key, or atomically computes the value asynchronously through a + * supplied mapping function or the supplied `loader`. If the asynchronous computation + * fails then the entry will be automatically removed. Note that multiple threads can + * concurrently load values for distinct keys. + * + * @param loader the loader used to obtain new values + * @param allLoader the loader used to obtain new values in bulk, called by [[AsyncLoadingCache.getAll(keys:Iterable[K])*]] + * @param reloadLoader the loader used to obtain already-cached values + * @tparam K1 the key type of the loader + * @tparam V1 the value type of the loader + * @return a cache having the requested features + * @throws java.lang.IllegalStateException if the value strength is weak or soft + */ def buildAsync[K1 <: K, V1 <: V]( - loader: K1 => V1, - allLoader: Option[Iterable[K1] => Map[K1, V1]] = None, - reloadLoader: Option[(K1, V1) => V1] = None + loader: K1 => V1, + allLoader: Option[Iterable[K1] => Map[K1, V1]] = None, + reloadLoader: Option[(K1, V1) => V1] = None ): AsyncLoadingCache[K1, V1] = - AsyncLoadingCache(underlying.buildAsync[K1, V1]( - toCacheLoader( - loader, - allLoader, - reloadLoader + AsyncLoadingCache( + underlying.buildAsync[K1, V1]( + toCacheLoader( + loader, + allLoader, + reloadLoader + ) ) - )) + ) /** - * Builds a cache, which either returns a [[scala.concurrent.Future]] already loaded or currently - * computing the value for a given key, or atomically computes the value asynchronously through a - * supplied mapping function or the supplied async `loader`. If the asynchronous - * computation fails then the entry will be automatically removed. - * Note that multiple threads can concurrently load values for distinct keys. - * - * @param loader the loader used to obtain new values - * @param allLoader the loader used to obtain new values in bulk, called by [[AsyncLoadingCache.getAll(keys:Iterable[K])*]] - * @param reloadLoader the loader used to obtain already-cached values - * @tparam K1 the key type of the loader - * @tparam V1 the value type of the loader - * @throws java.lang.IllegalStateException if the value strength is weak or soft - */ + * Builds a cache, which either returns a [[scala.concurrent.Future]] already loaded or currently + * computing the value for a given key, or atomically computes the value asynchronously through a + * supplied mapping function or the supplied async `loader`. If the asynchronous + * computation fails then the entry will be automatically removed. + * Note that multiple threads can concurrently load values for distinct keys. + * + * @param loader the loader used to obtain new values + * @param allLoader the loader used to obtain new values in bulk, called by [[AsyncLoadingCache.getAll(keys:Iterable[K])*]] + * @param reloadLoader the loader used to obtain already-cached values + * @tparam K1 the key type of the loader + * @tparam V1 the value type of the loader + * @throws java.lang.IllegalStateException if the value strength is weak or soft + */ def buildAsyncFuture[K1 <: K, V1 <: V]( - loader: K1 => Future[V1], - allLoader: Option[Iterable[K1] => Future[Map[K1, V1]]] = None, - reloadLoader: Option[(K1, V1) => Future[V1]] = None + loader: K1 => Future[V1], + allLoader: Option[Iterable[K1] => Future[Map[K1, V1]]] = None, + reloadLoader: Option[(K1, V1) => Future[V1]] = None ): AsyncLoadingCache[K1, V1] = - AsyncLoadingCache(underlying.buildAsync[K1, V1]( - toAsyncCacheLoader( - loader, - allLoader, - reloadLoader + AsyncLoadingCache( + underlying.buildAsync[K1, V1]( + toAsyncCacheLoader( + loader, + allLoader, + reloadLoader + ) ) - )) + ) private[this] def toCacheLoader[K1 <: K, V1 <: V]( - loader: K1 => V1, - allLoader: Option[Iterable[K1] => Map[K1, V1]], - reloadLoader: Option[(K1, V1) => V1] + loader: K1 => V1, + allLoader: Option[Iterable[K1] => Map[K1, V1]], + reloadLoader: Option[(K1, V1) => V1] ): caffeine.cache.CacheLoader[K1, V1] = allLoader match { case Some(l) => new CacheLoaderAdapter[K1, V1](loader, reloadLoader) { + override def loadAll(keys: lang.Iterable[_ <: K1]): util.Map[K1, V1] = l(keys.asScala).asJava } @@ -404,14 +454,21 @@ case class Scaffeine[K, V](underlying: caffeine.cache.Caffeine[K, V]) { } private[this] def toAsyncCacheLoader[K1 <: K, V1 <: V]( - loader: K1 => Future[V1], - allLoader: Option[Iterable[K1] => Future[Map[K1, V1]]], - reloadLoader: Option[(K1, V1) => Future[V1]] + loader: K1 => Future[V1], + allLoader: Option[Iterable[K1] => Future[Map[K1, V1]]], + reloadLoader: Option[(K1, V1) => Future[V1]] ): caffeine.cache.AsyncCacheLoader[K1, V1] = allLoader match { case Some(l) => new AsyncCacheLoaderAdapter[K1, V1](loader, reloadLoader) { - override def asyncLoadAll(keys: lang.Iterable[_ <: K1], executor: Executor): CompletableFuture[util.Map[K1, V1]] = - l(keys.asScala).map(_.asJava)(DirectExecutionContext).toJava.toCompletableFuture + + override def asyncLoadAll( + keys: lang.Iterable[_ <: K1], + executor: Executor + ): CompletableFuture[util.Map[K1, V1]] = + l(keys.asScala) + .map(_.asJava)(DirectExecutionContext) + .toJava + .toCompletableFuture } case None => new AsyncCacheLoaderAdapter[K1, V1](loader, reloadLoader) diff --git a/src/test/scala/com/github/blemale/scaffeine/AsyncCacheSpec.scala b/src/test/scala/com/github/blemale/scaffeine/AsyncCacheSpec.scala index 57daa6d..9a17a73 100644 --- a/src/test/scala/com/github/blemale/scaffeine/AsyncCacheSpec.scala +++ b/src/test/scala/com/github/blemale/scaffeine/AsyncCacheSpec.scala @@ -8,10 +8,10 @@ import org.scalatest.wordspec.AnyWordSpec import scala.concurrent.Future class AsyncCacheSpec - extends AnyWordSpec - with Matchers - with ScalaFutures - with OptionValues { + extends AnyWordSpec + with Matchers + with ScalaFutures + with OptionValues { "AsyncCache" should { "get value if present" in { @@ -51,7 +51,8 @@ class AsyncCacheSpec val cache = Scaffeine().buildAsync[String, String]() cache.put("foo", Future.successful("present")) - val values = cache.getAll(List("foo", "bar"), _.map(key => (key, "computed")).toMap) + val values = + cache.getAll(List("foo", "bar"), _.map(key => (key, "computed")).toMap) values.futureValue should contain only ("foo" -> "present", "bar" -> "computed") } @@ -60,7 +61,10 @@ class AsyncCacheSpec val cache = Scaffeine().buildAsync[String, String]() cache.put("foo", Future.successful("present")) - val values = cache.getAllFuture(List("foo", "bar"), keys => Future.successful(keys.map(key => (key, "computed")).toMap)) + val values = cache.getAllFuture( + List("foo", "bar"), + keys => Future.successful(keys.map(key => (key, "computed")).toMap) + ) values.futureValue should contain only ("foo" -> "present", "bar" -> "computed") } diff --git a/src/test/scala/com/github/blemale/scaffeine/AsyncLoadingCacheSpec.scala b/src/test/scala/com/github/blemale/scaffeine/AsyncLoadingCacheSpec.scala index c7e5c5e..d8042c5 100644 --- a/src/test/scala/com/github/blemale/scaffeine/AsyncLoadingCacheSpec.scala +++ b/src/test/scala/com/github/blemale/scaffeine/AsyncLoadingCacheSpec.scala @@ -8,16 +8,17 @@ import org.scalatest.wordspec.AnyWordSpec import scala.concurrent.Future class AsyncLoadingCacheSpec - extends AnyWordSpec - with Matchers - with ScalaFutures - with OptionValues { + extends AnyWordSpec + with Matchers + with ScalaFutures + with OptionValues { "AsyncLoadingCache" when { "created with synchronous loader" should { "get or load value" in { - val cache = Scaffeine().buildAsync[String, String]((_: String) => "loaded") + val cache = + Scaffeine().buildAsync[String, String]((_: String) => "loaded") cache.put("foo", Future.successful("present")) val fooValue = cache.get("foo") @@ -28,7 +29,8 @@ class AsyncLoadingCacheSpec } "get or load all given values" in { - val cache = Scaffeine().buildAsync[String, String]((_: String) => "loaded") + val cache = + Scaffeine().buildAsync[String, String]((_: String) => "loaded") cache.put("foo", Future.successful("present")) val values = cache.getAll(List("foo", "bar")) @@ -39,7 +41,8 @@ class AsyncLoadingCacheSpec "get or bulk load all given values" in { val cache = Scaffeine().buildAsync[String, String]( (_: String) => "loaded", - allLoader = Some((keys: Iterable[String]) => keys.map(_ -> "bulked").toMap) + allLoader = + Some((keys: Iterable[String]) => keys.map(_ -> "bulked").toMap) ) cache.put("foo", Future.successful("present")) @@ -49,7 +52,8 @@ class AsyncLoadingCacheSpec } "expose a synchronous view of itself" in { - val cache = Scaffeine().buildAsync[String, String]((_: String) => "loaded") + val cache = + Scaffeine().buildAsync[String, String]((_: String) => "loaded") val synchronousCache = cache.synchronous() @@ -59,7 +63,9 @@ class AsyncLoadingCacheSpec "created with asynchronous loader" should { "get or load value" in { - val cache = Scaffeine().buildAsyncFuture[String, String]((_: String) => Future.successful("loaded")) + val cache = Scaffeine().buildAsyncFuture[String, String]((_: String) => + Future.successful("loaded") + ) cache.put("foo", Future.successful("present")) val fooValue = cache.get("foo") @@ -70,7 +76,9 @@ class AsyncLoadingCacheSpec } "get or load all given values" in { - val cache = Scaffeine().buildAsyncFuture[String, String]((_: String) => Future.successful("loaded")) + val cache = Scaffeine().buildAsyncFuture[String, String]((_: String) => + Future.successful("loaded") + ) cache.put("foo", Future.successful("present")) val values = cache.getAll(List("foo", "bar")) @@ -81,7 +89,9 @@ class AsyncLoadingCacheSpec "get or bulk load all given values" in { val cache = Scaffeine().buildAsyncFuture[String, String]( (_: String) => Future.successful("loaded"), - allLoader = Some((keys: Iterable[String]) => Future.successful(keys.map(_ -> "bulked").toMap)) + allLoader = Some((keys: Iterable[String]) => + Future.successful(keys.map(_ -> "bulked").toMap) + ) ) cache.put("foo", Future.successful("present")) diff --git a/src/test/scala/com/github/blemale/scaffeine/CacheSpec.scala b/src/test/scala/com/github/blemale/scaffeine/CacheSpec.scala index bdfc743..b8323d8 100644 --- a/src/test/scala/com/github/blemale/scaffeine/CacheSpec.scala +++ b/src/test/scala/com/github/blemale/scaffeine/CacheSpec.scala @@ -4,10 +4,7 @@ import org.scalatest._ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class CacheSpec - extends AnyWordSpec - with Matchers - with OptionValues { +class CacheSpec extends AnyWordSpec with Matchers with OptionValues { "Cache" should { "get value if present" in { @@ -15,7 +12,7 @@ class CacheSpec cache.put("foo", "present") val valuePresent = cache.getIfPresent("foo") - val valueAbsent = cache.getIfPresent("bar") + val valueAbsent = cache.getIfPresent("bar") valuePresent.value should be("present") valueAbsent should be(None) @@ -25,7 +22,7 @@ class CacheSpec val cache = Scaffeine().build[String, String]() cache.put("foo", "present") - val present = cache.get("foo", _ => "computed") + val present = cache.get("foo", _ => "computed") val computed = cache.get("bar", _ => "computed") present should be("present") @@ -46,7 +43,8 @@ class CacheSpec val cache = Scaffeine().build[String, String]() cache.put("foo", "present") - val keyValues = cache.getAll(List("foo", "bar"), _.map(key => (key, "computed")).toMap) + val keyValues = + cache.getAll(List("foo", "bar"), _.map(key => (key, "computed")).toMap) keyValues should contain only ("foo" -> "present", "bar" -> "computed") } diff --git a/src/test/scala/com/github/blemale/scaffeine/LoadingCacheSpec.scala b/src/test/scala/com/github/blemale/scaffeine/LoadingCacheSpec.scala index c1bbcd0..8fe4f82 100644 --- a/src/test/scala/com/github/blemale/scaffeine/LoadingCacheSpec.scala +++ b/src/test/scala/com/github/blemale/scaffeine/LoadingCacheSpec.scala @@ -4,10 +4,7 @@ import org.scalatest._ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class LoadingCacheSpec - extends AnyWordSpec - with Matchers - with OptionValues { +class LoadingCacheSpec extends AnyWordSpec with Matchers with OptionValues { "LoadingCache" should { "be a cache" in { @@ -41,7 +38,8 @@ class LoadingCacheSpec Scaffeine() .build[String, String]( loader = (_: String) => "computed", - allLoader = Some((keys: Iterable[String]) => keys.map(_ -> "bulked").toMap) + allLoader = + Some((keys: Iterable[String]) => keys.map(_ -> "bulked").toMap) ) cache.put("foo", "present") diff --git a/src/test/scala/com/github/blemale/scaffeine/RemovalListenerSpec.scala b/src/test/scala/com/github/blemale/scaffeine/RemovalListenerSpec.scala index 1f49781..d682a01 100644 --- a/src/test/scala/com/github/blemale/scaffeine/RemovalListenerSpec.scala +++ b/src/test/scala/com/github/blemale/scaffeine/RemovalListenerSpec.scala @@ -6,14 +6,16 @@ import com.github.benmanes.caffeine.cache.RemovalCause import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class RemovalListenerSpec - extends AnyWordSpec - with Matchers { +class RemovalListenerSpec extends AnyWordSpec with Matchers { class StubListener extends ((String, String, RemovalCause) => Unit) { val callCounter = new AtomicInteger - override def apply(key: String, value: String, cause: RemovalCause): Unit = { + override def apply( + key: String, + value: String, + cause: RemovalCause + ): Unit = { val _ = callCounter.incrementAndGet() } } diff --git a/src/test/scala/com/github/blemale/scaffeine/ScaffeineSpec.scala b/src/test/scala/com/github/blemale/scaffeine/ScaffeineSpec.scala index 28aff94..aebb8ef 100644 --- a/src/test/scala/com/github/blemale/scaffeine/ScaffeineSpec.scala +++ b/src/test/scala/com/github/blemale/scaffeine/ScaffeineSpec.scala @@ -10,12 +10,9 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec import scala.concurrent.duration._ -import scala.concurrent.{ ExecutionContext, Future } +import scala.concurrent.{ExecutionContext, Future} -class ScaffeineSpec - extends AnyWordSpec - with Matchers - with PrivateMethodTester { +class ScaffeineSpec extends AnyWordSpec with Matchers with PrivateMethodTester { "Scaffeine" should { "create builder" in { @@ -25,7 +22,8 @@ class ScaffeineSpec } "create builder from spec" in { - val scaffeine: Scaffeine[Any, Any] = Scaffeine(caffeine.cache.CaffeineSpec.parse("initialCapacity=10")) + val scaffeine: Scaffeine[Any, Any] = + Scaffeine(caffeine.cache.CaffeineSpec.parse("initialCapacity=10")) scaffeine shouldBe a[Scaffeine[_, _]] } @@ -40,7 +38,8 @@ class ScaffeineSpec val scaffeine = Scaffeine().initialCapacity(99) val getInitialCapacity = PrivateMethod[Int]('getInitialCapacity) - val initialCapacity = scaffeine.underlying invokePrivate getInitialCapacity() + val initialCapacity = + scaffeine.underlying invokePrivate getInitialCapacity() initialCapacity should be(99) } @@ -49,7 +48,7 @@ class ScaffeineSpec val scaffeine = Scaffeine().executor(ExecutionContext.global) val getExecutor = PrivateMethod[Executor]('getExecutor) - val executor = scaffeine.underlying invokePrivate getExecutor() + val executor = scaffeine.underlying invokePrivate getExecutor() executor should be(ExecutionContext.global) } @@ -58,16 +57,17 @@ class ScaffeineSpec val scaffeine = Scaffeine().maximumSize(99) val getMaximumWeight = PrivateMethod[Long]('getMaximum) - val maximumSize = scaffeine.underlying invokePrivate getMaximumWeight() + val maximumSize = scaffeine.underlying invokePrivate getMaximumWeight() maximumSize should be(99L) } "set maximum weight" in { - val scaffeine = Scaffeine().maximumWeight(99).weigher((_: Any, _: Any) => 1) + val scaffeine = + Scaffeine().maximumWeight(99).weigher((_: Any, _: Any) => 1) val getMaximumWeight = PrivateMethod[Long]('getMaximum) - val maximumWeight = scaffeine.underlying invokePrivate getMaximumWeight() + val maximumWeight = scaffeine.underlying invokePrivate getMaximumWeight() maximumWeight should be(99L) } @@ -76,7 +76,7 @@ class ScaffeineSpec val scaffeine = Scaffeine().weigher((_: Any, _: Any) => 1) val isWeighted = PrivateMethod[Boolean]('isWeighted) - val weighted = scaffeine.underlying invokePrivate isWeighted() + val weighted = scaffeine.underlying invokePrivate isWeighted() weighted should be(true) } @@ -85,7 +85,7 @@ class ScaffeineSpec val scaffeine = Scaffeine().weakKeys() val isStrongKeys = PrivateMethod[Boolean]('isStrongKeys) - val strongKeys = scaffeine.underlying invokePrivate isStrongKeys() + val strongKeys = scaffeine.underlying invokePrivate isStrongKeys() strongKeys should be(false) } @@ -94,7 +94,7 @@ class ScaffeineSpec val scaffeine = Scaffeine().weakValues() val isWeakValues = PrivateMethod[Boolean]('isWeakValues) - val weakValues = scaffeine.underlying invokePrivate isWeakValues() + val weakValues = scaffeine.underlying invokePrivate isWeakValues() weakValues should be(true) } @@ -103,10 +103,10 @@ class ScaffeineSpec val scaffeine = Scaffeine().softValues() val isStrongValues = PrivateMethod[Boolean]('isStrongValues) - val isWeakValues = PrivateMethod[Boolean]('isWeakValues) + val isWeakValues = PrivateMethod[Boolean]('isWeakValues) val strongValues = scaffeine.underlying invokePrivate isStrongValues() - val weakValues = scaffeine.underlying invokePrivate isWeakValues() + val weakValues = scaffeine.underlying invokePrivate isWeakValues() strongValues should be(false) weakValues should be(false) @@ -115,8 +115,10 @@ class ScaffeineSpec "set expire after write" in { val scaffeine = Scaffeine().expireAfterWrite(10.minutes) - val getExpiresAfterWriteNanos = PrivateMethod[Long]('getExpiresAfterWriteNanos) - val expiresAfterWriteNanos = scaffeine.underlying invokePrivate getExpiresAfterWriteNanos() + val getExpiresAfterWriteNanos = + PrivateMethod[Long]('getExpiresAfterWriteNanos) + val expiresAfterWriteNanos = + scaffeine.underlying invokePrivate getExpiresAfterWriteNanos() expiresAfterWriteNanos should be(10.minutes.toNanos) } @@ -124,8 +126,10 @@ class ScaffeineSpec "set expire after access" in { val scaffeine = Scaffeine().expireAfterAccess(10.minutes) - val getExpiresAfterAccessNanos = PrivateMethod[Long]('getExpiresAfterAccessNanos) - val expiresAfterAccessNanos = scaffeine.underlying invokePrivate getExpiresAfterAccessNanos() + val getExpiresAfterAccessNanos = + PrivateMethod[Long]('getExpiresAfterAccessNanos) + val expiresAfterAccessNanos = + scaffeine.underlying invokePrivate getExpiresAfterAccessNanos() expiresAfterAccessNanos should be(10.minutes.toNanos) } @@ -138,7 +142,7 @@ class ScaffeineSpec ) val getExpiry = PrivateMethod[caffeine.cache.Expiry[Any, Any]]('getExpiry) - val expiry = scaffeine.underlying invokePrivate getExpiry(false) + val expiry = scaffeine.underlying invokePrivate getExpiry(false) expiry.expireAfterCreate(null, null, 0) should be(10.minutes.toNanos) expiry.expireAfterUpdate(null, null, 0, 0) should be(20.minutes.toNanos) @@ -148,8 +152,10 @@ class ScaffeineSpec "set refresh after write" in { val scaffeine = Scaffeine().refreshAfterWrite(10.minutes) - val getRefreshAfterWriteNanos = PrivateMethod[Long]('getRefreshAfterWriteNanos) - val refreshAfterWriteNanos = scaffeine.underlying invokePrivate getRefreshAfterWriteNanos() + val getRefreshAfterWriteNanos = + PrivateMethod[Long]('getRefreshAfterWriteNanos) + val refreshAfterWriteNanos = + scaffeine.underlying invokePrivate getRefreshAfterWriteNanos() refreshAfterWriteNanos should be(10.minutes.toNanos) } @@ -158,16 +164,21 @@ class ScaffeineSpec val scaffeine = Scaffeine().ticker(caffeine.cache.Ticker.disabledTicker()) val getTicker = PrivateMethod[caffeine.cache.Ticker]('getTicker) - val ticker = scaffeine.underlying invokePrivate getTicker() + val ticker = scaffeine.underlying invokePrivate getTicker() ticker should be(caffeine.cache.Ticker.disabledTicker()) } "set removal listener" in { - val scaffeine = Scaffeine().removalListener((_: Any, _: Any, _) => println("removed")) + val scaffeine = + Scaffeine().removalListener((_: Any, _: Any, _) => println("removed")) - val getRemovalListener = PrivateMethod[caffeine.cache.RemovalListener[Any, Any]]('getRemovalListener) - val removalListener = scaffeine.underlying invokePrivate getRemovalListener(false) + val getRemovalListener = + PrivateMethod[caffeine.cache.RemovalListener[Any, Any]]( + 'getRemovalListener + ) + val removalListener = + scaffeine.underlying invokePrivate getRemovalListener(false) removalListener shouldNot be(null) } @@ -175,12 +186,17 @@ class ScaffeineSpec "set cache writer" in { val writer = new caffeine.cache.CacheWriter[Any, Any] { override def write(key: Any, value: Any): Unit = println("write") - override def delete(key: Any, value: Any, cause: caffeine.cache.RemovalCause): Unit = println("delete") + override def delete( + key: Any, + value: Any, + cause: caffeine.cache.RemovalCause + ): Unit = println("delete") } val scaffeine = Scaffeine().writer(writer) - val getCacheWriter = PrivateMethod[caffeine.cache.CacheWriter[Any, Any]]('getCacheWriter) + val getCacheWriter = + PrivateMethod[caffeine.cache.CacheWriter[Any, Any]]('getCacheWriter) val cacheWriter = scaffeine.underlying invokePrivate getCacheWriter() cacheWriter should be(writer) @@ -190,16 +206,17 @@ class ScaffeineSpec val scaffeine = Scaffeine().recordStats() val isRecordingStats = PrivateMethod[Boolean]('isRecordingStats) - val recordingStats = scaffeine.underlying invokePrivate isRecordingStats() + val recordingStats = scaffeine.underlying invokePrivate isRecordingStats() recordingStats should be(true) } "set record stats supplier" in { - val scaffeine = Scaffeine().recordStats(() => StatsCounter.disabledStatsCounter()) + val scaffeine = + Scaffeine().recordStats(() => StatsCounter.disabledStatsCounter()) val isRecordingStats = PrivateMethod[Boolean]('isRecordingStats) - val recordingStats = scaffeine.underlying invokePrivate isRecordingStats() + val recordingStats = scaffeine.underlying invokePrivate isRecordingStats() recordingStats should be(true) } @@ -208,7 +225,7 @@ class ScaffeineSpec val scaffeine = Scaffeine().scheduler(Scheduler.systemScheduler()) val getScheduler = PrivateMethod[Scheduler]('getScheduler) - val scheduler = scaffeine.underlying invokePrivate getScheduler() + val scheduler = scaffeine.underlying invokePrivate getScheduler() scheduler should be(Scheduler.systemScheduler()) } @@ -230,7 +247,8 @@ class ScaffeineSpec Scaffeine() .build[Int, Int]( loader = (key: Int) => key + 1, - allLoader = Some((keys: Iterable[Int]) => keys.map(i => i -> (i + 1)).toMap), + allLoader = + Some((keys: Iterable[Int]) => keys.map(i => i -> (i + 1)).toMap), reloadLoader = Some((key: Int, _: Int) => key + 1) ) @@ -248,7 +266,8 @@ class ScaffeineSpec Scaffeine() .buildAsync[Int, Int]( loader = (key: Int) => key + 1, - allLoader = Some((keys: Iterable[Int]) => keys.map(i => i -> (i + 1)).toMap), + allLoader = + Some((keys: Iterable[Int]) => keys.map(i => i -> (i + 1)).toMap), reloadLoader = Some((key: Int, _: Int) => key + 1) ) @@ -256,7 +275,9 @@ class ScaffeineSpec } "build async loading cache from async loading function" in { - val cache = Scaffeine().buildAsyncFuture[Int, Int]((i: Int) => Future.successful(i + 1)) + val cache = Scaffeine().buildAsyncFuture[Int, Int]((i: Int) => + Future.successful(i + 1) + ) cache shouldBe a[AsyncLoadingCache[_, _]] } @@ -266,8 +287,11 @@ class ScaffeineSpec Scaffeine() .buildAsyncFuture[Int, Int]( loader = (key: Int) => Future.successful(key + 1), - allLoader = Some((keys: Iterable[Int]) => Future.successful(keys.map(i => i -> (i + 1)).toMap)), - reloadLoader = Some((key: Int, _: Int) => Future.successful(key + 1)) + allLoader = Some((keys: Iterable[Int]) => + Future.successful(keys.map(i => i -> (i + 1)).toMap) + ), + reloadLoader = + Some((key: Int, _: Int) => Future.successful(key + 1)) ) cache shouldBe a[AsyncLoadingCache[_, _]] diff --git a/src/test/scala/com/github/blemale/scaffeine/WeigherSpec.scala b/src/test/scala/com/github/blemale/scaffeine/WeigherSpec.scala index 620fc74..7491993 100644 --- a/src/test/scala/com/github/blemale/scaffeine/WeigherSpec.scala +++ b/src/test/scala/com/github/blemale/scaffeine/WeigherSpec.scala @@ -4,10 +4,7 @@ import org.scalatest._ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class WeigherSpec - extends AnyWordSpec - with Matchers - with OptionValues { +class WeigherSpec extends AnyWordSpec with Matchers with OptionValues { "Cache" should { "use weigher for calculate size based eviction" in { diff --git a/src/test/scala/com/github/blemale/scaffeine/example/AsyncLoadingCacheExample.scala b/src/test/scala/com/github/blemale/scaffeine/example/AsyncLoadingCacheExample.scala index 843e455..675112c 100644 --- a/src/test/scala/com/github/blemale/scaffeine/example/AsyncLoadingCacheExample.scala +++ b/src/test/scala/com/github/blemale/scaffeine/example/AsyncLoadingCacheExample.scala @@ -7,12 +7,12 @@ import org.scalatest.matchers.should.Matchers import scala.concurrent.Future class AsyncLoadingCacheExample - extends AnyFlatSpec - with Matchers - with ScalaFutures { + extends AnyFlatSpec + with Matchers + with ScalaFutures { "AsyncLoadingCache" should "be created from Scaffeine builder with synchronous loader" in { - import com.github.blemale.scaffeine.{ AsyncLoadingCache, Scaffeine } + import com.github.blemale.scaffeine.{AsyncLoadingCache, Scaffeine} import scala.concurrent.duration._ @@ -23,13 +23,11 @@ class AsyncLoadingCacheExample .maximumSize(500) .buildAsync((i: Int) => s"foo$i") - whenReady(cache.get(1)) { value => - value should be("foo1") - } + whenReady(cache.get(1))(value => value should be("foo1")) } "AsyncLoadingCache" should "be created from Scaffeine builder with asynchronous loader" in { - import com.github.blemale.scaffeine.{ AsyncLoadingCache, Scaffeine } + import com.github.blemale.scaffeine.{AsyncLoadingCache, Scaffeine} import scala.concurrent.duration._ @@ -40,8 +38,6 @@ class AsyncLoadingCacheExample .maximumSize(500) .buildAsyncFuture((i: Int) => Future.successful(s"foo$i")) - whenReady(cache.get(1)) { value => - value should be("foo1") - } + whenReady(cache.get(1))(value => value should be("foo1")) } } diff --git a/src/test/scala/com/github/blemale/scaffeine/example/CacheExample.scala b/src/test/scala/com/github/blemale/scaffeine/example/CacheExample.scala index cf5c8ae..d0d5029 100644 --- a/src/test/scala/com/github/blemale/scaffeine/example/CacheExample.scala +++ b/src/test/scala/com/github/blemale/scaffeine/example/CacheExample.scala @@ -3,12 +3,10 @@ package com.github.blemale.scaffeine.example import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class CacheExample - extends AnyFlatSpec - with Matchers { +class CacheExample extends AnyFlatSpec with Matchers { "Cache" should "be created from Scaffeine builder" in { - import com.github.blemale.scaffeine.{ Cache, Scaffeine } + import com.github.blemale.scaffeine.{Cache, Scaffeine} import scala.concurrent.duration._ diff --git a/src/test/scala/com/github/blemale/scaffeine/example/LoadingCacheExample.scala b/src/test/scala/com/github/blemale/scaffeine/example/LoadingCacheExample.scala index c5e0432..1d6c185 100644 --- a/src/test/scala/com/github/blemale/scaffeine/example/LoadingCacheExample.scala +++ b/src/test/scala/com/github/blemale/scaffeine/example/LoadingCacheExample.scala @@ -3,12 +3,10 @@ package com.github.blemale.scaffeine.example import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class LoadingCacheExample - extends AnyFlatSpec - with Matchers { +class LoadingCacheExample extends AnyFlatSpec with Matchers { "LoadingCache" should "be created from Scaffeine builder" in { - import com.github.blemale.scaffeine.{ LoadingCache, Scaffeine } + import com.github.blemale.scaffeine.{LoadingCache, Scaffeine} import scala.concurrent.duration._