diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 047ffdd9..460d02a7 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -35,4 +35,4 @@ jobs: env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - sbt --client "+clean; +compile; +Test/compile; +coverage; +test; +coverageReport; +coveralls;" + sbt --client "+clean; +compile; +Test/compile; lint; +coverage; +test; +coverageReport; +coveralls;" diff --git a/.scalafix.conf b/.scalafix.conf new file mode 100644 index 00000000..5b164b84 --- /dev/null +++ b/.scalafix.conf @@ -0,0 +1,19 @@ +rules = [ + ExplicitResultTypes + LeakingImplicitClassVal + NoAutoTupling + NoValInForComprehension + OrganizeImports + ProcedureSyntax + RedundantSyntax + RemoveUnused +] + +RemoveUnused { + imports = false // handled by OrganizeImports +} + +OrganizeImports { + # Allign with IntelliJ IDEA so that they don't fight each other + groupedImports = Merge +} diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 00000000..aadbc820 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,168 @@ +version = "3.7.17" +runner.dialect = scala213 +maxColumn = 300 + +// https://scalameta.org/scalafmt/docs/configuration.html#projectgit +project.git = true + +fileOverride { + "glob:**/project/**.scala" { + runner.dialect = scala212 + } + "glob:**/*.sbt" { + runner.dialect = sbt1 + } +} + +// --------------------- +// alignment +// --------------------- +align.preset = most +// align code behind -> e.g. in for ... yield +align.arrowEnumeratorGenerator = true +// multiline strings +align.stripMargin = true +assumeStandardLibraryStripMargin = true +// align also multiline statements +align.multiline = true +// don't break lines just because of alignment +align.allowOverflow = true +// align '=' +align.tokens."+" = [ + { + code = "=" + owners = [{ + parents = ["Template"] + }] + } + { + code = "=" + owners = [{ + regex = "Enumerator\\." + parents = ["Term\\.ForYield"] + }] + } +] +// align vals and types equally +align.treeCategory."Defn.Val" = "given/val/var/def" +align.treeCategory."Defn.Type" = "given/val/var/def" + +// --------------------- +// indentation +// --------------------- +// identation of function calls +indent.callSite = 2 +// indentation of "if" and "while" +indent.ctrlSite = 2 +// identation of definition of parameters in cls and fce +indent.defnSite = 2 +// indentation of "extends" and "with" +indent.extendSite = 2 +// indent nested match and infix expressions +// this causes issues with // comments, indents them +// indent.relativeToLhsLastLine = [match, infix] +// indentation of multiline nested parentheses +binPack.indentCallSiteOnce = false +binPack.indentCallSiteSingleArg = false + + +// --------------------- +// indent operator +// --------------------- +// apply indentation exempts only in some cases +//indentOperator.preset = "spray" +// exempt indentation of 2nd and additional lines in special cases +indentOperator.exemptScope = all + + +// --------------------- +// new lines +// --------------------- +// remove line breaks, leads to horizontal code +//newlines.source = fold +// newlines around statements +newlines.topLevelStatementBlankLines = [ + { + blanks = 1 + } + { + minBreaks = 1 + blanks = 1 + } +] +// newlines after package, class, trait header if body is not trivial +newlines.topLevelBodyIfMinStatements = [] +newlines.topLevelBodyMinStatements = 2 +newlines.beforeTemplateBodyIfBreakInParentCtors = true +// force newline in case/if/while when the body is multiline +newlines.beforeMultiline = keep +newlines.alwaysBeforeElseAfterCurlyIf = false +// break when assignment is multiline +// newlines.forceBeforeMultilineAssign = any +// formatting of bounds of type parameters: upper <:, lower >:, view <%, and context : bounds +newlines.beforeTypeBounds = unfold +// newlines in { case ... => f() } lambdas +// newlines.beforeCurlyLambdaParams = never +// newlines.afterCurlyLambdaParams = squash +// spaces after implicit in fce parameter list +//newlines.implicitParamListModifierForce = [after] +//newlines.avoidForSimpleOverflow = [punct, tooLong, slc] +// skip newlines in the result type +newlines.avoidInResultType = true +// don't put newline before : of the result type if line overflows +newlines.sometimesBeforeColonInMethodReturnType = false +// chains of calls +newlines.selectChains = keep +// in interpolation +newlines.inInterpolation = avoid +// dangling parenteses +danglingParentheses.preset = true +danglingParentheses.exclude = [] + +// config style arguments +//optIn.configStyleArguments = true +//runner.optimizer.forceConfigStyleOnOffset = 80 +//runner.optimizer.forceConfigStyleMinArgCount = 1 + + +// rewrite rules +// - drop redundant braces like { } around a block +// - drop redundant parentheses like ( ) around a statement +// - sort implicit final private lazy +// - prefer for { } yield () over for (;) yield () +// - organize imports +rewrite.rules = [RedundantBraces, RedundantParens, SortModifiers, PreferCurlyFors, Imports] +rewrite.redundantBraces.stringInterpolation = true +rewrite.imports.sort = scalastyle +rewrite.trailingCommas.style = always +// rewrite tokens to different characters +rewriteTokens = { + "⇒": "=>" + "→": "->" + "←": "<-" +} + +// vertical align a line breaks +verticalMultiline.atDefnSite = true +verticalMultiline.arityThreshold = 100 +verticalMultiline.newlineAfterOpenParen = true + +// wrap long standalone comments +comments.wrap = standalone +comments.wrapStandaloneSlcAsSlc = true + +// javadoc formatting +docstrings.style = SpaceAsterisk +docstrings.blankFirstLine = yes +docstrings.removeEmpty = true +docstrings.oneline = fold +docstrings.wrapMaxColumn = 80 + +// all calls in chains start on a new line +includeNoParensInSelectChains = true +// keep chains on multiple lines if already multiline +optIn.breakChainOnFirstMethodDot = true + +// scala 3 config +rewrite.scala3.convertToNewSyntax = true +//rewrite.trailingCommas.style = "multiple" diff --git a/build.sbt b/build.sbt index ccad203a..e5817d35 100644 --- a/build.sbt +++ b/build.sbt @@ -1,5 +1,7 @@ +import org.typelevel.sbt.tpolecat.DevMode import sbt.Keys._ import sbt._ +import org.typelevel.scalacoptions._ normalizedName := "play-redis" @@ -17,25 +19,25 @@ playVersion := "2.9.0" libraryDependencies ++= Seq( // play framework cache API - "com.typesafe.play" %% "play-cache" % playVersion.value % Provided, + "com.typesafe.play" %% "play-cache" % playVersion.value % Provided, // redis connector - "io.github.rediscala" %% "rediscala" % "1.14.0-akka", + "io.github.rediscala" %% "rediscala" % "1.14.0-akka", // test framework with mockito extension - "org.scalatest" %% "scalatest" % "3.2.17" % Test, - "org.scalamock" %% "scalamock" % "5.2.0" % Test, + "org.scalatest" %% "scalatest" % "3.2.17" % Test, + "org.scalamock" %% "scalamock" % "5.2.0" % Test, // test module for play framework - "com.typesafe.play" %% "play-test" % playVersion.value % Test, + "com.typesafe.play" %% "play-test" % playVersion.value % Test, // to run integration tests - "com.dimafeng" %% "testcontainers-scala-core" % "0.41.2" % Test + "com.dimafeng" %% "testcontainers-scala-core" % "0.41.2" % Test, ) resolvers ++= Seq( - "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" + "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/", ) javacOptions ++= Seq("-Xlint:unchecked", "-encoding", "UTF-8") -scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked") +scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked", "-Ywarn-unused") enablePlugins(CustomReleasePlugin) @@ -44,6 +46,11 @@ coverageExcludedFiles := ".*exceptions.*" Test / test := (Test / testOnly).toTask(" * -- -l \"org.scalatest.Ignore\"").value +semanticdbEnabled := true +semanticdbOptions += "-P:semanticdb:synthetics:on" +semanticdbVersion := scalafixSemanticdb.revision +ThisBuild / scalafixScalaBinaryVersion := CrossVersion.binaryScalaVersion(scalaVersion.value) + wartremoverWarnings ++= Warts.allBut( Wart.Any, Wart.AnyVal, @@ -66,3 +73,18 @@ wartremoverWarnings ++= Warts.allBut( Wart.TryPartial, Wart.Var, ) + +tpolecatDevModeOptions ~= { opts => + opts.filterNot(Set(ScalacOptions.warnError)) +} + +Test / tpolecatExcludeOptions ++= Set( + ScalacOptions.warnValueDiscard, + ScalacOptions.warnNonUnitStatement, +) + +ThisBuild / tpolecatCiModeEnvVar := "CI" +ThisBuild / tpolecatDefaultOptionsMode := DevMode + +addCommandAlias("fix", "; scalafixAll; scalafmtAll; scalafmtSbt") +addCommandAlias("lint", "; scalafmtSbtCheck; scalafmtCheckAll; scalafixAll --check") diff --git a/project/CustomReleasePlugin.scala b/project/CustomReleasePlugin.scala index 6ddef4c7..c9545029 100644 --- a/project/CustomReleasePlugin.scala +++ b/project/CustomReleasePlugin.scala @@ -19,38 +19,37 @@ object CustomReleasePlugin extends AutoPlugin { override def requires: Plugins = ReleasePlugin && GitVersioning && Sonatype - private def customizedReleaseProcess: Seq[ReleaseStep] = { + private def customizedReleaseProcess: Seq[ReleaseStep] = Seq[ReleaseStep]( checkSnapshotDependencies, inquireVersions, DocumentationUpdate.updateDocumentation, ) - } - override def projectSettings: Seq[Setting[_]] = Seq[Setting[_]]( - publishMavenStyle := true, - pomIncludeRepository := { _ => false }, + override def projectSettings: Seq[Setting[?]] = Seq[Setting[?]]( + publishMavenStyle := true, + pomIncludeRepository := { _ => false }, // customized release process - releaseProcess := customizedReleaseProcess, + releaseProcess := customizedReleaseProcess, // release details - homepage := Some(url("https://github.com/karelcemus/play-redis")), - licenses := Seq("Apache 2" -> url("https://www.apache.org/licenses/LICENSE-2.0")), - scmInfo := Some( + homepage := Some(url("https://github.com/karelcemus/play-redis")), + licenses := Seq("Apache 2" -> url("https://www.apache.org/licenses/LICENSE-2.0")), + scmInfo := Some( ScmInfo( url("https://github.com/KarelCemus/play-i18n.git"), - "scm:git@github.com:KarelCemus/play-i18n.git" - ) + "scm:git@github.com:KarelCemus/play-i18n.git", + ), ), - developers := List( - Developer(id = "karel.cemus", name = "Karel Cemus", email = "", url = url("https://github.com/KarelCemus/")) + developers := List( + Developer(id = "karel.cemus", name = "Karel Cemus", email = "", url = url("https://github.com/KarelCemus/")), ), // Publish settings - publishTo := sonatypePublishToBundle.value, + publishTo := sonatypePublishToBundle.value, // git tags without "v" prefix SbtGit.git.gitTagToVersionNumber := { tag: String => if (tag matches "[0-9]+\\..*") Some(tag) else None - } + }, ) private lazy val inquireVersions: ReleaseStep = { implicit st: State => @@ -65,9 +64,10 @@ object CustomReleasePlugin extends AutoPlugin { st.log.info("Press enter to use the default value") - //flatten the Option[Option[String]] as the get returns an Option, and the value inside is an Option + // flatten the Option[Option[String]] as the get returns an Option, and the value inside is an Option val releaseV = readVersion(suggestedReleaseV, "Release version [%s] : ", useDefs, st.get(ReleaseKeys.commandLineReleaseVersion).flatten) st.put(ReleaseKeys.versions, (releaseV, releaseV)) } + } diff --git a/project/DocumentationUpdate.scala b/project/DocumentationUpdate.scala index 85f2b572..9979978e 100644 --- a/project/DocumentationUpdate.scala +++ b/project/DocumentationUpdate.scala @@ -30,9 +30,8 @@ object DocumentationUpdate { private def artifactId(implicit st: State) = st.extracted.get(normalizedName) /** SBT dependency definition */ - private def sbtDependency(version: String)(implicit st: State) = { + private def sbtDependency(version: String)(implicit st: State) = s""" "$groupId" %% "$artifactId" % "$version" """.trim - } /** Major and minor version of Play framework */ private def playMinorVersion(implicit st: State) = { @@ -45,16 +44,16 @@ object DocumentationUpdate { def replacement(version: String)(implicit st: State) = s"$version" } - def updateDocumentation: ReleaseStep = ReleaseStep({ implicit st: State => + def updateDocumentation: ReleaseStep = ReleaseStep { implicit st: State => val commitMessage = s"Documentation updated to version $next" val program = List[State => State]( bumpVersionInDoc(_), bumpLatestVersionInReadme(_), - commitVersion(commitMessage)(_) + commitVersion(commitMessage)(_), ).reduce(_ andThen _) program(st) - }) + } private def bumpVersionInDoc(implicit st: State): State = { val latest = vcs.latest @@ -99,4 +98,5 @@ object DocumentationUpdate { } newState } + } diff --git a/project/ReleaseUtilities.scala b/project/ReleaseUtilities.scala index b27eebca..45db85c4 100644 --- a/project/ReleaseUtilities.scala +++ b/project/ReleaseUtilities.scala @@ -12,11 +12,10 @@ object ReleaseUtilities { def extracted = Project.extract(st) } - def vcs(implicit st: State): Vcs = { + def vcs(implicit st: State): Vcs = Project.extract(st).get(releaseVcs).getOrElse { sys.error("Aborting release. Working directory is not a repository of a recognized VCS.") } - } def processLogger(implicit st: State): ProcessLogger = new ProcessLogger { override def err(s: => String): Unit = st.log.info(s) @@ -29,16 +28,15 @@ object ReleaseUtilities { } /** - * Helper class implementing a transform operation over a file. - * The file is opened, transformed, and saved. + * Helper class implementing a transform operation over a file. The file is + * opened, transformed, and saved. */ implicit class FilesUpdater(val files: Seq[File]) extends AnyVal { - def transform(transform: String => String): Unit = { - files.foreach { - file => IO.write(file, transform(IO.read(file))) + def transform(transform: String => String): Unit = + files.foreach { file => + IO.write(file, transform(IO.read(file))) } - } def getAbsolutePaths: Seq[String] = files.map(_.getAbsolutePath) } @@ -46,7 +44,9 @@ object ReleaseUtilities { implicit def file2updater(file: File): FilesUpdater = new FilesUpdater(Seq(file)) implicit class RichVcs(private val thiz: Vcs) extends AnyVal { + /** latest version extracted from git tag on the current branch */ def latest(implicit st: State) = vcs.cmd("describe", "--abbrev=0", "--tags").!!.trim } + } diff --git a/project/plugins.sbt b/project/plugins.sbt index 034784ae..fbc63766 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -8,10 +8,13 @@ addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.9") addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.3.11") // library release -addSbtPlugin("com.github.sbt" % "sbt-git" % "2.0.1") +addSbtPlugin("com.github.sbt" % "sbt-git" % "2.0.1") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.21") -addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1") -addSbtPlugin("com.github.sbt" % "sbt-release" % "1.1.0") +addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1") +addSbtPlugin("com.github.sbt" % "sbt-release" % "1.1.0") // linters +addSbtPlugin("org.typelevel" % "sbt-tpolecat" % "0.5.0") addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.1.6") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.11.1") diff --git a/src/main/java/play/cache/redis/AsyncRedisList.java b/src/main/java/play/cache/redis/AsyncRedisList.java index 23ad7b17..9847a9e2 100644 --- a/src/main/java/play/cache/redis/AsyncRedisList.java +++ b/src/main/java/play/cache/redis/AsyncRedisList.java @@ -65,7 +65,7 @@ public interface AsyncRedisList { * @param index position of the element * @return element at the index or exception */ - CompletionStage apply(int index); + CompletionStage apply(long index); /** * Returns the element at index index in the list stored at key. @@ -83,7 +83,7 @@ public interface AsyncRedisList { * @return Some(element) at the index, None if no element exists, * or exception when the value is not a list */ - CompletionStage> get(int index); + CompletionStage> get(long index); /** * @return first element of the collection or an exception @@ -154,7 +154,7 @@ default CompletionStage> toList() { * @param element elements to be inserted * @return this collection to chain commands */ - CompletionStage> set(int position, Elem element); + CompletionStage> set(long position, Elem element); /** * Removes first value equal to the given value from the list. @@ -183,7 +183,7 @@ default CompletionStage> toList() { * @param count first N occurrences * @return this collection to chain commands */ - CompletionStage> remove(Elem element, int count); + CompletionStage> remove(Elem element, long count); /** * Removes the element at the given position. If the index @@ -194,7 +194,7 @@ default CompletionStage> toList() { * @param position element index to be removed * @return this collection to chain commands */ - CompletionStage> removeAt(int position); + CompletionStage> removeAt(long position); /** * @return read-only operations over the collection, does not modify data @@ -214,7 +214,7 @@ interface AsyncRedisListView { * @param n takes initial N elements * @return first N elements of the collection if exist */ - default CompletionStage> take(int n) { + default CompletionStage> take(long n) { return slice(0, n - 1); } @@ -224,7 +224,7 @@ default CompletionStage> take(int n) { * @param n ignore initial N elements * @return rest of the collection ignoring initial N elements */ - default CompletionStage> drop(int n) { + default CompletionStage> drop(long n) { return slice(n, -1); } @@ -260,7 +260,7 @@ default CompletionStage> all() { * @param end index of the last element included * @return collection at the specified range */ - CompletionStage> slice(int from, int end); + CompletionStage> slice(long from, long end); } interface AsyncRedisListModification { @@ -276,7 +276,7 @@ interface AsyncRedisListModification { * @param n takes initial N elements * @return this object to chain commands */ - default CompletionStage> take(int n) { + default CompletionStage> take(long n) { return slice(0, n - 1); } @@ -286,7 +286,7 @@ default CompletionStage> take(int n) { * @param n ignore initial N elements * @return this object to chain commands */ - default CompletionStage> drop(int n) { + default CompletionStage> drop(long n) { return slice(n, -1); } @@ -318,7 +318,7 @@ default CompletionStage> drop(int n) { * @param end index of the last element included * @return this object to chain commands */ - CompletionStage> slice(int from, int end); + CompletionStage> slice(long from, long end); } } \ No newline at end of file diff --git a/src/main/scala/play/api/cache/redis/CacheApi.scala b/src/main/scala/play/api/cache/redis/CacheApi.scala index a51dff3e..c7b4dca1 100644 --- a/src/main/scala/play/api/cache/redis/CacheApi.scala +++ b/src/main/scala/play/api/cache/redis/CacheApi.scala @@ -5,34 +5,41 @@ import scala.concurrent.duration.Duration import scala.reflect.ClassTag /** - *

Cache API inspired by basic Play play.api.cache.CacheApi. It implements all its - * operations and in addition it declares couple more useful operations handful - * with cache storage. Furthermore, due to its parametrization it allows to decide - * whether it produces blocking results or non-blocking promises.

+ *

Cache API inspired by basic Play play.api.cache.CacheApi. It implements + * all its operations and in addition it declares couple more useful operations + * handful with cache storage. Furthermore, due to its parametrization it + * allows to decide whether it produces blocking results or non-blocking + * promises.

*/ private[redis] trait AbstractCacheApi[Result[_]] { /** * Retrieve a value from the cache. * - * @param key cache storage key - * @return stored record, Some if exists, otherwise None + * @param key + * cache storage key + * @return + * stored record, Some if exists, otherwise None */ def get[T: ClassTag](key: String): Result[Option[T]] /** * Retrieve the values of all specified keys from the cache. * - * @param key cache storage keys - * @return stored record, Some if exists, otherwise None + * @param key + * cache storage keys + * @return + * stored record, Some if exists, otherwise None */ final def getAll[T: ClassTag](key: String*): Result[Seq[Option[T]]] = getAll(key) /** * Retrieve the values of all specified keys from the cache. * - * @param keys a collection of cache storage keys - * @return stored record, Some if exists, otherwise None + * @param keys + * a collection of cache storage keys + * @return + * stored record, Some if exists, otherwise None */ def getAll[T: ClassTag](keys: Iterable[String]): Result[Seq[Option[T]]] @@ -43,14 +50,18 @@ private[redis] trait AbstractCacheApi[Result[_]] { * Lazy invocation (default): The method **does wait** for the result of * `set` and when it fails, it applies the recovery policy. * - * Eager invocation: The method **does not wait** for the result of `set` - * if the value is not cached and also does not apply recovery policy if - * the `set` fails. - * - * @param key cache storage key - * @param expiration expiration period in seconds. - * @param orElse The default function to invoke if the value was not found in cache. - * @return stored or default record, Some if exists, otherwise None + * Eager invocation: The method **does not wait** for the result of `set` if + * the value is not cached and also does not apply recovery policy if the + * `set` fails. + * + * @param key + * cache storage key + * @param expiration + * expiration period in seconds. + * @param orElse + * The default function to invoke if the value was not found in cache. + * @return + * stored or default record, Some if exists, otherwise None */ def getOrElse[T: ClassTag](key: String, expiration: Duration = Duration.Inf)(orElse: => T): Result[T] @@ -61,104 +72,138 @@ private[redis] trait AbstractCacheApi[Result[_]] { * Lazy invocation (default): The method **does wait** for the result of * `set` and when it fails, it applies the recovery policy. * - * Eager invocation: The method **does not wait** for the result of `set` - * if the value is not cached and also does not apply recovery policy if - * the `set` fails. - * - * @param key cache storage key - * @param expiration expiration period in seconds. - * @param orElse The default function to invoke if the value was not found in cache. - * @return stored or default record, Some if exists, otherwise None + * Eager invocation: The method **does not wait** for the result of `set` if + * the value is not cached and also does not apply recovery policy if the + * `set` fails. + * + * @param key + * cache storage key + * @param expiration + * expiration period in seconds. + * @param orElse + * The default function to invoke if the value was not found in cache. + * @return + * stored or default record, Some if exists, otherwise None */ def getOrFuture[T: ClassTag](key: String, expiration: Duration = Duration.Inf)(orElse: => Future[T]): Future[T] /** * Determines whether value exists in cache. * - * @param key cache storage key - * @return record existence, true if exists, otherwise false + * @param key + * cache storage key + * @return + * record existence, true if exists, otherwise false */ def exists(key: String): Result[Boolean] /** - * Retrieves all keys matching the given pattern. This method invokes KEYS command + * Retrieves all keys matching the given pattern. This method invokes KEYS + * command * * '''Warning:''' complexity is O(n) where n are all keys in the database * - * @param pattern valid KEYS pattern with wildcards - * @return list of matching keys + * @param pattern + * valid KEYS pattern with wildcards + * @return + * list of matching keys */ def matching(pattern: String): Result[Seq[String]] /** - * Set a value into the cache. Expiration time in seconds (0 second means eternity). - * - * @param key cache storage key - * @param value value to store - * @param expiration record duration in seconds - * @return promise + * Set a value into the cache. Expiration time in seconds (0 second means + * eternity). + * + * @param key + * cache storage key + * @param value + * value to store + * @param expiration + * record duration in seconds + * @return + * promise */ def set(key: String, value: Any, expiration: Duration = Duration.Inf): Result[Done] /** - * Set a value into the cache if the given key is not already used, otherwise do nothing. - * Expiration time in seconds (0 second means eternity). - * - * Note: When expiration is defined, it is not an atomic operation. Redis does not - * provide a command for store-if-not-exists with duration. First, it sets the value - * if exists. Then, if the operation succeeded, it sets its expiration date. - * - * Note: When recovery policy used, it recovers with TRUE to indicate - * **"the lock was acquired"** despite actually **not storing** anything. - * - * @param key cache storage key - * @param value value to store - * @param expiration record duration in seconds - * @return true if value was set, false if was ignored because it existed before + * Set a value into the cache if the given key is not already used, otherwise + * do nothing. Expiration time in seconds (0 second means eternity). + * + * Note: When expiration is defined, it is not an atomic operation. Redis + * does not provide a command for store-if-not-exists with duration. First, + * it sets the value if exists. Then, if the operation succeeded, it sets its + * expiration date. + * + * Note: When recovery policy used, it recovers with TRUE to indicate **"the + * lock was acquired"** despite actually **not storing** anything. + * + * @param key + * cache storage key + * @param value + * value to store + * @param expiration + * record duration in seconds + * @return + * true if value was set, false if was ignored because it existed before */ def setIfNotExists(key: String, value: Any, expiration: Duration = Duration.Inf): Result[Boolean] /** - * Sets the given keys to their respective values for eternity. If any value is null, - * the particular key is excluded from the operation and removed from cache instead. - * The operation is atomic when there are no nulls. It replaces existing values. - * - * @param keyValues cache storage key-value pairs to store - * @return promise + * Sets the given keys to their respective values for eternity. If any value + * is null, the particular key is excluded from the operation and removed + * from cache instead. The operation is atomic when there are no nulls. It + * replaces existing values. + * + * @param keyValues + * cache storage key-value pairs to store + * @return + * promise */ def setAll(keyValues: (String, Any)*): Result[Done] /** - * Sets the given keys to their respective values for eternity. It sets all values if none of them - * exist, if at least a single of them exists, it does not set any value, thus it is either all or none. - * If any value is null, the particular key is excluded from the operation and removed from cache instead. - * The operation is atomic when there are no nulls. - * - * @param keyValues cache storage key-value pairs to store - * @return true if value was set, false if any value already existed before + * Sets the given keys to their respective values for eternity. It sets all + * values if none of them exist, if at least a single of them exists, it does + * not set any value, thus it is either all or none. If any value is null, + * the particular key is excluded from the operation and removed from cache + * instead. The operation is atomic when there are no nulls. + * + * @param keyValues + * cache storage key-value pairs to store + * @return + * true if value was set, false if any value already existed before */ def setAllIfNotExist(keyValues: (String, Any)*): Result[Boolean] /** - * If key already exists and is a string, this command appends the value at the end of - * the string. If key does not exist it is created and set as an empty string, so APPEND - * will be similar to SET in this special case. - * - * If it sets new value, it subsequently calls EXPIRE to set required expiration time - * - * @param key cache storage key - * @param value value to append - * @param expiration record duration, applies only when appends to nothing - * @return promise + * If key already exists and is a string, this command appends the value at + * the end of the string. If key does not exist it is created and set as an + * empty string, so APPEND will be similar to SET in this special case. + * + * If it sets new value, it subsequently calls EXPIRE to set required + * expiration time + * + * @param key + * cache storage key + * @param value + * value to append + * @param expiration + * record duration, applies only when appends to nothing + * @return + * promise */ def append(key: String, value: String, expiration: Duration = Duration.Inf): Result[Done] /** - * refreshes expiration time on a given key, useful, e.g., when we want to refresh session duration - * - * @param key cache storage key - * @param expiration new expiration in seconds - * @return promise + * refreshes expiration time on a given key, useful, e.g., when we want to + * refresh session duration + * + * @param key + * cache storage key + * @param expiration + * new expiration in seconds + * @return + * promise */ def expire(key: String, expiration: Duration): Result[Done] @@ -166,132 +211,173 @@ private[redis] trait AbstractCacheApi[Result[_]] { * Returns the remaining time to live of a key that has an expire set, * useful, e.g., when we want to check remaining session duration * - * @param key cache storage key - * @return the remaining time to live of a key. Some finite duration when - * the value exists and the expiration is set, Some infinite duration - * when the value exists but never expires, and None when the key does - * not exist. + * @param key + * cache storage key + * @return + * the remaining time to live of a key. Some finite duration when the value + * exists and the expiration is set, Some infinite duration when the value + * exists but never expires, and None when the key does not exist. */ def expiresIn(key: String): Result[Option[Duration]] /** * Remove a value under the given key from the cache * - * @param key cache storage key - * @return promise + * @param key + * cache storage key + * @return + * promise */ def remove(key: String): Result[Done] /** * Remove all values from the cache * - * @param key1 cache storage key - * @param key2 cache storage key - * @param keys cache storage keys - * @return promise + * @param key1 + * cache storage key + * @param key2 + * cache storage key + * @param keys + * cache storage keys + * @return + * promise */ def remove(key1: String, key2: String, keys: String*): Result[Done] /** - * Removes all keys in arguments. The other remove methods are for syntax sugar + * Removes all keys in arguments. The other remove methods are for syntax + * sugar * - * @param keys cache storage keys - * @return promise + * @param keys + * cache storage keys + * @return + * promise */ def removeAll(keys: String*): Result[Done] /** - *

Removes all keys matching the given pattern. This command has no direct support - * in Redis, it is combination of KEYS and DEL commands.

- * - *
    - *
  1. `KEYS pattern` command finds all keys matching the given pattern
  2. - *
  3. `DEL keys` expires all of them
  4. - *
- * - *

This is usable in scenarios when multiple keys contains same part of the key, such as - * record identification, user identification, etc. For example, we may have keys such - * as 'page/$id/header', 'page/$id/body', 'page/$id/footer' and we want to remove them - * all when the page is changed. We use the benefit of the '''naming convention''' we use and - * execute `removeAllMatching( s"page/$id/*" )`, which invalidates everything related to - * the given page. The benefit is we do not need to maintain the list of all keys to invalidate, - * we invalidate them all at once.

- * - *

* '''Warning:''' complexity is O(n) where n are all keys in the database

- * - * @param pattern this must be valid KEYS pattern - * @return nothing + *

Removes all keys matching the given pattern. This command has no direct + * support in Redis, it is combination of KEYS and DEL commands.

+ * + *
  1. `KEYS pattern` command finds all keys matching the given + * pattern
  2. `DEL keys` expires all of them
+ * + *

This is usable in scenarios when multiple keys contains same part of + * the key, such as record identification, user identification, etc. For + * example, we may have keys such as 'page/$id/header', + * 'page/$id/body', 'page/$id/footer' and we want to remove them all + * when the page is changed. We use the benefit of the '''naming + * convention''' we use and execute `removeAllMatching( s"page/$id/*" + * )`, which invalidates everything related to the given page. The benefit is + * we do not need to maintain the list of all keys to invalidate, we + * invalidate them all at once.

+ * + *

* '''Warning:''' complexity is O(n) where n are all keys in the + * database

+ * + * @param pattern + * this must be valid KEYS pattern + * @return + * nothing */ def removeMatching(pattern: String): Result[Done] /** * Remove all keys in cache * - * @return promise + * @return + * promise */ def invalidate(): Result[Done] /** - * Increments the stored string value representing 10-based signed integer - * by given value. - * - * @param key cache storage key - * @param by size of increment - * @return the value after the increment + * Increments the stored string value representing 10-based signed integer by + * given value. + * + * @param key + * cache storage key + * @param by + * size of increment + * @return + * the value after the increment * @since 1.3.0 */ def increment(key: String, by: Long = 1): Result[Long] /** - * Decrements the stored string value representing 10-based signed integer - * by given value. - * - * @param key cache storage key - * @param by size of decrement - * @return the value after the decrement + * Decrements the stored string value representing 10-based signed integer by + * given value. + * + * @param key + * cache storage key + * @param by + * size of decrement + * @return + * the value after the decrement * @since 1.3.0 */ def decrement(key: String, by: Long = 1): Result[Long] /** - * Scala wrapper around Redis list-related commands. This simplifies use of the lists. - * - * @param key the key storing the list - * @tparam T type of elements within the list - * @return Scala wrapper + * Scala wrapper around Redis list-related commands. This simplifies use of + * the lists. + * + * @param key + * the key storing the list + * @tparam T + * type of elements within the list + * @return + * Scala wrapper */ def list[T: ClassTag](key: String): RedisList[T, Result] /** - * Scala wrapper around Redis set-related commands. This simplifies use of the sets. - * - * @param key the key storing the set - * @tparam T type of elements within the set - * @return Scala wrapper + * Scala wrapper around Redis set-related commands. This simplifies use of + * the sets. + * + * @param key + * the key storing the set + * @tparam T + * type of elements within the set + * @return + * Scala wrapper */ def set[T: ClassTag](key: String): RedisSet[T, Result] /** - * Scala wrapper around Redis hash-related commands. This simplifies use of the hashes, i.e., maps. - * - * @param key the key storing the map - * @tparam T type of elements within the map - * @return Scala wrapper + * Scala wrapper around Redis hash-related commands. This simplifies use of + * the hashes, i.e., maps. + * + * @param key + * the key storing the map + * @tparam T + * type of elements within the map + * @return + * Scala wrapper */ def map[T: ClassTag](key: String): RedisMap[T, Result] /** * Scala wrapper around Redis sorted-set-related commands. * - * @param key the key storing the map - * @tparam T type of elements within the sorted-set - * @return Scala wrapper + * @param key + * the key storing the map + * @tparam T + * type of elements within the sorted-set + * @return + * Scala wrapper */ def zset[T: ClassTag](key: String): RedisSortedSet[T, Result] } -/** Synchronous and blocking implementation of the connection to the redis database */ +/** + * Synchronous and blocking implementation of the connection to the redis + * database + */ trait CacheApi extends AbstractCacheApi[SynchronousResult] -/** Asynchronous non-blocking implementation of the connection to the redis database */ +/** + * Asynchronous non-blocking implementation of the connection to the redis + * database + */ trait CacheAsyncApi extends AbstractCacheApi[AsynchronousResult] diff --git a/src/main/scala/play/api/cache/redis/Expiration.scala b/src/main/scala/play/api/cache/redis/Expiration.scala index bcae616b..5f2e8b11 100644 --- a/src/main/scala/play/api/cache/redis/Expiration.scala +++ b/src/main/scala/play/api/cache/redis/Expiration.scala @@ -1,17 +1,18 @@ package play.api.cache.redis import scala.concurrent.duration._ -import scala.language.implicitConversions /** - * Provides implicit converters to convert expiration date into duration, which is accepted by CacheApi. - * The conversion is performed from now, i.e., the formula is: + * Provides implicit converters to convert expiration date into duration, which + * is accepted by CacheApi. The conversion is performed from now, i.e., the + * formula is: * * {{{ * expireAt in seconds - now in seconds = duration in seconds * }}} */ private[redis] trait ExpirationImplicits { + import java.time.{LocalDateTime, ZoneId} import java.util.Date @@ -23,13 +24,16 @@ private[redis] trait ExpirationImplicits { /** * computes cache duration from the given expiration date time. * - * @param expireAt The class accepts timestamp in milliseconds since 1970 + * @param expireAt + * The class accepts timestamp in milliseconds since 1970 */ class Expiration(val expireAt: Long) extends AnyVal { /** returns now in milliseconds */ private def now: Long = System.currentTimeMillis() - /** converts given timestamp indication expiration date into duration from now */ + /** + * converts given timestamp indication expiration date into duration from now + */ def asExpiration: FiniteDuration = (expireAt - now).milliseconds } diff --git a/src/main/scala/play/api/cache/redis/RecoveryPolicy.scala b/src/main/scala/play/api/cache/redis/RecoveryPolicy.scala index 86f13fa6..fc918686 100644 --- a/src/main/scala/play/api/cache/redis/RecoveryPolicy.scala +++ b/src/main/scala/play/api/cache/redis/RecoveryPolicy.scala @@ -1,12 +1,11 @@ package play.api.cache.redis -import javax.inject.Inject - -import scala.concurrent.Future - import play.api.Logger import play.api.inject._ +import javax.inject.Inject +import scala.concurrent.Future + /** * Recovery policy triggers when a request fails. Based on the implementation, * it may try it again, recover with a default value or just simply log the @@ -17,14 +16,19 @@ import play.api.inject._ trait RecoveryPolicy { /** - * When a failure occurs, this method handles it. It may re-run it, return default value, - * log it or propagate the exception. + * When a failure occurs, this method handles it. It may re-run it, return + * default value, log it or propagate the exception. * - * @param rerun failed request (cache operation) - * @param default default value neutral to the operation - * @param failure incident report - * @tparam T expected result type - * @return failure recovery or exception + * @param rerun + * failed request (cache operation) + * @param default + * default value neutral to the operation + * @param failure + * incident report + * @tparam T + * expected result type + * @return + * failure recovery or exception */ def recoverFrom[T](rerun: => Future[T], default: => Future[T], failure: RedisException): Future[T] @@ -32,14 +36,13 @@ trait RecoveryPolicy { /** name of the policy used for internal purposes */ def name: String = this.getClass.getSimpleName - override def toString = s"RecoveryPolicy($name)" + override def toString: String = s"RecoveryPolicy($name)" // $COVERAGE-ON$ } /** - * Abstract recovery policy provides a general helpers for - * failure reporting. These might be usable when implementing - * own recovery policy. + * Abstract recovery policy provides a general helpers for failure reporting. + * These might be usable when implementing own recovery policy. */ trait Reports extends RecoveryPolicy { @@ -48,21 +51,20 @@ trait Reports extends RecoveryPolicy { protected def message(failure: RedisException): String = failure match { - case TimeoutException(_) => s"Command execution timed out." - case SerializationException(key, message, _) => s"$message for key '$key'." + case TimeoutException(_) => "Command execution timed out." + case SerializationException(key, message, _) => s"$message for key '$key'." case ExecutionFailedException(Some(key), command, _, _) => s"Command $command for key '$key' failed." case ExecutionFailedException(None, command, _, _) => s"Command $command failed." - case UnexpectedResponseException(Some(key), command) => s"Command $command for key '$key' returned unexpected response." - case UnexpectedResponseException(None, command) => s"Command $command returned unexpected response." + case UnexpectedResponseException(Some(key), command) => s"Command $command for key '$key' returned unexpected response." + case UnexpectedResponseException(None, command) => s"Command $command returned unexpected response." } protected def doLog(message: String, cause: Option[Throwable]): Unit /** reports a failure into logs */ - private def report(failure: RedisException): Unit = { + private def report(failure: RedisException): Unit = // create a failure report and report a failure doLog(message(failure), Option(failure.getCause)) - } abstract override def recoverFrom[T](rerun: => Future[T], default: => Future[T], failure: RedisException): Future[T] = { // log it and let through @@ -70,47 +72,44 @@ trait Reports extends RecoveryPolicy { // dive into super.recoverFrom(rerun, default, failure) } + } -/** - * Detailed reports policy produces logs with failure causes - */ +/** Detailed reports policy produces logs with failure causes */ trait DetailedReports extends Reports { protected def doLog(message: String, cause: Option[Throwable]): Unit = cause.fold(log.error(message))(log.error(message, _)) + } /** - * Condensed reports policy produces logs without causes, i.e., logs - * are shorter but less informative. + * Condensed reports policy produces logs without causes, i.e., logs are + * shorter but less informative. */ trait CondensedReports extends Reports { protected def doLog(message: String, cause: Option[Throwable]): Unit = log.error(message) + } -/** - * It fails on failure, i.e., propages the exception to upper layers - */ +/** It fails on failure, i.e., propages the exception to upper layers */ trait FailThrough extends RecoveryPolicy { - override def recoverFrom[T](rerun: => Future[T], default: => Future[T], failure: RedisException): Future[T] = { + override def recoverFrom[T](rerun: => Future[T], default: => Future[T], failure: RedisException): Future[T] = // fail through Future.failed(failure) - } + } -/** - * Recovers with a default value instead of failing through - */ +/** Recovers with a default value instead of failing through */ trait RecoverWithDefault extends RecoveryPolicy { - override def recoverFrom[T](rerun: => Future[T], default: => Future[T], failure: RedisException): Future[T] = { + override def recoverFrom[T](rerun: => Future[T], default: => Future[T], failure: RedisException): Future[T] = // return default value default - } + } /** @@ -119,16 +118,16 @@ trait RecoverWithDefault extends RecoveryPolicy { private[redis] class LogAndFailPolicy @Inject() extends FailThrough with DetailedReports /** - * When the command fails, it logs the failure and returns default value - * to prevent application failure. The returned value is neutral to the - * operation and it should behave like there is no cache + * When the command fails, it logs the failure and returns default value to + * prevent application failure. The returned value is neutral to the operation + * and it should behave like there is no cache */ private[redis] class LogAndDefaultPolicy @Inject() extends RecoverWithDefault with DetailedReports /** - * When the command fails, it logs the failure and returns default value - * to prevent application failure. The returned value is neutral to the - * operation and it should behave like there is no cache + * When the command fails, it logs the failure and returns default value to + * prevent application failure. The returned value is neutral to the operation + * and it should behave like there is no cache * * LogCondensed produces condensed messages without a stacktrace to avoid * extensive logs @@ -144,9 +143,9 @@ private[redis] class LogCondensedAndDefaultPolicy @Inject() extends RecoverWithD private[redis] class LogCondensedAndFailPolicy @Inject() extends FailThrough with CondensedReports /** - * This resolver represents an abstraction over translation - * of the policy name into the instance. It has two subclasses, - * one for guice and the other for compile-time injection. + * This resolver represents an abstraction over translation of the policy name + * into the instance. It has two subclasses, one for guice and the other for + * compile-time injection. */ trait RecoveryPolicyResolver { def resolve: PartialFunction[String, RecoveryPolicy] @@ -155,32 +154,36 @@ trait RecoveryPolicyResolver { // $COVERAGE-OFF$ class RecoveryPolicyResolverImpl extends RecoveryPolicyResolver { + override val resolve: PartialFunction[String, RecoveryPolicy] = { case "log-and-fail" => new LogAndFailPolicy case "log-and-default" => new LogAndDefaultPolicy case "log-condensed-and-fail" => new LogCondensedAndFailPolicy case "log-condensed-and-default" => new LogCondensedAndDefaultPolicy } + } object RecoveryPolicyResolver { - def bindings: Seq[Binding[_]] = Seq( + def bindings: Seq[Binding[?]] = Seq( bind[RecoveryPolicy].qualifiedWith("log-and-fail").to[LogAndFailPolicy], bind[RecoveryPolicy].qualifiedWith("log-and-default").to[LogAndDefaultPolicy], bind[RecoveryPolicy].qualifiedWith("log-condensed-and-fail").to[LogCondensedAndFailPolicy], bind[RecoveryPolicy].qualifiedWith("log-condensed-and-default").to[LogCondensedAndDefaultPolicy], // finally bind the resolver - bind[RecoveryPolicyResolver].to[RecoveryPolicyResolverGuice] + bind[RecoveryPolicyResolver].to[RecoveryPolicyResolverGuice], ) + } /** resolves a policies with guice enabled */ class RecoveryPolicyResolverGuice @Inject() (injector: Injector) extends RecoveryPolicyResolver { - override def resolve: PartialFunction[String, RecoveryPolicy] = { - case name => injector instanceOf bind[RecoveryPolicy].qualifiedWith(name) + override def resolve: PartialFunction[String, RecoveryPolicy] = { case name => + injector instanceOf bind[RecoveryPolicy].qualifiedWith(name) } + } // $COVERAGE-ON$ diff --git a/src/main/scala/play/api/cache/redis/RedisCacheComponents.scala b/src/main/scala/play/api/cache/redis/RedisCacheComponents.scala index 876dcaf6..22541d3e 100644 --- a/src/main/scala/play/api/cache/redis/RedisCacheComponents.scala +++ b/src/main/scala/play/api/cache/redis/RedisCacheComponents.scala @@ -1,13 +1,11 @@ package play.api.cache.redis -import scala.language.implicitConversions - import play.api.inject.ApplicationLifecycle import play.api.{Configuration, Environment} /** - *

Components for compile-time dependency injection. - * It binds components from configuration package

+ *

Components for compile-time dependency injection. It binds components + * from configuration package

*/ trait RedisCacheComponents { implicit def actorSystem: akka.actor.ActorSystem @@ -33,7 +31,7 @@ trait RedisCacheComponents { private lazy val manager = configuration.get("play.cache.redis")(play.api.cache.redis.configuration.RedisInstanceManager) - /** translates the cache name into the configuration */ + /** translates the cache name into the configuration */ private def redisInstance(name: String)(implicit resolver: RedisInstanceResolver): RedisInstance = manager.instanceOf(name).resolved(resolver) private def cacheApi(instance: RedisInstance): impl.RedisCaches = new impl.RedisCachesProvider(instance, akkaSerializer, environment).get diff --git a/src/main/scala/play/api/cache/redis/RedisCacheModule.scala b/src/main/scala/play/api/cache/redis/RedisCacheModule.scala index 0058f98c..f2a75a22 100644 --- a/src/main/scala/play/api/cache/redis/RedisCacheModule.scala +++ b/src/main/scala/play/api/cache/redis/RedisCacheModule.scala @@ -1,19 +1,19 @@ package play.api.cache.redis -import javax.inject._ -import scala.language.implicitConversions -import scala.reflect.ClassTag import play.api.inject._ import play.cache._ +import javax.inject._ +import scala.reflect.ClassTag + /** - * Play framework module implementing play.api.cache.CacheApi for redis-server key/value storage. For more details - * see README. + * Play framework module implementing play.api.cache.CacheApi for redis-server + * key/value storage. For more details see README. */ @Singleton class RedisCacheModule extends Module { - override def bindings(environment: play.api.Environment, config: play.api.Configuration): Seq[Binding[_]] = { + override def bindings(environment: play.api.Environment, config: play.api.Configuration): Seq[Binding[?]] = { def bindDefault = config.get[Boolean]("play.cache.redis.bind-default") // read the config and get the configuration of the redis @@ -25,7 +25,7 @@ class RedisCacheModule extends Module { val commons = Seq( // bind serializer bind[connector.AkkaSerializer].toProvider[connector.AkkaSerializerProvider], - bind[configuration.RedisInstanceResolver].to[GuiceRedisInstanceResolver] + bind[configuration.RedisInstanceResolver].to[GuiceRedisInstanceResolver], ) // bind recovery resolver val recovery = RecoveryPolicyResolver.bindings @@ -35,6 +35,7 @@ class RedisCacheModule extends Module { // return all bindings commons ++ caches ++ recovery ++ defaults } + } trait ProviderImplicits { @@ -47,38 +48,38 @@ private[redis] class RichBindingKey[T](val key: BindingKey[T]) extends AnyVal { trait GuiceProviderImplicits extends ProviderImplicits { def injector: Injector - protected implicit def implicitInjection[X](key: BindingKey[X]): X = injector instanceOf key + implicit protected def implicitInjection[X](key: BindingKey[X]): X = injector instanceOf key } object GuiceProvider extends ProviderImplicits { private class QualifiedBindingKey[T](key: BindingKey[T], f: impl.RedisCaches => T) { @inline private def provider(f: impl.RedisCaches => T)(implicit name: CacheName): Provider[T] = new NamedCacheInstanceProvider(f) - def toBindings(implicit name: CacheName): Binding[_] = key.named(name).to(provider(f)) + def toBindings(implicit name: CacheName): Binding[?] = key.named(name).to(provider(f)) } private def namedBinding[T: ClassTag](f: impl.RedisCaches => T) = new QualifiedBindingKey(bind[T], f) - def bindings(instance: RedisInstanceProvider): Seq[Binding[_]] = { + def bindings(instance: RedisInstanceProvider): Seq[Binding[?]] = { implicit val name: CacheName = new CacheName(instance.name) - Seq[Binding[_]]( + Seq[Binding[?]]( // bind implementation of all caches - bind[impl.RedisCaches].named(name).to(new GuiceRedisCacheProvider(instance)) - ) ++ Seq[QualifiedBindingKey[_]]( - // expose a single-implementation providers - namedBinding(_.redisConnector), - namedBinding(_.sync), - namedBinding(_.async), - namedBinding(_.scalaAsync), - namedBinding(_.scalaSync), - namedBinding(_.javaSync), - namedBinding[play.cache.AsyncCacheApi](_.javaAsync), - namedBinding[play.cache.redis.AsyncCacheApi](_.javaAsync) - ).map(_.toBindings) + bind[impl.RedisCaches].named(name).to(new GuiceRedisCacheProvider(instance)), + ) ++ Seq[QualifiedBindingKey[?]]( + // expose a single-implementation providers + namedBinding(_.redisConnector), + namedBinding(_.sync), + namedBinding(_.async), + namedBinding(_.scalaAsync), + namedBinding(_.scalaSync), + namedBinding(_.javaSync), + namedBinding[play.cache.AsyncCacheApi](_.javaAsync), + namedBinding[play.cache.redis.AsyncCacheApi](_.javaAsync), + ).map(_.toBindings) } - def defaults(instance: RedisInstanceProvider): Seq[Binding[_]] = { + def defaults(instance: RedisInstanceProvider): Seq[Binding[?]] = { implicit val name: CacheName = new CacheName(instance.name) @inline def defaultBinding[T: ClassTag]: Binding[T] = bind[T].to(bind[T].named(name)) @@ -96,22 +97,25 @@ object GuiceProvider extends ProviderImplicits { defaultBinding[play.api.cache.AsyncCacheApi], // java default api defaultBinding[play.cache.SyncCacheApi], - defaultBinding[play.cache.AsyncCacheApi] + defaultBinding[play.cache.AsyncCacheApi], ) } + } class GuiceRedisCacheProvider(instance: RedisInstanceProvider) extends Provider[RedisCaches] with GuiceProviderImplicits { @Inject() var injector: Injector = _ + override lazy val get: RedisCaches = new impl.RedisCachesProvider( instance = instance.resolved(bind[configuration.RedisInstanceResolver]), serializer = bind[connector.AkkaSerializer], - environment = bind[play.api.Environment] + environment = bind[play.api.Environment], )( system = bind[akka.actor.ActorSystem], lifecycle = bind[ApplicationLifecycle], - recovery = bind[RecoveryPolicyResolver] + recovery = bind[RecoveryPolicyResolver], ).get + } class NamedCacheInstanceProvider[T](f: RedisCaches => T)(implicit name: CacheName) extends Provider[T] with GuiceProviderImplicits { @@ -127,7 +131,9 @@ object CacheName { @Singleton class GuiceRedisInstanceResolver @Inject() (val injector: Injector) extends configuration.RedisInstanceResolver with GuiceProviderImplicits { - override def resolve: PartialFunction[String, RedisInstance] = { - case name => bind[RedisInstance].named(name) + + override def resolve: PartialFunction[String, RedisInstance] = { case name => + bind[RedisInstance].named(name) } + } diff --git a/src/main/scala/play/api/cache/redis/RedisCollection.scala b/src/main/scala/play/api/cache/redis/RedisCollection.scala index 8b8bfa7a..2ca8086a 100644 --- a/src/main/scala/play/api/cache/redis/RedisCollection.scala +++ b/src/main/scala/play/api/cache/redis/RedisCollection.scala @@ -11,7 +11,8 @@ private[redis] trait RedisCollection[Collection, Result[_]] { * * Time complexity: O(1) * - * @return size of the list + * @return + * size of the list */ def size: Result[Long] diff --git a/src/main/scala/play/api/cache/redis/RedisList.scala b/src/main/scala/play/api/cache/redis/RedisList.scala index 187cf65c..c525ba5c 100644 --- a/src/main/scala/play/api/cache/redis/RedisList.scala +++ b/src/main/scala/play/api/cache/redis/RedisList.scala @@ -1,167 +1,177 @@ package play.api.cache.redis /** - * Redis Lists are simply lists of strings, sorted by insertion order. - * It is possible to add elements to a Redis List pushing new elements - * on the head (on the left) or on the tail (on the right) of the list. + * Redis Lists are simply lists of strings, sorted by insertion order. It is + * possible to add elements to a Redis List pushing new elements on the head + * (on the left) or on the tail (on the right) of the list. * - * @tparam Elem Data type of the inserted element + * @tparam Elem + * Data type of the inserted element */ trait RedisList[Elem, Result[_]] extends RedisCollection[List[Elem], Result] { override type This = RedisList[Elem, Result] /** - * Insert all the specified values at the head of the list stored at key. - * If key does not exist, it is created as empty list before performing - * the push operations. When key holds a value that is not a list, an - * error is returned. + * Insert all the specified values at the head of the list stored at key. If + * key does not exist, it is created as empty list before performing the push + * operations. When key holds a value that is not a list, an error is + * returned. * - * It is possible to push multiple elements using a single command call - * just specifying multiple arguments at the end of the command. Elements - * are inserted one after the other to the head of the list, from the - * leftmost element to the rightmost element. So for instance the command - * LPUSH mylist a b c will result into a list containing c as first element, - * b as second element and a as third element. + * It is possible to push multiple elements using a single command call just + * specifying multiple arguments at the end of the command. Elements are + * inserted one after the other to the head of the list, from the leftmost + * element to the rightmost element. So for instance the command LPUSH mylist + * a b c will result into a list containing c as first element, b as second + * element and a as third element. * - * @param element element to be prepended - * @return this collection to chain commands + * @param element + * element to be prepended + * @return + * this collection to chain commands */ def prepend(element: Elem): Result[This] /** - * Insert all the specified values at the tail of the list stored at key. - * If key does not exist, it is created as empty list before performing - * the push operation. When key holds a value that is not a list, an error - * is returned. - * * - * It is possible to push multiple elements using a single command call - * just specifying multiple arguments at the end of the command. Elements - * are inserted one after the other to the tail of the list, from the - * leftmost element to the rightmost element. So for instance the command + * Insert all the specified values at the tail of the list stored at key. If + * key does not exist, it is created as empty list before performing the push + * operation. When key holds a value that is not a list, an error is + * returned. * It is possible to push multiple elements using a single + * command call just specifying multiple arguments at the end of the command. + * Elements are inserted one after the other to the tail of the list, from + * the leftmost element to the rightmost element. So for instance the command * RPUSH mylist a b c will result into a list containing a as first element, * b as second element and c as third element. * - * @param element to be apended - * @return this collection to chain commands + * @param element + * to be apended + * @return + * this collection to chain commands */ def append(element: Elem): Result[This] /** - * Insert all the specified values at the head of the list stored at key. - * If key does not exist, it is created as empty list before performing - * the push operations. When key holds a value that is not a list, an - * error is returned. + * Insert all the specified values at the head of the list stored at key. If + * key does not exist, it is created as empty list before performing the push + * operations. When key holds a value that is not a list, an error is + * returned. * - * It is possible to push multiple elements using a single command call - * just specifying multiple arguments at the end of the command. Elements - * are inserted one after the other to the head of the list, from the - * leftmost element to the rightmost element. So for instance the command - * LPUSH mylist a b c will result into a list containing c as first element, - * b as second element and a as third element. + * It is possible to push multiple elements using a single command call just + * specifying multiple arguments at the end of the command. Elements are + * inserted one after the other to the head of the list, from the leftmost + * element to the rightmost element. So for instance the command LPUSH mylist + * a b c will result into a list containing c as first element, b as second + * element and a as third element. * - * @param element element to be prepended - * @return this collection to chain commands + * @param element + * element to be prepended + * @return + * this collection to chain commands */ def +:(element: Elem): Result[This] /** - * Insert all the specified values at the tail of the list stored at key. - * If key does not exist, it is created as empty list before performing - * the push operation. When key holds a value that is not a list, an error - * is returned. - * * - * It is possible to push multiple elements using a single command call - * just specifying multiple arguments at the end of the command. Elements - * are inserted one after the other to the tail of the list, from the - * leftmost element to the rightmost element. So for instance the command + * Insert all the specified values at the tail of the list stored at key. If + * key does not exist, it is created as empty list before performing the push + * operation. When key holds a value that is not a list, an error is + * returned. * It is possible to push multiple elements using a single + * command call just specifying multiple arguments at the end of the command. + * Elements are inserted one after the other to the tail of the list, from + * the leftmost element to the rightmost element. So for instance the command * RPUSH mylist a b c will result into a list containing a as first element, * b as second element and c as third element. * - * @param element to be apended - * @return this collection to chain commands + * @param element + * to be apended + * @return + * this collection to chain commands */ def :+(element: Elem): Result[This] /** - * Insert all the specified values at the head of the list stored at key. - * If key does not exist, it is created as empty list before performing - * the push operations. When key holds a value that is not a list, an - * error is returned. + * Insert all the specified values at the head of the list stored at key. If + * key does not exist, it is created as empty list before performing the push + * operations. When key holds a value that is not a list, an error is + * returned. * - * It is possible to push multiple elements using a single command call - * just specifying multiple arguments at the end of the command. Elements - * are inserted one after the other to the head of the list, from the - * leftmost element to the rightmost element. So for instance the command - * LPUSH mylist a b c will result into a list containing c as first element, - * b as second element and a as third element. + * It is possible to push multiple elements using a single command call just + * specifying multiple arguments at the end of the command. Elements are + * inserted one after the other to the head of the list, from the leftmost + * element to the rightmost element. So for instance the command LPUSH mylist + * a b c will result into a list containing c as first element, b as second + * element and a as third element. * - * @param elements element to be prepended - * @return this collection to chain commands + * @param elements + * element to be prepended + * @return + * this collection to chain commands */ def ++:(elements: Iterable[Elem]): Result[This] /** - * Insert all the specified values at the tail of the list stored at key. - * If key does not exist, it is created as empty list before performing - * the push operation. When key holds a value that is not a list, an error - * is returned. - * * - * It is possible to push multiple elements using a single command call - * just specifying multiple arguments at the end of the command. Elements - * are inserted one after the other to the tail of the list, from the - * leftmost element to the rightmost element. So for instance the command + * Insert all the specified values at the tail of the list stored at key. If + * key does not exist, it is created as empty list before performing the push + * operation. When key holds a value that is not a list, an error is + * returned. * It is possible to push multiple elements using a single + * command call just specifying multiple arguments at the end of the command. + * Elements are inserted one after the other to the tail of the list, from + * the leftmost element to the rightmost element. So for instance the command * RPUSH mylist a b c will result into a list containing a as first element, * b as second element and c as third element. * - * @param elements to be apended - * @return this collection to chain commands + * @param elements + * to be apended + * @return + * this collection to chain commands */ def :++(elements: Iterable[Elem]): Result[This] /** - * Returns the element at index index in the list stored at key. - * The index is zero-based, so 0 means the first element, 1 the - * second element and so on. Negative indices can be used to designate - * elements starting at the tail of the list. Here, -1 means - * the last element, -2 means the penultimate and so forth. When - * the value at key is not a list, an error is returned. + * Returns the element at index index in the list stored at key. The index is + * zero-based, so 0 means the first element, 1 the second element and so on. + * Negative indices can be used to designate elements starting at the tail of + * the list. Here, -1 means the last element, -2 means the penultimate and so + * forth. When the value at key is not a list, an error is returned. * - * Time complexity: O(N) where N is the number of elements to traverse - * to get to the element at index. This makes asking for the first or - * the last element of the list O(1). - * - * @param index position of the element - * @return element at the index or exception + * Time complexity: O(N) where N is the number of elements to traverse to get + * to the element at index. This makes asking for the first or the last + * element of the list O(1). * + * @param index + * position of the element + * @return + * element at the index or exception */ - def apply(index: Int): Result[Elem] + def apply(index: Long): Result[Elem] /** - * Returns the element at index index in the list stored at key. - * The index is zero-based, so 0 means the first element, 1 the - * second element and so on. Negative indices can be used to designate - * elements starting at the tail of the list. Here, -1 means - * the last element, -2 means the penultimate and so forth. When - * the value at key is not a list, an error is returned. + * Returns the element at index index in the list stored at key. The index is + * zero-based, so 0 means the first element, 1 the second element and so on. + * Negative indices can be used to designate elements starting at the tail of + * the list. Here, -1 means the last element, -2 means the penultimate and so + * forth. When the value at key is not a list, an error is returned. * - * Time complexity: O(N) where N is the number of elements to traverse - * to get to the element at index. This makes asking for the first or - * the last element of the list O(1). + * Time complexity: O(N) where N is the number of elements to traverse to get + * to the element at index. This makes asking for the first or the last + * element of the list O(1). * - * @param index position of the element - * @return Some( element ) at the index, None if no element exists, - * or exception when the value is not a list + * @param index + * position of the element + * @return + * Some( element ) at the index, None if no element exists, or exception + * when the value is not a list */ - def get(index: Int): Result[Option[Elem]] + def get(index: Long): Result[Option[Elem]] /** - * @return first element of the collection or an exception + * @return + * first element of the collection or an exception */ def head: Result[Elem] = apply(0) /** - * @return first element of the collection or None + * @return + * first element of the collection or None */ def headOption: Result[Option[Elem]] = get(0) @@ -170,51 +180,61 @@ trait RedisList[Elem, Result[_]] extends RedisCollection[List[Elem], Result] { * * Time complexity: O(1) * - * @return head element if exists + * @return + * head element if exists */ def headPop: Result[Option[Elem]] /** - * @return last element of the collection or an exception + * @return + * last element of the collection or an exception */ def last: Result[Elem] = apply(-1) /** - * @return last element of the collection or None + * @return + * last element of the collection or None */ def lastOption: Result[Option[Elem]] = get(-1) /** - * @return Helper to this.view.all returning all object in the list + * @return + * Helper to this.view.all returning all object in the list */ def toList: Result[Seq[Elem]] = view.all /** - * Inserts value in the list stored at key either before the - * reference value pivot. When key does not exist, it is considered - * an empty list and no operation is performed. An error is returned - * when key exists but does not hold a list value. + * Inserts value in the list stored at key either before the reference value + * pivot. When key does not exist, it is considered an empty list and no + * operation is performed. An error is returned when key exists but does not + * hold a list value. * - * Time complexity: O(N) where N is the number of elements to traverse - * before seeing the value pivot. This means that inserting somewhere - * on the left end on the list (head) can be considered O(1) and - * inserting somewhere on the right end (tail) is O(N). + * Time complexity: O(N) where N is the number of elements to traverse before + * seeing the value pivot. This means that inserting somewhere on the left + * end on the list (head) can be considered O(1) and inserting somewhere on + * the right end (tail) is O(N). * - * @param pivot insert before this value - * @param element elements to be inserted - * @return new size of the collection or None if pivot not found + * @param pivot + * insert before this value + * @param element + * elements to be inserted + * @return + * new size of the collection or None if pivot not found */ def insertBefore(pivot: Elem, element: Elem): Result[Option[Long]] /** - * Sets the list element at index to value. For more information on the - * index argument, see LINDEX. An error is returned for out of range indexes. + * Sets the list element at index to value. For more information on the index + * argument, see LINDEX. An error is returned for out of range indexes. * - * @param position position to insert at - * @param element elements to be inserted - * @return this collection to chain commands + * @param position + * position to insert at + * @param element + * elements to be inserted + * @return + * this collection to chain commands */ - def set(position: Int, element: Elem): Result[This] + def set(position: Long, element: Elem): Result[This] /** * Removes first N values equal to the given value from the list. @@ -225,30 +245,37 @@ trait RedisList[Elem, Result[_]] extends RedisCollection[List[Elem], Result] { * * Time complexity: O(N) * - * @param element element to be removed from the list - * @param count first N occurrences - * @return this collection to chain commands + * @param element + * element to be removed from the list + * @param count + * first N occurrences + * @return + * this collection to chain commands */ - def remove(element: Elem, count: Int = 1): Result[This] + def remove(element: Elem, count: Long = 1L): Result[This] /** - * Removes the element at the given position. If the index - * is out of range, it throws an exception. + * Removes the element at the given position. If the index is out of range, + * it throws an exception. * * Time complexity: O(N) * - * @param position element index to be removed - * @return this collection to chain commands + * @param position + * element index to be removed + * @return + * this collection to chain commands */ - def removeAt(position: Int): Result[This] + def removeAt(position: Long): Result[This] /** - * @return read-only operations over the collection, does not modify data + * @return + * read-only operations over the collection, does not modify data */ def view: RedisListView /** - * @return write-only operations over the collection, modify data + * @return + * write-only operations over the collection, modify data */ def modify: RedisListModification @@ -257,86 +284,99 @@ trait RedisList[Elem, Result[_]] extends RedisCollection[List[Elem], Result] { /** * Helper method of slice. For more details see that method. * - * @param n takes initial N elements - * @return first N elements of the collection if exist + * @param n + * takes initial N elements + * @return + * first N elements of the collection if exist */ - def take(n: Int): Result[Seq[Elem]] = slice(0, n - 1) + def take(n: Long): Result[Seq[Elem]] = slice(0, n - 1) /** * Helper method of slice. For more details see that method. * - * @param n ignore initial N elements - * @return rest of the collection ignoring initial N elements + * @param n + * ignore initial N elements + * @return + * rest of the collection ignoring initial N elements */ - def drop(n: Int): Result[Seq[Elem]] = slice(n, -1) + def drop(n: Long): Result[Seq[Elem]] = slice(n, -1) /** * Helper method of slice. For more details see that method. * - * @return whole collection + * @return + * whole collection */ def all: Result[Seq[Elem]] = slice(0, -1) /** - * Returns the specified elements of the list stored at key. - * The offsets start and stop are zero-based indexes, with 0 being - * the first element of the list (the head of the list), 1 being the - * next element and so on. These offsets can also be negative numbers - * indicating offsets starting at the end of the list. - * For example, -1 is the last element of the list, -2 the penultimate, - * and so on. + * Returns the specified elements of the list stored at key. The offsets + * start and stop are zero-based indexes, with 0 being the first element of + * the list (the head of the list), 1 being the next element and so on. + * These offsets can also be negative numbers indicating offsets starting + * at the end of the list. For example, -1 is the last element of the list, + * -2 the penultimate, and so on. * - * Time complexity: O(S+N) where S is the distance of start offset from HEAD - * for small lists, from nearest end (HEAD or TAIL) for large lists; + * Time complexity: O(S+N) where S is the distance of start offset from + * HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; * and N is the number of elements in the specified range. * - * Out-of-range indexes - * Out of range indexes will not produce an error. If start is larger than - * the end of the list, an empty list is returned. If stop is larger than - * the actual end of the list, Redis will treat it like the last element - * of the list. + * Out-of-range indexes Out of range indexes will not produce an error. If + * start is larger than the end of the list, an empty list is returned. If + * stop is larger than the actual end of the list, Redis will treat it like + * the last element of the list. * - * @param from initial index - * @param end index of the last element included - * @return collection at the specified range + * @param from + * initial index + * @param end + * index of the last element included + * @return + * collection at the specified range */ - def slice(from: Int, end: Int): Result[Seq[Elem]] + def slice(from: Long, end: Long): Result[Seq[Elem]] } trait RedisListModification { /** - * @return RedisList object with Redis API + * @return + * RedisList object with Redis API */ def collection: This /** * Helper method of slice. For more details see that method. * - * @param n takes initial N elements - * @return this object to chain commands + * @param n + * takes initial N elements + * @return + * this object to chain commands */ - def take(n: Int): Result[RedisListModification] = slice(0, n - 1) + def take(n: Long): Result[RedisListModification] = slice(0, n - 1) /** * Helper method of slice. For more details see that method. * - * @param n ignore initial N elements - * @return this object to chain commands + * @param n + * ignore initial N elements + * @return + * this object to chain commands */ - def drop(n: Int): Result[RedisListModification] = slice(n, -1) + def drop(n: Long): Result[RedisListModification] = slice(n, -1) /** * Helper method of slice. Wiping the whole collection * - * @return this object to chain commands + * @return + * this object to chain commands */ def clear(): Result[RedisListModification] /** * Trim an existing list so that it will contain only the specified range * of elements specified. Both start and stop are zero-based indexes, where - * 0 is the first element of the list (the head), 1 the next element and so on. + * 0 is the first element of the list (the head), 1 the next element and so + * on. * * For example: LTRIM foobar 0 2 will modify the list stored at foobar so * that only the first three elements of the list will remain. @@ -350,11 +390,14 @@ trait RedisList[Elem, Result[_]] extends RedisCollection[List[Elem], Result] { * (which causes key to be removed). If end is larger than the end of the * list, Redis will treat it like the last element of the list. * - * @param from initial index - * @param end index of the last element included - * @return this object to chain commands + * @param from + * initial index + * @param end + * index of the last element included + * @return + * this object to chain commands */ - def slice(from: Int, end: Int): Result[RedisListModification] + def slice(from: Long, end: Long): Result[RedisListModification] } } diff --git a/src/main/scala/play/api/cache/redis/RedisMap.scala b/src/main/scala/play/api/cache/redis/RedisMap.scala index ed281232..303242df 100644 --- a/src/main/scala/play/api/cache/redis/RedisMap.scala +++ b/src/main/scala/play/api/cache/redis/RedisMap.scala @@ -1,12 +1,13 @@ package play.api.cache.redis /** - * Redis Hashes are simply hash maps with strings as keys. It is possible to add - * elements to a Redis Hashes by adding new elements into the collection. + * Redis Hashes are simply hash maps with strings as keys. It is possible to + * add elements to a Redis Hashes by adding new elements into the collection. * * This simplified wrapper implements only unordered Maps. * - * @tparam Elem Data type of the inserted element + * @tparam Elem + * Data type of the inserted element */ trait RedisMap[Elem, Result[_]] extends RedisCollection[Map[String, Elem], Result] { @@ -15,87 +16,112 @@ trait RedisMap[Elem, Result[_]] extends RedisCollection[Map[String, Elem], Resul /** * Insert the value at the given key into the map * - * @param field key - * @param value inserted value - * @return the map for the chaining calls + * @param field + * key + * @param value + * inserted value + * @return + * the map for the chaining calls */ def add(field: String, value: Elem): Result[This] /** * Returns the value at the given key into the map * - * @param field key - * @return Some if the value exists in the map, None otherwise + * @param field + * key + * @return + * Some if the value exists in the map, None otherwise */ def get(field: String): Result[Option[Elem]] /** - * Returns the values stored at given keys in the map. The collection - * or results has same size as the collection of given fields, it preserves + * Returns the values stored at given keys in the map. The collection or + * results has same size as the collection of given fields, it preserves * ordering. * - * @param fields keys to get - * @return Some if the value exists in the map, None otherwise + * @param fields + * keys to get + * @return + * Some if the value exists in the map, None otherwise */ def getFields(fields: String*): Result[Seq[Option[Elem]]] = getFields(fields) /** - * Returns the values stored at given keys in the map. The collection - * or results has same size as the collection of given fields, it preserves + * Returns the values stored at given keys in the map. The collection or + * results has same size as the collection of given fields, it preserves * ordering. * - * @param fields keys to get - * @return Some if the value exists in the map, None otherwise + * @param fields + * keys to get + * @return + * Some if the value exists in the map, None otherwise */ def getFields(fields: Iterable[String]): Result[Seq[Option[Elem]]] /** - *

Tests if the field is contained in the map. Returns true if exists, otherwise returns false

+ *

Tests if the field is contained in the map. Returns true if exists, + * otherwise returns false

* - * @note Time complexity: O(1) - * @param field tested field - * @return true if exists in the map, otherwise false + * @note + * Time complexity: O(1) + * @param field + * tested field + * @return + * true if exists in the map, otherwise false */ def contains(field: String): Result[Boolean] /** - *

Removes the specified members from the sorted map stored at key. Non existing members are ignored. - * An error is returned when key exists and does not hold a sorted map.

+ *

Removes the specified members from the sorted map stored at key. Non + * existing members are ignored. An error is returned when key exists and + * does not hold a sorted map.

* - * @note Time complexity: O(N) where N is the number of members to be removed. - * @param field fields to be removed - * @return the map for chaining calls + * @note + * Time complexity: O(N) where N is the number of members + * to be removed. + * @param field + * fields to be removed + * @return + * the map for chaining calls */ def remove(field: String*): Result[This] /** * Increment a value at the given key in the map * - * @param field key - * @param incrementBy increment by this - * @return value after incrementation + * @param field + * key + * @param incrementBy + * increment by this + * @return + * value after incrementation */ def increment(field: String, incrementBy: Long = 1): Result[Long] /** *

Returns all elements in the map

* - * @note Time complexity: O(N) where N is the map cardinality. - * @return all elements in the map + * @note + * Time complexity: O(N) where N is the map cardinality. + * @return + * all elements in the map */ def toMap: Result[Map[String, Elem]] /** * Returns all keys defined in the map * - * @return all used keys + * @return + * all used keys */ def keySet: Result[Set[String]] /** * Returns all values stored in the map * - * @return all stored values + * @return + * all stored values */ def values: Result[Set[Elem]] } diff --git a/src/main/scala/play/api/cache/redis/RedisSet.scala b/src/main/scala/play/api/cache/redis/RedisSet.scala index 80a846a0..30550f9e 100644 --- a/src/main/scala/play/api/cache/redis/RedisSet.scala +++ b/src/main/scala/play/api/cache/redis/RedisSet.scala @@ -6,48 +6,65 @@ package play.api.cache.redis * * This simplified wrapper implements only unordered Sets. * - * @tparam Elem Data type of the inserted element + * @tparam Elem + * Data type of the inserted element */ trait RedisSet[Elem, Result[_]] extends RedisCollection[Set[Elem], Result] { override type This = RedisSet[Elem, Result] /** - *

Add the specified members to the set stored at key. Specified members that are already a member of this - * set are ignored. If key does not exist, a new set is created before adding the specified members.

+ *

Add the specified members to the set stored at key. Specified members + * that are already a member of this set are ignored. If key does not exist, + * a new set is created before adding the specified members.

* - * @note An error is returned when the value stored at key is not a set. - * @note Time complexity: O(1) for each element added, so O(N) to add N elements when the - * command is called with multiple arguments. - * @param element elements to be added - * @return the set for chaining calls + * @note + * An error is returned when the value stored at key is not a set. + * @note + * Time complexity: O(1) for each element added, so O(N) + * to add N elements when the command is called with multiple arguments. + * @param element + * elements to be added + * @return + * the set for chaining calls */ def add(element: Elem*): Result[This] /** - *

Tests if the element is contained in the set. Returns true if exists, otherwise returns false

+ *

Tests if the element is contained in the set. Returns true if exists, + * otherwise returns false

* - * @note Time complexity: O(1) - * @param element tested element - * @return true if exists in the set, otherwise false + * @note + * Time complexity: O(1) + * @param element + * tested element + * @return + * true if exists in the set, otherwise false */ def contains(element: Elem): Result[Boolean] /** - *

Removes the specified members from the sorted set stored at key. Non existing members are ignored. - * An error is returned when key exists and does not hold a sorted set.

+ *

Removes the specified members from the sorted set stored at key. Non + * existing members are ignored. An error is returned when key exists and + * does not hold a sorted set.

* - * @note Time complexity: O(N) where N is the number of members to be removed. - * @param element elements to be removed - * @return the set for chaining calls + * @note + * Time complexity: O(N) where N is the number of members + * to be removed. + * @param element + * elements to be removed + * @return + * the set for chaining calls */ def remove(element: Elem*): Result[This] /** *

Returns all elements in the set

* - * @note Time complexity: O(N) where N is the set cardinality. - * @return all elements in the set + * @note + * Time complexity: O(N) where N is the set cardinality. + * @return + * all elements in the set */ def toSet: Result[Set[Elem]] } diff --git a/src/main/scala/play/api/cache/redis/RedisSortedSet.scala b/src/main/scala/play/api/cache/redis/RedisSortedSet.scala index 8c9df70e..8e683871 100644 --- a/src/main/scala/play/api/cache/redis/RedisSortedSet.scala +++ b/src/main/scala/play/api/cache/redis/RedisSortedSet.scala @@ -6,45 +6,64 @@ trait RedisSortedSet[Elem, Result[_]] extends RedisCollection[TreeSet[Elem], Res override type This = RedisSortedSet[Elem, Result] /** - * Adds all the specified members with the specified scores to the sorted set stored at key. - * It is possible to specify multiple score / member pairs. If a specified member is already - * a member of the sorted set, the score is updated and the element reinserted at the right - * position to ensure the correct ordering. + * Adds all the specified members with the specified scores to the sorted set + * stored at key. It is possible to specify multiple score / member pairs. If + * a specified member is already a member of the sorted set, the score is + * updated and the element reinserted at the right position to ensure the + * correct ordering. * - * If key does not exist, a new sorted set with the specified members as sole members is created, - * like if the sorted set was empty. + * If key does not exist, a new sorted set with the specified members as sole + * members is created, like if the sorted set was empty. * - * @note If the key exists but does not hold a sorted set, an error is returned. - * @note Time complexity: O(log(N)) for each item added, where N is the number of elements in the sorted set. - * @param scoreValues values and corresponding scores to be added - * @return the sorted set for chaining calls + * @note + * If the key exists but does not hold a sorted set, an error is returned. + * @note + * Time complexity: O(log(N)) for each item added, where N + * is the number of elements in the sorted set. + * @param scoreValues + * values and corresponding scores to be added + * @return + * the sorted set for chaining calls */ def add(scoreValues: (Double, Elem)*): Result[This] /** - *

Tests if the element is contained in the sorted set. Returns true if exists, otherwise returns false

+ *

Tests if the element is contained in the sorted set. Returns true if + * exists, otherwise returns false

* - * @note Time complexity: O(1) - * @param element tested element - * @return true if exists in the set, otherwise false + * @note + * Time complexity: O(1) + * @param element + * tested element + * @return + * true if exists in the set, otherwise false */ def contains(element: Elem): Result[Boolean] /** - *

Removes the specified members from the sorted set stored at key. Non existing members are ignored. - * An error is returned when key exists and does not hold a sorted set.

+ *

Removes the specified members from the sorted set stored at key. Non + * existing members are ignored. An error is returned when key exists and + * does not hold a sorted set.

* - * @note Time complexity: O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed. - * @param element elements to be removed - * @return the sorted set for chaining calls + * @note + * Time complexity: O(M*log(N)) with N being the number of + * elements in the sorted set and M the number of elements to be removed. + * @param element + * elements to be removed + * @return + * the sorted set for chaining calls */ def remove(element: Elem*): Result[This] /** - * Returns the specified range of elements in the sorted set stored at key which sorted in order specified by param `isReverse`. - * @param start the start index of the range - * @param stop the stop index of the range - * @param isReverse whether sorted in descending order or not + * Returns the specified range of elements in the sorted set stored at key + * which sorted in order specified by param `isReverse`. + * @param start + * the start index of the range + * @param stop + * the stop index of the range + * @param isReverse + * whether sorted in descending order or not * @return */ def range(start: Long, stop: Long, isReverse: Boolean = false): Result[Seq[Elem]] diff --git a/src/main/scala/play/api/cache/redis/configuration/Equals.scala b/src/main/scala/play/api/cache/redis/configuration/Equals.scala index 24151e1a..1816d77c 100644 --- a/src/main/scala/play/api/cache/redis/configuration/Equals.scala +++ b/src/main/scala/play/api/cache/redis/configuration/Equals.scala @@ -6,8 +6,8 @@ private[configuration] object Equals { // $COVERAGE-OFF$ @inline - def check[T](a: T, b: T)(property: (T => Any)*): Boolean = { + def check[T](a: T, b: T)(property: (T => Any)*): Boolean = property.forall(property => property(a) === property(b)) - } // $COVERAGE-ON$ + } diff --git a/src/main/scala/play/api/cache/redis/configuration/HostnameResolver.scala b/src/main/scala/play/api/cache/redis/configuration/HostnameResolver.scala index aba0bdc3..abe56558 100644 --- a/src/main/scala/play/api/cache/redis/configuration/HostnameResolver.scala +++ b/src/main/scala/play/api/cache/redis/configuration/HostnameResolver.scala @@ -7,4 +7,5 @@ object HostnameResolver { implicit class HostNameResolver(private val name: String) extends AnyVal { def resolvedIpAddress: String = InetAddress.getByName(name).getHostAddress } + } diff --git a/src/main/scala/play/api/cache/redis/configuration/RedisConfigLoader.scala b/src/main/scala/play/api/cache/redis/configuration/RedisConfigLoader.scala index 6ac8e738..d0a91f37 100644 --- a/src/main/scala/play/api/cache/redis/configuration/RedisConfigLoader.scala +++ b/src/main/scala/play/api/cache/redis/configuration/RedisConfigLoader.scala @@ -3,34 +3,30 @@ package play.api.cache.redis.configuration import com.typesafe.config.Config import play.api.cache.redis._ -/** - * Config loader helper, provides some useful methods - * easing config load - */ +/** Config loader helper, provides some useful methods easing config load */ private[configuration] object RedisConfigLoader { - implicit class ConfigOption(val config: Config) extends AnyVal { - def getOption[T](path: String, f: Config => String => T): Option[T] = { + implicit class ConfigOption(private val config: Config) extends AnyVal { + + def getOption[T](path: String, f: Config => String => T): Option[T] = if (config hasPath path) Some(f(config)(path)) else None - } - def getNullable[T](path: String, f: Config => String => T): Option[Option[T]] = { + def getNullable[T](path: String, f: Config => String => T): Option[Option[T]] = if (config hasPathOrNull path) Some(getOption(path, f)) else None - } + } - implicit class ConfigPath(val path: String) extends AnyVal { + implicit class ConfigPath(private val path: String) extends AnyVal { def /(suffix: String): String = if (path === "") suffix else s"$path.$suffix" } - def required(path: String) = throw new IllegalStateException(s"Configuration key '$path' is missing.") + def required(path: String): Nothing = throw new IllegalStateException(s"Configuration key '$path' is missing.") } /** - * Extended RedisConfig loader, it requires a default settings - * to be able to actually load the configuration. This default - * settings are used as a fallback value when the overloading - * settings are missing + * Extended RedisConfig loader, it requires a default settings to be able to + * actually load the configuration. This default settings are used as a + * fallback value when the overloading settings are missing */ private[configuration] trait RedisConfigLoader[T] { outer => @@ -38,9 +34,9 @@ private[configuration] trait RedisConfigLoader[T] { outer => } /** - * Extended RedisConfig loader to a produce redis instance, it requires - * a default settings to be able to actually load the configuration and its name. This default - * settings are used as a fallback value when the overloading + * Extended RedisConfig loader to a produce redis instance, it requires a + * default settings to be able to actually load the configuration and its name. + * This default settings are used as a fallback value when the overloading * settings are missing */ private[configuration] trait RedisConfigInstanceLoader[T] { outer => diff --git a/src/main/scala/play/api/cache/redis/configuration/RedisHost.scala b/src/main/scala/play/api/cache/redis/configuration/RedisHost.scala index 30183859..e65cbc48 100644 --- a/src/main/scala/play/api/cache/redis/configuration/RedisHost.scala +++ b/src/main/scala/play/api/cache/redis/configuration/RedisHost.scala @@ -1,31 +1,35 @@ package play.api.cache.redis.configuration -import play.api.ConfigLoader - import com.typesafe.config.Config +import play.api.ConfigLoader -/** - * Configures a single node either a standalone or within a cluster. - */ +/** Configures a single node either a standalone or within a cluster. */ trait RedisHost { + /** host with redis server */ def host: String + /** port redis listens on */ def port: Int + /** redis database identifier to work with */ def database: Option[Int] + /** when enabled security, this returns username for the AUTH command */ def username: Option[String] + /** when enabled security, this returns password for the AUTH command */ def password: Option[String] // $COVERAGE-OFF$ /** trait-specific equals */ override def equals(obj: scala.Any): Boolean = equalsAsHost(obj) + /** trait-specific equals, invokable from children */ protected def equalsAsHost(obj: scala.Any): Boolean = obj match { case that: RedisHost => Equals.check(this, that)(_.host, _.port, _.username, _.database, _.password) case _ => false } + /** to string */ override def toString: String = (password, database) match { case (Some(password), Some(database)) => s"redis://${username.getOrElse("redis")}:$password@$host:$port?db=$database" @@ -34,6 +38,7 @@ trait RedisHost { case (None, None) => s"redis://$host:$port" } // $COVERAGE-ON$ + } object RedisHost extends ConfigLoader[RedisHost] { @@ -47,21 +52,22 @@ object RedisHost extends ConfigLoader[RedisHost] { port = config.getInt(path / "port"), database = config.getOption(path / "database", _.getInt), username = config.getOption(path / "username", _.getString), - password = config.getOption(path / "password", _.getString) + password = config.getOption(path / "password", _.getString), ) /** read environment url or throw an exception */ def fromConnectionString(connectionString: String): RedisHost = ConnectionString findFirstMatchIn connectionString match { // read the environment variable and fill missing information from the local configuration file - case Some(matcher) => new RedisHost { - override val host: String = matcher.group("host") - override val port: Int = matcher.group("port").toInt - override val database: Option[Nothing] = None - override val username: Option[String] = Option(matcher.group("username")) - override val password: Option[String] = Option(matcher.group("password")) - } + case Some(matcher) => + new RedisHost { + override val host: String = matcher.group("host") + override val port: Int = matcher.group("port").toInt + override val database: Option[Nothing] = None + override val username: Option[String] = Option(matcher.group("username")) + override val password: Option[String] = Option(matcher.group("password")) + } // unexpected format - case None => throw new IllegalArgumentException(s"Unexpected format of the connection string: '$connectionString'. Expected format is 'redis://[user:password@]host:port'.") + case None => throw new IllegalArgumentException(s"Unexpected format of the connection string: '$connectionString'. Expected format is 'redis://[user:password@]host:port'.") } def apply(host: String, port: Int, database: Option[Int] = None, username: Option[String] = None, password: Option[String] = None): RedisHost = @@ -77,16 +83,13 @@ object RedisHost extends ConfigLoader[RedisHost] { } // $COVERAGE-OFF$ - def unapply(host: RedisHost): Option[(String, Int, Option[Int],Option[String], Option[String])] = { + def unapply(host: RedisHost): Some[(String, Int, Option[Int], Option[String], Option[String])] = Some((host.host, host.port, host.database, host.username, host.password)) - } // $COVERAGE-ON$ + } -/** - * - * A helper trait delegating properties into the inner settings object - */ +/** A helper trait delegating properties into the inner settings object */ trait RedisDelegatingHost extends RedisHost { def innerHost: RedisHost override def host: String = innerHost.host diff --git a/src/main/scala/play/api/cache/redis/configuration/RedisInstance.scala b/src/main/scala/play/api/cache/redis/configuration/RedisInstance.scala index ba28889c..41cb9170 100644 --- a/src/main/scala/play/api/cache/redis/configuration/RedisInstance.scala +++ b/src/main/scala/play/api/cache/redis/configuration/RedisInstance.scala @@ -3,38 +3,44 @@ package play.api.cache.redis.configuration import play.api.cache.redis._ /** - * Abstraction over clusters and standalone instances. This trait - * encapsulates a common settings and simplifies pattern matching. + * Abstraction over clusters and standalone instances. This trait encapsulates + * a common settings and simplifies pattern matching. */ sealed trait RedisInstance extends RedisSettings { + /** name of the redis instance */ def name: String // $COVERAGE-OFF$ /** trait-specific equals */ override def equals(obj: scala.Any): Boolean = equalsAsInstance(obj) + /** trait-specific equals, invokable from children */ protected def equalsAsInstance(obj: scala.Any): Boolean = obj match { case that: RedisInstance => this.name === that.name && equalsAsSettings(that) case _ => false } // $COVERAGE-ON$ + } /** - * Type of Redis Instance - a cluster. It encapsulates common settings of the instance - * and the list of cluster nodes. + * Type of Redis Instance - a cluster. It encapsulates common settings of the + * instance and the list of cluster nodes. */ sealed trait RedisCluster extends RedisInstance { + /** nodes definition when cluster is defined */ def nodes: List[RedisHost] + // $COVERAGE-OFF$ /** trait-specific equals */ override def equals(obj: scala.Any): Boolean = obj match { case that: RedisCluster => equalsAsInstance(that) && this.nodes === that.nodes case _ => false } + /** to string */ - override def toString = s"Cluster[${nodes mkString ","}]" + override def toString: String = s"Cluster[${nodes mkString ","}]" // $COVERAGE-ON$ } @@ -50,25 +56,29 @@ object RedisCluster { override val nodes: List[RedisHost] = _nodes override val settings: RedisSettings = _settings } + } /** - * A type of Redis Instance - a standalone instance. It encapsulates - * common settings of the instance and provides a connection settings. + * A type of Redis Instance - a standalone instance. It encapsulates common + * settings of the instance and provides a connection settings. */ sealed trait RedisStandalone extends RedisInstance with RedisHost { + // $COVERAGE-OFF$ /** trait-specific equals */ override def equals(obj: scala.Any): Boolean = obj match { case that: RedisStandalone => equalsAsInstance(that) && equalsAsHost(that) case _ => false } + /** to string */ override def toString: String = database match { case Some(database) => s"Standalone($name@$host:$port?db=$database)" case None => s"Standalone($name@$host:$port)" } // $COVERAGE-ON$ + } object RedisStandalone { @@ -83,11 +93,12 @@ object RedisStandalone { override val innerHost: RedisHost = _host override val settings: RedisSettings = _settings } + } /** - * Type of Redis Instance - a sentinel. It encapsulates common settings of - * the instance, name of the master group, and the list of sentinel nodes. + * Type of Redis Instance - a sentinel. It encapsulates common settings of the + * instance, name of the master group, and the list of sentinel nodes. */ sealed trait RedisSentinel extends RedisInstance { @@ -99,9 +110,11 @@ sealed trait RedisSentinel extends RedisInstance { override def equals(obj: scala.Any): Boolean = obj match { case that: RedisSentinel => equalsAsInstance(that) && this.sentinels === that.sentinels + case _ => false } + /** to string */ - override def toString = s"Sentinel[${sentinels mkString ","}]" + override def toString: String = s"Sentinel[${sentinels mkString ","}]" } object RedisSentinel { @@ -113,13 +126,12 @@ object RedisSentinel { settings: RedisSettings, username: Option[String] = None, password: Option[String] = None, - database: Option[Int] = None + database: Option[Int] = None, ): RedisSentinel = create(name, masterGroup, username, password, database, sentinels, settings) @inline - private def create(_name: String, _masterGroup: String, _username: Option[String], _password: Option[String], _database: Option[Int], - _sentinels: List[RedisHost], _settings: RedisSettings): RedisSentinel = + private def create(_name: String, _masterGroup: String, _username: Option[String], _password: Option[String], _database: Option[Int], _sentinels: List[RedisHost], _settings: RedisSettings): RedisSentinel = new RedisSentinel with RedisDelegatingSettings { override val name: String = _name override val masterGroup: String = _masterGroup diff --git a/src/main/scala/play/api/cache/redis/configuration/RedisInstanceManager.scala b/src/main/scala/play/api/cache/redis/configuration/RedisInstanceManager.scala index abf8a6e5..0064e760 100644 --- a/src/main/scala/play/api/cache/redis/configuration/RedisInstanceManager.scala +++ b/src/main/scala/play/api/cache/redis/configuration/RedisInstanceManager.scala @@ -1,18 +1,17 @@ package play.api.cache.redis.configuration +import com.typesafe.config.Config import play.api.ConfigLoader import play.api.cache.redis._ -import com.typesafe.config.Config /** - * Cache manager maintains a list of the redis caches in the application. - * It also provides a configuration of the instance based on the name - * of the cache. + * Cache manager maintains a list of the redis caches in the application. It + * also provides a configuration of the instance based on the name of the + * cache. * - * This object should be used only during the configuration phase to - * simplify binding creation and application configuration. While - * the application is running, there should be no need to use this - * manager. + * This object should be used only during the configuration phase to simplify + * binding creation and application configuration. While the application is + * running, there should be no need to use this manager. */ trait RedisInstanceManager extends Iterable[RedisInstanceProvider] { @@ -38,6 +37,7 @@ trait RedisInstanceManager extends Iterable[RedisInstanceProvider] { case _ => false } // $COVERAGE-ON$ + } private[redis] object RedisInstanceManager extends ConfigLoader[RedisInstanceManager] { @@ -52,12 +52,15 @@ private[redis] object RedisInstanceManager extends ConfigLoader[RedisInstanceMan // construct a manager if (hasInstances) new RedisInstanceManagerImpl(config, path) else new RedisInstanceManagerFallback(config, path) } + } /** - * Redis manager reading 'play.cache.redis.instances' tree for cache definitions. + * Redis manager reading 'play.cache.redis.instances' tree for cache + * definitions. */ class RedisInstanceManagerImpl(config: Config, path: String)(implicit defaults: RedisSettings) extends RedisInstanceManager { + import JavaCompatibilityBase._ import RedisConfigLoader._ @@ -75,10 +78,12 @@ class RedisInstanceManagerImpl(config: Config, path: String)(implicit defaults: override def instanceOfOption(name: String): Option[RedisInstanceProvider] = if (config hasPath (path / "instances" / name)) Some(RedisInstanceProvider.load(config, path / "instances" / name, name)) else None + } /** - * Redis manager reading 'play.cache.redis' root for a single fallback default cache. + * Redis manager reading 'play.cache.redis' root for a single fallback default + * cache. */ class RedisInstanceManagerFallback(config: Config, path: String)(implicit defaults: RedisSettings) extends RedisInstanceManager { import RedisConfigLoader._ @@ -93,4 +98,5 @@ class RedisInstanceManagerFallback(config: Config, path: String)(implicit defaul /** returns a configuration of a single named redis instance */ override def instanceOfOption(name: String): Option[RedisInstanceProvider] = if (name === this.name) Some(defaultInstance) else None + } diff --git a/src/main/scala/play/api/cache/redis/configuration/RedisInstanceProvider.scala b/src/main/scala/play/api/cache/redis/configuration/RedisInstanceProvider.scala index c0c78e4e..7d30a779 100644 --- a/src/main/scala/play/api/cache/redis/configuration/RedisInstanceProvider.scala +++ b/src/main/scala/play/api/cache/redis/configuration/RedisInstanceProvider.scala @@ -1,10 +1,9 @@ package play.api.cache.redis.configuration -import java.net.InetAddress - +import com.typesafe.config.Config import play.api.cache.redis._ -import com.typesafe.config.Config +import java.net.InetAddress trait RedisInstanceResolver { def resolve: PartialFunction[String, RedisInstance] @@ -27,7 +26,7 @@ final class ResolvedRedisInstance(val instance: RedisInstance) extends RedisInst override def hashCode(): Int = name.hashCode - override def toString = s"ResolvedRedisInstance($name@$instance)" + override def toString: String = s"ResolvedRedisInstance($name@$instance)" // $COVERAGE-ON$ } @@ -42,7 +41,7 @@ final class UnresolvedRedisInstance(val name: String) extends RedisInstanceProvi override def hashCode(): Int = name.hashCode - override def toString = s"UnresolvedRedisInstance($name)" + override def toString: String = s"UnresolvedRedisInstance($name)" // $COVERAGE-ON$ } @@ -50,7 +49,7 @@ private[configuration] object RedisInstanceProvider extends RedisConfigInstanceL import RedisConfigLoader._ override def load(config: Config, path: String, name: String)(implicit defaults: RedisSettings): RedisInstanceProvider = { - config.getOption(path / "source", _.getString) getOrElse defaults.source match { + config.getOption(path / "source", _.getString).getOrElse(defaults.source) match { // required static configuration of the standalone instance using application.conf case "standalone" => RedisInstanceStandalone // required static configuration of the cluster using application.conf @@ -64,35 +63,36 @@ private[configuration] object RedisInstanceProvider extends RedisConfigInstanceL // supplied custom configuration case "custom" => RedisInstanceCustom // found but unrecognized - case other => invalidConfiguration( - s""" - |Unrecognized configuration provider '$other' in ${config.getValue(path / "source").origin().filename()} - |at ${config.getValue(path / "source").origin().lineNumber()}. - |Expected values are 'standalone', 'cluster', 'connection-string', and 'custom'. - """.stripMargin - ) + case other => + invalidConfiguration( + s""" + |Unrecognized configuration provider '$other' in ${config.getValue(path / "source").origin().filename()} + |at ${config.getValue(path / "source").origin().lineNumber()}. + |Expected values are 'standalone', 'cluster', 'connection-string', and 'custom'. + """.stripMargin, + ) } }.load(config, path, name) + } -/** - * Statically configured single standalone redis instance - */ +/** Statically configured single standalone redis instance */ private[configuration] object RedisInstanceStandalone extends RedisConfigInstanceLoader[RedisInstanceProvider] { + override def load(config: Config, path: String, instanceName: String)(implicit defaults: RedisSettings) = new ResolvedRedisInstance( RedisStandalone.apply( name = instanceName, host = RedisHost.load(config, path), - settings = RedisSettings.withFallback(defaults).load(config, path) - ) + settings = RedisSettings.withFallback(defaults).load(config, path), + ), ) + } -/** - * Statically configured redis cluster - */ +/** Statically configured redis cluster */ private[configuration] object RedisInstanceCluster extends RedisConfigInstanceLoader[RedisInstanceProvider] { + import JavaCompatibilityBase._ import RedisConfigLoader._ @@ -101,14 +101,13 @@ private[configuration] object RedisInstanceCluster extends RedisConfigInstanceLo RedisCluster.apply( name = instanceName, nodes = config.getConfigList(path / "cluster").asScala.map(config => RedisHost.load(config)).toList, - settings = RedisSettings.withFallback(defaults).load(config, path) - ) + settings = RedisSettings.withFallback(defaults).load(config, path), + ), ) + } -/** - * Statically configured redis cluster driven by DNS configuration - */ +/** Statically configured redis cluster driven by DNS configuration */ private[configuration] object RedisInstanceAwsCluster extends RedisConfigInstanceLoader[RedisInstanceProvider] { import RedisConfigLoader._ @@ -117,14 +116,16 @@ private[configuration] object RedisInstanceAwsCluster extends RedisConfigInstanc RedisCluster.apply( name = instanceName, nodes = InetAddress.getAllByName(config.getString(path / "host")).map(address => RedisHost(address.getHostAddress, 6379)).toList, - settings = RedisSettings.withFallback(defaults).load(config, path) - ) + settings = RedisSettings.withFallback(defaults).load(config, path), + ), ) + } /** - * Reads a configuration from the connection string, possibly from an environmental variable. - * This instance configuration is designed to work in PaaS environments such as Heroku. + * Reads a configuration from the connection string, possibly from an + * environmental variable. This instance configuration is designed to work in + * PaaS environments such as Heroku. */ private[configuration] object RedisInstanceEnvironmental extends RedisConfigInstanceLoader[RedisInstanceProvider] { import RedisConfigLoader._ @@ -134,15 +135,15 @@ private[configuration] object RedisInstanceEnvironmental extends RedisConfigInst RedisStandalone.apply( name = instanceName, host = RedisHost.fromConnectionString(config getString path./("connection-string")), - settings = RedisSettings.withFallback(defaults).load(config, path) - ) + settings = RedisSettings.withFallback(defaults).load(config, path), + ), ) + } -/** - * Statically configures redis sentinel - */ +/** Statically configures redis sentinel */ private[configuration] object RedisInstanceSentinel extends RedisConfigInstanceLoader[RedisInstanceProvider] { + import JavaCompatibilityBase._ import RedisConfigLoader._ @@ -154,17 +155,21 @@ private[configuration] object RedisInstanceSentinel extends RedisConfigInstanceL masterGroup = config.getString(path / "master-group"), password = config.getOption(path / "password", _.getString), database = config.getOption(path / "database", _.getInt), - settings = RedisSettings.withFallback(defaults).load(config, path) - ) + settings = RedisSettings.withFallback(defaults).load(config, path), + ), ) + } /** - * This binder indicates that the user provides his own configuration of this named cache. + * This binder indicates that the user provides his own configuration of this + * named cache. */ private[configuration] object RedisInstanceCustom extends RedisConfigInstanceLoader[RedisInstanceProvider] { + override def load(config: Config, path: String, instanceName: String)(implicit defaults: RedisSettings) = new UnresolvedRedisInstance( - name = instanceName + name = instanceName, ) + } diff --git a/src/main/scala/play/api/cache/redis/configuration/RedisSettings.scala b/src/main/scala/play/api/cache/redis/configuration/RedisSettings.scala index 10276d0d..60b3ac17 100644 --- a/src/main/scala/play/api/cache/redis/configuration/RedisSettings.scala +++ b/src/main/scala/play/api/cache/redis/configuration/RedisSettings.scala @@ -1,35 +1,42 @@ package play.api.cache.redis.configuration -import play.api.ConfigLoader - import com.typesafe.config.Config +import play.api.ConfigLoader /** - * Configures non-connection related settings of redis instance, - * e.g., synchronization timeout, Akka dispatcher, and recovery policy. + * Configures non-connection related settings of redis instance, e.g., + * synchronization timeout, Akka dispatcher, and recovery policy. */ trait RedisSettings { + /** the name of the invocation context executing all commands to Redis */ def invocationContext: String + /** the name of the invocation policy used in getOrElse methods */ def invocationPolicy: String + /** timeout configuration */ def timeout: RedisTimeouts + /** recovery policy used with the instance */ def recovery: String + /** configuration source */ def source: String + /** instance prefix */ def prefix: Option[String] // $COVERAGE-OFF$ /** trait-specific equals */ override def equals(obj: scala.Any): Boolean = equalsAsSettings(obj) + /** trait-specific equals, invokable from children */ protected def equalsAsSettings(obj: scala.Any): Boolean = obj match { case that: RedisSettings => Equals.check(this, that)(_.invocationContext, _.invocationPolicy, _.timeout, _.recovery, _.source, _.prefix) case _ => false } // $COVERAGE-ON$ + } object RedisSettings extends ConfigLoader[RedisSettings] { @@ -41,18 +48,19 @@ object RedisSettings extends ConfigLoader[RedisSettings] { recovery = loadRecovery(config, path).get, timeout = loadTimeouts(config, path)(RedisTimeouts.requiredDefault), source = loadSource(config, path).get, - prefix = loadPrefix(config, path) + prefix = loadPrefix(config, path), ) def withFallback(fallback: RedisSettings): ConfigLoader[RedisSettings] = - (config: Config, path: String) => apply( - dispatcher = loadInvocationContext(config, path) getOrElse fallback.invocationContext, - invocationPolicy = loadInvocationPolicy(config, path) getOrElse fallback.invocationPolicy, - recovery = loadRecovery(config, path) getOrElse fallback.recovery, - timeout = loadTimeouts(config, path)(fallback.timeout), - source = loadSource(config, path) getOrElse fallback.source, - prefix = loadPrefix(config, path) orElse fallback.prefix - ) + (config: Config, path: String) => + apply( + dispatcher = loadInvocationContext(config, path) getOrElse fallback.invocationContext, + invocationPolicy = loadInvocationPolicy(config, path) getOrElse fallback.invocationPolicy, + recovery = loadRecovery(config, path) getOrElse fallback.recovery, + timeout = loadTimeouts(config, path)(fallback.timeout), + source = loadSource(config, path) getOrElse fallback.source, + prefix = loadPrefix(config, path) orElse fallback.prefix, + ) def apply(dispatcher: String, invocationPolicy: String, timeout: RedisTimeouts, recovery: String, source: String, prefix: Option[String] = None): RedisSettings = create(dispatcher, invocationPolicy, prefix, timeout, recovery, source) @@ -84,11 +92,10 @@ object RedisSettings extends ConfigLoader[RedisSettings] { private def loadTimeouts(config: Config, path: String)(defaults: RedisTimeouts): RedisTimeouts = RedisTimeouts.load(config, path)(defaults) + } -/** - * A helper trait delegating properties into the inner settings object - */ +/** A helper trait delegating properties into the inner settings object */ trait RedisDelegatingSettings extends RedisSettings { def settings: RedisSettings override def prefix: Option[String] = settings.prefix diff --git a/src/main/scala/play/api/cache/redis/configuration/RedisTimeouts.scala b/src/main/scala/play/api/cache/redis/configuration/RedisTimeouts.scala index 37dd758b..22f34664 100644 --- a/src/main/scala/play/api/cache/redis/configuration/RedisTimeouts.scala +++ b/src/main/scala/play/api/cache/redis/configuration/RedisTimeouts.scala @@ -1,35 +1,45 @@ package play.api.cache.redis.configuration -import java.util.concurrent.TimeUnit -import scala.concurrent.duration.FiniteDuration import com.typesafe.config.Config import play.api.cache.redis._ -/** - * Aggregates the timeout configuration settings - */ +import java.util.concurrent.TimeUnit +import scala.concurrent.duration.FiniteDuration + +/** Aggregates the timeout configuration settings */ trait RedisTimeouts { - /** sync timeout applies in sync API and indicates how long to wait before the future is resolved */ + /** + * sync timeout applies in sync API and indicates how long to wait before the + * future is resolved + */ def sync: FiniteDuration /** redis timeout indicates how long to wait for the response */ def redis: Option[FiniteDuration] - /** connection timeout applies when the connection is not established to fail requests eagerly */ + /** + * connection timeout applies when the connection is not established to fail + * requests eagerly + */ def connection: Option[FiniteDuration] } final case class RedisTimeoutsImpl( - /** sync timeout applies in sync API and indicates how long to wait before the future is resolved */ + /** + * sync timeout applies in sync API and indicates how long to wait before the + * future is resolved + */ sync: FiniteDuration, /** redis timeout indicates how long to wait for the response */ redis: Option[FiniteDuration], - /** fail after timeout applies when the connection is not established to fail requests eagerly */ - connection: Option[FiniteDuration] - + /** + * fail after timeout applies when the connection is not established to fail + * requests eagerly + */ + connection: Option[FiniteDuration], ) extends RedisTimeouts { // $COVERAGE-OFF$ @@ -38,6 +48,7 @@ final case class RedisTimeoutsImpl( case _ => false } // $COVERAGE-ON$ + } object RedisTimeouts { @@ -56,22 +67,20 @@ object RedisTimeouts { def load(config: Config, path: String)(default: RedisTimeouts): RedisTimeouts = RedisTimeouts( sync = loadSyncTimeout(config, path) getOrElse default.sync, redis = loadRedisTimeout(config, path) getOrElse default.redis, - connection = loadConnectionTimeout(config, path) getOrElse default.connection + connection = loadConnectionTimeout(config, path) getOrElse default.connection, ) - private def loadSyncTimeout(config: Config, path: String): Option[FiniteDuration] = { + private def loadSyncTimeout(config: Config, path: String): Option[FiniteDuration] = config.getOption(path / "sync-timeout", _.getDuration).map(duration => FiniteDuration(duration.getSeconds, TimeUnit.SECONDS)) - } - private def loadRedisTimeout(config: Config, path: String): Option[Option[FiniteDuration]] = { + private def loadRedisTimeout(config: Config, path: String): Option[Option[FiniteDuration]] = config.getNullable(path / "redis-timeout", _.getDuration).map { _.map(duration => FiniteDuration(duration.getSeconds, TimeUnit.SECONDS)) } - } - private def loadConnectionTimeout(config: Config, path: String): Option[Option[FiniteDuration]] = { + private def loadConnectionTimeout(config: Config, path: String): Option[Option[FiniteDuration]] = config.getNullable(path / "connection-timeout", _.getDuration).map { _.map(duration => FiniteDuration(duration.getSeconds, TimeUnit.SECONDS) + FiniteDuration(duration.getNano, TimeUnit.NANOSECONDS)) } - } + } diff --git a/src/main/scala/play/api/cache/redis/connector/AkkaSerializer.scala b/src/main/scala/play/api/cache/redis/connector/AkkaSerializer.scala index 4d30bf25..007dd562 100644 --- a/src/main/scala/play/api/cache/redis/connector/AkkaSerializer.scala +++ b/src/main/scala/play/api/cache/redis/connector/AkkaSerializer.scala @@ -1,38 +1,44 @@ package play.api.cache.redis.connector +import akka.actor.ActorSystem +import akka.serialization._ +import play.api.cache.redis._ + import java.util.Base64 import javax.inject._ -import scala.language.implicitConversions import scala.reflect.ClassTag import scala.util._ -import play.api.cache.redis._ -import akka.actor.ActorSystem -import akka.serialization._ /** - * Provides a encode and decode methods to serialize objects into strings - * and vise versa. + * Provides a encode and decode methods to serialize objects into strings and + * vise versa. */ trait AkkaSerializer { /** - * Method accepts a value to be serialized into the string. - * Based on the implementation, it returns a string representing the value or - * provides an exception, if the computation fails. + * Method accepts a value to be serialized into the string. Based on the + * implementation, it returns a string representing the value or provides an + * exception, if the computation fails. * - * @param value value to be serialized - * @return serialized string or exception + * @param value + * value to be serialized + * @return + * serialized string or exception */ def encode(value: Any): Try[String] /** - * Method accepts a valid serialized string and based on the accepted class it deserializes it. - * If the expected class does not match expectations, deserialization fails with an exception. - * Also, if the string is not valid representation, it also fails. + * Method accepts a valid serialized string and based on the accepted class + * it deserializes it. If the expected class does not match expectations, + * deserialization fails with an exception. Also, if the string is not valid + * representation, it also fails. * - * @param value valid serialized entity - * @tparam T expected class - * @return deserialized object or exception + * @param value + * valid serialized entity + * @tparam T + * expected class + * @return + * deserialized object or exception */ def decode[T: ClassTag](value: String): Try[T] } @@ -40,8 +46,8 @@ trait AkkaSerializer { /** * Akka encoder provides implementation of serialization using Akka serializer. * The implementation considers all primitives, nulls, and refs. This enables - * us to use Akka settings to modify serializer mapping and use different serializers - * for different objects. + * us to use Akka settings to modify serializer mapping and use different + * serializers for different objects. */ private[connector] class AkkaEncoder(serializer: Serialization) { @@ -74,13 +80,14 @@ private[connector] class AkkaEncoder(serializer: Serialization) { /** unsafe method converting AnyRef into BASE64 string */ private def anyRefToString(value: AnyRef): String = (anyRefToBinary _ andThen binaryToString)(value) + } /** - * Akka decoder provides implementation of deserialization using Akka serializer. - * The implementation considers all primitives, nulls, and refs. This enables - * us to use Akka settings to modify serializer mapping and use different serializers - * for different objects. + * Akka decoder provides implementation of deserialization using Akka + * serializer. The implementation considers all primitives, nulls, and refs. + * This enables us to use Akka settings to modify serializer mapping and use + * different serializers for different objects. */ private[connector] class AkkaDecoder(serializer: Serialization) { @@ -90,25 +97,31 @@ private[connector] class AkkaDecoder(serializer: Serialization) { private val Nothing = ClassTag(classOf[Nothing]) - /** unsafe method decoding a string into an object. It directly throws exceptions */ + /** + * unsafe method decoding a string into an object. It directly throws + * exceptions + */ def decode[T](value: String)(implicit classTag: ClassTag[T]): T = untypedDecode[T](value).asInstanceOf[T] - /** unsafe method decoding a string into an object. It directly throws exceptions. It does not perform type cast */ + /** + * unsafe method decoding a string into an object. It directly throws + * exceptions. It does not perform type cast + */ private def untypedDecode[T](value: String)(implicit tag: ClassTag[T]): Any = value match { // AnyVal is not supported by default, have to be implemented manually - case "" => null - case _ if tag =~= Nothing => throw new IllegalArgumentException("Type Nothing is not supported. You have probably forgot to specify expected data type.") - case string if tag =~= Java.String => string - case boolean if tag =~= Java.Boolean || tag =~= Scala.Boolean => boolean.toBoolean - case byte if tag =~= Java.Byte || tag =~= Scala.Byte => byte.toByte - case char if tag =~= Java.Char || tag =~= Scala.Char => char.charAt(0) - case short if tag =~= Java.Short || tag =~= Scala.Short => short.toShort - case int if tag =~= Java.Int || tag =~= Scala.Int => int.toInt - case long if tag =~= Java.Long || tag =~= Scala.Long => long.toLong - case float if tag =~= Java.Float || tag =~= Scala.Float => float.toFloat - case double if tag =~= Java.Double || tag =~= Scala.Double => double.toDouble - case anyRef => stringToAnyRef[T](anyRef) + case "" => null + case _ if tag =~= Nothing => throw new IllegalArgumentException("Type Nothing is not supported. You have probably forgot to specify expected data type.") + case string if tag =~= Java.String => string + case boolean if tag =~= Java.Boolean || tag =~= Scala.Boolean => boolean.toBoolean + case byte if tag =~= Java.Byte || tag =~= Scala.Byte => byte.toByte + case char if tag =~= Java.Char || tag =~= Scala.Char => char.charAt(0) + case short if tag =~= Java.Short || tag =~= Scala.Short => short.toShort + case int if tag =~= Java.Int || tag =~= Scala.Int => int.toInt + case long if tag =~= Java.Long || tag =~= Scala.Long => long.toLong + case float if tag =~= Java.Float || tag =~= Scala.Float => float.toFloat + case double if tag =~= Java.Double || tag =~= Scala.Double => double.toDouble + case anyRef => stringToAnyRef[T](anyRef) } /** consumes BASE64 string and returns array of bytes */ @@ -117,19 +130,20 @@ private[connector] class AkkaDecoder(serializer: Serialization) { /** deserializes the binary stream into the object */ private def binaryToAnyRef[T](binary: Array[Byte])(implicit classTag: ClassTag[T]): AnyRef = - serializer.deserialize(binary, classTag.runtimeClass.asInstanceOf[Class[_ <: AnyRef]]).get + serializer.deserialize(binary, classTag.runtimeClass.asInstanceOf[Class[? <: AnyRef]]).get /** converts BASE64 string directly into the object */ private def stringToAnyRef[T: ClassTag](base64: String): AnyRef = (stringToBinary _ andThen binaryToAnyRef[T])(base64) + } @Singleton private[connector] class AkkaSerializerImpl @Inject() (system: ActorSystem) extends AkkaSerializer { /** - * serializer dispatcher used to serialize the objects into bytes; - * the instance is retrieved from Akka based on its configuration + * serializer dispatcher used to serialize the objects into bytes; the + * instance is retrieved from Akka based on its configuration */ protected val serializer: Serialization = SerializationExtension(system) @@ -140,51 +154,63 @@ private[connector] class AkkaSerializerImpl @Inject() (system: ActorSystem) exte private val decoder = new AkkaDecoder(serializer) /** - * Method accepts a value to be serialized into the string. - * Based on the implementation, it returns a string representing the value or - * provides an exception, if the computation fails. + * Method accepts a value to be serialized into the string. Based on the + * implementation, it returns a string representing the value or provides an + * exception, if the computation fails. * - * @param value value to be serialized - * @return serialized string or exception + * @param value + * value to be serialized + * @return + * serialized string or exception */ override def encode(value: Any): Try[String] = Try(encoder.encode(value)) /** - * Method accepts a valid serialized string and based on the accepted class it deserializes it. - * If the expected class does not match expectations, deserialization fails with an exception. - * Also, if the string is not valid representation, it also fails. + * Method accepts a valid serialized string and based on the accepted class + * it deserializes it. If the expected class does not match expectations, + * deserialization fails with an exception. Also, if the string is not valid + * representation, it also fails. * - * @param value valid serialized entity - * @tparam T expected class - * @return deserialized object or exception + * @param value + * valid serialized entity + * @tparam T + * expected class + * @return + * deserialized object or exception */ override def decode[T: ClassTag](value: String): Try[T] = Try(decoder.decode[T](value)) + } -/** - * Registry of known Scala and Java primitives - */ +/** Registry of known Scala and Java primitives */ private[connector] object Primitives { /** primitive types with simplified encoding */ - val primitives: Seq[Class[_]] = Seq( - classOf[Boolean], classOf[java.lang.Boolean], - classOf[Byte], classOf[java.lang.Byte], - classOf[Char], classOf[java.lang.Character], - classOf[Short], classOf[java.lang.Short], - classOf[Int], classOf[java.lang.Integer], - classOf[Long], classOf[java.lang.Long], - classOf[Float], classOf[java.lang.Float], - classOf[Double], classOf[java.lang.Double], - classOf[String] + val primitives: Seq[Class[?]] = Seq( + classOf[Boolean], + classOf[java.lang.Boolean], + classOf[Byte], + classOf[java.lang.Byte], + classOf[Char], + classOf[java.lang.Character], + classOf[Short], + classOf[java.lang.Short], + classOf[Int], + classOf[java.lang.Integer], + classOf[Long], + classOf[java.lang.Long], + classOf[Float], + classOf[java.lang.Float], + classOf[Double], + classOf[java.lang.Double], + classOf[String], ) + } -/** - * Registry of class tags for Java primitives - */ +/** Registry of class tags for Java primitives */ private[connector] object JavaClassTag { val Byte: ClassTag[Byte] = ClassTag(classOf[java.lang.Byte]) diff --git a/src/main/scala/play/api/cache/redis/connector/ExpectedFuture.scala b/src/main/scala/play/api/cache/redis/connector/ExpectedFuture.scala index 7d488ead..92ef09c2 100644 --- a/src/main/scala/play/api/cache/redis/connector/ExpectedFuture.scala +++ b/src/main/scala/play/api/cache/redis/connector/ExpectedFuture.scala @@ -1,13 +1,12 @@ package play.api.cache.redis.connector -import scala.concurrent.{ExecutionContext, Future} -import scala.language.implicitConversions - import play.api.cache.redis._ +import scala.concurrent.{ExecutionContext, Future} + /** - * The extended future implements advanced response handling. - * It unifies maintenance of unexpected responses + * The extended future implements advanced response handling. It unifies + * maintenance of unexpected responses */ private[connector] trait ExpectedFuture[T] { @@ -33,9 +32,9 @@ private[connector] trait ExpectedFuture[T] { } /** handles both expected and unexpected responses and failure recovery */ - def expects[U](expected: PartialFunction[T, U])(implicit context: ExecutionContext): Future[U] = { + def expects[U](expected: PartialFunction[T, U])(implicit context: ExecutionContext): Future[U] = future map (expected orElse onUnexpected) recover onException - } + } private[connector] object ExpectedFuture { @@ -46,8 +45,8 @@ private[connector] object ExpectedFuture { private[connector] class ExpectedFutureWithoutKey[T](protected val future: Future[T], protected val cmd: String) extends ExpectedFuture[T] { - protected def onUnexpected: PartialFunction[Any, Nothing] = { - case _ => unexpected(None, cmd) + protected def onUnexpected: PartialFunction[Any, Nothing] = { case _ => + unexpected(None, cmd) } protected def onFailed(ex: Throwable): Nothing = @@ -57,13 +56,13 @@ private[connector] class ExpectedFutureWithoutKey[T](protected val future: Futur def withKeys(keys: Iterable[String]): ExpectedFutureWithKey[T] = withKey(keys mkString " ") - override def toString = s"ExpectedFuture($cmd)" + override def toString: String = s"ExpectedFuture($cmd)" } private[connector] class ExpectedFutureWithKey[T](protected val future: Future[T], protected val cmd: String, key: String, statement: => String) extends ExpectedFuture[T] { - protected def onUnexpected: PartialFunction[Any, Nothing] = { - case _ => unexpected(Some(key), cmd) + protected def onUnexpected: PartialFunction[Any, Nothing] = { case _ => + unexpected(Some(key), cmd) } protected def onFailed(ex: Throwable): Nothing = @@ -77,11 +76,12 @@ private[connector] class ExpectedFutureWithKey[T](protected val future: Future[T def asCommand(commandOverride: => String) = new ExpectedFutureWithKey(future, cmd, key, s"$cmd $commandOverride") - override def toString = s"ExpectedFuture($statement)" + override def toString: String = s"ExpectedFuture($statement)" } /** - * Constructs expected future from provided parameters, this serves as syntax sugar + * Constructs expected future from provided parameters, this serves as syntax + * sugar */ private[connector] class ExpectedFutureBuilder[T](val future: Future[T]) extends AnyVal { diff --git a/src/main/scala/play/api/cache/redis/connector/RedisCommands.scala b/src/main/scala/play/api/cache/redis/connector/RedisCommands.scala index 1ceded07..22476c27 100644 --- a/src/main/scala/play/api/cache/redis/connector/RedisCommands.scala +++ b/src/main/scala/play/api/cache/redis/connector/RedisCommands.scala @@ -1,13 +1,13 @@ package play.api.cache.redis.connector -import javax.inject._ -import scala.concurrent.Future +import akka.actor.{ActorSystem, Scheduler} import play.api.Logger import play.api.cache.redis.configuration._ import play.api.inject.ApplicationLifecycle -import akka.actor.{ActorSystem, Scheduler} import redis.{RedisClient => RedisStandaloneClient, RedisCluster => RedisClusterClient, _} +import javax.inject._ +import scala.concurrent.Future import scala.concurrent.duration.FiniteDuration /** @@ -21,6 +21,7 @@ private[connector] class RedisCommandsProvider(instance: RedisInstance)(implicit case standalone: RedisStandalone => new RedisCommandsStandalone(standalone).get case sentinel: RedisSentinel => new RedisCommandsSentinel(sentinel).get } + } private[connector] trait AbstractRedisCommands { @@ -50,9 +51,12 @@ private[connector] trait AbstractRedisCommands { /** * Creates a connection to the single instance of redis * - * @param lifecycle application lifecycle to trigger on stop hook - * @param configuration configures clusters - * @param system actor system + * @param lifecycle + * application lifecycle to trigger on stop hook + * @param configuration + * configures clusters + * @param system + * actor system */ private[connector] class RedisCommandsStandalone(configuration: RedisStandalone)(implicit system: ActorSystem, val lifecycle: ApplicationLifecycle) extends Provider[RedisCommands] with AbstractRedisCommands { import configuration._ @@ -62,16 +66,16 @@ private[connector] class RedisCommandsStandalone(configuration: RedisStandalone) port = port, db = database, username = username, - password = password + password = password, ) with FailEagerly with RedisRequestTimeout { protected val connectionTimeout: Option[FiniteDuration] = configuration.timeout.connection protected val timeout: Option[FiniteDuration] = configuration.timeout.redis - protected implicit val scheduler: Scheduler = system.scheduler + implicit protected val scheduler: Scheduler = system.scheduler - override def send[T](redisCommand: RedisCommand[_ <: protocol.RedisReply, T]): Future[T] = super.send(redisCommand) + override def send[T](redisCommand: RedisCommand[? <: protocol.RedisReply, T]): Future[T] = super.send(redisCommand) override def onConnectStatus: Boolean => Unit = (status: Boolean) => connected = status } @@ -79,8 +83,8 @@ private[connector] class RedisCommandsStandalone(configuration: RedisStandalone) // $COVERAGE-OFF$ override def start(): Unit = database.fold { log.info(s"Redis cache actor started. It is connected to $host:$port") - } { - database => log.info(s"Redis cache actor started. It is connected to $host:$port?database=$database") + } { database => + log.info(s"Redis cache actor started. It is connected to $host:$port?database=$database") } override def stop(): Future[Unit] = Future successful { @@ -89,28 +93,34 @@ private[connector] class RedisCommandsStandalone(configuration: RedisStandalone) log.info("Redis cache stopped.") } // $COVERAGE-ON$ + } /** * Creates a connection to the redis cluster. * - * @param lifecycle application lifecycle to trigger on stop hook - * @param configuration configures clusters - * @param system actor system + * @param lifecycle + * application lifecycle to trigger on stop hook + * @param configuration + * configures clusters + * @param system + * actor system */ private[connector] class RedisCommandsCluster(configuration: RedisCluster)(implicit system: ActorSystem, val lifecycle: ApplicationLifecycle) extends Provider[RedisCommands] with AbstractRedisCommands { + import HostnameResolver._ import configuration._ val client: RedisClusterClient = new RedisClusterClient( - nodes.map { - case RedisHost(host, port, database, username, password) => RedisServer(host.resolvedIpAddress, port, username, password, database) - } + nodes.map { case RedisHost(host, port, database, username, password) => + RedisServer(host.resolvedIpAddress, port, username, password, database) + }, ) with RedisRequestTimeout { + protected val timeout: Option[FiniteDuration] = configuration.timeout.redis - protected implicit val scheduler: Scheduler = system.scheduler + implicit protected val scheduler: Scheduler = system.scheduler } // $COVERAGE-OFF$ @@ -129,39 +139,42 @@ private[connector] class RedisCommandsCluster(configuration: RedisCluster)(impli log.info("Redis cluster cache stopped.") } // $COVERAGE-ON$ + } /** * Creates a connection to multiple redis sentinels. * - * @param lifecycle application lifecycle to trigger on stop hook - * @param configuration configures sentinels - * @param system actor system + * @param lifecycle + * application lifecycle to trigger on stop hook + * @param configuration + * configures sentinels + * @param system + * actor system */ private[connector] class RedisCommandsSentinel(configuration: RedisSentinel)(implicit system: ActorSystem, val lifecycle: ApplicationLifecycle) extends Provider[RedisCommands] with AbstractRedisCommands { import HostnameResolver._ val client: SentinelMonitoredRedisClient with RedisRequestTimeout = new SentinelMonitoredRedisClient( - configuration.sentinels.map { - case RedisHost(host, port, _, _, _) => (host.resolvedIpAddress, port) + configuration.sentinels.map { case RedisHost(host, port, _, _, _) => + (host.resolvedIpAddress, port) }, master = configuration.masterGroup, username = configuration.username, password = configuration.password, - db = configuration.database + db = configuration.database, ) with RedisRequestTimeout { protected val timeout: Option[FiniteDuration] = configuration.timeout.redis - protected implicit val scheduler: Scheduler = system.scheduler + implicit protected val scheduler: Scheduler = system.scheduler - override def send[T](redisCommand: RedisCommand[_ <: protocol.RedisReply, T]): Future[T] = super.send(redisCommand) + override def send[T](redisCommand: RedisCommand[? <: protocol.RedisReply, T]): Future[T] = super.send(redisCommand) } // $COVERAGE-OFF$ - override def start(): Unit = { + override def start(): Unit = log.info(s"Redis sentinel cache actor started. It is connected to ${configuration.toString}") - } override def stop(): Future[Unit] = Future successful { log.info("Stopping the redis sentinel cache actor ...") @@ -169,4 +182,5 @@ private[connector] class RedisCommandsSentinel(configuration: RedisSentinel)(imp log.info("Redis sentinel cache stopped.") } // $COVERAGE-ON$ + } diff --git a/src/main/scala/play/api/cache/redis/connector/RedisConnector.scala b/src/main/scala/play/api/cache/redis/connector/RedisConnector.scala index a0d6fffe..44c24f2f 100644 --- a/src/main/scala/play/api/cache/redis/connector/RedisConnector.scala +++ b/src/main/scala/play/api/cache/redis/connector/RedisConnector.scala @@ -9,78 +9,102 @@ import scala.reflect.ClassTag * * Subset of REDIS commands, basic commands. * - * @see https://redis.io/commands + * @see + * https://redis.io/commands */ private[redis] trait CoreCommands { /** * Retrieve a value from the cache. * - * @param key cache storage key - * @return stored record, Some if exists, otherwise None + * @param key + * cache storage key + * @return + * stored record, Some if exists, otherwise None */ def get[T: ClassTag](key: String): Future[Option[T]] /** * Retrieve a values from the cache. * - * @param keys cache storage key - * @return stored record, Some if exists, otherwise None + * @param keys + * cache storage key + * @return + * stored record, Some if exists, otherwise None */ def mGet[T: ClassTag](keys: String*): Future[Seq[Option[T]]] /** * Determines whether value exists in cache. * - * @param key cache storage key - * @return record existence, true if exists, otherwise false + * @param key + * cache storage key + * @return + * record existence, true if exists, otherwise false */ def exists(key: String): Future[Boolean] /** - * Retrieves all keys matching the given pattern. This method invokes KEYS command + * Retrieves all keys matching the given pattern. This method invokes KEYS + * command * * '''Warning:''' complexity is O(n) where n are all keys in the database * - * @param pattern valid KEYS pattern with wildcards - * @return list of matching keys + * @param pattern + * valid KEYS pattern with wildcards + * @return + * list of matching keys */ def matching(pattern: String): Future[Seq[String]] /** - * Set a value into the cache. Expiration time in seconds (0 second means eternity). + * Set a value into the cache. Expiration time in seconds (0 second means + * eternity). * - * @param key cache storage key - * @param value value to store - * @param expiration record duration in seconds - * @param ifNotExists set only if the key does not exist - * @return promise + * @param key + * cache storage key + * @param value + * value to store + * @param expiration + * record duration in seconds + * @param ifNotExists + * set only if the key does not exist + * @return + * promise */ def set(key: String, value: Any, expiration: Duration = Duration.Inf, ifNotExists: Boolean = false): Future[Boolean] /** * Set a value into the cache. Expiration time is the eternity. * - * @param keyValues cache storage key-value pairs to store - * @return promise + * @param keyValues + * cache storage key-value pairs to store + * @return + * promise */ def mSet(keyValues: (String, Any)*): Future[Unit] /** - * Set a value into the cache. Expiration time is the eternity. - * It either set all values or it sets none if any of them already exists. + * Set a value into the cache. Expiration time is the eternity. It either set + * all values or it sets none if any of them already exists. * - * @param keyValues cache storage key-value pairs to store - * @return promise + * @param keyValues + * cache storage key-value pairs to store + * @return + * promise */ def mSetIfNotExist(keyValues: (String, Any)*): Future[Boolean] /** - * refreshes expiration time on a given key, useful, e.g., when we want to refresh session duration + * refreshes expiration time on a given key, useful, e.g., when we want to + * refresh session duration * - * @param key cache storage key - * @param expiration new expiration in seconds - * @return promise + * @param key + * cache storage key + * @param expiration + * new expiration in seconds + * @return + * promise */ def expire(key: String, expiration: Duration): Future[Unit] @@ -88,51 +112,64 @@ private[redis] trait CoreCommands { * returns the remaining time to live of a key that has an expire set, * useful, e.g., when we want to check remaining session duration * - * @param key cache storage key - * @return the remaining time to live of a key in milliseconds + * @param key + * cache storage key + * @return + * the remaining time to live of a key in milliseconds */ def expiresIn(key: String): Future[Option[Duration]] /** - * Removes all keys in arguments. The other remove methods are for syntax sugar + * Removes all keys in arguments. The other remove methods are for syntax + * sugar * - * @param keys cache storage keys - * @return promise + * @param keys + * cache storage keys + * @return + * promise */ def remove(keys: String*): Future[Unit] /** * Remove all keys in cache * - * @return promise + * @return + * promise */ def invalidate(): Future[Unit] /** * Sends PING command to REDIS and expects PONG in return * - * @return promise + * @return + * promise */ def ping(): Future[Unit] /** - * Increments the stored string value representing 10-based signed integer - * by given value. + * Increments the stored string value representing 10-based signed integer by + * given value. * - * @param key cache storage key - * @param by size of increment - * @return the value after the increment + * @param key + * cache storage key + * @param by + * size of increment + * @return + * the value after the increment */ def increment(key: String, by: Long): Future[Long] /** - * If key already exists and is a string, this command appends the value at the - * end of the string. If key does not exist it is created and set as an empty string, - * so APPEND will be similar to SET in this special case. + * If key already exists and is a string, this command appends the value at + * the end of the string. If key does not exist it is created and set as an + * empty string, so APPEND will be similar to SET in this special case. * - * @param key cache storage key - * @param value value to be appended - * @return number of characters of current value + * @param key + * cache storage key + * @param value + * value to be appended + * @return + * number of characters of current value */ def append(key: String, value: String): Future[Long] } @@ -142,19 +179,25 @@ private[redis] trait CoreCommands { * * Subset of REDIS commands, Hash-related commands. * - * @see https://redis.io/commands + * @see + * https://redis.io/commands */ private[redis] trait HashCommands { /** - * Removes the specified fields from the hash stored at key. Specified fields that do not exist within this - * hash are ignored. If key does not exist, it is treated as an empty hash and this command returns 0. + * Removes the specified fields from the hash stored at key. Specified fields + * that do not exist within this hash are ignored. If key does not exist, it + * is treated as an empty hash and this command returns 0. * * Time complexity: O(N) where N is the number of fields to be removed. * - * @param key cache storage key - * @param field fields to be removed - * @return the number of fields that were removed from the hash, not including specified but non existing fields. + * @param key + * cache storage key + * @param field + * fields to be removed + * @return + * the number of fields that were removed from the hash, not including + * specified but non existing fields. */ def hashRemove(key: String, field: String*): Future[Long] @@ -163,9 +206,12 @@ private[redis] trait HashCommands { * * Time complexity: O(1) * - * @param key cache storage key - * @param field tested field name - * @return true if the field exists, false otherwise + * @param key + * cache storage key + * @param field + * tested field name + * @return + * true if the field exists, false otherwise */ def hashExists(key: String, field: String): Future[Boolean] @@ -174,42 +220,57 @@ private[redis] trait HashCommands { * * Time complexity: O(1) * - * @param key cache storage key - * @param field accessed field - * @return Some value if the field exists, otherwise None + * @param key + * cache storage key + * @param field + * accessed field + * @return + * Some value if the field exists, otherwise None */ def hashGet[T: ClassTag](key: String, field: String): Future[Option[T]] /** - * Returns the values associated with fields in the hash stored at given keys. + * Returns the values associated with fields in the hash stored at given + * keys. * * Time complexity: O(n), where n is number of fields * - * @param key cache storage key - * @param fields accessed fields to get - * @return Some value if the field exists, otherwise None + * @param key + * cache storage key + * @param fields + * accessed fields to get + * @return + * Some value if the field exists, otherwise None */ def hashGet[T: ClassTag](key: String, fields: Seq[String]): Future[Seq[Option[T]]] /** - * Returns all fields and values of the hash stored at key. In the returned value, every field name is followed - * by its value, so the length of the reply is twice the size of the hash. + * Returns all fields and values of the hash stored at key. In the returned + * value, every field name is followed by its value, so the length of the + * reply is twice the size of the hash. * * Time complexity: O(N) where N is the size of the hash. * - * @param key cache storage key - * @tparam T expected type of the elements - * @return the stored map + * @param key + * cache storage key + * @tparam T + * expected type of the elements + * @return + * the stored map */ def hashGetAll[T: ClassTag](key: String): Future[Map[String, T]] /** * Increment a value at the given key in the map * - * @param key cache storage key - * @param field key - * @param incrementBy increment by this - * @return value after incrementation + * @param key + * cache storage key + * @param field + * key + * @param incrementBy + * increment by this + * @return + * value after incrementation */ def hashIncrement(key: String, field: String, incrementBy: Long): Future[Long] @@ -218,8 +279,10 @@ private[redis] trait HashCommands { * * Time complexity: O(1) * - * @param key cache storage key - * @return size of the hash + * @param key + * cache storage key + * @return + * size of the hash */ def hashSize(key: String): Future[Long] @@ -228,20 +291,28 @@ private[redis] trait HashCommands { * * Time complexity: O(N) where N is the size of the hash. * - * @param key cache storage key - * @return set of field names + * @param key + * cache storage key + * @return + * set of field names */ def hashKeys(key: String): Future[Set[String]] /** - * Sets field in the hash stored at key to value. If key does not exist, a new key holding a hash is created. If field already exists in the hash, it is overwritten. + * Sets field in the hash stored at key to value. If key does not exist, a + * new key holding a hash is created. If field already exists in the hash, it + * is overwritten. * * Time complexity: O(1) * - * @param key cache storage key - * @param field field to be set - * @param value value to be set - * @return true if the field was newly set, false if was updated + * @param key + * cache storage key + * @param field + * field to be set + * @param value + * value to be set + * @return + * true if the field was newly set, false if was updated */ def hashSet(key: String, field: String, value: Any): Future[Boolean] @@ -250,8 +321,10 @@ private[redis] trait HashCommands { * * Time complexity: O(N) where N is the size of the hash. * - * @param key cache storage key - * @return all values in the hash object + * @param key + * cache storage key + * @return + * all values in the hash object */ def hashValues[T: ClassTag](key: String): Future[Set[T]] } @@ -261,134 +334,181 @@ private[redis] trait HashCommands { * * Subset of REDIS commands, List-related commands. * - * @see https://redis.io/commands + * @see + * https://redis.io/commands */ private[redis] trait ListCommands { /** - * Insert (LPUSH) all the specified values at the head of the list stored at key. - * If key does not exist, it is created as empty list before performing the push operations. - * When key holds a value that is not a list, an error is returned. + * Insert (LPUSH) all the specified values at the head of the list stored at + * key. If key does not exist, it is created as empty list before performing + * the push operations. When key holds a value that is not a list, an error + * is returned. * * Time complexity: O(1) * - * @param key cache storage key - * @param value prepended values - * @return new length of the list + * @param key + * cache storage key + * @param value + * prepended values + * @return + * new length of the list */ def listPrepend(key: String, value: Any*): Future[Long] /** - * Insert (RPUSH) all the specified values at the tail of the list stored at key. If key - * does not exist, it is created as empty list before performing the push operation. - * When key holds a value that is not a list, an error is returned. + * Insert (RPUSH) all the specified values at the tail of the list stored at + * key. If key does not exist, it is created as empty list before performing + * the push operation. When key holds a value that is not a list, an error is + * returned. * * Time complexity: O(1) * - * @param key cache storage key - * @param value appended values - * @return new length of the list + * @param key + * cache storage key + * @param value + * appended values + * @return + * new length of the list */ def listAppend(key: String, value: Any*): Future[Long] /** - * Returns the length of the list stored at key (LLEN). If key does not exist, it is interpreted as an empty - * list and 0 is returned. An error is returned when the value stored at key is not a list. + * Returns the length of the list stored at key (LLEN). If key does not + * exist, it is interpreted as an empty list and 0 is returned. An error is + * returned when the value stored at key is not a list. * * Time complexity: O(1) * - * @param key cache storage key - * @return length of the list + * @param key + * cache storage key + * @return + * length of the list */ def listSize(key: String): Future[Long] /** - * Inserts value in the list stored at key either before or after the reference value pivot. - * When key does not exist, it is considered an empty list and no operation is performed. - * An error is returned when key exists but does not hold a list value. + * Inserts value in the list stored at key either before or after the + * reference value pivot. When key does not exist, it is considered an empty + * list and no operation is performed. An error is returned when key exists + * but does not hold a list value. * - * Time complexity: O(N) where N is the number of elements to traverse before seeing the value pivot. + * Time complexity: O(N) where N is the number of elements to traverse before + * seeing the value pivot. * - * @param key cache storage key - * @param pivot value used as markup - * @param value value to be inserted - * @return the length of the list after the insert operation, or None when the value pivot was not found. + * @param key + * cache storage key + * @param pivot + * value used as markup + * @param value + * value to be inserted + * @return + * the length of the list after the insert operation, or None when the + * value pivot was not found. */ def listInsert(key: String, pivot: Any, value: Any): Future[Option[Long]] /** - * Sets the list element at index to value. For more information on the index argument, see LINDEX. An error is - * returned for out of range indexes. + * Sets the list element at index to value. For more information on the index + * argument, see LINDEX. An error is returned for out of range indexes. * - * Time complexity: O(N) where N is the length of the list. Setting either the first or the last element - * of the list is O(1). + * Time complexity: O(N) where N is the length of the list. Setting either + * the first or the last element of the list is O(1). * - * @param key cache storage key - * @param position position to be overwritten - * @param value value to be set - * @return promise + * @param key + * cache storage key + * @param position + * position to be overwritten + * @param value + * value to be set + * @return + * promise */ - def listSetAt(key: String, position: Int, value: Any): Future[Unit] + def listSetAt(key: String, position: Long, value: Any): Future[Unit] /** * Removes and returns the first element of the list stored at key (LPOP). * * Time complexity: O(1) * - * @param key cache storage key - * @tparam T type of the value - * @return head of the list, if existed + * @param key + * cache storage key + * @tparam T + * type of the value + * @return + * head of the list, if existed */ def listHeadPop[T: ClassTag](key: String): Future[Option[T]] /** - * Returns the specified elements of the list stored at key (LRANGE). The offsets start and stop are zero-based - * indexes, with 0 being the first element of the list (the head of the list), 1 being the next element and so on. - * - * These offsets can also be negative numbers indicating offsets starting at the end of the list. For example, - * -1 is the last element of the list, -2 the penultimate, and so on. - * - * Time complexity: O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end - * (HEAD or TAIL) for large lists; and N is the number of elements in the specified range. - * - * @param key cache storage key - * @param start initial index of the subset - * @param end last index of the subset (included) - * @tparam T type of the values - * @return subset of existing set - */ - def listSlice[T: ClassTag](key: String, start: Int, end: Int): Future[Seq[T]] - - /** - * Removes (LREM) the first count occurrences of elements equal to value from the list stored at key. The count - * argument influences the operation in the following ways: - * count > 0: Remove elements equal to value moving from head to tail. - * count < 0: Remove elements equal to value moving from tail to head. - * count = 0: Remove all elements equal to value. - * - * @param key cache storage key - * @param value value to be removed - * @param count number of elements to be removed - * @return number of removed elements - */ - def listRemove(key: String, value: Any, count: Int): Future[Long] - - /** - * Trim an existing list so that it will contain only the specified range of elements specified. Both start and stop - * are zero-based indexes, where 0 is the first element of the list (the head), 1 the next element and so on. - * - * For example: LTRIM foobar 0 2 will modify the list stored at foobar so that only the first three elements of - * the list will remain. start and end can also be negative numbers indicating offsets from the end of the list, - * where -1 is the last element of the list, -2 the penultimate element and so on. - * - * Time complexity: O(N) where N is the number of elements to be removed by the operation. - * - * @param key cache storage key - * @param start initial index of preserved subset - * @param end last index of preserved subset (included) - * @return promise - */ - def listTrim(key: String, start: Int, end: Int): Future[Unit] + * Returns the specified elements of the list stored at key (LRANGE). The + * offsets start and stop are zero-based indexes, with 0 being the first + * element of the list (the head of the list), 1 being the next element and + * so on. + * + * These offsets can also be negative numbers indicating offsets starting at + * the end of the list. For example, -1 is the last element of the list, -2 + * the penultimate, and so on. + * + * Time complexity: O(S+N) where S is the distance of start offset from HEAD + * for small lists, from nearest end (HEAD or TAIL) for large lists; and N is + * the number of elements in the specified range. + * + * @param key + * cache storage key + * @param start + * initial index of the subset + * @param end + * last index of the subset (included) + * @tparam T + * type of the values + * @return + * subset of existing set + */ + def listSlice[T: ClassTag](key: String, start: Long, end: Long): Future[Seq[T]] + + /** + * Removes (LREM) the first count occurrences of elements equal to value from + * the list stored at key. The count argument influences the operation in the + * following ways: count > 0: Remove elements equal to value moving from head + * to tail. count < 0: Remove elements equal to value moving from tail to + * head. count = 0: Remove all elements equal to value. + * + * @param key + * cache storage key + * @param value + * value to be removed + * @param count + * number of elements to be removed + * @return + * number of removed elements + */ + def listRemove(key: String, value: Any, count: Long): Future[Long] + + /** + * Trim an existing list so that it will contain only the specified range of + * elements specified. Both start and stop are zero-based indexes, where 0 is + * the first element of the list (the head), 1 the next element and so on. + * + * For example: LTRIM foobar 0 2 will modify the list stored at foobar so + * that only the first three elements of the list will remain. start and end + * can also be negative numbers indicating offsets from the end of the list, + * where -1 is the last element of the list, -2 the penultimate element and + * so on. + * + * Time complexity: O(N) where N is the number of elements to be removed by + * the operation. + * + * @param key + * cache storage key + * @param start + * initial index of preserved subset + * @param end + * last index of preserved subset (included) + * @return + * promise + */ + def listTrim(key: String, start: Long, end: Long): Future[Unit] } /** @@ -396,21 +516,27 @@ private[redis] trait ListCommands { * * Subset of REDIS commands, unordered Set-related commands. * - * @see https://redis.io/commands + * @see + * https://redis.io/commands */ private[redis] trait SetCommands { /** - * Add the specified members to the set stored at key. Specified members that are already a member of this set - * are ignored. If key does not exist, a new set is created before adding the specified members. + * Add the specified members to the set stored at key. Specified members that + * are already a member of this set are ignored. If key does not exist, a new + * set is created before adding the specified members. * * An error is returned when the value stored at key is not a set. * - * @note Time complexity: O(1) for each element added, so O(N) to add N elements when the command is called - * with multiple arguments. - * @param key cache storage key - * @param value values to be added - * @return number of inserted elements ignoring already existing + * @note + * Time complexity: O(1) for each element added, so O(N) to add N elements + * when the command is called with multiple arguments. + * @param key + * cache storage key + * @param value + * values to be added + * @return + * number of inserted elements ignoring already existing */ def setAdd(key: String, value: Any*): Future[Long] @@ -419,8 +545,11 @@ private[redis] trait SetCommands { * * Time complexity: O(1) * - * @param key cache storage key - * @return the cardinality (number of elements) of the set, or 0 if key does not exist. + * @param key + * cache storage key + * @return + * the cardinality (number of elements) of the set, or 0 if key does not + * exist. */ def setSize(key: String): Future[Long] @@ -431,9 +560,12 @@ private[redis] trait SetCommands { * * Time complexity: O(N) where N is the set cardinality. * - * @param key cache storage key - * @tparam T expected type of the elements - * @return the subset + * @param key + * cache storage key + * @tparam T + * expected type of the elements + * @return + * the subset */ def setMembers[T: ClassTag](key: String): Future[Set[T]] @@ -442,23 +574,30 @@ private[redis] trait SetCommands { * * Time complexity: O(1) * - * @param key cache storage key - * @param value tested element - * @return true if the element exists in the set, otherwise false + * @param key + * cache storage key + * @param value + * tested element + * @return + * true if the element exists in the set, otherwise false */ def setIsMember(key: String, value: Any): Future[Boolean] /** - * Remove the specified members from the set stored at key. Specified members that are not a member of this set - * are ignored. If key does not exist, it is treated as an empty set and this command returns 0. + * Remove the specified members from the set stored at key. Specified members + * that are not a member of this set are ignored. If key does not exist, it + * is treated as an empty set and this command returns 0. * * An error is returned when the value stored at key is not a set. * * Time complexity: O(N) where N is the number of members to be removed. * - * @param key cache storage key - * @param value values to be removed - * @return total number of removed values, non existing are ignored + * @param key + * cache storage key + * @param value + * values to be removed + * @return + * total number of removed values, non existing are ignored */ def setRemove(key: String, value: Any*): Future[Long] } @@ -468,60 +607,80 @@ private[redis] trait SetCommands { * * Subset of REDIS commands, sorted set related commands. * - * @see https://redis.io/commands + * @see + * https://redis.io/commands */ private[redis] trait SortedSetCommands { /** - * Adds all the specified members with the specified scores to the sorted set stored at key. - * It is possible to specify multiple score / member pairs. If a specified member is already - * a member of the sorted set, the score is updated and the element reinserted at the right - * position to ensure the correct ordering. + * Adds all the specified members with the specified scores to the sorted set + * stored at key. It is possible to specify multiple score / member pairs. If + * a specified member is already a member of the sorted set, the score is + * updated and the element reinserted at the right position to ensure the + * correct ordering. * - * If key does not exist, a new sorted set with the specified members as sole members is created, - * like if the sorted set was empty. If the key exists but does not hold a sorted set, an error - * is returned. + * If key does not exist, a new sorted set with the specified members as sole + * members is created, like if the sorted set was empty. If the key exists + * but does not hold a sorted set, an error is returned. * - * @note Time complexity: O(log(N)) for each item added, where N is the number of elements in the sorted set. - * @param key cache storage key - * @param scoreValues values and corresponding scores to be added - * @return number of inserted elements ignoring already existing + * @note + * Time complexity: O(log(N)) for each item added, where N is the number of + * elements in the sorted set. + * @param key + * cache storage key + * @param scoreValues + * values and corresponding scores to be added + * @return + * number of inserted elements ignoring already existing */ def sortedSetAdd(key: String, scoreValues: (Double, Any)*): Future[Long] /** - * Returns the sorted set cardinality (number of elements) of the sorted set stored at key. + * Returns the sorted set cardinality (number of elements) of the sorted set + * stored at key. * * Time complexity: O(1) * - * @param key cache storage key - * @return the cardinality (number of elements) of the set, or 0 if key does not exist. + * @param key + * cache storage key + * @return + * the cardinality (number of elements) of the set, or 0 if key does not + * exist. */ def sortedSetSize(key: String): Future[Long] /** * Returns the score of member in the sorted set at key. * - * If member does not exist in the sorted set, or key does not exist, nil is returned. + * If member does not exist in the sorted set, or key does not exist, nil is + * returned. * * Time complexity: O(1) * - * @param key cache storage key - * @param value tested element - * @return the score of member (a double precision floating point number). + * @param key + * cache storage key + * @param value + * tested element + * @return + * the score of member (a double precision floating point number). */ def sortedSetScore(key: String, value: Any): Future[Option[Double]] /** - * Removes the specified members from the sorted set stored at key. Non existing members are ignored. + * Removes the specified members from the sorted set stored at key. Non + * existing members are ignored. * * An error is returned when key exists and does not hold a sorted set. * - * Time complexity: O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed. + * Time complexity: O(M*log(N)) with N being the number of elements in the + * sorted set and M the number of elements to be removed. * - * @param key cache storage key - * @param value values to be removed - * @return total number of removed values, non existing are ignored + * @param key + * cache storage key + * @param value + * values to be removed + * @return + * total number of removed values, non existing are ignored */ def sortedSetRemove(key: String, value: Any*): Future[Long] @@ -529,25 +688,37 @@ private[redis] trait SortedSetCommands { * Returns the specified range of elements in the sorted set stored at key. * * An error is returned when key exists and does not hold a sorted set. - * @param key cache storage key - * @param start the start index of the range - * @param stop the stop index of the range - * @note The start and stop arguments represent zero-based indexes, where 0 is the first element, - * 1 is the next element, and so on. These arguments specify an inclusive range. - * @return list of elements in the specified range + * @param key + * cache storage key + * @param start + * the start index of the range + * @param stop + * the stop index of the range + * @note + * The start and stop arguments represent zero-based indexes, where 0 is + * the first element, 1 is the next element, and so on. These arguments + * specify an inclusive range. + * @return + * list of elements in the specified range */ def sortedSetRange[T: ClassTag](key: String, start: Long, stop: Long): Future[Seq[T]] /** * Returns the specified range of elements in the sorted set stored at key. - * The elements are considered to be ordered from the highest to the lowest score. - * Descending lexicographical order is used for elements with equal score. - * - * @param key cache storage key - * @param start the start index of the range - * @param stop the stop index of the range - * @note Apart from the reversed ordering, the zrevRange is similar to zrange. - * @return list of elements in the specified range + * The elements are considered to be ordered from the highest to the lowest + * score. Descending lexicographical order is used for elements with equal + * score. + * + * @param key + * cache storage key + * @param start + * the start index of the range + * @param stop + * the stop index of the range + * @note + * Apart from the reversed ordering, the zrevRange is similar to zrange. + * @return + * list of elements in the specified range */ def sortedSetReverseRange[T: ClassTag](key: String, start: Long, stop: Long): Future[Seq[T]] } @@ -555,11 +726,7 @@ private[redis] trait SortedSetCommands { /** * Internal non-blocking Redis API implementing REDIS protocol * - * @see https://redis.io/commands + * @see + * https://redis.io/commands */ -trait RedisConnector extends AnyRef - with CoreCommands - with ListCommands - with SetCommands - with HashCommands - with SortedSetCommands +trait RedisConnector extends AnyRef with CoreCommands with ListCommands with SetCommands with HashCommands with SortedSetCommands diff --git a/src/main/scala/play/api/cache/redis/connector/RedisConnectorImpl.scala b/src/main/scala/play/api/cache/redis/connector/RedisConnectorImpl.scala index a5ce873d..16b1ded1 100644 --- a/src/main/scala/play/api/cache/redis/connector/RedisConnectorImpl.scala +++ b/src/main/scala/play/api/cache/redis/connector/RedisConnectorImpl.scala @@ -1,22 +1,27 @@ package play.api.cache.redis.connector +import play.api.Logger +import play.api.cache.redis._ +import redis._ + import java.util.concurrent.TimeUnit import scala.concurrent.Future import scala.concurrent.duration.Duration import scala.reflect.ClassTag -import play.api.Logger -import play.api.cache.redis._ -import redis._ /** - * The connector directly connects with the REDIS instance, implements protocol commands - * and is supposed to by used internally by another wrappers. The connector does not - * directly implement [[play.api.cache.redis.CacheApi]] but provides fundamental functionality. + * The connector directly connects with the REDIS instance, implements protocol + * commands and is supposed to by used internally by another wrappers. The + * connector does not directly implement [[play.api.cache.redis.CacheApi]] but + * provides fundamental functionality. * - * @param serializer encodes/decodes objects into/from a string - * @param redis implementation of the commands + * @param serializer + * encodes/decodes objects into/from a string + * @param redis + * implementation of the commands */ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: RedisCommands)(implicit runtime: RedisRuntime) extends RedisConnector { + import ExpectedFuture._ import runtime._ @@ -29,7 +34,7 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R case Some(response: String) => log.trace(s"Hit on key '$key'.") Some(decode[T](key, response)) - case None => + case None => log.debug(s"Miss on key '$key'.") None } @@ -37,21 +42,25 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R override def mGet[T: ClassTag](keys: String*): Future[Seq[Option[T]]] = redis.mget[String](keys: _*) executing "MGET" withKeys keys expects { // list is always returned - case list => keys.zip(list).map { - case (key, Some(response: String)) => - log.trace(s"Hit on key '$key'.") - Some(decode[T](key, response)) - case (key, None) => - log.debug(s"Miss on key '$key'.") - None - } + case list => + keys.zip(list).map { + case (key, Some(response: String)) => + log.trace(s"Hit on key '$key'.") + Some(decode[T](key, response)) + case (key, None) => + log.debug(s"Miss on key '$key'.") + None + } } /** decodes the object, reports an exception if fails */ private def decode[T: ClassTag](key: String, encoded: String): T = - serializer.decode[T](encoded).recover { - case ex => serializationFailed(key, "Deserialization failed", ex) - }.get + serializer + .decode[T](encoded) + .recover { case ex => + serializationFailed(key, "Deserialization failed", ex) + } + .get override def set(key: String, value: Any, expiration: Duration, ifNotExists: Boolean): Future[Boolean] = // no value to set @@ -61,18 +70,21 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R /** encodes the object, reports an exception if fails */ private def encode(key: String, value: Any): Future[String] = Future.fromTry { - serializer.encode(value).recover { - case ex => serializationFailed(key, "Serialization failed", ex) + serializer.encode(value).recover { case ex => + serializationFailed(key, "Serialization failed", ex) } } - /** implements the advanced set operation storing already encoded value into the storage */ + /** + * implements the advanced set operation storing already encoded value into + * the storage + */ private def doSet(key: String, value: String, expiration: Duration, ifNotExists: Boolean): Future[Boolean] = redis.set[String]( key, value, pxMilliseconds = if (expiration.isFinite) Some(expiration.toMillis) else None, - NX = ifNotExists + NX = ifNotExists, ) executing "SET" withKey key andParameters s"$value${s" PX $expiration" when expiration.isFinite}${" NX" when ifNotExists}" logging { case true if expiration.isFinite => log.debug(s"Set on key '$key' for ${expiration.toMillis} milliseconds.") case true => log.debug(s"Set on key '$key' for infinite seconds.") @@ -83,7 +95,10 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R override def mSetIfNotExist(keyValues: (String, Any)*): Future[Boolean] = mSetUsing(mSetEternallyIfNotExist, true, keyValues: _*) - /** eternally stores or removes all given values, using the given mSet implementation */ + /** + * eternally stores or removes all given values, using the given mSet + * implementation + */ private def mSetUsing[T](mSet: Seq[(String, String)] => Future[T], default: T, keyValues: (String, Any)*): Future[T] = { val (toBeRemoved, toBeSet) = keyValues.partition(_.isNull) // remove all keys to be removed @@ -96,8 +111,8 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R /** eternally stores already encoded values into the storage */ private def mSetEternally(keyValues: (String, String)*): Future[Unit] = - redis.mset(keyValues.toMap) executing "MSET" withKeys keyValues.map(_._1) asCommand keyValues.map(_.asString).mkString(" ") logging { - case _ => log.debug(s"Set on keys ${keyValues.map(_.key)} for infinite seconds.") + redis.mset(keyValues.toMap) executing "MSET" withKeys keyValues.map(_._1) asCommand keyValues.map(_.asString).mkString(" ") logging { case _ => + log.debug(s"Set on keys ${keyValues.map(_.key)} for infinite seconds.") } /** eternally stores already encoded values into the storage */ @@ -108,17 +123,17 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R } override def expire(key: String, expiration: Duration): Future[Unit] = - redis.expire(key, expiration.toSeconds.toInt) executing "EXPIRE" withKey key andParameter s"$expiration" logging { - case true => log.debug(s"Expiration set on key '$key'.") // expiration was set + redis.expire(key, expiration.toSeconds) executing "EXPIRE" withKey key andParameter s"$expiration" logging { + case true => log.debug(s"Expiration set on key '$key'.") // expiration was set case false => log.debug(s"Expiration set on key '$key' failed. Key does not exist.") // Nothing was removed } override def expiresIn(key: String): Future[Option[Duration]] = redis.pttl(key) executing "PTTL" withKey key expects { - case -2 => + case -2 => log.debug(s"PTTL on key '$key' returns -2, it does not exist.") None - case -1 => + case -1 => log.debug(s"PTTL on key '$key' returns -1, it has no associated expiration.") Some(Duration.Inf) case expiration => @@ -127,8 +142,8 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R } override def matching(pattern: String): Future[Seq[String]] = - redis.keys(pattern) executing "KEYS" withKey pattern logging { - case keys => log.debug(s"KEYS on '$pattern' responded '${keys.mkString(", ")}'.") + redis.keys(pattern) executing "KEYS" withKey pattern logging { case keys => + log.debug(s"KEYS on '$pattern' responded '${keys.mkString(", ")}'.") } // coverage is disabled as testing it would require @@ -136,8 +151,8 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R // the tests are in progress // $COVERAGE-OFF$ override def invalidate(): Future[Unit] = - redis.flushdb() executing "FLUSHDB" logging { - case _ => log.info("Invalidated.") // cache was invalidated + redis.flushdb() executing "FLUSHDB" logging { case _ => + log.info("Invalidated.") // cache was invalidated } // $COVERAGE-ON$ @@ -160,23 +175,23 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R } override def ping(): Future[Unit] = - redis.ping() executing "PING" logging { - case "PONG" => () + redis.ping() executing "PING" logging { case "PONG" => + () } override def increment(key: String, by: Long): Future[Long] = - redis.incrby(key, by) executing "INCRBY" withKey key andParameter s"$by" logging { - case value => log.debug(s"The value at key '$key' was incremented by $by to $value.") + redis.incrby(key, by) executing "INCRBY" withKey key andParameter s"$by" logging { case value => + log.debug(s"The value at key '$key' was incremented by $by to $value.") } override def append(key: String, value: String): Future[Long] = - redis.append(key, value) executing "APPEND" withKey key andParameter value logging { - case length => log.debug(s"The value was appended to key '$key'.") + redis.append(key, value) executing "APPEND" withKey key andParameter value logging { case _ => + log.debug(s"The value was appended to key '$key'.") } override def listPrepend(key: String, values: Any*): Future[Long] = - Future.sequence(values.map(encode(key, _))).flatMap(redis.lpush(key, _: _*)) executing "LPUSH" withKey key andParameters values logging { - case length => log.debug(s"The $length values was prepended to key '$key'.") + Future.sequence(values.map(encode(key, _))).flatMap(redis.lpush(key, _: _*)) executing "LPUSH" withKey key andParameters values logging { case length => + log.debug(s"The $length values was prepended to key '$key'.") } recover { case ExecutionFailedException(_, _, _, ex) if ex.getMessage startsWith "WRONGTYPE" => log.warn(s"Value at '$key' is not a list to be prepended.") @@ -184,8 +199,8 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R } override def listAppend(key: String, values: Any*): Future[Long] = - Future.sequence(values.map(encode(key, _))).flatMap(redis.rpush(key, _: _*)) executing "RPUSH" withKey key andParameters values logging { - case length => log.debug(s"The $length values was appended to key '$key'.") + Future.sequence(values.map(encode(key, _))).flatMap(redis.rpush(key, _: _*)) executing "RPUSH" withKey key andParameters values logging { case length => + log.debug(s"The $length values was appended to key '$key'.") } recover { case ExecutionFailedException(_, _, _, ex) if ex.getMessage startsWith "WRONGTYPE" => log.warn(s"Value at '$key' is not a list to be appended.") @@ -193,17 +208,16 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R } override def listSize(key: String): Future[Long] = - redis.llen(key) executing "LLEN" withKey key logging { - case length => log.debug(s"The collection at '$key' has $length items.") + redis.llen(key) executing "LLEN" withKey key logging { case length => + log.debug(s"The collection at '$key' has $length items.") } - override def listSetAt(key: String, position: Int, value: Any): Future[Unit] = - encode(key, value).flatMap(redis.lset(key, position, _)) executing "LSET" withKey key andParameter value logging { - case _ => log.debug(s"Updated value at $position in '$key' to $value.") - } recover { - case ExecutionFailedException(_, _, _, actors.ReplyErrorException("ERR index out of range")) => - log.debug(s"Update of the value at $position in '$key' failed due to index out of range.") - throw new IndexOutOfBoundsException("Index out of range") + override def listSetAt(key: String, position: Long, value: Any): Future[Unit] = + encode(key, value).flatMap(redis.lset(key, position, _)) executing "LSET" withKey key andParameter value logging { case _ => + log.debug(s"Updated value at $position in '$key' to $value.") + } map (_ => ()) recover { case ExecutionFailedException(_, _, _, actors.ReplyErrorException("ERR index out of range")) => + log.debug(s"Update of the value at $position in '$key' failed due to index out of range.") + throw new IndexOutOfBoundsException("Index out of range") } override def listHeadPop[T: ClassTag](key: String): Future[Option[T]] = @@ -211,52 +225,50 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R case Some(encoded) => log.trace(s"Hit on head in key '$key'.") Some(decode[T](key, encoded)) - case None => + case None => log.trace(s"Miss on head in key '$key'.") None } - override def listSlice[T: ClassTag](key: String, start: Int, end: Int): Future[Seq[T]] = - redis.lrange[String](key, start, end) executing "LRANGE" withKey key andParameters s"$start $end" expects { - case values => - log.debug(s"The range on '$key' from $start to $end included returned ${values.size} values.") - values.map(decode[T](key, _)) + override def listSlice[T: ClassTag](key: String, start: Long, end: Long): Future[Seq[T]] = + redis.lrange[String](key, start, end) executing "LRANGE" withKey key andParameters s"$start $end" expects { case values => + log.debug(s"The range on '$key' from $start to $end included returned ${values.size} values.") + values.map(decode[T](key, _)) } - override def listRemove(key: String, value: Any, count: Int): Future[Long] = - encode(key, value).flatMap(redis.lrem(key, count, _)) executing "LREM" withKey key andParameters s"$value $count" logging { - case removed => log.debug(s"Removed $removed occurrences of $value in '$key'.") + override def listRemove(key: String, value: Any, count: Long): Future[Long] = + encode(key, value).flatMap(redis.lrem(key, count, _)) executing "LREM" withKey key andParameters s"$value $count" logging { case removed => + log.debug(s"Removed $removed occurrences of $value in '$key'.") } - override def listTrim(key: String, start: Int, end: Int): Future[Unit] = - redis.ltrim(key, start, end) executing "LTRIM" withKey key andParameter s"$start $end" logging { - case _ => log.debug(s"Trimmed collection at '$key' to $start:$end ") + override def listTrim(key: String, start: Long, end: Long): Future[Unit] = + redis.ltrim(key, start, end) executing "LTRIM" withKey key andParameter s"$start $end" logging { case _ => + log.debug(s"Trimmed collection at '$key' to $start:$end ") } override def listInsert(key: String, pivot: Any, value: Any): Future[Option[Long]] = for { - pivot <- encode(key, pivot) - value <- encode(key, value) + pivot <- encode(key, pivot) + value <- encode(key, value) result <- redis.linsert(key, api.BEFORE, pivot, value) executing "LINSERT" withKey key andParameter s"$pivot $value" expects { - case -1L | 0L => - log.debug(s"Insert into the list at '$key' failed. Pivot not found.") - None - case length => - log.debug(s"Inserted $value into the list at '$key'. New size is $length.") - Some(length) - } recover[Option[Long]] { - case ExecutionFailedException(_, _, _, ex) if ex.getMessage startsWith "WRONGTYPE" => - log.warn(s"Value at '$key' is not a list.") - throw new IllegalArgumentException(s"Value at '$key' is not a list.") - } + case -1L | 0L => + log.debug(s"Insert into the list at '$key' failed. Pivot not found.") + None + case length => + log.debug(s"Inserted $value into the list at '$key'. New size is $length.") + Some(length) + } recover [Option[Long]] { + case ExecutionFailedException(_, _, _, ex) if ex.getMessage startsWith "WRONGTYPE" => + log.warn(s"Value at '$key' is not a list.") + throw new IllegalArgumentException(s"Value at '$key' is not a list.") + } } yield result override def setAdd(key: String, values: Any*): Future[Long] = { // encodes the value def toEncoded(value: Any) = encode(key, value) - Future.sequence(values map toEncoded).flatMap(redis.sadd(key, _: _*)) executing "SADD" withKey key andParameters values expects { - case inserted => - log.debug(s"Inserted $inserted elements into the set at '$key'.") - inserted + Future.sequence(values map toEncoded).flatMap(redis.sadd(key, _: _*)) executing "SADD" withKey key andParameters values expects { case inserted => + log.debug(s"Inserted $inserted elements into the set at '$key'.") + inserted } recover { case ExecutionFailedException(_, _, _, ex) if ex.getMessage startsWith "WRONGTYPE" => log.warn(s"Value at '$key' is not a set.") @@ -265,15 +277,14 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R } override def setSize(key: String): Future[Long] = - redis.scard(key) executing "SCARD" withKey key logging { - case length => log.debug(s"The collection at '$key' has $length items.") + redis.scard(key) executing "SCARD" withKey key logging { case length => + log.debug(s"The collection at '$key' has $length items.") } override def setMembers[T: ClassTag](key: String): Future[Set[T]] = - redis.smembers[String](key) executing "SMEMBERS" withKey key expects { - case items => - log.debug(s"Returned ${items.size} items from the collection at '$key'.") - items.map(decode[T](key, _)).toSet + redis.smembers[String](key) executing "SMEMBERS" withKey key expects { case items => + log.debug(s"Returned ${items.size} items from the collection at '$key'.") + items.map(decode[T](key, _)).toSet } override def setIsMember(key: String, value: Any): Future[Boolean] = @@ -286,8 +297,8 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R // encodes the value def toEncoded(value: Any): Future[String] = encode(key, value) - Future.sequence(values map toEncoded).flatMap(redis.srem(key, _: _*)) executing "SREM" withKey key andParameters values logging { - case removed => log.debug(s"Removed $removed elements from the collection at '$key'.") + Future.sequence(values map toEncoded).flatMap(redis.srem(key, _: _*)) executing "SREM" withKey key andParameters values logging { case removed => + log.debug(s"Removed $removed elements from the collection at '$key'.") } } @@ -295,10 +306,9 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R // encodes the value def toEncoded(scoreValue: (Double, Any)) = encode(key, scoreValue._2).map((scoreValue._1, _)) - Future.sequence(scoreValues.map(toEncoded)).flatMap(redis.zadd(key, _: _*)) executing "ZADD" withKey key andParameters scoreValues expects { - case inserted => - log.debug(s"Inserted $inserted elements into the zset at '$key'.") - inserted + Future.sequence(scoreValues.map(toEncoded)).flatMap(redis.zadd(key, _: _*)) executing "ZADD" withKey key andParameters scoreValues expects { case inserted => + log.debug(s"Inserted $inserted elements into the zset at '$key'.") + inserted } recover { case ExecutionFailedException(_, _, _, ex) if ex.getMessage startsWith "WRONGTYPE" => log.warn(s"Value at '$key' is not a zset.") @@ -307,50 +317,45 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R } override def sortedSetSize(key: String): Future[Long] = - redis.zcard(key) executing "ZCARD" withKey key logging { - case length => log.debug(s"The zset at '$key' has $length items.") + redis.zcard(key) executing "ZCARD" withKey key logging { case length => + log.debug(s"The zset at '$key' has $length items.") } - override def sortedSetScore(key: String, value: Any): Future[Option[Double]] = { + override def sortedSetScore(key: String, value: Any): Future[Option[Double]] = encode(key, value) flatMap (redis.zscore(key, _)) executing "ZSCORE" withKey key andParameter value logging { case Some(score) => log.debug(s"The score of item: $value is $score in the collection at '$key'.") case None => log.debug(s"Item $value does not exist in the collection at '$key'") } - } override def sortedSetRemove(key: String, values: Any*): Future[Long] = { // encodes the value def toEncoded(value: Any) = encode(key, value) - Future.sequence(values map toEncoded).flatMap(redis.zrem(key, _: _*)) executing "ZREM" withKey key andParameters values logging { - case removed => log.debug(s"Removed $removed elements from the zset at '$key'.") + Future.sequence(values map toEncoded).flatMap(redis.zrem(key, _: _*)) executing "ZREM" withKey key andParameters values logging { case removed => + log.debug(s"Removed $removed elements from the zset at '$key'.") } } - override def sortedSetRange[T: ClassTag](key: String, start: Long, stop: Long): Future[Seq[T]] = { - redis.zrange[String](key, start, stop) executing "ZRANGE" withKey key andParameter s"$start $stop" expects { - case encodedSeq => - log.debug(s"Got range from $start to $stop in the zset at '$key'.") - encodedSeq.map(encoded => decode[T](key, encoded)) + override def sortedSetRange[T: ClassTag](key: String, start: Long, stop: Long): Future[Seq[T]] = + redis.zrange[String](key, start, stop) executing "ZRANGE" withKey key andParameter s"$start $stop" expects { case encodedSeq => + log.debug(s"Got range from $start to $stop in the zset at '$key'.") + encodedSeq.map(encoded => decode[T](key, encoded)) } - } - override def sortedSetReverseRange[T: ClassTag](key: String, start: Long, stop: Long): Future[Seq[T]] = { - redis.zrevrange[String](key, start, stop) executing "ZREVRANGE" withKey key andParameter s"$start $stop" expects { - case encodedSeq => - log.debug(s"Got reverse range from $start to $stop in the zset at '$key'.") - encodedSeq.map(encoded => decode[T](key, encoded)) + override def sortedSetReverseRange[T: ClassTag](key: String, start: Long, stop: Long): Future[Seq[T]] = + redis.zrevrange[String](key, start, stop) executing "ZREVRANGE" withKey key andParameter s"$start $stop" expects { case encodedSeq => + log.debug(s"Got reverse range from $start to $stop in the zset at '$key'.") + encodedSeq.map(encoded => decode[T](key, encoded)) } - } override def hashRemove(key: String, fields: String*): Future[Long] = - redis.hdel(key, fields: _*) executing "HDEL" withKey key andParameters fields logging { - case removed => log.debug(s"Removed $removed elements from the collection at '$key'.") + redis.hdel(key, fields: _*) executing "HDEL" withKey key andParameters fields logging { case removed => + log.debug(s"Removed $removed elements from the collection at '$key'.") } override def hashIncrement(key: String, field: String, incrementBy: Long): Future[Long] = - redis.hincrby(key, field, incrementBy) executing "HINCRBY" withKey key andParameters s"$field $incrementBy" logging { - case value => log.debug(s"Field '$field' in '$key' was incremented to $value.") + redis.hincrby(key, field, incrementBy) executing "HINCRBY" withKey key andParameters s"$field $incrementBy" logging { case value => + log.debug(s"Field '$field' in '$key' was incremented to $value.") } override def hashExists(key: String, field: String): Future[Boolean] = @@ -364,16 +369,15 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R case Some(encoded) => log.debug(s"Item $field exists in the collection at '$key'.") Some(decode[T](key, encoded)) - case None => + case None => log.debug(s"Item $field is not in the collection at '$key'.") None } override def hashGet[T: ClassTag](key: String, fields: Seq[String]): Future[Seq[Option[T]]] = - redis.hmget[String](key, fields: _*) executing "HMGET" withKey key andParameters fields expects { - case encoded => - log.debug(s"Collection at '$key' with fields '$fields' has returned ${encoded.size} items.") - encoded.map(_.map(decode[T](key, _))) + redis.hmget[String](key, fields: _*) executing "HMGET" withKey key andParameters fields expects { case encoded => + log.debug(s"Collection at '$key' with fields '$fields' has returned ${encoded.size} items.") + encoded.map(_.map(decode[T](key, _))) } override def hashGetAll[T: ClassTag](key: String): Future[Map[String, T]] = @@ -381,21 +385,20 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R case empty if empty.isEmpty => log.debug(s"Collection at '$key' is empty.") Map.empty[String, T] - case encoded => + case encoded => log.debug(s"Collection at '$key' has ${encoded.size} items.") encoded.map { case (itemKey, value) => itemKey -> decode[T](itemKey, value) } } override def hashSize(key: String): Future[Long] = - redis.hlen(key) executing "HLEN" withKey key logging { - case length => log.debug(s"The collection at '$key' has $length items.") + redis.hlen(key) executing "HLEN" withKey key logging { case length => + log.debug(s"The collection at '$key' has $length items.") } override def hashKeys(key: String): Future[Set[String]] = - redis.hkeys(key) executing "HKEYS" withKey key expects { - case keys => - log.debug(s"The collection at '$key' defines: ${keys mkString " "}.") - keys.toSet + redis.hkeys(key) executing "HKEYS" withKey key expects { case keys => + log.debug(s"The collection at '$key' defines: ${keys mkString " "}.") + keys.toSet } override def hashSet(key: String, field: String, value: Any): Future[Boolean] = @@ -409,13 +412,12 @@ private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: R } override def hashValues[T: ClassTag](key: String): Future[Set[T]] = - redis.hvals[String](key) executing "HVALS" withKey key expects { - case values => - log.debug(s"The collection at '$key' contains ${values.size} values.") - values.map(decode[T](key, _)).toSet + redis.hvals[String](key) executing "HVALS" withKey key expects { case values => + log.debug(s"The collection at '$key' contains ${values.size} values.") + values.map(decode[T](key, _)).toSet } // $COVERAGE-OFF$ - override def toString = s"RedisConnector(name=$name)" + override def toString: String = s"RedisConnector(name=$name)" // $COVERAGE-ON$ } diff --git a/src/main/scala/play/api/cache/redis/connector/RedisConnectorProvider.scala b/src/main/scala/play/api/cache/redis/connector/RedisConnectorProvider.scala index 32bc7d60..31dc2984 100644 --- a/src/main/scala/play/api/cache/redis/connector/RedisConnectorProvider.scala +++ b/src/main/scala/play/api/cache/redis/connector/RedisConnectorProvider.scala @@ -1,15 +1,12 @@ package play.api.cache.redis.connector -import javax.inject.Provider - +import akka.actor.ActorSystem import play.api.cache.redis._ import play.api.inject.ApplicationLifecycle -import akka.actor.ActorSystem +import javax.inject.Provider -/** - * Provides an instance of named redis connector - */ +/** Provides an instance of named redis connector */ private[redis] class RedisConnectorProvider(instance: RedisInstance, serializer: AkkaSerializer)(implicit system: ActorSystem, lifecycle: ApplicationLifecycle, runtime: RedisRuntime) extends Provider[RedisConnector] { private[connector] lazy val commands = new RedisCommandsProvider(instance).get diff --git a/src/main/scala/play/api/cache/redis/connector/RequestTimeout.scala b/src/main/scala/play/api/cache/redis/connector/RequestTimeout.scala index b3a3e886..57c500b9 100644 --- a/src/main/scala/play/api/cache/redis/connector/RequestTimeout.scala +++ b/src/main/scala/play/api/cache/redis/connector/RequestTimeout.scala @@ -1,43 +1,39 @@ package play.api.cache.redis.connector -import scala.concurrent.{ExecutionContext, Future} -import scala.concurrent.duration._ - import akka.actor.Scheduler import akka.pattern.after import redis._ +import scala.concurrent.duration._ +import scala.concurrent.{ExecutionContext, Future} + /** - * - * Helper for manipulation with the request to the redis. - * It defines the common variables and methods to avoid - * code duplication + * Helper for manipulation with the request to the redis. It defines the common + * variables and methods to avoid code duplication */ trait RequestTimeout extends Request { - protected implicit val scheduler: Scheduler + implicit protected val scheduler: Scheduler } object RequestTimeout { // fails @inline - def fail(failAfter: FiniteDuration)(implicit scheduler: Scheduler, context: ExecutionContext): Future[Nothing] = { + def fail(failAfter: FiniteDuration)(implicit scheduler: Scheduler, context: ExecutionContext): Future[Nothing] = after(failAfter, scheduler)(Future.failed(redis.actors.NoConnectionException)) - } // first completed @inline - def invokeOrFail[T](continue: => Future[T], failAfter: FiniteDuration)(implicit scheduler: Scheduler, context: ExecutionContext): Future[T] = { + def invokeOrFail[T](continue: => Future[T], failAfter: FiniteDuration)(implicit scheduler: Scheduler, context: ExecutionContext): Future[T] = Future.firstCompletedOf(Seq(continue, fail(failAfter))) - } + } /** - * Actor extension maintaining current connected status. - * The operations are not invoked when the connection - * is not established, the failed future is returned - * instead. + * Actor extension maintaining current connected status. The operations are not + * invoked when the connection is not established, the failed future is + * returned instead. */ trait FailEagerly extends RequestTimeout { import RequestTimeout._ @@ -48,18 +44,19 @@ trait FailEagerly extends RequestTimeout { @inline protected def connectionTimeout: Option[FiniteDuration] - abstract override def send[T](redisCommand: RedisCommand[_ <: protocol.RedisReply, T]): Future[T] = { + abstract override def send[T](redisCommand: RedisCommand[? <: protocol.RedisReply, T]): Future[T] = { // proceed with the command @inline def continue: Future[T] = super.send(redisCommand) // based on connection status if (connected) continue else connectionTimeout.fold(continue)(invokeOrFail(continue, _)) } + } /** - * Actor extension implementing a request timeout, if enabled. - * This is due to no internal timeout provided by - * the redis-scala to avoid never-completed futures. + * Actor extension implementing a request timeout, if enabled. This is due to + * no internal timeout provided by the redis-scala to avoid never-completed + * futures. */ trait RedisRequestTimeout extends RequestTimeout { import RequestTimeout._ @@ -69,7 +66,7 @@ trait RedisRequestTimeout extends RequestTimeout { /** indicates the timeout on the redis request */ protected def timeout: Option[FiniteDuration] - abstract override def send[T](redisCommand: RedisCommand[_ <: protocol.RedisReply, T]): Future[T] = { + abstract override def send[T](redisCommand: RedisCommand[? <: protocol.RedisReply, T]): Future[T] = { // proceed with the command @inline def continue: Future[T] = super.send(redisCommand) // based on connection status diff --git a/src/main/scala/play/api/cache/redis/connector/package.scala b/src/main/scala/play/api/cache/redis/connector/package.scala index 76638403..95d3aba2 100644 --- a/src/main/scala/play/api/cache/redis/connector/package.scala +++ b/src/main/scala/play/api/cache/redis/connector/package.scala @@ -1,13 +1,12 @@ package play.api.cache.redis import scala.concurrent.Future -import scala.language.implicitConversions package object connector { implicit def future2expected[T](future: Future[T]): ExpectedFutureBuilder[T] = new ExpectedFutureBuilder[T](future) - implicit class TupleHelper[+A, +B](val tuple: (A, B)) extends AnyVal { + implicit class TupleHelper[+A, +B](private val tuple: (A, B)) extends AnyVal { @inline def key: A = tuple._1 @inline def value: B = tuple._2 @inline def asString: String = s"$key $value" @@ -17,4 +16,5 @@ package object connector { implicit class StringWhen(private val value: String) extends AnyVal { def when(condition: Boolean): String = if (condition) value else "" } + } diff --git a/src/main/scala/play/api/cache/redis/exceptions.scala b/src/main/scala/play/api/cache/redis/exceptions.scala index 276efacc..7e850a08 100644 --- a/src/main/scala/play/api/cache/redis/exceptions.scala +++ b/src/main/scala/play/api/cache/redis/exceptions.scala @@ -1,38 +1,25 @@ package play.api.cache.redis -/** - * Generic exception produced by the library indicating internal failure - */ +/** Generic exception produced by the library indicating internal failure */ sealed abstract class RedisException(message: String, cause: Throwable) extends RuntimeException(message, cause) { def this(message: String) = this(message, null) } -/** - * Request timeouts -*/ -final case class TimeoutException( - cause: Throwable) extends RedisException("Command execution timed out", cause) +/** Request timeouts */ +final case class TimeoutException(cause: Throwable) extends RedisException("Command execution timed out", cause) -/** - * Command execution failed with exception - */ -final case class ExecutionFailedException( - key: Option[String], command: String, statement: String, cause: Throwable) extends RedisException(s"Execution of '$command'${key.map(key => s" for key '$key'") getOrElse ""} failed", cause) +/** Command execution failed with exception */ +final case class ExecutionFailedException(key: Option[String], command: String, statement: String, cause: Throwable) extends RedisException(s"Execution of '$command'${key.map(key => s" for key '$key'") getOrElse ""} failed", cause) -/** - * Request succeeded but returned unexpected value - */ -final case class UnexpectedResponseException( - key: Option[String], command: String) extends RedisException(s"Command '$command'${key.map(key => s" for key '$key'") getOrElse ""} returned unexpected response") +/** Request succeeded but returned unexpected value */ +final case class UnexpectedResponseException(key: Option[String], command: String) extends RedisException(s"Command '$command'${key.map(key => s" for key '$key'") getOrElse ""} returned unexpected response") -/** - * Value serialization or deserialization failed. - */ -final case class SerializationException( - key: String, message: String, cause: Throwable) extends RedisException(s"$message for $key", cause) +/** Value serialization or deserialization failed. */ +final case class SerializationException(key: String, message: String, cause: Throwable) extends RedisException(s"$message for $key", cause) /** - * Helper trait providing simplified and unified API to exception handling in play-redis + * Helper trait providing simplified and unified API to exception handling in + * play-redis */ trait ExceptionImplicits { @@ -65,4 +52,5 @@ trait ExceptionImplicits { @throws[IllegalStateException] def invalidConfiguration(message: String): Nothing = throw new IllegalStateException(message) + } diff --git a/src/main/scala/play/api/cache/redis/impl/AsyncJavaRedis.scala b/src/main/scala/play/api/cache/redis/impl/AsyncJavaRedis.scala index 873de9d7..951d34a1 100644 --- a/src/main/scala/play/api/cache/redis/impl/AsyncJavaRedis.scala +++ b/src/main/scala/play/api/cache/redis/impl/AsyncJavaRedis.scala @@ -1,56 +1,56 @@ package play.api.cache.redis.impl -import scala.concurrent.duration._ -import scala.concurrent.{ExecutionContext, Future} -import scala.reflect.ClassTag - import play.api.Environment import play.api.cache.redis._ import play.cache.redis._ +import scala.concurrent.duration._ +import scala.concurrent.{ExecutionContext, Future} +import scala.reflect.ClassTag + /** * Implements Play Java version of play.api.CacheApi * - * This acts as an adapter to Play Scala CacheApi, because Java Api is slightly different than Scala Api + * This acts as an adapter to Play Scala CacheApi, because Java Api is slightly + * different than Scala Api */ private[impl] class AsyncJavaRedis(internal: CacheAsyncApi)(implicit environment: Environment, runtime: RedisRuntime) extends play.cache.AsyncCacheApi with play.cache.redis.AsyncCacheApi { import JavaCompatibility._ - def set(key: String, value: scala.Any, expiration: Int): CompletionStage[Done] = { + def set(key: String, value: scala.Any, expiration: Int): CompletionStage[Done] = async { implicit context => set(key, value, expiration.seconds) } - } - def set(key: String, value: scala.Any): CompletionStage[Done] = { + def set(key: String, value: scala.Any): CompletionStage[Done] = async { implicit context => set(key, value, Duration.Inf) } - } - private def set(key: String, value: scala.Any, duration: Duration)(implicit ec: ExecutionContext): Future[Done] = { - Future.from( - // set the value - internal.set(key, value, duration), - // and set its type to be able to read it - internal.set(classTagKey(key), classTagOf(value), duration) - ).asDone - } + private def set(key: String, value: scala.Any, duration: Duration)(implicit ec: ExecutionContext): Future[Done] = + Future + .from( + // set the value + internal.set(key, value, duration), + // and set its type to be able to read it + internal.set(classTagKey(key), classTagOf(value), duration), + ) + .asDone - def remove(key: String): CompletionStage[Done] = { + def remove(key: String): CompletionStage[Done] = async { implicit context => - Future.from( - internal.remove(key), - internal.remove(classTagKey(key)) - ).asDone + Future + .from( + internal.remove(key), + internal.remove(classTagKey(key)), + ) + .asDone } - } - def get[T](key: String): CompletionStage[Optional[T]] = { + def get[T](key: String): CompletionStage[Optional[T]] = async { implicit context => getOrElseOption[T](key, None).map(_.asJava) } - } def getOrElse[T](key: String, block: Callable[T]): CompletionStage[T] = getOrElseUpdate[T](key, (() => Future.successful(block.call()).asJava).asJava) @@ -64,11 +64,10 @@ private[impl] class AsyncJavaRedis(internal: CacheAsyncApi)(implicit environment def getOrElseUpdate[T](key: String, block: Callable[CompletionStage[T]], expiration: Int): CompletionStage[T] = getOrElse[T](key, Some(block), duration = expiration.seconds) - private def getOrElse[T](key: String, callable: Option[Callable[CompletionStage[T]]], duration: Duration = Duration.Inf): CompletionStage[T] = { + private def getOrElse[T](key: String, callable: Option[Callable[CompletionStage[T]]], duration: Duration = Duration.Inf): CompletionStage[T] = async { implicit context => getOrElseOption(key, callable, duration).map[T](play.libs.Scala.orNull) } - } private def getOrElseOption[T](key: String, callable: Option[Callable[CompletionStage[T]]], duration: Duration = Duration.Inf)(implicit context: ExecutionContext): Future[Option[T]] = { // get the tag and decode it @@ -83,8 +82,8 @@ private[impl] class AsyncJavaRedis(internal: CacheAsyncApi)(implicit environment // compute or else and save it into cache def orElse(callable: Callable[CompletionStage[T]]) = callable.call().asScala def saveOrElse(value: T) = set(key, value, duration) - def savedOrElse(callable: Callable[CompletionStage[T]]) = orElse(callable).flatMap { - value => runtime.invocation.invoke(saveOrElse(value), Some(value)) + def savedOrElse(callable: Callable[CompletionStage[T]]) = orElse(callable).flatMap { value => + runtime.invocation.invoke(saveOrElse(value), Some(value)) } getValue.flatMap { @@ -93,125 +92,123 @@ private[impl] class AsyncJavaRedis(internal: CacheAsyncApi)(implicit environment } } - def getAll[T](classTag: Class[T], keys: JavaList[String]): CompletionStage[JavaList[Optional[T]]] = { + def getAll[T](classTag: Class[T], keys: JavaList[String]): CompletionStage[JavaList[Optional[T]]] = async { implicit context => internal.getAll(keys.asScala)(classTag).map(_.map(_.asJava).asJava) } - } def removeAll(): CompletionStage[Done] = internal.invalidate().asJava - def exists(key: String): CompletionStage[java.lang.Boolean] = { + def exists(key: String): CompletionStage[java.lang.Boolean] = async { implicit context => internal.exists(key).map(Boolean.box) } - } - def matching(pattern: String): CompletionStage[JavaList[String]] = { + def matching(pattern: String): CompletionStage[JavaList[String]] = async { implicit context => internal.matching(pattern).map(_.asJava) } - } - def setIfNotExists(key: String, value: Any): CompletionStage[java.lang.Boolean] = { + def setIfNotExists(key: String, value: Any): CompletionStage[java.lang.Boolean] = async { implicit context => - Future.from( - internal.setIfNotExists(key, value).map(Boolean.box), - internal.setIfNotExists(classTagKey(key), classTagOf(value)).map(Boolean.box) - ).map(_.head) + Future + .from( + internal.setIfNotExists(key, value).map(Boolean.box), + internal.setIfNotExists(classTagKey(key), classTagOf(value)).map(Boolean.box), + ) + .map(_.head) } - } - def setIfNotExists(key: String, value: Any, expiration: Int): CompletionStage[java.lang.Boolean] = { + def setIfNotExists(key: String, value: Any, expiration: Int): CompletionStage[java.lang.Boolean] = async { implicit context => - Future.from( - internal.setIfNotExists(key, value, expiration.seconds).map(Boolean.box), - internal.setIfNotExists(classTagKey(key), classTagOf(value), expiration.seconds).map(Boolean.box) - ).map(_.head) + Future + .from( + internal.setIfNotExists(key, value, expiration.seconds).map(Boolean.box), + internal.setIfNotExists(classTagKey(key), classTagOf(value), expiration.seconds).map(Boolean.box), + ) + .map(_.head) } - } - def setAll(keyValues: KeyValue*): CompletionStage[Done] = { - async { implicit context => + def setAll(keyValues: KeyValue*): CompletionStage[Done] = + async { _ => internal.setAll( keyValues.flatMap { kv => Iterable((kv.key, kv.value), (classTagKey(kv.key), classTagOf(kv.value))) - }: _* + }: _*, ) } - } - def setAllIfNotExist(keyValues: KeyValue*): CompletionStage[java.lang.Boolean] = { + def setAllIfNotExist(keyValues: KeyValue*): CompletionStage[java.lang.Boolean] = async { implicit context => - internal.setAllIfNotExist( - keyValues.flatMap(kv => Seq((kv.key, kv.value), (classTagKey(kv.key), classTagOf(kv.value)))): _* - ).map(Boolean.box) + internal + .setAllIfNotExist( + keyValues.flatMap(kv => Seq((kv.key, kv.value), (classTagKey(kv.key), classTagOf(kv.value)))): _*, + ) + .map(Boolean.box) } - } - def append(key: String, value: String): CompletionStage[Done] = { + def append(key: String, value: String): CompletionStage[Done] = async { implicit context => - Future.from( - internal.append(key, value), - internal.setIfNotExists(classTagKey(key), classTagOf(value)) - ).asDone + Future + .from( + internal.append(key, value), + internal.setIfNotExists(classTagKey(key), classTagOf(value)).asDone, + ) + .asDone } - } - def append(key: String, value: String, expiration: Int): CompletionStage[Done] = { + def append(key: String, value: String, expiration: Int): CompletionStage[Done] = async { implicit context => - Future.from( - internal.append(key, value, expiration.seconds), - internal.setIfNotExists(classTagKey(key), classTagOf(value), expiration.seconds) - ).asDone + Future + .from( + internal.append(key, value, expiration.seconds), + internal.setIfNotExists(classTagKey(key), classTagOf(value), expiration.seconds).asDone, + ) + .asDone } - } - def expire(key: String, expiration: Int): CompletionStage[Done] = { + def expire(key: String, expiration: Int): CompletionStage[Done] = async { implicit context => - Future.from( - internal.expire(key, expiration.seconds), - internal.expire(classTagKey(key), expiration.seconds) - ).asDone + Future + .from( + internal.expire(key, expiration.seconds), + internal.expire(classTagKey(key), expiration.seconds), + ) + .asDone } - } - def expiresIn(key: String): CompletionStage[Optional[java.lang.Long]] = { + def expiresIn(key: String): CompletionStage[Optional[java.lang.Long]] = async { implicit context => internal.expiresIn(key).map(_.map(_.toSeconds).map(Long.box).asJava) } - } - def remove(key1: String, key2: String, keys: String*): CompletionStage[Done] = { + def remove(key1: String, key2: String, keys: String*): CompletionStage[Done] = removeAllKeys(Seq(key1, key2) ++ keys: _*) - } - def removeAllKeys(keys: String*): CompletionStage[Done] = { - async { implicit context => + def removeAllKeys(keys: String*): CompletionStage[Done] = + async { _ => internal.removeAll(keys.flatMap(_.withClassTag): _*) } - } - def removeMatching(pattern: String): CompletionStage[Done] = { + def removeMatching(pattern: String): CompletionStage[Done] = async { implicit context => - Future.from( - internal.removeMatching(pattern), - internal.removeMatching(classTagKey(pattern)) - ).asDone + Future + .from( + internal.removeMatching(pattern), + internal.removeMatching(classTagKey(pattern)), + ) + .asDone } - } - def increment(key: String, by: java.lang.Long): CompletionStage[java.lang.Long] = { + def increment(key: String, by: java.lang.Long): CompletionStage[java.lang.Long] = async { implicit context => internal.increment(key, by).map(Long.box) } - } - def decrement(key: String, by: java.lang.Long): CompletionStage[java.lang.Long] = { + def decrement(key: String, by: java.lang.Long): CompletionStage[java.lang.Long] = async { implicit context => internal.decrement(key, by).map(Long.box) } - } def list[T](key: String, classTag: Class[T]): AsyncRedisList[T] = new RedisListJavaImpl(internal.list[T](key)(classTag)) diff --git a/src/main/scala/play/api/cache/redis/impl/AsyncRedisImpl.scala b/src/main/scala/play/api/cache/redis/impl/AsyncRedisImpl.scala index 40f01615..316e9e3d 100644 --- a/src/main/scala/play/api/cache/redis/impl/AsyncRedisImpl.scala +++ b/src/main/scala/play/api/cache/redis/impl/AsyncRedisImpl.scala @@ -1,14 +1,12 @@ package play.api.cache.redis.impl +import play.api.cache.redis._ + import scala.concurrent.Future import scala.concurrent.duration.Duration import scala.reflect.ClassTag -import play.api.cache.redis._ - -/** - * Implementation of **asynchronous** Redis API - */ +/** Implementation of **asynchronous** Redis API */ private[impl] trait AsyncRedis extends play.api.cache.AsyncCacheApi with CacheAsyncApi private[impl] class AsyncRedisImpl(redis: RedisConnector)(implicit runtime: RedisRuntime) extends RedisCache(redis, Builders.AsynchronousBuilder) with AsyncRedis { diff --git a/src/main/scala/play/api/cache/redis/impl/Builders.scala b/src/main/scala/play/api/cache/redis/impl/Builders.scala index 35c62cb6..4a45fc28 100644 --- a/src/main/scala/play/api/cache/redis/impl/Builders.scala +++ b/src/main/scala/play/api/cache/redis/impl/Builders.scala @@ -3,23 +3,30 @@ package play.api.cache.redis.impl import scala.concurrent.Future /** - * Transforms future result produced by redis implementation to the result of the desired type + * Transforms future result produced by redis implementation to the result of + * the desired type */ -object Builders { +private object Builders { + import dsl._ import play.api.cache.redis._ import akka.pattern.AskTimeoutException - trait ResultBuilder[Result[X]] { + trait ResultBuilder[Result[_]] { + /** name of the builder used for internal purposes */ def name: String - /** converts future result produced by Redis to the result of desired type */ + + /** + * converts future result produced by Redis to the result of desired type + */ def toResult[T](run: => Future[T], default: => Future[T])(implicit runtime: RedisRuntime): Result[T] + /** maps the value */ def map[T, U](result: Result[T])(f: T => U)(implicit runtime: RedisRuntime): Result[U] // $COVERAGE-OFF$ /** show the builder name */ - override def toString = s"ResultBuilder($name)" + override def toString: String = s"ResultBuilder($name)" // $COVERAGE-ON$ } @@ -36,6 +43,7 @@ object Builders { override def map[T, U](result: AsynchronousResult[T])(f: T => U)(implicit runtime: RedisRuntime): AsynchronousResult[U] = result.map(f) + } /** converts the future into the value */ @@ -61,5 +69,7 @@ object Builders { override def map[T, U](result: SynchronousResult[T])(f: T => U)(implicit runtime: RedisRuntime): SynchronousResult[U] = f(result) + } + } diff --git a/src/main/scala/play/api/cache/redis/impl/InvocationPolicy.scala b/src/main/scala/play/api/cache/redis/impl/InvocationPolicy.scala index 949d0ace..6bd6837a 100644 --- a/src/main/scala/play/api/cache/redis/impl/InvocationPolicy.scala +++ b/src/main/scala/play/api/cache/redis/impl/InvocationPolicy.scala @@ -3,26 +3,31 @@ package play.api.cache.redis.impl import scala.concurrent.{ExecutionContext, Future} /** - * Invocation policy implements whether to wait for the operation result or not. - * This applies only in the limited number of operations. The best examples are `getOrElse` - * and `getOrFuture`. First, both methods invoke `get`, then, if missed, compute `orElse` clause. - * Finally, there is the invocation of `set`, however, in some scenarios, there is not required to - * wait for the result of `set` operation. The value can be returned earlier. This is the difference - * between `Eager` (not waiting) and `Lazy` (waiting) invocation policies. + * Invocation policy implements whether to wait for the operation result or + * not. This applies only in the limited number of operations. The best + * examples are `getOrElse` and `getOrFuture`. First, both methods invoke + * `get`, then, if missed, compute `orElse` clause. Finally, there is the + * invocation of `set`, however, in some scenarios, there is not required to + * wait for the result of `set` operation. The value can be returned earlier. + * This is the difference between `Eager` (not waiting) and `Lazy` (waiting) + * invocation policies. */ sealed trait InvocationPolicy { def invoke[T](f: => Future[Any], thenReturn: T)(implicit context: ExecutionContext): Future[T] } object EagerInvocation extends InvocationPolicy { + override def invoke[T](f: => Future[Any], thenReturn: T)(implicit context: ExecutionContext): Future[T] = { f: Unit Future successful thenReturn } + } object LazyInvocation extends InvocationPolicy { - override def invoke[T](f: => Future[Any], thenReturn: T)(implicit context: ExecutionContext): Future[T] = { + + override def invoke[T](f: => Future[Any], thenReturn: T)(implicit context: ExecutionContext): Future[T] = f.map(_ => thenReturn) - } + } diff --git a/src/main/scala/play/api/cache/redis/impl/JavaCompatibility.scala b/src/main/scala/play/api/cache/redis/impl/JavaCompatibility.scala index d6b56e5d..39a03032 100644 --- a/src/main/scala/play/api/cache/redis/impl/JavaCompatibility.scala +++ b/src/main/scala/play/api/cache/redis/impl/JavaCompatibility.scala @@ -1,13 +1,11 @@ package play.api.cache.redis.impl -import scala.concurrent.{ExecutionContext, Future} -import scala.language.implicitConversions -import scala.reflect.ClassTag - +import akka.Done import play.api.Environment import play.api.cache.redis._ -import akka.Done +import scala.concurrent.{ExecutionContext, Future} +import scala.reflect.ClassTag private[impl] object JavaCompatibility extends JavaCompatibilityBase { import scala.compat.java8.{FutureConverters, OptionConverters} @@ -20,61 +18,60 @@ private[impl] object JavaCompatibility extends JavaCompatibilityBase { type JavaSet[T] = java.util.Set[T] object JavaList { + def apply[T](values: T*): JavaList[T] = { val list = new java.util.ArrayList[T]() list.addAll(values.asJava): Unit list } + } - implicit class Java8Stage[T](val future: Future[T]) extends AnyVal { + implicit class Java8Stage[T](private val future: Future[T]) extends AnyVal { @inline def asJava: CompletionStage[T] = FutureConverters.toJava(future) @inline def asDone(implicit ec: ExecutionContext): Future[Done] = future.map(_ => Done) } - implicit class Java8Callable[T](val f: () => T) extends AnyVal { + implicit class Java8Callable[T](private val f: () => T) extends AnyVal { @inline def asJava: Callable[T] = () => f() } - implicit class Java8Optional[T](val option: Option[T]) extends AnyVal { + implicit class Java8Optional[T](private val option: Option[T]) extends AnyVal { @inline def asJava: Optional[T] = OptionConverters.toJava(option) } - implicit class ScalaCompatibility[T](val future: CompletionStage[T]) extends AnyVal { + implicit class ScalaCompatibility[T](private val future: CompletionStage[T]) extends AnyVal { @inline def asScala: Future[T] = FutureConverters.toScala(future) } - implicit class RichFuture(val future: Future.type) extends AnyVal { + implicit class RichFuture(private val future: Future.type) extends AnyVal { @inline def from[T](futures: Future[T]*)(implicit ec: ExecutionContext): Future[Seq[T]] = future.sequence(futures) } @inline implicit def class2tag[T](classOf: Class[T]): ClassTag[T] = ClassTag(classOf) - @inline def async[T](doAsync: ExecutionContext => Future[T])(implicit runtime: RedisRuntime): CompletionStage[T] = { + @inline def async[T](doAsync: ExecutionContext => Future[T])(implicit runtime: RedisRuntime): CompletionStage[T] = doAsync { // save the HTTP context if any and restore it later for orElse clause play.core.j.ClassLoaderExecutionContext.fromThread(runtime.context) }.asJava - } @inline def classTagKey(key: String): String = s"classTag::$key" - @inline def classTagOf(value: Any): String = { + @inline def classTagOf(value: Any): String = if (Option(value).isEmpty) "null" else value.getClass.getCanonicalName - } - @inline def classTagFrom[T](tag: String)(implicit environment: Environment): ClassTag[T] = { + @inline def classTagFrom[T](tag: String)(implicit environment: Environment): ClassTag[T] = if (tag === "null") ClassTag.Null.asInstanceOf[ClassTag[T]] else ClassTag(classTagNameToClass(tag, environment)) - } - implicit class CacheKey(val key: String) extends AnyVal { + implicit class CacheKey(private val key: String) extends AnyVal { @inline def withClassTag: Seq[String] = Seq(key, classTagKey(key)) } // $COVERAGE-OFF$ /** java primitives are serialized into their type names instead of classes */ - private def classTagNameToClass(name: String, environment: Environment): Class[_] = name match { + private def classTagNameToClass(name: String, environment: Environment): Class[?] = name match { case "boolean[]" => classOf[Array[java.lang.Boolean]] case "byte[]" => classOf[Array[java.lang.Byte]] case "char[]" => classOf[Array[java.lang.Character]] diff --git a/src/main/scala/play/api/cache/redis/impl/RedisCache.scala b/src/main/scala/play/api/cache/redis/impl/RedisCache.scala index 5aa38560..926e28db 100644 --- a/src/main/scala/play/api/cache/redis/impl/RedisCache.scala +++ b/src/main/scala/play/api/cache/redis/impl/RedisCache.scala @@ -1,13 +1,15 @@ package play.api.cache.redis.impl +import play.api.cache.redis._ + import scala.concurrent._ import scala.concurrent.duration.Duration -import scala.language.implicitConversions import scala.reflect.ClassTag -import play.api.cache.redis._ - -/**

Implementation of plain API using redis-server cache and Brando connector implementation.

*/ +/** + *

Implementation of plain API using redis-server cache and Brando connector + * implementation.

+ */ private[impl] class RedisCache[Result[_]](redis: RedisConnector, builder: Builders.ResultBuilder[Result])(implicit runtime: RedisRuntime) extends AbstractCacheApi[Result] { // implicit ask timeout and execution context @@ -47,11 +49,14 @@ private[impl] class RedisCache[Result[_]](redis: RedisConnector, builder: Builde override def append(key: String, value: String, expiration: Duration): Result[Done] = key.prefixed { key => - redis.append(key, value).flatMap { result => - // if the new string length is equal to the appended string, it means they should equal - // when the finite duration is required, set it - if (result === value.length && expiration.isFinite) redis.expire(key, expiration) else Future.successful[Unit](()) - }.recoverWithDone + redis + .append(key, value) + .flatMap { result => + // if the new string length is equal to the appended string, it means they should equal + // when the finite duration is required, set it + if (result === value.length.toLong && expiration.isFinite) redis.expire(key, expiration) else Future.successful[Unit](()) + } + .recoverWithDone } override def expire(key: String, expiration: Duration): Result[Done] = @@ -60,14 +65,15 @@ private[impl] class RedisCache[Result[_]](redis: RedisConnector, builder: Builde } /** - * cached implementation of the matching function - * - * - when a prefix is empty, it simply delegates the invocation to the connector - * - when a prefix is defined, it unprefixes the keys when returned - */ + * cached implementation of the matching function + * + * - when a prefix is empty, it simply delegates the invocation to the + * connector + * - when a prefix is defined, it unprefixes the keys when returned + */ private val doMatching = runtime.prefix match { case RedisEmptyPrefix => (pattern: String) => redis.matching(pattern) - case _ => (pattern: String) => redis.matching(pattern).map(_.unprefixed) + case _ => (pattern: String) => redis.matching(pattern).map(_.unprefixed) } override def matching(pattern: String): Result[Seq[String]] = pattern.prefixed { pattern => @@ -79,12 +85,15 @@ private[impl] class RedisCache[Result[_]](redis: RedisConnector, builder: Builde override def getOrFuture[T: ClassTag](key: String, expiration: Duration)(orElse: => Future[T]): Future[T] = key.prefixed { key => - redis.get[T](key).flatMap { - // cache hit, return the unwrapped value - case Some(value) => value.toFuture - // cache miss, compute the value, store it into the cache but do not wait for the result and ignore it, directly return the value - case None => orElse flatMap { value => runtime.invocation.invoke(redis.set(key, value, expiration), thenReturn = value) } - }.recoverWithFuture(orElse) + redis + .get[T](key) + .flatMap { + // cache hit, return the unwrapped value + case Some(value) => value.toFuture + // cache miss, compute the value, store it into the cache but do not wait for the result and ignore it, directly return the value + case None => orElse flatMap { value => runtime.invocation.invoke(redis.set(key, value, expiration), thenReturn = value) } + } + .recoverWithFuture(orElse) } override def remove(key: String): Result[Done] = @@ -149,6 +158,6 @@ private[impl] class RedisCache[Result[_]](redis: RedisConnector, builder: Builde } // $COVERAGE-OFF$ - override def toString = s"RedisCache(name=${runtime.name})" + override def toString: String = s"RedisCache(name=${runtime.name})" // $COVERAGE-ON$ } diff --git a/src/main/scala/play/api/cache/redis/impl/RedisCaches.scala b/src/main/scala/play/api/cache/redis/impl/RedisCaches.scala index 47614789..8ee47be1 100644 --- a/src/main/scala/play/api/cache/redis/impl/RedisCaches.scala +++ b/src/main/scala/play/api/cache/redis/impl/RedisCaches.scala @@ -1,11 +1,11 @@ package play.api.cache.redis.impl -import javax.inject.Provider +import akka.actor.ActorSystem import play.api.Environment import play.api.cache.redis._ import play.api.inject.ApplicationLifecycle -import akka.actor.ActorSystem -import play.api.cache.{AsyncCacheApi, DefaultSyncCacheApi} + +import javax.inject.Provider /** * Aggregates all available redis APIs into a single handler. This simplifies @@ -24,9 +24,9 @@ trait RedisCaches { private[redis] class RedisCachesProvider(instance: RedisInstance, serializer: connector.AkkaSerializer, environment: Environment)(implicit system: ActorSystem, lifecycle: ApplicationLifecycle, recovery: RecoveryPolicyResolver) extends Provider[RedisCaches] { import RedisRuntime._ - private implicit lazy val runtime: RedisRuntime = RedisRuntime(instance, instance.recovery, instance.invocationPolicy, instance.prefix)(system) + implicit private lazy val runtime: RedisRuntime = RedisRuntime(instance, instance.recovery, instance.invocationPolicy, instance.prefix)(system) - private implicit def implicitEnvironment: Environment = environment + implicit private def implicitEnvironment: Environment = environment lazy val get: RedisCaches = new RedisCaches { lazy val redisConnector: RedisConnector = new connector.RedisConnectorProvider(instance, serializer).get @@ -38,4 +38,5 @@ private[redis] class RedisCachesProvider(instance: RedisInstance, serializer: co lazy val javaAsync: play.cache.redis.AsyncCacheApi = java lazy val javaSync: play.cache.SyncCacheApi = new play.cache.DefaultSyncCacheApi(java) } + } diff --git a/src/main/scala/play/api/cache/redis/impl/RedisListImpl.scala b/src/main/scala/play/api/cache/redis/impl/RedisListImpl.scala index 241b8aa6..8fbe8105 100644 --- a/src/main/scala/play/api/cache/redis/impl/RedisListImpl.scala +++ b/src/main/scala/play/api/cache/redis/impl/RedisListImpl.scala @@ -1,11 +1,12 @@ package play.api.cache.redis.impl -import scala.language.implicitConversions -import scala.reflect.ClassTag - import play.api.cache.redis._ -/**

Implementation of List API using redis-server cache implementation.

*/ +import scala.reflect.ClassTag + +/** + *

Implementation of List API using redis-server cache implementation.

+ */ private[impl] class RedisListImpl[Elem: ClassTag, Result[_]](key: String, redis: RedisConnector)(implicit builder: Builders.ResultBuilder[Result], runtime: RedisRuntime) extends RedisList[Elem, Result] { // implicit ask timeout and execution context @@ -32,13 +33,16 @@ private[impl] class RedisListImpl[Elem: ClassTag, Result[_]](key: String, redis: private def appendAll(elements: Elem*): Result[This] = redis.listAppend(key, elements: _*).map(_ => This).recoverWithDefault(This) - override def apply(index: Int): Result[Elem] = redis.listSlice[Elem](key, index, index).map { - _.headOption getOrElse (throw new NoSuchElementException(s"Element at index $index is missing.")) - }.recoverWithDefault { - throw new NoSuchElementException(s"Element at index $index is missing.") - } + override def apply(index: Long): Result[Elem] = redis + .listSlice[Elem](key, index, index) + .map { + _.headOption getOrElse (throw new NoSuchElementException(s"Element at index $index is missing.")) + } + .recoverWithDefault { + throw new NoSuchElementException(s"Element at index $index is missing.") + } - override def get(index: Int): Result[Option[Elem]] = + override def get(index: Long): Result[Option[Elem]] = redis.listSlice[Elem](key, index, index).map(_.headOption).recoverWithDefault(None) override def headPop: Result[Option[Elem]] = redis.listHeadPop[Elem](key).recoverWithDefault(None) @@ -48,7 +52,7 @@ private[impl] class RedisListImpl[Elem: ClassTag, Result[_]](key: String, redis: override def insertBefore(pivot: Elem, element: Elem): Result[Option[Long]] = redis.listInsert(key, pivot, element).recoverWithDefault(None) - override def set(position: Int, element: Elem): Result[This] = + override def set(position: Long, element: Elem): Result[This] = redis.listSetAt(key, position, element).map(_ => This).recoverWithDefault(This) override def isEmpty: Result[Boolean] = @@ -60,7 +64,7 @@ private[impl] class RedisListImpl[Elem: ClassTag, Result[_]](key: String, redis: override def view: RedisListView = ListView private object ListView extends RedisListView { - override def slice(start: Int, end: Int): Result[Seq[Elem]] = redis.listSlice[Elem](key, start, end).recoverWithDefault(Seq.empty) + override def slice(start: Long, end: Long): Result[Seq[Elem]] = redis.listSlice[Elem](key, start, end).recoverWithDefault(Seq.empty) } override def modify: RedisListModification = ListModifier @@ -69,22 +73,34 @@ private[impl] class RedisListImpl[Elem: ClassTag, Result[_]](key: String, redis: override def collection: This = This - override def clear(): Result[RedisListModification] = - redis.remove(key).map { - _ => this: RedisListModification - }.recoverWithDefault(this) + override def clear(): Result[RedisListModification] = + redis + .remove(key) + .map { _ => + this: RedisListModification + } + .recoverWithDefault(this) + + override def slice(start: Long, end: Long): Result[RedisListModification] = + redis + .listTrim(key, start, end) + .map { _ => + this: RedisListModification + } + .recoverWithDefault(this) - override def slice(start: Int, end: Int): Result[RedisListModification] = - redis.listTrim(key, start, end).map { - _ => this: RedisListModification - }.recoverWithDefault(this) } - override def remove(element: Elem, count: Int): Result[This] = + override def remove(element: Elem, count: Long): Result[This] = redis.listRemove(key, element, count).map(_ => This).recoverWithDefault(This) - override def removeAt(position: Int): Result[This] = - redis.listSetAt(key, position, "play-redis:DELETED").flatMap { - _ => redis.listRemove(key, "play-redis:DELETED", count = 0) - }.map(_ => This).recoverWithDefault(This) + override def removeAt(position: Long): Result[This] = + redis + .listSetAt(key, position, "play-redis:DELETED") + .flatMap { _ => + redis.listRemove(key, "play-redis:DELETED", count = 0) + } + .map(_ => This) + .recoverWithDefault(This) + } diff --git a/src/main/scala/play/api/cache/redis/impl/RedisListJavaImpl.scala b/src/main/scala/play/api/cache/redis/impl/RedisListJavaImpl.scala index 25d0a7f2..9f66b39d 100644 --- a/src/main/scala/play/api/cache/redis/impl/RedisListJavaImpl.scala +++ b/src/main/scala/play/api/cache/redis/impl/RedisListJavaImpl.scala @@ -1,10 +1,10 @@ package play.api.cache.redis.impl -import scala.concurrent.Future - import play.api.cache.redis.RedisList import play.cache.redis.AsyncRedisList +import scala.concurrent.Future + class RedisListJavaImpl[Elem](internal: RedisList[Elem, Future])(implicit runtime: RedisRuntime) extends AsyncRedisList[Elem] { import JavaCompatibility._ @@ -13,73 +13,61 @@ class RedisListJavaImpl[Elem](internal: RedisList[Elem, Future])(implicit runtim private lazy val modifier: AsyncRedisList.AsyncRedisListModification[Elem] = newModifier() private lazy val viewer: AsyncRedisList.AsyncRedisListView[Elem] = newViewer() - private def newViewer(): AsyncRedisList.AsyncRedisListView[Elem] = { + private def newViewer(): AsyncRedisList.AsyncRedisListView[Elem] = new AsyncRedisListViewJavaImpl(internal.view) - } - private def newModifier(): AsyncRedisList.AsyncRedisListModification[Elem] = { + private def newModifier(): AsyncRedisList.AsyncRedisListModification[Elem] = new AsyncRedisListModificationJavaImpl(internal.modify) - } - override def prepend(element: Elem): CompletionStage[AsyncRedisList[Elem]] = { + override def prepend(element: Elem): CompletionStage[AsyncRedisList[Elem]] = async { implicit context => internal.prepend(element).map(_ => this) } - } - override def append(element: Elem): CompletionStage[AsyncRedisList[Elem]] = { + override def append(element: Elem): CompletionStage[AsyncRedisList[Elem]] = async { implicit context => internal.append(element).map(_ => this) } - } - override def apply(index: Int): CompletionStage[Elem] = { - async { implicit context => + override def apply(index: Long): CompletionStage[Elem] = + async { _ => internal.apply(index) } - } - override def get(index: Int): CompletionStage[Optional[Elem]] = { + override def get(index: Long): CompletionStage[Optional[Elem]] = async { implicit context => internal.get(index).map(_.asJava) } - } - override def headPop(): CompletionStage[Optional[Elem]] = { + override def headPop(): CompletionStage[Optional[Elem]] = async { implicit context => internal.headPop.map(_.asJava) } - } - override def insertBefore(pivot: Elem, element: Elem): CompletionStage[Optional[java.lang.Long]] = { + override def insertBefore(pivot: Elem, element: Elem): CompletionStage[Optional[java.lang.Long]] = async { implicit context => internal.insertBefore(pivot, element).map(_.map(Long.box).asJava) } - } - override def set(position: Int, element: Elem): CompletionStage[AsyncRedisList[Elem]] = { + override def set(position: Long, element: Elem): CompletionStage[AsyncRedisList[Elem]] = async { implicit context => internal.set(position, element).map(_ => this) } - } - override def remove(element: Elem): CompletionStage[AsyncRedisList[Elem]] = { + override def remove(element: Elem): CompletionStage[AsyncRedisList[Elem]] = async { implicit context => internal.remove(element).map(_ => this) } - } - override def remove(element: Elem, count: Int): CompletionStage[AsyncRedisList[Elem]] = { + override def remove(element: Elem, count: Long): CompletionStage[AsyncRedisList[Elem]] = async { implicit context => internal.remove(element, count).map(_ => this) } - } - override def removeAt(position: Int): CompletionStage[AsyncRedisList[Elem]] = { + override def removeAt(position: Long): CompletionStage[AsyncRedisList[Elem]] = async { implicit context => internal.removeAt(position).map(_ => this) } - } override def view(): AsyncRedisList.AsyncRedisListView[Elem] = viewer @@ -87,28 +75,27 @@ class RedisListJavaImpl[Elem](internal: RedisList[Elem, Future])(implicit runtim private class AsyncRedisListViewJavaImpl(view: internal.RedisListView) extends AsyncRedisList.AsyncRedisListView[Elem] { - override def slice(from: Int, end: Int): CompletionStage[JavaList[Elem]] = { + override def slice(from: Long, end: Long): CompletionStage[JavaList[Elem]] = async { implicit context => view.slice(from, end).map(_.asJava) } - } + } private class AsyncRedisListModificationJavaImpl(modification: internal.RedisListModification) extends AsyncRedisList.AsyncRedisListModification[Elem] { override def collection(): AsyncRedisList[Elem] = This - override def clear(): CompletionStage[AsyncRedisList.AsyncRedisListModification[Elem]] = { + override def clear(): CompletionStage[AsyncRedisList.AsyncRedisListModification[Elem]] = async { implicit context => modification.clear().map(_ => this) } - } - override def slice(from: Int, end: Int): CompletionStage[AsyncRedisList.AsyncRedisListModification[Elem]] = { + override def slice(from: Long, end: Long): CompletionStage[AsyncRedisList.AsyncRedisListModification[Elem]] = async { implicit context => modification.slice(from, end).map(_ => this) } - } + } } diff --git a/src/main/scala/play/api/cache/redis/impl/RedisMapImpl.scala b/src/main/scala/play/api/cache/redis/impl/RedisMapImpl.scala index 7704f71b..c51985a3 100644 --- a/src/main/scala/play/api/cache/redis/impl/RedisMapImpl.scala +++ b/src/main/scala/play/api/cache/redis/impl/RedisMapImpl.scala @@ -1,10 +1,9 @@ package play.api.cache.redis.impl -import scala.language.implicitConversions -import scala.reflect.ClassTag - import play.api.cache.redis._ +import scala.reflect.ClassTag + /**

Implementation of Set API using redis-server cache implementation.

*/ private[impl] class RedisMapImpl[Elem: ClassTag, Result[_]](key: String, redis: RedisConnector)(implicit builder: Builders.ResultBuilder[Result], runtime: RedisRuntime) extends RedisMap[Elem, Result] { @@ -49,4 +48,5 @@ private[impl] class RedisMapImpl[Elem: ClassTag, Result[_]](key: String, redis: override def nonEmpty: Result[Boolean] = redis.hashSize(key).map(_ > 0).recoverWithDefault(false) + } diff --git a/src/main/scala/play/api/cache/redis/impl/RedisMapJavaImpl.scala b/src/main/scala/play/api/cache/redis/impl/RedisMapJavaImpl.scala index 2dcae4f9..a12d71fa 100644 --- a/src/main/scala/play/api/cache/redis/impl/RedisMapJavaImpl.scala +++ b/src/main/scala/play/api/cache/redis/impl/RedisMapJavaImpl.scala @@ -1,64 +1,56 @@ package play.api.cache.redis.impl -import scala.concurrent.Future - import play.api.cache.redis.RedisMap import play.cache.redis.AsyncRedisMap +import scala.concurrent.Future + class RedisMapJavaImpl[Elem](internal: RedisMap[Elem, Future])(implicit runtime: RedisRuntime) extends AsyncRedisMap[Elem] { import JavaCompatibility._ - def add(field: String, value: Elem): CompletionStage[AsyncRedisMap[Elem]] = { + def add(field: String, value: Elem): CompletionStage[AsyncRedisMap[Elem]] = async { implicit context => internal.add(field, value).map(_ => this) } - } - def get(field: String): CompletionStage[Optional[Elem]] = { + def get(field: String): CompletionStage[Optional[Elem]] = async { implicit context => internal.get(field).map(_.asJava) } - } - def contains(field: String): CompletionStage[java.lang.Boolean] = { + def contains(field: String): CompletionStage[java.lang.Boolean] = async { implicit context => internal.contains(field).map(Boolean.box) } - } - def remove(field: String*): CompletionStage[AsyncRedisMap[Elem]] = { + def remove(field: String*): CompletionStage[AsyncRedisMap[Elem]] = async { implicit context => internal.remove(field: _*).map(_ => this) } - } - def increment(field: String): CompletionStage[java.lang.Long] = { + def increment(field: String): CompletionStage[java.lang.Long] = async { implicit context => internal.increment(field).map(Long.box) } - } - def increment(field: String, incrementBy: java.lang.Long): CompletionStage[java.lang.Long] = { + def increment(field: String, incrementBy: java.lang.Long): CompletionStage[java.lang.Long] = async { implicit context => internal.increment(field, incrementBy).map(Long.box) } - } - def toMap: CompletionStage[JavaMap[String, Elem]] = { + def toMap: CompletionStage[JavaMap[String, Elem]] = async { implicit context => internal.toMap.map(_.asJava) } - } - def keySet(): CompletionStage[JavaSet[String]] = { + def keySet(): CompletionStage[JavaSet[String]] = async { implicit context => internal.keySet.map(_.asJava) } - } - def values(): CompletionStage[JavaSet[Elem]] = { + def values(): CompletionStage[JavaSet[Elem]] = async { implicit context => internal.values.map(_.asJava) } - } + } diff --git a/src/main/scala/play/api/cache/redis/impl/RedisPrefix.scala b/src/main/scala/play/api/cache/redis/impl/RedisPrefix.scala index 308ccd36..023d71bd 100644 --- a/src/main/scala/play/api/cache/redis/impl/RedisPrefix.scala +++ b/src/main/scala/play/api/cache/redis/impl/RedisPrefix.scala @@ -1,8 +1,8 @@ package play.api.cache.redis.impl /** - * Each instance can apply its own prefix, e.g., to use multiple instances - * with the same redis database. + * Each instance can apply its own prefix, e.g., to use multiple instances with + * the same redis database. */ sealed trait RedisPrefix extends Any { @inline def prefixed(key: String): String @@ -12,7 +12,7 @@ sealed trait RedisPrefix extends Any { } final class RedisPrefixImpl(val prefix: String) extends AnyVal with RedisPrefix { - @inline override def prefixed(key: String) = s"$prefix:$key" + @inline override def prefixed(key: String): String = s"$prefix:$key" @inline override def unprefixed(key: String): String = key.drop(prefix.length + 1) @inline override def prefixed(keys: Seq[String]): Seq[String] = keys.map(prefixed) @inline override def unprefixed(keys: Seq[String]): Seq[String] = keys.map(unprefixed) diff --git a/src/main/scala/play/api/cache/redis/impl/RedisRuntime.scala b/src/main/scala/play/api/cache/redis/impl/RedisRuntime.scala index 8d91866f..4fa65c0a 100644 --- a/src/main/scala/play/api/cache/redis/impl/RedisRuntime.scala +++ b/src/main/scala/play/api/cache/redis/impl/RedisRuntime.scala @@ -1,16 +1,14 @@ package play.api.cache.redis.impl -import scala.concurrent.ExecutionContext -import scala.concurrent.duration.FiniteDuration -import scala.language.implicitConversions - +import akka.actor.ActorSystem import play.api.cache.redis._ -import akka.actor.ActorSystem +import scala.concurrent.ExecutionContext +import scala.concurrent.duration.FiniteDuration /** - * Runtime info about the current cache instance. It includes - * a configuration, recovery policy, and the execution context. + * Runtime info about the current cache instance. It includes a configuration, + * recovery policy, and the execution context. */ private[redis] trait RedisRuntime extends connector.RedisRuntime { implicit def policy: RecoveryPolicy @@ -19,13 +17,13 @@ private[redis] trait RedisRuntime extends connector.RedisRuntime { implicit def timeout: akka.util.Timeout } -private[redis] final case class RedisRuntimeImpl( +final private[redis] case class RedisRuntimeImpl( name: String, context: ExecutionContext, policy: RecoveryPolicy, invocation: InvocationPolicy, prefix: RedisPrefix, - timeout: akka.util.Timeout + timeout: akka.util.Timeout, ) extends RedisRuntime private[redis] object RedisRuntime { @@ -39,7 +37,7 @@ private[redis] object RedisRuntime { implicit def string2invocation(invocation: String): InvocationPolicy = invocation.toLowerCase.trim match { case "lazy" => LazyInvocation case "eager" => EagerInvocation - case other => throw new IllegalArgumentException("Illegal invocation policy. Valid values are 'lazy' and 'eager'. See the documentation for more details.") + case _ => throw new IllegalArgumentException("Illegal invocation policy. Valid values are 'lazy' and 'eager'. See the documentation for more details.") } def apply(instance: RedisInstance, recovery: RecoveryPolicy, invocation: InvocationPolicy, prefix: RedisPrefix)(implicit system: ActorSystem): RedisRuntime = @@ -47,4 +45,5 @@ private[redis] object RedisRuntime { def apply(name: String, syncTimeout: FiniteDuration, context: ExecutionContext, recovery: RecoveryPolicy, invocation: InvocationPolicy, prefix: RedisPrefix = RedisEmptyPrefix): RedisRuntime = RedisRuntimeImpl(name, context, recovery, invocation, prefix, akka.util.Timeout(syncTimeout)) + } diff --git a/src/main/scala/play/api/cache/redis/impl/RedisSetImpl.scala b/src/main/scala/play/api/cache/redis/impl/RedisSetImpl.scala index 355270f0..2d9c2ebf 100644 --- a/src/main/scala/play/api/cache/redis/impl/RedisSetImpl.scala +++ b/src/main/scala/play/api/cache/redis/impl/RedisSetImpl.scala @@ -2,7 +2,6 @@ package play.api.cache.redis.impl import play.api.cache.redis._ -import scala.language.implicitConversions import scala.reflect.ClassTag /**

Implementation of Set API using redis-server cache implementation.

*/ @@ -14,31 +13,25 @@ private[impl] class RedisSetImpl[Elem: ClassTag, Result[_]](key: String, redis: @inline private def This: This = this - override def add(elements: Elem*): Result[RedisSet[Elem, Result]] = { + override def add(elements: Elem*): Result[RedisSet[Elem, Result]] = redis.setAdd(key, elements: _*).map(_ => This).recoverWithDefault(This) - } - override def contains(element: Elem): Result[Boolean] = { + override def contains(element: Elem): Result[Boolean] = redis.setIsMember(key, element).recoverWithDefault(false) - } - override def remove(element: Elem*): Result[RedisSet[Elem, Result]] = { + override def remove(element: Elem*): Result[RedisSet[Elem, Result]] = redis.setRemove(key, element: _*).map(_ => This).recoverWithDefault(This) - } - override def toSet: Result[Set[Elem]] = { + override def toSet: Result[Set[Elem]] = redis.setMembers[Elem](key).recoverWithDefault(Set.empty) - } - override def size: Result[Long] = { + override def size: Result[Long] = redis.setSize(key).recoverWithDefault(0) - } - override def isEmpty: Result[Boolean] = { + override def isEmpty: Result[Boolean] = redis.setSize(key).map(_ === 0).recoverWithDefault(true) - } - override def nonEmpty: Result[Boolean] = { + override def nonEmpty: Result[Boolean] = redis.setSize(key).map(_ > 0).recoverWithDefault(false) - } + } diff --git a/src/main/scala/play/api/cache/redis/impl/RedisSetJavaImpl.scala b/src/main/scala/play/api/cache/redis/impl/RedisSetJavaImpl.scala index f474311e..5f64a5eb 100644 --- a/src/main/scala/play/api/cache/redis/impl/RedisSetJavaImpl.scala +++ b/src/main/scala/play/api/cache/redis/impl/RedisSetJavaImpl.scala @@ -8,27 +8,24 @@ import scala.concurrent.Future class RedisSetJavaImpl[Elem](internal: RedisSet[Elem, Future])(implicit runtime: RedisRuntime) extends AsyncRedisSet[Elem] { import JavaCompatibility._ - override def add(elements: Elem*): CompletionStage[AsyncRedisSet[Elem]] = { + override def add(elements: Elem*): CompletionStage[AsyncRedisSet[Elem]] = async { implicit context => internal.add(elements: _*).map(_ => this) } - } - override def contains(element: Elem): CompletionStage[java.lang.Boolean] = { + override def contains(element: Elem): CompletionStage[java.lang.Boolean] = async { implicit context => internal.contains(element).map(Boolean.box) } - } - override def remove(elements: Elem*): CompletionStage[AsyncRedisSet[Elem]] = { + override def remove(elements: Elem*): CompletionStage[AsyncRedisSet[Elem]] = async { implicit context => internal.remove(elements: _*).map(_ => this) } - } - override def toSet: CompletionStage[JavaSet[Elem]] = { + override def toSet: CompletionStage[JavaSet[Elem]] = async { implicit context => internal.toSet.map(_.asJava) } - } + } diff --git a/src/main/scala/play/api/cache/redis/impl/RedisSortedSetImpl.scala b/src/main/scala/play/api/cache/redis/impl/RedisSortedSetImpl.scala index b7a068a0..daffd441 100644 --- a/src/main/scala/play/api/cache/redis/impl/RedisSortedSetImpl.scala +++ b/src/main/scala/play/api/cache/redis/impl/RedisSortedSetImpl.scala @@ -2,16 +2,15 @@ package play.api.cache.redis.impl import play.api.cache.redis._ -import scala.language.implicitConversions import scala.reflect.ClassTag /**

Implementation of Set API using redis-server cache implementation.

*/ private[impl] class RedisSortedSetImpl[Elem: ClassTag, Result[_]]( key: String, - redis: RedisConnector + redis: RedisConnector, )(implicit builder: Builders.ResultBuilder[Result], - runtime: RedisRuntime + runtime: RedisRuntime, ) extends RedisSortedSet[Elem, Result] { // implicit ask timeout and execution context @@ -26,17 +25,15 @@ private[impl] class RedisSortedSetImpl[Elem: ClassTag, Result[_]]( override def contains(element: Elem): Result[Boolean] = redis.sortedSetScore(key, element).map(_.isDefined).recoverWithDefault(false) - override def remove(element: Elem*): Result[RedisSortedSet[Elem, Result]] = { + override def remove(element: Elem*): Result[RedisSortedSet[Elem, Result]] = redis.sortedSetRemove(key, element: _*).map(_ => This).recoverWithDefault(This) - } - override def range(start: Long, stop: Long, isReverse: Boolean = false): Result[Seq[Elem]] = { + override def range(start: Long, stop: Long, isReverse: Boolean = false): Result[Seq[Elem]] = if (isReverse) { redis.sortedSetReverseRange[Elem](key, start, stop).recoverWithDefault(Seq.empty) } else { redis.sortedSetRange[Elem](key, start, stop).recoverWithDefault(Seq.empty) } - } override def size: Result[Long] = redis.sortedSetSize(key).recoverWithDefault(0) @@ -46,4 +43,5 @@ private[impl] class RedisSortedSetImpl[Elem: ClassTag, Result[_]]( override def nonEmpty: Result[Boolean] = builder.map(isEmpty)(x => !x) + } diff --git a/src/main/scala/play/api/cache/redis/impl/SyncRedis.scala b/src/main/scala/play/api/cache/redis/impl/SyncRedis.scala index 84c6c503..808d7266 100644 --- a/src/main/scala/play/api/cache/redis/impl/SyncRedis.scala +++ b/src/main/scala/play/api/cache/redis/impl/SyncRedis.scala @@ -1,12 +1,13 @@ package play.api.cache.redis.impl +import play.api.cache.redis._ + import scala.concurrent.duration.Duration import scala.reflect.ClassTag -import play.api.cache.redis._ - /** - * Implementation of **synchronous** and **blocking** Redis API. It also implements standard Play Scala CacheApi + * Implementation of **synchronous** and **blocking** Redis API. It also + * implements standard Play Scala CacheApi */ private[impl] class SyncRedis(redis: RedisConnector)(implicit runtime: RedisRuntime) extends RedisCache[SynchronousResult](redis, Builders.SynchronousBuilder) with CacheApi { // helpers for dsl @@ -25,4 +26,5 @@ private[impl] class SyncRedis(redis: RedisConnector)(implicit runtime: RedisRunt // try to hit the cache, return on hit, set and return orElse on miss or failure redis.get[T](key).recoverWithDefault(Some(computeAndSet)).getOrElse(computeAndSet) } + } diff --git a/src/main/scala/play/api/cache/redis/impl/dsl.scala b/src/main/scala/play/api/cache/redis/impl/dsl.scala index 9b471a7d..70215186 100644 --- a/src/main/scala/play/api/cache/redis/impl/dsl.scala +++ b/src/main/scala/play/api/cache/redis/impl/dsl.scala @@ -1,13 +1,12 @@ package play.api.cache.redis.impl -import scala.concurrent.{ExecutionContext, Future} -import scala.language.implicitConversions - import play.api.cache.redis._ +import scala.concurrent.{ExecutionContext, Future} + /** - * Implicit helpers used within the redis cache implementation. These - * handful tools simplifies code readability but has no major function. + * Implicit helpers used within the redis cache implementation. These handful + * tools simplifies code readability but has no major function. */ private[impl] object dsl { @@ -15,14 +14,17 @@ private[impl] object dsl { @inline implicit def runtime2prefix(implicit runtime: RedisRuntime): RedisPrefix = runtime.prefix /** enriches any ref by toFuture converting a value to Future.successful */ - implicit class RichFuture[T](val any: T) extends AnyVal { + implicit class RichFuture[T](private val any: T) extends AnyVal { @inline def toFuture: Future[T] = Future.successful(any) } /** helper function enabling us to recover from command execution */ implicit class RecoveryFuture[T](future: => Future[T]) { - /** Transforms the promise into desired builder results, possibly recovers with provided default value */ + /** + * Transforms the promise into desired builder results, possibly recovers + * with provided default value + */ @inline def recoverWithDefault[Result[_]](default: => T)(implicit builder: Builders.ResultBuilder[Result], runtime: RedisRuntime): Result[T] = builder.toResult(future, Future.successful(default)) @@ -32,33 +34,42 @@ private[impl] object dsl { // recover from known exceptions case failure: RedisException => runtime.policy.recoverFrom(future, default, failure) } + } /** helper function enabling us to recover from command execution */ - implicit class RecoveryUnitFuture(val future: Future[Unit]) extends AnyVal { - /** Transforms the promise into desired builder results, possibly recovers with provided default value */ + implicit class RecoveryUnitFuture(private val future: Future[Unit]) extends AnyVal { + + /** + * Transforms the promise into desired builder results, possibly recovers + * with provided default value + */ @inline def recoverWithDone[Result[_]](implicit builder: Builders.ResultBuilder[Result], runtime: RedisRuntime): Result[Done] = builder.toResult(future.map(unitAsDone), Future.successful(Done)) + } /** maps units into akka.Done */ - @inline private def unitAsDone(unit: Unit): Done = Done + @inline private val unitAsDone: Any => Done = _ => Done /** applies prefixer to produce final cache key */ - implicit class CacheKey(val key: String) extends AnyVal { + implicit class CacheKey(private val key: String) extends AnyVal { def prefixed[T](f: String => T)(implicit prefixer: RedisPrefix): T = f(prefixer prefixed key) } /** applies prefixer to produce final cache key */ - implicit class CacheKeys(val keys: Seq[String]) extends AnyVal { + implicit class CacheKeys(private val keys: Seq[String]) extends AnyVal { def prefixed[T](f: Seq[String] => T)(implicit prefixer: RedisPrefix): T = f(prefixer prefixed keys) def unprefixed(implicit prefixer: RedisPrefix): Seq[String] = prefixer unprefixed keys } /** applies prefixer to produce final cache key */ - implicit class CacheKeyValues[X](val keys: Seq[(String, X)]) extends AnyVal { + implicit class CacheKeyValues[X](private val keys: Seq[(String, X)]) extends AnyVal { + def prefixed[T](f: Seq[(String, X)] => T)(implicit prefixer: RedisPrefix): T = f { keys.map { case (key, value) => prefixer.prefixed(key) -> value } } + } + } diff --git a/src/main/scala/play/api/cache/redis/package.scala b/src/main/scala/play/api/cache/redis/package.scala index cc121201..6c33049b 100644 --- a/src/main/scala/play/api/cache/redis/package.scala +++ b/src/main/scala/play/api/cache/redis/package.scala @@ -21,7 +21,8 @@ package object redis extends AnyRef with ExpirationImplicits with ExceptionImpli } @SuppressWarnings(Array("org.wartremover.warts.Equals")) - implicit final class HigherKindedAnyOps[F[_]](private val self: F[_]) extends AnyVal { - def =~=(other: F[_]): Boolean = self == other + implicit final class HigherKindedAnyOps[F[_]](private val self: F[?]) extends AnyVal { + def =~=(other: F[?]): Boolean = self == other } + } diff --git a/src/test/scala/play/api/cache/redis/ExpirationSpec.scala b/src/test/scala/play/api/cache/redis/ExpirationSpec.scala index 51a24a6b..f6602fb5 100644 --- a/src/test/scala/play/api/cache/redis/ExpirationSpec.scala +++ b/src/test/scala/play/api/cache/redis/ExpirationSpec.scala @@ -6,9 +6,7 @@ import java.time.Instant import java.util.Date import scala.concurrent.duration._ -/** - *

This specification tests expiration conversion

- */ +/**

This specification tests expiration conversion

*/ class ExpirationSpec extends UnitSpec { "Expiration" should { @@ -32,4 +30,5 @@ class ExpirationSpec extends UnitSpec { expiration mustBe <=(expirationTo) } } + } diff --git a/src/test/scala/play/api/cache/redis/RecoveryPolicySpec.scala b/src/test/scala/play/api/cache/redis/RecoveryPolicySpec.scala index 907e4eb0..0ae7c979 100644 --- a/src/test/scala/play/api/cache/redis/RecoveryPolicySpec.scala +++ b/src/test/scala/play/api/cache/redis/RecoveryPolicySpec.scala @@ -24,7 +24,7 @@ class RecoveryPolicySpec extends AsyncUnitSpec { "Recovery Policy" should { "log detailed report" in { - val policy = new RecoverWithDefault with DetailedReports { + val policy = new RecoverWithDefault with DetailedReports { override val log: Logger = Logger(getClass) } @@ -52,7 +52,7 @@ class RecoveryPolicySpec extends AsyncUnitSpec { } "fail through" in { - val policy = new FailThrough {} + val policy = new FailThrough {} policy.recoverFrom(rerun, default, ex.any).assertingFailure(ex.any) } @@ -61,4 +61,5 @@ class RecoveryPolicySpec extends AsyncUnitSpec { policy.recoverFrom(rerun, default, ex.any) mustEqual default } } + } diff --git a/src/test/scala/play/api/cache/redis/RedisCacheComponentsSpec.scala b/src/test/scala/play/api/cache/redis/RedisCacheComponentsSpec.scala index 14db7b83..b59c36df 100644 --- a/src/test/scala/play/api/cache/redis/RedisCacheComponentsSpec.scala +++ b/src/test/scala/play/api/cache/redis/RedisCacheComponentsSpec.scala @@ -7,7 +7,7 @@ import play.api.inject.ApplicationLifecycle class RedisCacheComponentsSpec extends IntegrationSpec with RedisStandaloneContainer { - private val prefix = "components-sync" + private val prefix = "components-sync" test("miss on get") { cache => cache.get[String](s"$prefix-test-1") mustEqual None @@ -23,13 +23,15 @@ class RedisCacheComponentsSpec extends IntegrationSpec with RedisStandaloneConta cache.exists(s"$prefix-test-11") mustEqual true } - private def test(name: String)(cache: CacheApi => Assertion): Unit = { + private def test(name: String)(cache: CacheApi => Assertion): Unit = s"should $name" in { val components: TestComponents = new TestComponents { - override lazy val configuration: Configuration = Helpers.configuration.fromHocon( - s"play.cache.redis.port: ${container.mappedPort(defaultPort)}" - ) + override lazy val configuration: Configuration = Helpers + .configuration + .fromHocon( + s"play.cache.redis.port: ${container.mappedPort(defaultPort)}", + ) override lazy val actorSystem: ActorSystem = system override lazy val applicationLifecycle: ApplicationLifecycle = injector.instanceOf[ApplicationLifecycle] override lazy val environment: Environment = injector.instanceOf[Environment] @@ -40,9 +42,9 @@ class RedisCacheComponentsSpec extends IntegrationSpec with RedisStandaloneConta cache(components.syncRedis) } } - } private trait TestComponents extends RedisCacheComponents with FakeApplication { def syncRedis: CacheApi } + } diff --git a/src/test/scala/play/api/cache/redis/RedisCacheModuleSpec.scala b/src/test/scala/play/api/cache/redis/RedisCacheModuleSpec.scala index 468088af..48bce2e8 100644 --- a/src/test/scala/play/api/cache/redis/RedisCacheModuleSpec.scala +++ b/src/test/scala/play/api/cache/redis/RedisCacheModuleSpec.scala @@ -11,12 +11,12 @@ import scala.concurrent.duration.DurationInt import scala.reflect.ClassTag class RedisCacheModuleSpec extends IntegrationSpec with RedisStandaloneContainer { -import Helpers._ + import Helpers._ - private final val defaultCacheName: String = "play" + final private val defaultCacheName: String = "play" test("bind defaults") { - _.bindings(new RedisCacheModule).configure(s"play.cache.redis.port" -> container.mappedPort(defaultPort)) + _.bindings(new RedisCacheModule).configure("play.cache.redis.port" -> container.mappedPort(defaultPort)) } { injector => injector.checkBinding[RedisConnector] injector.checkBinding[CacheApi] @@ -31,7 +31,7 @@ import Helpers._ test("not bind defaults") { _.bindings(new RedisCacheModule) .configure("play.cache.redis.bind-default" -> false) - .configure(s"play.cache.redis.port" -> container.mappedPort(defaultPort)) + .configure("play.cache.redis.port" -> container.mappedPort(defaultPort)) } { injector => // bind named caches injector.checkNamedBinding[CacheApi] @@ -62,24 +62,24 @@ import Helpers._ _.bindings(new RedisCacheModule).configure( configuration.fromHocon( s""" - |play.cache.redis { - | instances { - | play { - | host: ${container.host} - | port: ${container.mappedPort(defaultPort)} - | database: 1 - | } - | other { - | host: ${container.host} - | port: ${container.mappedPort(defaultPort)} - | database: 2 - | password: something - | } - | } - | default-cache: other - |} - """.stripMargin - ) + |play.cache.redis { + | instances { + | play { + | host: ${container.host} + | port: ${container.mappedPort(defaultPort)} + | database: 1 + | } + | other { + | host: ${container.host} + | port: ${container.mappedPort(defaultPort)} + | database: 2 + | password: something + | } + | } + | default-cache: other + |} + """.stripMargin, + ), ) } { injector => val other = "other" @@ -139,33 +139,30 @@ import Helpers._ recovery = "log-and-default", source = "my-instance", prefix = None, - ) + ), ) private def binding[T: ClassTag]: BindingKey[T] = BindingKey(implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]]) - private implicit class RichBindingKey[T](private val key: BindingKey[T]) { + implicit private class RichBindingKey[T](private val key: BindingKey[T]) { def namedCache(name: String): BindingKey[T] = key.qualifiedWith(new NamedCacheImpl(name)) } - private implicit class InjectorAssertions(private val injector: Injector) { + implicit private class InjectorAssertions(private val injector: Injector) { - def checkBinding[T <: AnyRef : ClassTag]: Assertion = { + def checkBinding[T <: AnyRef: ClassTag]: Assertion = injector.instanceOf(binding[T]) mustBe a[T] - } - def checkNamedBinding[T <: AnyRef : ClassTag]: Assertion = { + def checkNamedBinding[T <: AnyRef: ClassTag]: Assertion = checkNamedBinding(defaultCacheName) - } - def checkNamedBinding[T <: AnyRef : ClassTag](name: String): Assertion = { + def checkNamedBinding[T <: AnyRef: ClassTag](name: String): Assertion = injector.instanceOf(binding[T].namedCache(name)) mustBe a[T] - } - } + } - private def test(name: String)(createBuilder: GuiceApplicationBuilder => GuiceApplicationBuilder)(f: Injector => Assertion): Unit = { + private def test(name: String)(createBuilder: GuiceApplicationBuilder => GuiceApplicationBuilder)(f: Injector => Assertion): Unit = s"should $name" in { val builder = createBuilder(new GuiceApplicationBuilder) @@ -176,5 +173,5 @@ import Helpers._ f(injector) } } - } + } diff --git a/src/test/scala/play/api/cache/redis/configuration/HostnameResolverSpec.scala b/src/test/scala/play/api/cache/redis/configuration/HostnameResolverSpec.scala index 1360552a..1a1b764e 100644 --- a/src/test/scala/play/api/cache/redis/configuration/HostnameResolverSpec.scala +++ b/src/test/scala/play/api/cache/redis/configuration/HostnameResolverSpec.scala @@ -12,4 +12,5 @@ class HostnameResolverSpec extends UnitSpec { "resolving IP address remains an address" in { "127.0.0.1".resolvedIpAddress mustEqual "127.0.0.1" } + } diff --git a/src/test/scala/play/api/cache/redis/configuration/RedisHostSpec.scala b/src/test/scala/play/api/cache/redis/configuration/RedisHostSpec.scala index 501480d4..4158e56c 100644 --- a/src/test/scala/play/api/cache/redis/configuration/RedisHostSpec.scala +++ b/src/test/scala/play/api/cache/redis/configuration/RedisHostSpec.scala @@ -3,11 +3,9 @@ package play.api.cache.redis.configuration import play.api.ConfigLoader import play.api.cache.redis.test._ -import scala.language.implicitConversions - class RedisHostSpec extends UnitSpec with ImplicitOptionMaterialization { - private implicit val loader: ConfigLoader[RedisHost] = RedisHost + implicit private val loader: ConfigLoader[RedisHost] = RedisHost "host with database, username, and password" in { val configuration = Helpers.configuration.fromHocon { @@ -21,7 +19,11 @@ class RedisHostSpec extends UnitSpec with ImplicitOptionMaterialization { """.stripMargin } configuration.get[RedisHost]("play.cache.redis") mustEqual RedisHost( - host = "localhost", port = 6378, database = 1, username = "my-user", password = "something" + host = "localhost", + port = 6378, + database = 1, + username = "my-user", + password = "something", ) } @@ -36,7 +38,11 @@ class RedisHostSpec extends UnitSpec with ImplicitOptionMaterialization { """.stripMargin } configuration.get[RedisHost]("play.cache.redis") mustEqual RedisHost( - host = "localhost", port = 6378, database = 1, username = None, password = "something" + host = "localhost", + port = 6378, + database = 1, + username = None, + password = "something", ) } @@ -59,4 +65,5 @@ class RedisHostSpec extends UnitSpec with ImplicitOptionMaterialization { RedisHost.fromConnectionString("redis:/localhost:6378") } } + } diff --git a/src/test/scala/play/api/cache/redis/configuration/RedisInstanceManagerSpec.scala b/src/test/scala/play/api/cache/redis/configuration/RedisInstanceManagerSpec.scala index bf38cb6f..4d23dca6 100644 --- a/src/test/scala/play/api/cache/redis/configuration/RedisInstanceManagerSpec.scala +++ b/src/test/scala/play/api/cache/redis/configuration/RedisInstanceManagerSpec.scala @@ -4,13 +4,12 @@ import play.api.cache.redis.test._ import play.api.{ConfigLoader, Configuration} import scala.concurrent.duration._ -import scala.language.implicitConversions -class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterialization{ +class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterialization { "default configuration" in new TestCase { - protected override def hocon: String = + override protected def hocon: String = """ |play.cache.redis {} """ @@ -19,7 +18,7 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati RedisStandalone( name = defaultCacheName, host = RedisHost(localhost, defaultPort, database = 0), - settings = defaultsSettings + settings = defaultsSettings, ) manager mustEqual RedisInstanceManagerTest(defaultCacheName)(defaultCache) @@ -32,8 +31,9 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati } "single default instance" in new TestCase { - protected override def hocon: String = - """ + + override protected def hocon: String = + """ |play.cache.redis { | host: redis.localhost.cz | port: 6378 @@ -50,16 +50,26 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati | recovery: log-and-fail |} """ - private val settings: RedisSettings = RedisSettingsTest(invocationContext = "my-dispatcher", invocationPolicy = "eager", timeout = RedisTimeouts(5.minutes, 5.seconds, 300.millis), recovery = "log-and-fail", source = "standalone", prefix = "redis.") + + private val settings: RedisSettings = RedisSettingsTest( + invocationContext = "my-dispatcher", + invocationPolicy = "eager", + timeout = RedisTimeouts(5.minutes, 5.seconds, 300.millis), + recovery = "log-and-fail", + source = "standalone", + prefix = "redis.", + ) manager mustEqual RedisInstanceManagerTest(defaultCacheName)( - RedisStandalone(defaultCacheName, RedisHost("redis.localhost.cz", 6378, database = 2, password = "something"), settings) + RedisStandalone(defaultCacheName, RedisHost("redis.localhost.cz", 6378, database = 2, password = "something"), settings), ) + } "named caches" in new TestCase { - protected override def hocon: String = - """ + + override protected def hocon: String = + """ |play.cache.redis { | instances { | @@ -90,8 +100,14 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati |} """ - private val otherSettings: RedisSettings = RedisSettingsTest( - invocationContext = "my-dispatcher", invocationPolicy = "eager", timeout = RedisTimeouts(5.minutes, 5.seconds, 300.millis), recovery = "log-and-fail", source = "standalone", prefix = "redis.") + private val otherSettings: RedisSettings = RedisSettingsTest( + invocationContext = "my-dispatcher", + invocationPolicy = "eager", + timeout = RedisTimeouts(5.minutes, 5.seconds, 300.millis), + recovery = "log-and-fail", + source = "standalone", + prefix = "redis.", + ) private val defaultCache: RedisInstanceProvider = RedisStandalone(defaultCacheName, RedisHost(localhost, defaultPort, database = 1), otherSettings) private val otherCache: RedisInstanceProvider = RedisStandalone("other", RedisHost("redis.localhost.cz", 6378, database = 2, password = "something"), defaultsSettings) @@ -103,8 +119,9 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati } "cluster mode" in new TestCase { - protected override def hocon: String = - """ + + override protected def hocon: String = + """ |play.cache.redis { | instances { | play { @@ -119,17 +136,19 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati | } |} """ - private def node(port: Int) = RedisHost(localhost, port) + + private def node(port: Int) = RedisHost(localhost, port) manager mustEqual RedisInstanceManagerTest(defaultCacheName)( - RedisCluster( - name = defaultCacheName, nodes = node(6380) :: node(6381) :: node(6382) :: node(6383) :: Nil, settings = defaultsSettings.copy(source = "cluster")) + RedisCluster(name = defaultCacheName, nodes = node(6380) :: node(6381) :: node(6382) :: node(6383) :: Nil, settings = defaultsSettings.copy(source = "cluster")), ) + } "AWS cluster mode" in new TestCase { - protected override def hocon: String = - """ + + override protected def hocon: String = + """ |play.cache.redis { | instances { | play { @@ -139,14 +158,16 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati | } |} """ + private val provider = manager.defaultInstance.asInstanceOf[ResolvedRedisInstance] private val instance = provider.instance.asInstanceOf[RedisCluster] instance.nodes must contain(RedisHost("127.0.0.1", 6379)) } "sentinel mode" in new TestCase { - protected override def hocon: String = - """ + + override protected def hocon: String = + """ |play.cache.redis { | instances { | play { @@ -164,31 +185,41 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati |} """ - private def node(port: Int) = RedisHost(localhost, port) + private def node(port: Int) = RedisHost(localhost, port) manager mustEqual RedisInstanceManagerTest(defaultCacheName)( RedisSentinel( - name = defaultCacheName, masterGroup = "primary", sentinels = node(6380) :: node(6381) :: node(6382) :: Nil, settings = defaultsSettings.copy(source = "sentinel"), password = "my-password", database = 1) + name = defaultCacheName, + masterGroup = "primary", + sentinels = node(6380) :: node(6381) :: node(6382) :: Nil, + settings = defaultsSettings.copy(source = "sentinel"), + password = "my-password", + database = 1, + ), ) + } "connection string mode" in new TestCase { - protected override def hocon: String = - """ + + override protected def hocon: String = + """ |play.cache.redis { | source: "connection-string" | connection-string: "redis://localhost:6379" |} """ - manager mustEqual RedisInstanceManagerTest(defaultCacheName)( - RedisStandalone(defaultCacheName, RedisHost(localhost, defaultPort), defaultsSettings.copy(source = "connection-string")) + manager mustEqual RedisInstanceManagerTest(defaultCacheName)( + RedisStandalone(defaultCacheName, RedisHost(localhost, defaultPort), defaultsSettings.copy(source = "connection-string")), ) + } "custom mode" in new TestCase { - protected override def hocon: String = - """ + + override protected def hocon: String = + """ |play.cache.redis { | source: custom |} @@ -198,8 +229,9 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati } "typo in mode with simple syntax" in new TestCase { - protected override def hocon: String = - """ + + override protected def hocon: String = + """ |play.cache.redis { | source: typo |} @@ -209,8 +241,9 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati } "typo in mode with advanced syntax" in new TestCase { - protected override def hocon: String = - """ + + override protected def hocon: String = + """ |play.cache.redis { | instances { | play { @@ -224,8 +257,9 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati } "fail when requesting undefined cache" in new TestCase { - protected override def hocon: String = - """ + + override protected def hocon: String = + """ |play.cache.redis { | instances { | play { @@ -237,7 +271,7 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati |} """ - manager.instanceOfOption(defaultCacheName) mustBe a[Some[_]] + manager.instanceOfOption(defaultCacheName) mustBe a[Some[?]] manager.instanceOfOption("other") mustEqual None the[IllegalArgumentException] thrownBy manager.instanceOf("other") @@ -246,7 +280,7 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati private trait TestCase { - private implicit val loader: ConfigLoader[RedisInstanceManager] = RedisInstanceManager + implicit private val loader: ConfigLoader[RedisInstanceManager] = RedisInstanceManager private val configuration: Configuration = Helpers.configuration.fromHocon(hocon) @@ -254,16 +288,17 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati protected def hocon: String - protected implicit def implicitlyInstance2resolved(instance: RedisInstance): RedisInstanceProvider = - new ResolvedRedisInstance(instance) + implicit protected def implicitlyInstance2resolved(instance: RedisInstance): RedisInstanceProvider = + new ResolvedRedisInstance(instance) - protected implicit def implicitlyString2unresolved(name: String): RedisInstanceProvider = + implicit protected def implicitlyString2unresolved(name: String): RedisInstanceProvider = new UnresolvedRedisInstance(name) - protected final case class RedisInstanceManagerTest( - default: String)( - providers: RedisInstanceProvider* - ) extends RedisInstanceManager { + final protected case class RedisInstanceManagerTest( + default: String, + )( + providers: RedisInstanceProvider*, + ) extends RedisInstanceManager { override def caches: Set[String] = providers.map(_.name).toSet @@ -273,20 +308,8 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati throw new RuntimeException("Default instance is not defined.") } - } - } -} - - - - - - - - - - - - + } + } +} diff --git a/src/test/scala/play/api/cache/redis/configuration/RedisInstanceProviderSpec.scala b/src/test/scala/play/api/cache/redis/configuration/RedisInstanceProviderSpec.scala index c3ff1d67..f02dafb4 100644 --- a/src/test/scala/play/api/cache/redis/configuration/RedisInstanceProviderSpec.scala +++ b/src/test/scala/play/api/cache/redis/configuration/RedisInstanceProviderSpec.scala @@ -2,16 +2,17 @@ package play.api.cache.redis.configuration import play.api.cache.redis.test.UnitSpec -class RedisInstanceProviderSpec extends UnitSpec { +class RedisInstanceProviderSpec extends UnitSpec { private val defaultCache: RedisStandalone = - RedisStandalone( - name = defaultCacheName, host = RedisHost(localhost, defaultPort, database = Some(0)), settings = defaultsSettings) + RedisStandalone(name = defaultCacheName, host = RedisHost(localhost, defaultPort, database = Some(0)), settings = defaultsSettings) -private implicit val resolver: RedisInstanceResolver = new RedisInstanceResolver { - def resolve: PartialFunction[String, RedisStandalone] = { - case `defaultCacheName` => defaultCache + implicit private val resolver: RedisInstanceResolver = new RedisInstanceResolver { + + def resolve: PartialFunction[String, RedisStandalone] = { case `defaultCacheName` => + defaultCache } + } "resolve already resolved" in { @@ -25,4 +26,5 @@ private implicit val resolver: RedisInstanceResolver = new RedisInstanceResolve "fail when not able to resolve" in { the[Exception] thrownBy new UnresolvedRedisInstance("other").resolved } + } diff --git a/src/test/scala/play/api/cache/redis/configuration/RedisTimeoutsSpec.scala b/src/test/scala/play/api/cache/redis/configuration/RedisTimeoutsSpec.scala index c9d752a8..8c3ced8d 100644 --- a/src/test/scala/play/api/cache/redis/configuration/RedisTimeoutsSpec.scala +++ b/src/test/scala/play/api/cache/redis/configuration/RedisTimeoutsSpec.scala @@ -1,10 +1,10 @@ package play.api.cache.redis.configuration -import scala.concurrent.duration._ -import play.api.cache.redis._ import play.api.cache.redis.test.{Helpers, ImplicitOptionMaterialization, UnitSpec} -class RedisTimeoutsSpec extends UnitSpec with ImplicitOptionMaterialization{ +import scala.concurrent.duration._ + +class RedisTimeoutsSpec extends UnitSpec with ImplicitOptionMaterialization { private def orDefault = RedisTimeouts(1.second, None, 500.millis) @@ -63,7 +63,7 @@ class RedisTimeoutsSpec extends UnitSpec with ImplicitOptionMaterialization{ } val expected = RedisTimeouts(sync = 1.second, redis = None, connection = None) val actual = RedisTimeouts.load(configuration.underlying, "play.cache.redis")(orDefault) - actual mustEqual expected + actual mustEqual expected } "load defaults" in { @@ -71,4 +71,5 @@ class RedisTimeoutsSpec extends UnitSpec with ImplicitOptionMaterialization{ RedisTimeouts.requiredDefault.redis mustEqual None RedisTimeouts.requiredDefault.connection mustEqual None } + } diff --git a/src/test/scala/play/api/cache/redis/connector/ExpectedFutureSpec.scala b/src/test/scala/play/api/cache/redis/connector/ExpectedFutureSpec.scala index 00214d1d..e3869e6f 100644 --- a/src/test/scala/play/api/cache/redis/connector/ExpectedFutureSpec.scala +++ b/src/test/scala/play/api/cache/redis/connector/ExpectedFutureSpec.scala @@ -5,7 +5,6 @@ import play.api.cache.redis.test.{AsyncUnitSpec, SimulatedException} import scala.concurrent.{ExecutionContext, Future} - class ExpectedFutureSpec extends AsyncUnitSpec { import ExpectedFutureSpec._ @@ -33,6 +32,7 @@ class ExpectedFutureSpec extends AsyncUnitSpec { Future.failed[String](SimulatedException).asExpected.assertingFailure[ExecutionFailedException] } } + } new TestSuite("Execution without the key")((future: Future[String]) => future.executing(cmd)) @@ -44,8 +44,9 @@ class ExpectedFutureSpec extends AsyncUnitSpec { Future.successful("expected").executing(cmd).withKey("key").andParameters("SET").andParameter(1).toString mustEqual s"ExpectedFuture($cmd key SET 1)" Future.successful("expected").executing(cmd).withKeys(Seq("key1", "key2")).andParameters(Seq("SET", 1)).toString mustEqual s"ExpectedFuture($cmd key1 key2 SET 1)" - Future.successful("expected").executing(cmd).withKey("key").asCommand("other 2").toString mustEqual s"ExpectedFuture(TEST CMD other 2)" + Future.successful("expected").executing(cmd).withKey("key").asCommand("other 2").toString mustEqual "ExpectedFuture(TEST CMD other 2)" } + } object ExpectedFutureSpec { @@ -53,14 +54,15 @@ object ExpectedFutureSpec { private val cmd = "TEST CMD" private val expectation: PartialFunction[Any, String] = { - case "failing" => throw SimulatedException + case "failing" => throw SimulatedException case "expected" => "ok" } private type ExpectationBuilder[T] = Future[T] => ExpectedFuture[String] - private implicit class FutureBuilder[T](private val future: Future[T]) extends AnyVal { + implicit private class FutureBuilder[T](private val future: Future[T]) extends AnyVal { def asExpected(implicit ev: ExpectationBuilder[T], context: ExecutionContext): Future[String] = ev(future).expects(expectation) def asCommand(implicit ev: ExpectationBuilder[T]): String = ev(future).toString } + } diff --git a/src/test/scala/play/api/cache/redis/connector/FailEagerlySpec.scala b/src/test/scala/play/api/cache/redis/connector/FailEagerlySpec.scala index 8f59cb8c..96c7da64 100644 --- a/src/test/scala/play/api/cache/redis/connector/FailEagerlySpec.scala +++ b/src/test/scala/play/api/cache/redis/connector/FailEagerlySpec.scala @@ -34,7 +34,7 @@ class FailEagerlySpec extends AsyncUnitSpec with ImplicitFutureMaterialization { failEagerly.send(cmd).assertTimeout(200.millis) } - def test(name: String)(f: FailEagerlyImpl => Future[Assertion]): Unit = { + def test(name: String)(f: FailEagerlyImpl => Future[Assertion]): Unit = name in { val system = ActorSystem("test", classLoader = Some(getClass.getClassLoader)) val application = StoppableApplication(system) @@ -43,7 +43,7 @@ class FailEagerlySpec extends AsyncUnitSpec with ImplicitFutureMaterialization { f(impl) } } - } + } object FailEagerlySpec { @@ -56,12 +56,12 @@ object FailEagerlySpec { } class FailEagerlyBase(implicit system: ActorSystem) extends RequestTimeout { - protected implicit val scheduler: Scheduler = system.scheduler + implicit protected val scheduler: Scheduler = system.scheduler implicit val executionContext: ExecutionContext = system.dispatcher - def send[T](redisCommand: RedisCommand[_ <: RedisReply, T]): Future[T] = { + def send[T](redisCommand: RedisCommand[? <: RedisReply, T]): Future[T] = redisCommand.asInstanceOf[RedisCommandTest[T]].returning - } + } final class FailEagerlyImpl(implicit system: ActorSystem) extends FailEagerlyBase with FailEagerly { @@ -74,4 +74,5 @@ object FailEagerlySpec { def markDisconnected(): Unit = connected = false } + } diff --git a/src/test/scala/play/api/cache/redis/connector/RedisClusterSpec.scala b/src/test/scala/play/api/cache/redis/connector/RedisClusterSpec.scala index 759d6258..a298c545 100644 --- a/src/test/scala/play/api/cache/redis/connector/RedisClusterSpec.scala +++ b/src/test/scala/play/api/cache/redis/connector/RedisClusterSpec.scala @@ -57,18 +57,21 @@ class RedisClusterSpec extends IntegrationSpec with RedisClusterContainer { } yield Passed } - def test(name: String)(f: RedisConnector => Future[Assertion]): Unit = { + def test(name: String)(f: RedisConnector => Future[Assertion]): Unit = name in { lazy val clusterInstance = RedisCluster( name = "play", - nodes = 0.until(redisMaster).map { i => - RedisHost(container.containerIpAddress, container.mappedPort(initialPort + i)) - }.toList, + nodes = 0 + .until(redisMaster) + .map { i => + RedisHost(container.containerIpAddress, container.mappedPort(initialPort + i)) + } + .toList, settings = RedisSettings.load( config = Helpers.configuration.default.underlying, - path = "play.cache.redis" - ) + path = "play.cache.redis", + ), ) def runTest: Future[Assertion] = { @@ -81,24 +84,23 @@ class RedisClusterSpec extends IntegrationSpec with RedisClusterContainer { for { connector <- Future(new RedisConnectorProvider(clusterInstance, serializer).get) // initialize the connector by flushing the database - keys <- connector.matching("*") - _ <- Future.sequence(keys.map(connector.remove(_))) + keys <- connector.matching("*") + _ <- Future.sequence(keys.map(connector.remove(_))) // run the test - _ <- f(connector) + _ <- f(connector) } yield Passed } } @SuppressWarnings(Array("org.wartremover.warts.Recursion")) - def makeAttempt(id: Int): Future[Assertion] = { + def makeAttempt(id: Int): Future[Assertion] = runTest.recoverWith { case cause: Throwable if id <= 1 => log.error(s"RedisClusterSpec: test '$name', attempt $id failed, will retry", cause) Future.waitFor(1.second).flatMap(_ => makeAttempt(id + 1)) } - } makeAttempt(1) } - } + } diff --git a/src/test/scala/play/api/cache/redis/connector/RedisConnectorFailureSpec.scala b/src/test/scala/play/api/cache/redis/connector/RedisConnectorFailureSpec.scala index a12cfe26..681a4763 100644 --- a/src/test/scala/play/api/cache/redis/connector/RedisConnectorFailureSpec.scala +++ b/src/test/scala/play/api/cache/redis/connector/RedisConnectorFailureSpec.scala @@ -12,11 +12,11 @@ import scala.util.{Failure, Success} class RedisConnectorFailureSpec extends AsyncUnitSpec with ImplicitFutureMaterialization { - private val score = 1D + private val score = 1d private val encodedValue = "encoded" private val disconnected = Future.failed(SimulatedException) - "Serializer fail" when { + "Serializer fail" when { test("serialization fails") { (serializer, _, connector) => for { @@ -39,9 +39,10 @@ class RedisConnectorFailureSpec extends AsyncUnitSpec with ImplicitFutureMateria test("SET returning false") { (serializer, commands, connector) => for { _ <- serializer.encode(cacheValue, encodedValue) - _ = (commands.set[String](_: String, _: String, _: Option[Long], _: Option[Long], _: Boolean, _: Boolean)(_: ByteStringSerializer[String])) - .expects(cacheKey, encodedValue, None, None, false, false, *) - .returns(false) + _ = (commands + .set[String](_: String, _: String, _: Option[Long], _: Option[Long], _: Boolean, _: Boolean)(_: ByteStringSerializer[String])) + .expects(cacheKey, encodedValue, None, None, false, false, *) + .returns(false) _ <- connector.set(cacheKey, cacheValue).assertingEqual(false) } yield Passed } @@ -60,19 +61,22 @@ class RedisConnectorFailureSpec extends AsyncUnitSpec with ImplicitFutureMateria test("failed SET") { (serializer, commands, connector) => for { _ <- serializer.encode(cacheValue, encodedValue) - _ = (commands.set[String](_: String, _: String, _: Option[Long], _: Option[Long], _: Boolean, _: Boolean)(_: ByteStringSerializer[String])) - .expects(cacheKey, encodedValue, None, None, false, false, *) - .returns(disconnected) + _ = (commands + .set[String](_: String, _: String, _: Option[Long], _: Option[Long], _: Boolean, _: Boolean)(_: ByteStringSerializer[String])) + .expects(cacheKey, encodedValue, None, None, false, false, *) + .returns(disconnected) _ <- serializer.encode(cacheValue, encodedValue) - _ = (commands.set[String](_: String, _: String, _: Option[Long], _: Option[Long], _: Boolean, _: Boolean)(_: ByteStringSerializer[String])) - .expects(cacheKey, encodedValue, None, Some(1.minute.toMillis), false, false, *) - .returns(disconnected) + _ = (commands + .set[String](_: String, _: String, _: Option[Long], _: Option[Long], _: Boolean, _: Boolean)(_: ByteStringSerializer[String])) + .expects(cacheKey, encodedValue, None, Some(1.minute.toMillis), false, false, *) + .returns(disconnected) _ <- serializer.encode(cacheValue, encodedValue) - _ = (commands.set[String](_: String, _: String, _: Option[Long], _: Option[Long], _: Boolean, _: Boolean)(_: ByteStringSerializer[String])) - .expects(cacheKey, encodedValue, None, None, true, false, *) - .returns(disconnected) + _ = (commands + .set[String](_: String, _: String, _: Option[Long], _: Option[Long], _: Boolean, _: Boolean)(_: ByteStringSerializer[String])) + .expects(cacheKey, encodedValue, None, None, true, false, *) + .returns(disconnected) _ <- connector.set(cacheKey, cacheValue).assertingFailure[ExecutionFailedException, SimulatedException] _ <- connector.set(cacheKey, cacheValue, 1.minute).assertingFailure[ExecutionFailedException, SimulatedException] @@ -83,9 +87,10 @@ class RedisConnectorFailureSpec extends AsyncUnitSpec with ImplicitFutureMateria test("failed MSET") { (serializer, commands, connector) => for { _ <- serializer.encode(cacheValue, encodedValue) - _ = (commands.mset[String](_: Map[String, String])(_: ByteStringSerializer[String])) - .expects(Map(cacheKey -> encodedValue), *) - .returns(disconnected) + _ = (commands + .mset[String](_: Map[String, String])(_: ByteStringSerializer[String])) + .expects(Map(cacheKey -> encodedValue), *) + .returns(disconnected) _ <- connector.mSet(cacheKey -> cacheValue).assertingFailure[ExecutionFailedException, SimulatedException] } yield Passed } @@ -93,9 +98,10 @@ class RedisConnectorFailureSpec extends AsyncUnitSpec with ImplicitFutureMateria test("failed MSETNX") { (serializer, commands, connector) => for { _ <- serializer.encode(cacheValue, encodedValue) - _ = (commands.msetnx[String](_: Map[String, String])(_: ByteStringSerializer[String])) - .expects(Map(cacheKey -> encodedValue), *) - .returns(disconnected) + _ = (commands + .msetnx[String](_: Map[String, String])(_: ByteStringSerializer[String])) + .expects(Map(cacheKey -> encodedValue), *) + .returns(disconnected) _ <- connector.mSetIfNotExist(cacheKey -> cacheValue).assertingFailure[ExecutionFailedException, SimulatedException] } yield Passed } @@ -116,9 +122,10 @@ class RedisConnectorFailureSpec extends AsyncUnitSpec with ImplicitFutureMateria test("failed LRANGE") { (_, commands, connector) => for { - _ <- (commands.lrange[String](_: String, _: Long, _: Long)(_: ByteStringDeserializer[String])) - .expects(cacheKey, 0, -1, *) - .returns(disconnected) + _ <- (commands + .lrange[String](_: String, _: Long, _: Long)(_: ByteStringDeserializer[String])) + .expects(cacheKey, 0, -1, *) + .returns(disconnected) _ <- connector.listSlice[String](cacheKey, 0, -1).assertingFailure[ExecutionFailedException, SimulatedException] } yield Passed } @@ -126,9 +133,10 @@ class RedisConnectorFailureSpec extends AsyncUnitSpec with ImplicitFutureMateria test("failed LREM") { (serializer, commands, connector) => for { _ <- serializer.encode(cacheValue, encodedValue) - _ = (commands.lrem(_: String, _: Long, _: String)(_: ByteStringSerializer[String])) - .expects(cacheKey, 2L, encodedValue, *) - .returns(disconnected) + _ = (commands + .lrem(_: String, _: Long, _: String)(_: ByteStringSerializer[String])) + .expects(cacheKey, 2L, encodedValue, *) + .returns(disconnected) _ <- connector.listRemove(cacheKey, cacheValue, 2).assertingFailure[ExecutionFailedException, SimulatedException] } yield Passed } @@ -144,9 +152,10 @@ class RedisConnectorFailureSpec extends AsyncUnitSpec with ImplicitFutureMateria for { _ <- serializer.encode("pivot", "encodedPivot") _ <- serializer.encode(cacheValue, encodedValue) - _ = (commands.linsert[String](_: String, _: ListPivot, _: String, _: String)(_: ByteStringSerializer[String])) - .expects(cacheKey, BEFORE, "encodedPivot", encodedValue, *) - .returns(disconnected) + _ = (commands + .linsert[String](_: String, _: ListPivot, _: String, _: String)(_: ByteStringSerializer[String])) + .expects(cacheKey, BEFORE, "encodedPivot", encodedValue, *) + .returns(disconnected) // run the test _ <- connector.listInsert(cacheKey, "pivot", cacheValue).assertingFailure[ExecutionFailedException, SimulatedException] } yield Passed @@ -161,21 +170,23 @@ class RedisConnectorFailureSpec extends AsyncUnitSpec with ImplicitFutureMateria } test("failed HSET") { (serializer, commands, connector) => -for { - _ <- serializer.encode(cacheValue, encodedValue) - _ = (commands.hset[String](_: String, _: String, _: String)(_: ByteStringSerializer[String])) - .expects(cacheKey, "field", encodedValue, *) - .returns(disconnected) - _ <- connector.hashSet(cacheKey, "field", cacheValue).assertingFailure[ExecutionFailedException, SimulatedException] - } yield Passed + for { + _ <- serializer.encode(cacheValue, encodedValue) + _ = (commands + .hset[String](_: String, _: String, _: String)(_: ByteStringSerializer[String])) + .expects(cacheKey, "field", encodedValue, *) + .returns(disconnected) + _ <- connector.hashSet(cacheKey, "field", cacheValue).assertingFailure[ExecutionFailedException, SimulatedException] + } yield Passed } test("failed ZADD") { (serializer, commands, connector) => for { _ <- serializer.encode(cacheValue, encodedValue) - _ = (commands.zaddMock[String](_: String, _: Seq[(Double, String)])(_: ByteStringSerializer[String])) - .expects(cacheKey, Seq((score, encodedValue)), *) - .returns(disconnected) + _ = (commands + .zaddMock[String](_: String, _: Seq[(Double, String)])(_: ByteStringSerializer[String])) + .expects(cacheKey, Seq((score, encodedValue)), *) + .returns(disconnected) _ <- connector.sortedSetAdd(cacheKey, (score, cacheValue)).assertingFailure[ExecutionFailedException, SimulatedException] } yield Passed } @@ -190,9 +201,10 @@ for { test("failed ZSCORE") { (serializer, commands, connector) => for { _ <- serializer.encode(cacheValue, encodedValue) - _ = (commands.zscore[String](_: String, _: String)(_: ByteStringSerializer[String])) - .expects(cacheKey, encodedValue, *) - .returns(disconnected) + _ = (commands + .zscore[String](_: String, _: String)(_: ByteStringSerializer[String])) + .expects(cacheKey, encodedValue, *) + .returns(disconnected) _ <- connector.sortedSetScore(cacheKey, cacheValue).assertingFailure[ExecutionFailedException, SimulatedException] } yield Passed } @@ -200,33 +212,36 @@ for { test("failed ZREM") { (serializer, commands, connector) => for { _ <- serializer.encode(cacheValue, encodedValue) - _ = (commands.zremMock(_: String, _: Seq[String])(_: ByteStringSerializer[String])) - .expects(cacheKey, Seq(encodedValue), *) - .returns(disconnected) + _ = (commands + .zremMock(_: String, _: Seq[String])(_: ByteStringSerializer[String])) + .expects(cacheKey, Seq(encodedValue), *) + .returns(disconnected) _ <- connector.sortedSetRemove(cacheKey, cacheValue).assertingFailure[ExecutionFailedException, SimulatedException] } yield Passed } test("failed ZRANGE") { (_, commands, connector) => for { - _ <- (commands.zrange[String](_: String, _: Long, _: Long)(_: ByteStringDeserializer[String])) - .expects(cacheKey, 1, 5, *) - .returns(disconnected) + _ <- (commands + .zrange[String](_: String, _: Long, _: Long)(_: ByteStringDeserializer[String])) + .expects(cacheKey, 1, 5, *) + .returns(disconnected) _ <- connector.sortedSetRange[String](cacheKey, 1, 5).assertingFailure[ExecutionFailedException, SimulatedException] } yield Passed } test("failed ZREVRANGE") { (_, commands, connector) => for { - _ <- (commands.zrevrange[String](_: String, _: Long, _: Long)(_: ByteStringDeserializer[String])) - .expects(cacheKey, 1, 5, *) - .returns(disconnected) + _ <- (commands + .zrevrange[String](_: String, _: Long, _: Long)(_: ByteStringDeserializer[String])) + .expects(cacheKey, 1, 5, *) + .returns(disconnected) _ <- connector.sortedSetReverseRange[String](cacheKey, 1, 5).assertingFailure[ExecutionFailedException, SimulatedException] } yield Passed } } - private def test(name: String)(f: (SerializerAssertions, RedisCommandsMock, RedisConnector) => Future[Assertion]): Unit = { + private def test(name: String)(f: (SerializerAssertions, RedisCommandsMock, RedisConnector) => Future[Assertion]): Unit = name in { implicit val runtime: RedisRuntime = mock[RedisRuntime] val serializer = mock[AkkaSerializer] @@ -237,27 +252,24 @@ for { f(new SerializerAssertions(serializer), commands, connector) } - } private class SerializerAssertions(mock: AkkaSerializer) { - def failOnEncode[T](value: T): Future[Unit] = { + def failOnEncode[T](value: T): Future[Unit] = Future.successful { (mock.encode(_: Any)).expects(value).returns(Failure(SimulatedException)) } - } - def encode[T](value: T, encoded: String): Future[Unit] = { + def encode[T](value: T, encoded: String): Future[Unit] = Future.successful { (mock.encode(_: Any)).expects(value).returns(Success(encoded)) } - } - def failOnDecode(value: String): Future[Unit] = { + def failOnDecode(value: String): Future[Unit] = Future.successful { (mock.decode(_: String)(_: ClassTag[String])).expects(value, *).returns(Failure(SimulatedException)) } - } + } private trait RedisCommandsMock extends RedisCommands { @@ -267,9 +279,10 @@ for { def zaddMock[V: ByteStringSerializer](key: String, scoreMembers: Seq[(Double, V)]): Future[Long] - override final def zrem[V: ByteStringSerializer](key: String, members: V*): Future[Long] = + final override def zrem[V: ByteStringSerializer](key: String, members: V*): Future[Long] = zremMock(key, members) def zremMock[V: ByteStringSerializer](key: String, members: Seq[V]): Future[Long] } + } diff --git a/src/test/scala/play/api/cache/redis/connector/RedisRequestTimeoutSpec.scala b/src/test/scala/play/api/cache/redis/connector/RedisRequestTimeoutSpec.scala index dfb0c659..8a7a1b24 100644 --- a/src/test/scala/play/api/cache/redis/connector/RedisRequestTimeoutSpec.scala +++ b/src/test/scala/play/api/cache/redis/connector/RedisRequestTimeoutSpec.scala @@ -30,17 +30,19 @@ class RedisRequestTimeoutSpec extends AsyncUnitSpec { } private class RequestTimeoutBase(implicit system: ActorSystem) extends RequestTimeout { - protected implicit val scheduler: Scheduler = system.scheduler + implicit protected val scheduler: Scheduler = system.scheduler implicit val executionContext: ExecutionContextExecutor = system.dispatcher - def send[T](redisCommand: RedisCommand[_ <: RedisReply, T]): Future[T] = { + def send[T](redisCommand: RedisCommand[? <: RedisReply, T]): Future[T] = redisCommand.asInstanceOf[RedisCommandTest[T]].returning - } + } private class RedisRequestTimeoutImpl( - override val timeout: Option[FiniteDuration] - )(implicit - system: ActorSystem - ) extends RequestTimeoutBase with RedisRequestTimeout + override val timeout: Option[FiniteDuration], + )(implicit + system: ActorSystem, + ) extends RequestTimeoutBase + with RedisRequestTimeout + } diff --git a/src/test/scala/play/api/cache/redis/connector/RedisSentinelSpec.scala b/src/test/scala/play/api/cache/redis/connector/RedisSentinelSpec.scala index 6f73d0d7..ae0bfb80 100644 --- a/src/test/scala/play/api/cache/redis/connector/RedisSentinelSpec.scala +++ b/src/test/scala/play/api/cache/redis/connector/RedisSentinelSpec.scala @@ -61,7 +61,7 @@ class RedisSentinelSpec extends IntegrationSpec with RedisSentinelContainer { } yield Passed } - def test(name: String)(f: RedisConnector => Future[Assertion]): Unit = { + def test(name: String)(f: RedisConnector => Future[Assertion]): Unit = name in { implicit val system: ActorSystem = ActorSystem("test", classLoader = Some(getClass.getClassLoader)) implicit val runtime: RedisRuntime = RedisRuntime("sentinel", syncTimeout = 5.seconds, ExecutionContext.global, new LogAndFailPolicy, LazyInvocation) @@ -71,13 +71,16 @@ class RedisSentinelSpec extends IntegrationSpec with RedisSentinelContainer { lazy val sentinelInstance = RedisSentinel( name = "sentinel", masterGroup = master, - sentinels = 0.until(nodes).map { i => - RedisHost(container.containerIpAddress, container.mappedPort(sentinelPort + i)) - }.toList, + sentinels = 0 + .until(nodes) + .map { i => + RedisHost(container.containerIpAddress, container.mappedPort(sentinelPort + i)) + } + .toList, settings = RedisSettings.load( config = Helpers.configuration.default.underlying, - path = "play.cache.redis" - ) + path = "play.cache.redis", + ), ) application.runAsyncInApplication { @@ -85,11 +88,11 @@ class RedisSentinelSpec extends IntegrationSpec with RedisSentinelContainer { for { // initialize the connector by flushing the database keys <- connector.matching("*") - _ <- Future.sequence(keys.map(connector.remove(_))) + _ <- Future.sequence(keys.map(connector.remove(_))) // run the test - _ <- f(connector) + _ <- f(connector) } yield Passed } } - } + } diff --git a/src/test/scala/play/api/cache/redis/connector/RedisStandaloneSpec.scala b/src/test/scala/play/api/cache/redis/connector/RedisStandaloneSpec.scala index b37ee667..2321fd7f 100644 --- a/src/test/scala/play/api/cache/redis/connector/RedisStandaloneSpec.scala +++ b/src/test/scala/play/api/cache/redis/connector/RedisStandaloneSpec.scala @@ -150,9 +150,7 @@ class RedisStandaloneSpec extends IntegrationSpec with RedisStandaloneContainer } test("remove with empty args") { (_, connector) => - for { - _ <- connector.remove(List.empty: _*).assertingSuccess - } yield Passed + connector.remove().assertingSuccess } test("clear with setting null") { (cacheKey, connector) => @@ -185,8 +183,7 @@ class RedisStandaloneSpec extends IntegrationSpec with RedisStandaloneContainer _ <- connector.matching(s"$cacheKey-*A").map(_.toSet).assertingEqual(Set(s"$cacheKey-key-A", s"$cacheKey-note-A")) _ <- connector.matching(s"$cacheKey-key-*").map(_.toSet).assertingEqual(Set(s"$cacheKey-key-A", s"$cacheKey-key-B")) _ <- connector.matching(s"$cacheKey-* A * ").assertingEqual(Seq.empty) - } - yield Passed + } yield Passed } test("remove multiple keys at once") { (cacheKey, connector) => @@ -381,15 +378,15 @@ class RedisStandaloneSpec extends IntegrationSpec with RedisStandaloneContainer } yield Passed } - test("set add") { (cacheKey, connector) => - for { - _ <- connector.setSize(cacheKey).assertingEqual(0) - _ <- connector.setAdd(cacheKey, "A", "B").assertingEqual(2) - _ <- connector.setSize(cacheKey).assertingEqual(2) - _ <- connector.setAdd(cacheKey, "C", "B").assertingEqual(1) - _ <- connector.setSize(cacheKey).assertingEqual(3) - } yield Passed - } + test("set add") { (cacheKey, connector) => + for { + _ <- connector.setSize(cacheKey).assertingEqual(0) + _ <- connector.setAdd(cacheKey, "A", "B").assertingEqual(2) + _ <- connector.setSize(cacheKey).assertingEqual(2) + _ <- connector.setAdd(cacheKey, "C", "B").assertingEqual(1) + _ <- connector.setSize(cacheKey).assertingEqual(3) + } yield Passed + } test("set add into invalid type") { (cacheKey, connector) => for { @@ -399,23 +396,23 @@ class RedisStandaloneSpec extends IntegrationSpec with RedisStandaloneContainer } yield Passed } - test("set rank") { (cacheKey, connector) => - for { - _ <- connector.setSize(cacheKey).assertingEqual(0) - _ <- connector.setAdd(cacheKey, "A", "B").assertingEqual(2) - _ <- connector.setSize(cacheKey).assertingEqual(2) + test("set rank") { (cacheKey, connector) => + for { + _ <- connector.setSize(cacheKey).assertingEqual(0) + _ <- connector.setAdd(cacheKey, "A", "B").assertingEqual(2) + _ <- connector.setSize(cacheKey).assertingEqual(2) - _ <- connector.setIsMember(cacheKey, "A").assertingEqual(true) - _ <- connector.setIsMember(cacheKey, "B").assertingEqual(true) - _ <- connector.setIsMember(cacheKey, "C").assertingEqual(false) + _ <- connector.setIsMember(cacheKey, "A").assertingEqual(true) + _ <- connector.setIsMember(cacheKey, "B").assertingEqual(true) + _ <- connector.setIsMember(cacheKey, "C").assertingEqual(false) - _ <- connector.setAdd(cacheKey, "C", "B").assertingEqual(1) + _ <- connector.setAdd(cacheKey, "C", "B").assertingEqual(1) - _ <- connector.setIsMember(cacheKey, "A").assertingEqual(true) - _ <- connector.setIsMember(cacheKey, "B").assertingEqual(true) - _ <- connector.setIsMember(cacheKey, "C").assertingEqual(true) - } yield Passed - } + _ <- connector.setIsMember(cacheKey, "A").assertingEqual(true) + _ <- connector.setIsMember(cacheKey, "B").assertingEqual(true) + _ <- connector.setIsMember(cacheKey, "C").assertingEqual(true) + } yield Passed + } test("set size") { (cacheKey, connector) => for { @@ -438,41 +435,41 @@ class RedisStandaloneSpec extends IntegrationSpec with RedisStandaloneContainer } yield Passed } - test("set slice") { (cacheKey, connector) => - for { - _ <- connector.setSize(cacheKey).assertingEqual(0) - _ <- connector.setAdd(cacheKey, "A", "B", "C").assertingEqual(3) - _ <- connector.setSize(cacheKey).assertingEqual(3) + test("set slice") { (cacheKey, connector) => + for { + _ <- connector.setSize(cacheKey).assertingEqual(0) + _ <- connector.setAdd(cacheKey, "A", "B", "C").assertingEqual(3) + _ <- connector.setSize(cacheKey).assertingEqual(3) - _ <- connector.setMembers[String](cacheKey).assertingEqual(Set("A", "B", "C")) + _ <- connector.setMembers[String](cacheKey).assertingEqual(Set("A", "B", "C")) - _ <- connector.setSize(cacheKey).assertingEqual(3) - } yield Passed - } + _ <- connector.setSize(cacheKey).assertingEqual(3) + } yield Passed + } test("hash set values") { (cacheKey, connector) => for { _ <- connector.hashSize(cacheKey).assertingEqual(0) - _ <- connector.hashGetAll[String] (cacheKey).assertingEqual(Map.empty) + _ <- connector.hashGetAll[String](cacheKey).assertingEqual(Map.empty) _ <- connector.hashKeys(cacheKey).assertingEqual(Set.empty) - _ <- connector.hashValues[String] (cacheKey).assertingEqual(Set.empty) + _ <- connector.hashValues[String](cacheKey).assertingEqual(Set.empty) - _ <- connector.hashGet[String] (cacheKey, "KA").assertingEqual(None) + _ <- connector.hashGet[String](cacheKey, "KA").assertingEqual(None) _ <- connector.hashSet(cacheKey, "KA", "VA1").assertingEqual(true) - _ <- connector.hashGet[String] (cacheKey, "KA").assertingEqual(Some("VA1")) + _ <- connector.hashGet[String](cacheKey, "KA").assertingEqual(Some("VA1")) _ <- connector.hashSet(cacheKey, "KA", "VA2").assertingEqual(false) - _ <- connector.hashGet[String] (cacheKey, "KA").assertingEqual(Some("VA2")) + _ <- connector.hashGet[String](cacheKey, "KA").assertingEqual(Some("VA2")) _ <- connector.hashSet(cacheKey, "KB", "VB").assertingEqual(true) - _ <- connector.hashGet[String] (cacheKey, Seq("KA", "KB", "KC")).assertingEqual(Seq(Some("VA2"), Some("VB"), None)) + _ <- connector.hashGet[String](cacheKey, Seq("KA", "KB", "KC")).assertingEqual(Seq(Some("VA2"), Some("VB"), None)) _ <- connector.hashExists(cacheKey, "KB").assertingEqual(true) _ <- connector.hashExists(cacheKey, "KC").assertingEqual(false) _ <- connector.hashSize(cacheKey).assertingEqual(2) - _ <- connector.hashGetAll[String] (cacheKey).assertingEqual(Map("KA" -> "VA2", "KB" -> "VB")) + _ <- connector.hashGetAll[String](cacheKey).assertingEqual(Map("KA" -> "VA2", "KB" -> "VB")) _ <- connector.hashKeys(cacheKey).assertingEqual(Set("KA", "KB")) - _ <- connector.hashValues[String] (cacheKey).assertingEqual(Set("VA2", "VB")) + _ <- connector.hashValues[String](cacheKey).assertingEqual(Set("VA2", "VB")) _ <- connector.hashRemove(cacheKey, "KB").assertingEqual(1) _ <- connector.hashRemove(cacheKey, "KC").assertingEqual(0) @@ -480,9 +477,9 @@ class RedisStandaloneSpec extends IntegrationSpec with RedisStandaloneContainer _ <- connector.hashExists(cacheKey, "KA").assertingEqual(true) _ <- connector.hashSize(cacheKey).assertingEqual(1) - _ <- connector.hashGetAll[String] (cacheKey).assertingEqual(Map("KA" -> "VA2")) + _ <- connector.hashGetAll[String](cacheKey).assertingEqual(Map("KA" -> "VA2")) _ <- connector.hashKeys(cacheKey).assertingEqual(Set("KA")) - _ <- connector.hashValues[String] (cacheKey).assertingEqual(Set("VA2")) + _ <- connector.hashValues[String](cacheKey).assertingEqual(Set("VA2")) _ <- connector.hashSet(cacheKey, "KD", 5).assertingEqual(true) _ <- connector.hashIncrement(cacheKey, "KD", 2).assertingEqual(7) @@ -514,25 +511,25 @@ class RedisStandaloneSpec extends IntegrationSpec with RedisStandaloneContainer for { _ <- connector.set(cacheKey, "value").assertingSuccess _ <- connector.get[String](cacheKey).assertingEqual(Some("value")) - _ <- connector.sortedSetAdd(cacheKey, 1D -> "VA1").assertingFailure[IllegalArgumentException] + _ <- connector.sortedSetAdd(cacheKey, 1d -> "VA1").assertingFailure[IllegalArgumentException] } yield Passed } test("sorted set score") { (cacheKey, connector) => for { _ <- connector.sortedSetSize(cacheKey).assertingEqual(0) - _ <- connector.sortedSetAdd(cacheKey, 1D -> "A", 3D -> "B").assertingEqual(2) + _ <- connector.sortedSetAdd(cacheKey, 1d -> "A", 3d -> "B").assertingEqual(2) _ <- connector.sortedSetSize(cacheKey).assertingEqual(2) - _ <- connector.sortedSetScore(cacheKey, "A").assertingEqual(Some(1D)) - _ <- connector.sortedSetScore(cacheKey, "B").assertingEqual(Some(3D)) + _ <- connector.sortedSetScore(cacheKey, "A").assertingEqual(Some(1d)) + _ <- connector.sortedSetScore(cacheKey, "B").assertingEqual(Some(3d)) _ <- connector.sortedSetScore(cacheKey, "C").assertingEqual(None) - _ <- connector.sortedSetAdd(cacheKey, 2D -> "C", 4D -> "B").assertingEqual(1) + _ <- connector.sortedSetAdd(cacheKey, 2d -> "C", 4d -> "B").assertingEqual(1) - _ <- connector.sortedSetScore(cacheKey, "A").assertingEqual(Some(1D)) - _ <- connector.sortedSetScore(cacheKey, "B").assertingEqual(Some(4D)) - _ <- connector.sortedSetScore(cacheKey, "C").assertingEqual(Some(2D)) + _ <- connector.sortedSetScore(cacheKey, "A").assertingEqual(Some(1d)) + _ <- connector.sortedSetScore(cacheKey, "B").assertingEqual(Some(4d)) + _ <- connector.sortedSetScore(cacheKey, "C").assertingEqual(Some(2d)) } yield Passed } @@ -547,7 +544,7 @@ class RedisStandaloneSpec extends IntegrationSpec with RedisStandaloneContainer test("sorted set remove") { (cacheKey, connector) => for { _ <- connector.sortedSetSize(cacheKey).assertingEqual(0) - _ <- connector.sortedSetAdd(cacheKey, 1D -> "A", 2D -> "B", 3D -> "C").assertingEqual(3) + _ <- connector.sortedSetAdd(cacheKey, 1d -> "A", 2d -> "B", 3d -> "C").assertingEqual(3) _ <- connector.sortedSetSize(cacheKey).assertingEqual(3) _ <- connector.sortedSetRemove(cacheKey, "A").assertingEqual(1) @@ -560,7 +557,7 @@ class RedisStandaloneSpec extends IntegrationSpec with RedisStandaloneContainer test("sorted set range") { (cacheKey, connector) => for { _ <- connector.sortedSetSize(cacheKey).assertingEqual(0) - _ <- connector.sortedSetAdd(cacheKey, 1D -> "A", 2D -> "B", 4D -> "C").assertingEqual(3) + _ <- connector.sortedSetAdd(cacheKey, 1d -> "A", 2d -> "B", 4d -> "C").assertingEqual(3) _ <- connector.sortedSetSize(cacheKey).assertingEqual(3) _ <- connector.sortedSetRange[String](cacheKey, 0, 1).assertingEqual(Vector("A", "B")) @@ -574,7 +571,7 @@ class RedisStandaloneSpec extends IntegrationSpec with RedisStandaloneContainer test("sorted set reverse range") { (cacheKey, connector) => for { _ <- connector.sortedSetSize(cacheKey).assertingEqual(0) - _ <- connector.sortedSetAdd(cacheKey, 1D -> "A", 2D -> "B", 4D -> "C").assertingEqual(3) + _ <- connector.sortedSetAdd(cacheKey, 1d -> "A", 2d -> "B", 4d -> "C").assertingEqual(3) _ <- connector.sortedSetSize(cacheKey).assertingEqual(3) _ <- connector.sortedSetReverseRange[String](cacheKey, 0, 1).assertingEqual(Vector("C", "B")) @@ -585,7 +582,7 @@ class RedisStandaloneSpec extends IntegrationSpec with RedisStandaloneContainer } yield Passed } - def test(name: String)(f: (String, RedisConnector) => Future[Assertion]): Unit = { + def test(name: String)(f: (String, RedisConnector) => Future[Assertion]): Unit = name in { implicit val system: ActorSystem = ActorSystem("test", classLoader = Some(getClass.getClassLoader)) implicit val runtime: RedisRuntime = RedisRuntime("standalone", syncTimeout = 5.seconds, ExecutionContext.global, new LogAndFailPolicy, LazyInvocation) @@ -597,22 +594,21 @@ class RedisStandaloneSpec extends IntegrationSpec with RedisStandaloneContainer host = RedisHost(container.containerIpAddress, container.mappedPort(defaultPort)), settings = RedisSettings.load( config = Helpers.configuration.default.underlying, - path = "play.cache.redis" - ) + path = "play.cache.redis", + ), ) - + val cacheKey = name.toLowerCase().replace(" ", "-") application.runAsyncInApplication { for { connector <- Future(new RedisConnectorProvider(instance, serializer).get) // initialize the connector by flushing the database - _ <- connector.invalidate() + _ <- connector.invalidate() // run the test - _ <- f(cacheKey, connector) + _ <- f(cacheKey, connector) } yield Passed } } - } - } +} diff --git a/src/test/scala/play/api/cache/redis/connector/SerializerSpec.scala b/src/test/scala/play/api/cache/redis/connector/SerializerSpec.scala index 838ec754..57d1e015 100644 --- a/src/test/scala/play/api/cache/redis/connector/SerializerSpec.scala +++ b/src/test/scala/play/api/cache/redis/connector/SerializerSpec.scala @@ -15,7 +15,7 @@ class SerializerSpec extends AsyncUnitSpec { "encode" when { test("byte") { implicit serializer => - 0xAB.toByte.encoded mustEqual "-85" + 0xab.toByte.encoded mustEqual "-85" JavaTypes.byteValue.encoded mustEqual "5" } @@ -94,7 +94,7 @@ class SerializerSpec extends AsyncUnitSpec { "decode" when { test("byte") { implicit serializer => - "-85".decoded[Byte] mustEqual 0xAB.toByte + "-85".decoded[Byte] mustEqual 0xab.toByte } test("byte[]") { implicit serializer => @@ -171,31 +171,30 @@ class SerializerSpec extends AsyncUnitSpec { } } - private def test(name: String)(f: AkkaSerializer => Unit): Unit = { + private def test(name: String)(f: AkkaSerializer => Unit): Unit = name in { val system = ActorSystem.apply(s"test-${Random.nextInt()}", classLoader = Some(getClass.getClassLoader)) val serializer: AkkaSerializer = new AkkaSerializerImpl(system) f(serializer) system.terminate().map(_ => Passed) } - } + } object SerializerSpec { - private implicit class ValueEncoder(private val any: Any) extends AnyVal { + implicit private class ValueEncoder(private val any: Any) extends AnyVal { def encoded(implicit s: AkkaSerializer): String = s.encode(any).get } - private implicit class StringDecoder(private val string: String) extends AnyVal { + implicit private class StringDecoder(private val string: String) extends AnyVal { def decoded[T: ClassTag](implicit s: AkkaSerializer): T = s.decode[T](string).get } - private implicit class StringOps(private val string: String) extends AnyVal { + implicit private class StringOps(private val string: String) extends AnyVal { def withoutWhitespaces: String = string.replaceAll("\\s", "") } /** Plain test object to be cached */ - private final case class SimpleObject(key: String, value: Int) + final private case class SimpleObject(key: String, value: Int) } - diff --git a/src/test/scala/play/api/cache/redis/impl/AsyncJavaRedisSpec.scala b/src/test/scala/play/api/cache/redis/impl/AsyncJavaRedisSpec.scala index 32519527..15739b0c 100644 --- a/src/test/scala/play/api/cache/redis/impl/AsyncJavaRedisSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/AsyncJavaRedisSpec.scala @@ -11,7 +11,7 @@ import scala.concurrent.duration._ import scala.jdk.CollectionConverters.IterableHasAsScala class AsyncJavaRedisSpec extends AsyncUnitSpec with AsyncRedisMock with RedisRuntimeMock { -import Helpers._ + import Helpers._ private val expiration = 5.seconds private val expirationLong = expiration.toSeconds @@ -33,26 +33,26 @@ import Helpers._ } yield Passed } - test("get null") { (async, cache) => - for { - _ <- async.expect.getClassTag(cacheKey, Some("null")) + test("get null") { (async, cache) => + for { + _ <- async.expect.getClassTag(cacheKey, Some("null")) _ <- cache.get[String](cacheKey).assertingEqual(Optional.empty) - } yield Passed - } + } yield Passed + } - test("set") { (async, cache) => - for { + test("set") { (async, cache) => + for { _ <- async.expect.set(cacheKey, cacheValue, Duration.Inf) _ <- cache.set(cacheKey, cacheValue).assertingDone - } yield Passed - } + } yield Passed + } - test("set with expiration") { (async, cache) => - for { - _ <- async.expect.set(cacheKey, cacheValue, expiration) - _ <- cache.set(cacheKey, cacheValue, expiration.toSeconds.toInt).assertingDone - } yield Passed - } + test("set with expiration") { (async, cache) => + for { + _ <- async.expect.set(cacheKey, cacheValue, expiration) + _ <- cache.set(cacheKey, cacheValue, expiration.toSeconds.toInt).assertingDone + } yield Passed + } test("set null") { (async, cache) => for { @@ -154,8 +154,8 @@ import Helpers._ for { _ <- async.expect.getAllKeys[String](Iterable(cacheKey, cacheKey, cacheKey), Seq(Some(cacheValue), None, None)) _ <- cache - .getAll(classOf[String], cacheKey, cacheKey, cacheKey) - .asserting(_.asScala.toList mustEqual List(Optional.of(cacheValue), Optional.empty, Optional.empty)) + .getAll(classOf[String], cacheKey, cacheKey, cacheKey) + .asserting(_.asScala.toList mustEqual List(Optional.of(cacheValue), Optional.empty, Optional.empty)) } yield Passed } @@ -164,8 +164,8 @@ import Helpers._ for { _ <- async.expect.getAllKeys[String](Iterable(cacheKey, cacheKey, cacheKey), Seq(Some(cacheValue), None, None)) _ <- cache - .getAll(classOf[String], JavaList(cacheKey, cacheKey, cacheKey)) - .asserting(_.asScala.toList mustEqual List(Optional.of(cacheValue), Optional.empty, Optional.empty)) + .getAll(classOf[String], JavaList(cacheKey, cacheKey, cacheKey)) + .asserting(_.asScala.toList mustEqual List(Optional.of(cacheValue), Optional.empty, Optional.empty)) } yield Passed } @@ -289,12 +289,12 @@ import Helpers._ } yield Passed } - test("remove matching") { (async, cache) => - for { - _ <- async.expect.removeMatching("pattern") - _ <- cache.removeMatching("pattern").assertingDone - } yield Passed - } + test("remove matching") { (async, cache) => + for { + _ <- async.expect.removeMatching("pattern") + _ <- cache.removeMatching("pattern").assertingDone + } yield Passed + } test("exists") { (async, cache) => for { @@ -326,7 +326,7 @@ import Helpers._ val list = mock[RedisListMock] for { _ <- async.expect.list[String](cacheKey, list) - _ <- cache.list(cacheKey, classOf[String]) mustBe a[AsyncRedisList[_]] + _ <- cache.list(cacheKey, classOf[String]) mustBe a[AsyncRedisList[?]] } yield Passed } @@ -335,7 +335,7 @@ import Helpers._ val set = mock[RedisSetMock] for { _ <- async.expect.set[String](cacheKey, set) - _ <- cache.set(cacheKey, classOf[String]) mustBe a[AsyncRedisSet[_]] + _ <- cache.set(cacheKey, classOf[String]) mustBe a[AsyncRedisSet[?]] } yield Passed } @@ -344,11 +344,11 @@ import Helpers._ val map = mock[RedisMapMock] for { _ <- async.expect.map[String](cacheKey, map) - _ <- cache.map(cacheKey, classOf[String]) mustBe a[AsyncRedisMap[_]] + _ <- cache.map(cacheKey, classOf[String]) mustBe a[AsyncRedisMap[?]] } yield Passed } - private def test(name: String)(f: (AsyncRedisMock, play.cache.redis.AsyncCacheApi) => Future[Assertion]): Unit = { + private def test(name: String)(f: (AsyncRedisMock, play.cache.redis.AsyncCacheApi) => Future[Assertion]): Unit = name in { implicit val runtime: RedisRuntime = redisRuntime( invocationPolicy = LazyInvocation, @@ -357,12 +357,12 @@ import Helpers._ implicit val environment: Environment = Environment( rootPath = new java.io.File("."), classLoader = getClass.getClassLoader, - mode = Mode.Test + mode = Mode.Test, ) val async = mock[AsyncRedisMock] val cache: play.cache.redis.AsyncCacheApi = new AsyncJavaRedis(async) f(async, cache) } - } + } diff --git a/src/test/scala/play/api/cache/redis/impl/AsyncRedisMock.scala b/src/test/scala/play/api/cache/redis/impl/AsyncRedisMock.scala index 98fc3df0..6365e883 100644 --- a/src/test/scala/play/api/cache/redis/impl/AsyncRedisMock.scala +++ b/src/test/scala/play/api/cache/redis/impl/AsyncRedisMock.scala @@ -20,22 +20,24 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => final override def getAll[T: ClassTag](keys: Iterable[String]): AsynchronousResult[Seq[Option[T]]] = getAllKeys(keys) - def getAllKeys[T: ClassTag](keys: Iterable[String]): AsynchronousResult[Seq[Option[T]]] + def getAllKeys[T](keys: Iterable[String]): AsynchronousResult[Seq[Option[T]]] } - final protected implicit class AsyncRedisOps(async: AsyncRedisMock) { + implicit final protected class AsyncRedisOps(async: AsyncRedisMock) { + def expect: AsyncRedisExpectation = new AsyncRedisExpectation(async) + } - protected final class AsyncRedisExpectation(async: AsyncRedisMock) { + final protected class AsyncRedisExpectation(async: AsyncRedisMock) { private def classTagKey(key: String): String = s"classTag::$key" private def classTagValue: Any => String = { - case null => "null" + case null => "null" case v if v.getClass =~= classOf[String] => "java.lang.String" - case other => throw new IllegalArgumentException(s"Unexpected value for classTag: ${other.getClass.getSimpleName}") + case other => throw new IllegalArgumentException(s"Unexpected value for classTag: ${other.getClass.getSimpleName}") } def getClassTag(key: String, value: Option[String]): Future[Unit] = @@ -43,58 +45,62 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => def get[T: ClassTag](key: String, value: Option[T]): Future[Unit] = Future.successful { - (async.get(_: String)(_: ClassTag[_])) + (async + .get(_: String)(_: ClassTag[?])) .expects(key, implicitly[ClassTag[T]]) .returning(Future.successful(value)) .once() } - def getAllKeys[T: ClassTag](keys: Iterable[String], values: Seq[Option[T]]): Future[Unit] = + def getAllKeys[T](keys: Iterable[String], values: Seq[Option[T]]): Future[Unit] = Future.successful { - (async.getAllKeys(_: Iterable[String])(_: ClassTag[_])) - .expects(keys, implicitly[ClassTag[T]]) + (async + .getAllKeys[T](_: Iterable[String])) + .expects(keys) .returning(Future.successful(values)) .once() } def setValue[T](key: String, value: T, duration: Duration): Future[Unit] = Future.successful { - (async.set(_: String, _: Any, _: Duration)) + (async + .set(_: String, _: Any, _: Duration)) .expects(key, if (Option(value).isEmpty) * else value, duration) .returning(Future.successful(Done)) .once() } - def setClassTag[T: ClassTag](key: String, value: T, duration: Duration): Future[Unit] = + def setClassTag[T](key: String, value: T, duration: Duration): Future[Unit] = setValue(classTagKey(key), value, duration) - def set[T: ClassTag](key: String, value: T, duration: Duration): Future[Unit] = + def set[T](key: String, value: T, duration: Duration): Future[Unit] = for { _ <- setValue(key, value, duration) _ <- setClassTag(key, classTagValue(value), duration) } yield () - def setValueIfNotExists[T: ClassTag](key: String, value: T, duration: Duration, exists: Boolean): Future[Unit] = + def setValueIfNotExists[T](key: String, value: T, duration: Duration, exists: Boolean): Future[Unit] = Future.successful { - (async.setIfNotExists(_: String, _: Any, _: Duration)) + (async + .setIfNotExists(_: String, _: Any, _: Duration)) .expects(key, if (Option(value).isEmpty) * else value, duration) .returning(Future.successful(exists)) .once() } - def setClassTagIfNotExists[T: ClassTag](key: String, value: T, duration: Duration, exists: Boolean): Future[Unit] = + def setClassTagIfNotExists[T](key: String, value: T, duration: Duration, exists: Boolean): Future[Unit] = setValueIfNotExists(classTagKey(key), classTagValue(value), duration, exists) - def setIfNotExists[T: ClassTag](key: String, value: T, duration: Duration, exists: Boolean): Future[Unit] = + def setIfNotExists[T](key: String, value: T, duration: Duration, exists: Boolean): Future[Unit] = for { _ <- setValueIfNotExists(key, value, duration, exists) _ <- setClassTagIfNotExists(key, value, duration, exists) } yield () - def setAll[T: ClassTag](values: (String, Any)*): Future[Unit] = + def setAll(values: (String, Any)*): Future[Unit] = Future.successful { - val valuesWithClassTags = values.flatMap { - case (k, v) => Seq((k, v), (classTagKey(k), classTagValue(v))) + val valuesWithClassTags = values.flatMap { case (k, v) => + Seq((k, v), (classTagKey(k), classTagValue(v))) } (async.setAll _) .expects(valuesWithClassTags) @@ -102,10 +108,10 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => .once() } - def setAllIfNotExist[T: ClassTag](values: Seq[(String, Any)], exists: Boolean): Future[Unit] = + def setAllIfNotExist(values: Seq[(String, Any)], exists: Boolean): Future[Unit] = Future.successful { - val valuesWithClassTags = values.flatMap { - case (k, v) => Seq((k, v), (classTagKey(k), classTagValue(v))) + val valuesWithClassTags = values.flatMap { case (k, v) => + Seq((k, v), (classTagKey(k), classTagValue(v))) } (async.setAllIfNotExist _) .expects(valuesWithClassTags) @@ -115,11 +121,13 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => def expire(key: String, duration: Duration): Future[Unit] = Future.successful { - (async.expire(_: String, _: Duration)) + (async + .expire(_: String, _: Duration)) .expects(classTagKey(key), duration) .returning(Future.successful(Done)) .once() - (async.expire(_: String, _: Duration)) + (async + .expire(_: String, _: Duration)) .expects(key, duration) .returning(Future.successful(Done)) .once() @@ -127,7 +135,8 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => def expiresIn(key: String, duration: Option[Duration]): Future[Unit] = Future.successful { - (async.expiresIn(_: String)) + (async + .expiresIn(_: String)) .expects(key) .returning(Future.successful(duration)) .once() @@ -135,15 +144,17 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => def matching(pattern: String, keys: Seq[String]): Future[Unit] = Future.successful { - (async.matching(_: String)) + (async + .matching(_: String)) .expects(pattern) .returning(Future.successful(keys)) .once() } def removeMatching(pattern: String): Future[Unit] = { - def removePattern(patternToRemove: String) = - (async.removeMatching(_: String)) + def removePattern(patternToRemove: String): Unit = + (async + .removeMatching(_: String)) .expects(patternToRemove) .returning(Future.successful(Done)) .once() @@ -156,7 +167,8 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => def exists(key: String, exists: Boolean): Future[Unit] = Future.successful { - (async.exists(_: String)) + (async + .exists(_: String)) .expects(key) .returning(Future.successful(exists)) .once() @@ -164,15 +176,17 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => def increment(key: String, by: Long, result: Long): Future[Unit] = Future.successful { - (async.increment(_: String, _: Long)) + (async + .increment(_: String, _: Long)) .expects(key, by) .returning(Future.successful(result)) .once() } - def decrement(key: String, by:Long, result:Long): Future[Unit] = + def decrement(key: String, by: Long, result: Long): Future[Unit] = Future.successful { - (async.decrement(_: String, _:Long)) + (async + .decrement(_: String, _: Long)) .expects(key, by) .returning(Future.successful(result)) .once() @@ -180,11 +194,13 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => def remove(key: String): Future[Unit] = Future.successful { - (async.remove(_: String)) + (async + .remove(_: String)) .expects(classTagKey(key)) .returning(Future.successful(Done)) .once() - (async.remove(_: String)) + (async + .remove(_: String)) .expects(key) .returning(Future.successful(Done)) .once() @@ -192,8 +208,8 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => def removeAll(keys: String*): Future[Unit] = Future.successful { - val keysWithClassTags = keys.flatMap { - key => Seq(key, classTagKey(key)) + val keysWithClassTags = keys.flatMap { key => + Seq(key, classTagKey(key)) } (async.removeAllKeys _) .expects(keysWithClassTags) @@ -203,7 +219,8 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => def append(key: String, value: String, expiration: Duration): Future[Unit] = Future.successful { - (async.append(_: String, _: String, _: Duration)) + (async + .append(_: String, _: String, _: Duration)) .expects(key, value, expiration) .returning(Future.successful(Done)) .once() @@ -219,7 +236,8 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => def list[T: ClassTag](key: String, mock: RedisList[T, AsynchronousResult]): Future[Unit] = Future.successful { - (async.list[T](_: String)(_: ClassTag[T])) + (async + .list[T](_: String)(_: ClassTag[T])) .expects(key, implicitly[ClassTag[T]]) .returning(mock) .once() @@ -227,7 +245,8 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => def set[T: ClassTag](key: String, mock: RedisSet[T, AsynchronousResult]): Future[Unit] = Future.successful { - (async.set[T](_: String)(_: ClassTag[T])) + (async + .set[T](_: String)(_: ClassTag[T])) .expects(key, implicitly[ClassTag[T]]) .returning(mock) .once() @@ -235,10 +254,13 @@ private[impl] trait AsyncRedisMock { this: AsyncMockFactoryBase => def map[T: ClassTag](key: String, mock: RedisMap[T, AsynchronousResult]): Future[Unit] = Future.successful { - (async.map[T](_: String)(_: ClassTag[T])) + (async + .map[T](_: String)(_: ClassTag[T])) .expects(key, implicitly[ClassTag[T]]) .returning(mock) .once() } + } -} \ No newline at end of file + +} diff --git a/src/test/scala/play/api/cache/redis/impl/AsyncRedisSpec.scala b/src/test/scala/play/api/cache/redis/impl/AsyncRedisSpec.scala index d32a6111..94300344 100644 --- a/src/test/scala/play/api/cache/redis/impl/AsyncRedisSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/AsyncRedisSpec.scala @@ -1,11 +1,10 @@ package play.api.cache.redis.impl -import scala.concurrent.duration._ import play.api.cache.redis._ import play.api.cache.redis.test._ import scala.concurrent.Future - +import scala.concurrent.duration._ class AsyncRedisSpec extends AsyncUnitSpec with RedisConnectorMock with RedisRuntimeMock with ImplicitFutureMaterialization { import Helpers._ @@ -59,16 +58,18 @@ class AsyncRedisSpec extends AsyncUnitSpec with RedisConnectorMock with RedisRun _ <- connector.expect.get[String](cacheKey, None) _ <- connector.expect.get[String](cacheKey, None) _ <- connector.expect.set(cacheKey, cacheValue, Duration.Inf, result = true) - orElse = probe.orElse.generic( - Future.failed(SimulatedException.asRedis), - Future.successful(cacheValue), - ) + orElse = probe + .orElse + .generic( + Future.failed(SimulatedException.asRedis), + Future.successful(cacheValue), + ) _ <- cache.getOrElseUpdate[String](cacheKey)(orElse.execute()).assertingEqual(cacheValue) _ = orElse.calls mustEqual 2 } yield Passed } - private def test(name: String, policy: RecoveryPolicy = recoveryPolicy.default)(f: (RedisConnectorMock, AsyncRedis) => Future[Assertion]): Unit = { + private def test(name: String, policy: RecoveryPolicy = recoveryPolicy.default)(f: (RedisConnectorMock, AsyncRedis) => Future[Assertion]): Unit = name in { implicit val runtime: RedisRuntime = redisRuntime( invocationPolicy = LazyInvocation, @@ -79,5 +80,5 @@ class AsyncRedisSpec extends AsyncUnitSpec with RedisConnectorMock with RedisRun f(connector, cache) } - } + } diff --git a/src/test/scala/play/api/cache/redis/impl/BuildersSpec.scala b/src/test/scala/play/api/cache/redis/impl/BuildersSpec.scala index e3725e81..df5b8cf0 100644 --- a/src/test/scala/play/api/cache/redis/impl/BuildersSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/BuildersSpec.scala @@ -11,8 +11,8 @@ class BuildersSpec extends AsyncUnitSpec with RedisRuntimeMock { import Builders._ private case class Task( - response: String, - execution: () => Future[String], + response: String, + execution: () => Future[String], ) extends (() => Future[String]) { def this(response: String)(f: String => Future[String]) = @@ -36,25 +36,25 @@ class BuildersSpec extends AsyncUnitSpec with RedisRuntimeMock { "run regular task" in { implicit val runtime: RedisRuntime = redisRuntime() - AsynchronousBuilder.toResult(Task.regular(), Task.resolved()).assertingEqual(Task.regular.response) + AsynchronousBuilder.toResult(Task.regular(), Task.resolved()).assertingEqual(Task.regular.response) } "run resolved task" in { implicit val runtime: RedisRuntime = redisRuntime() - AsynchronousBuilder.toResult(Task.resolved(),Task.regular()).assertingEqual(Task.resolved.response) + AsynchronousBuilder.toResult(Task.resolved(), Task.regular()).assertingEqual(Task.resolved.response) } "recover with default policy" in { implicit val runtime: RedisRuntime = redisRuntime(recoveryPolicy = recoveryPolicy.default) - AsynchronousBuilder.toResult(Task.failing(),Task.resolved()).assertingEqual(Task.resolved.response) + AsynchronousBuilder.toResult(Task.failing(), Task.resolved()).assertingEqual(Task.resolved.response) } - "recover with fail through policy" in { + "recover with fail through policy" in { implicit val runtime: RedisRuntime = redisRuntime(recoveryPolicy = recoveryPolicy.failThrough) - AsynchronousBuilder.toResult(Task.failing(),Task.resolved()).assertingFailure[TimeoutException] + AsynchronousBuilder.toResult(Task.failing(), Task.resolved()).assertingFailure[TimeoutException] } - "map value" in { + "map value" in { implicit val runtime: RedisRuntime = redisRuntime(recoveryPolicy = recoveryPolicy.failThrough) AsynchronousBuilder.map(Future(5))(_ + 5).assertingEqual(10) } @@ -66,22 +66,22 @@ class BuildersSpec extends AsyncUnitSpec with RedisRuntimeMock { SynchronousBuilder.name mustEqual "SynchronousBuilder" } - "run regular task" in { + "run regular task" in { implicit val runtime: RedisRuntime = redisRuntime() SynchronousBuilder.toResult(Task.regular(), Task.resolved()) mustEqual Task.regular.response } - "run resolved task" in { + "run resolved task" in { implicit val runtime: RedisRuntime = redisRuntime() SynchronousBuilder.toResult(Task.resolved(), Task.regular()) mustEqual Task.resolved.response } - "recover from failure with default policy" in { + "recover from failure with default policy" in { implicit val runtime: RedisRuntime = redisRuntime(recoveryPolicy = recoveryPolicy.default) SynchronousBuilder.toResult(Task.failing(), Task.resolved()) mustEqual Task.resolved.response } - "don't recover from failure with fail through policy" in { + "don't recover from failure with fail through policy" in { implicit val runtime: RedisRuntime = redisRuntime(recoveryPolicy = recoveryPolicy.failThrough) assertThrows[TimeoutException] { SynchronousBuilder.toResult(Task.failing(), Task.resolved()) @@ -91,30 +91,31 @@ class BuildersSpec extends AsyncUnitSpec with RedisRuntimeMock { "don't recover on timeout due to long running task with fail through policy" in { implicit val runtime: RedisRuntime = redisRuntime( recoveryPolicy = recoveryPolicy.failThrough, - timeout = 1.millis + timeout = 1.millis, ) assertThrows[TimeoutException] { SynchronousBuilder.toResult(Task.infinite(), Task.resolved()) } } - "recover from timeout due to long running task with default policy" in { + "recover from timeout due to long running task with default policy" in { implicit val runtime: RedisRuntime = redisRuntime( recoveryPolicy = recoveryPolicy.default, - timeout = 1.millis + timeout = 1.millis, ) SynchronousBuilder.toResult(Task.infinite(), Task.resolved()) mustEqual Task.resolved.response } - "recover from akka ask timeout" in { + "recover from akka ask timeout" in { implicit val runtime: RedisRuntime = redisRuntime(recoveryPolicy = recoveryPolicy.default) val actorFailure = Future.failed(new AskTimeoutException("Simulated actor ask timeout")) SynchronousBuilder.toResult(actorFailure, Task.resolved()) mustEqual Task.resolved.response } - "map value" in { + "map value" in { implicit val runtime: RedisRuntime = redisRuntime() SynchronousBuilder.map(5)(_ + 5) mustEqual 10 } } + } diff --git a/src/test/scala/play/api/cache/redis/impl/InvocationPolicySpec.scala b/src/test/scala/play/api/cache/redis/impl/InvocationPolicySpec.scala index 1595ad5e..aecf706f 100644 --- a/src/test/scala/play/api/cache/redis/impl/InvocationPolicySpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/InvocationPolicySpec.scala @@ -8,7 +8,7 @@ import scala.util.Success class InvocationPolicySpec extends UnitSpec { - private implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.parasitic + implicit private val ec: ExecutionContext = scala.concurrent.ExecutionContext.parasitic private class Probe { private val promise = Promise[Unit]() @@ -31,4 +31,5 @@ class InvocationPolicySpec extends UnitSpec { probe.resolve() outcome.isCompleted mustEqual true } + } diff --git a/src/test/scala/play/api/cache/redis/impl/RedisCacheSpec.scala b/src/test/scala/play/api/cache/redis/impl/RedisCacheSpec.scala index 9c71c56c..02aa431b 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisCacheSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisCacheSpec.scala @@ -6,7 +6,7 @@ import play.api.cache.redis.test._ import scala.concurrent.Future import scala.concurrent.duration.Duration -class RedisCacheSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnectorMock with ImplicitFutureMaterialization { +class RedisCacheSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnectorMock with ImplicitFutureMaterialization { import Helpers._ test("get and miss") { (cache, connector) => @@ -51,12 +51,12 @@ class RedisCacheSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConn } yield Passed } - test("set") { (cache, connector) => - for { - _ <- connector.expect.set(cacheKey, cacheValue, result = true) - _ <- cache.set(cacheKey, cacheValue).assertingDone - } yield Passed - } + test("set") { (cache, connector) => + for { + _ <- connector.expect.set(cacheKey, cacheValue, result = true) + _ <- cache.set(cacheKey, cacheValue).assertingDone + } yield Passed + } test("set recover with default") { (cache, connector) => for { @@ -67,7 +67,7 @@ class RedisCacheSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConn test("set if not exists (exists)") { (cache, connector) => for { - _ <- connector.expect.set(cacheKey, cacheValue, setIfNotExists=true,result = false) + _ <- connector.expect.set(cacheKey, cacheValue, setIfNotExists = true, result = false) _ <- cache.setIfNotExists(cacheKey, cacheValue).assertingEqual(false) } yield Passed } @@ -208,7 +208,7 @@ class RedisCacheSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConn test("matching with a prefix", prefix = Some("the-prefix")) { (cache, connector) => for { - _ <- connector.expect.matching(s"the-prefix:pattern", result = Seq(s"the-prefix:$cacheKey")) + _ <- connector.expect.matching("the-prefix:pattern", result = Seq(s"the-prefix:$cacheKey")) _ <- cache.matching("pattern").assertingEqual(Seq(cacheKey)) } yield Passed } @@ -417,8 +417,8 @@ class RedisCacheSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConn policy: RecoveryPolicy = recoveryPolicy.default, prefix: Option[String] = None, )( - f: (RedisCache[AsynchronousResult], RedisConnectorMock) => Future[Assertion] - ): Unit = { + f: (RedisCache[AsynchronousResult], RedisConnectorMock) => Future[Assertion], + ): Unit = name in { implicit val runtime: RedisRuntime = redisRuntime( invocationPolicy = LazyInvocation, @@ -429,5 +429,5 @@ class RedisCacheSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConn val cache: RedisCache[AsynchronousResult] = new RedisCache[AsynchronousResult](connector, Builders.AsynchronousBuilder) f(cache, connector) } - } - } + +} diff --git a/src/test/scala/play/api/cache/redis/impl/RedisConnectorMock.scala b/src/test/scala/play/api/cache/redis/impl/RedisConnectorMock.scala index 6fc2fa34..e0061bc2 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisConnectorMock.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisConnectorMock.scala @@ -17,7 +17,7 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def removeValues(keys: Seq[String]): Future[Unit] - override final def mGet[T: ClassTag](keys: String*): Future[Seq[Option[T]]] = + final override def mGet[T: ClassTag](keys: String*): Future[Seq[Option[T]]] = mGetKeys[T](keys) def mGetKeys[T: ClassTag](keys: Seq[String]): Future[Seq[Option[T]]] @@ -83,16 +83,19 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def hashGetAllValues[T: ClassTag](key: String): Future[Map[String, T]] } - final protected implicit class RedisConnectorExpectationOps(connector: RedisConnectorMock) { + implicit final protected class RedisConnectorExpectationOps(connector: RedisConnectorMock) { + def expect: RedisConnectorExpectation = new RedisConnectorExpectation(connector) + } - protected final class RedisConnectorExpectation(connector: RedisConnectorMock) { + final protected class RedisConnectorExpectation(connector: RedisConnectorMock) { def get[T: ClassTag](key: String, result: Try[Option[T]]): Future[Unit] = Future.successful { - (connector.get(_: String)(_: ClassTag[_])) + (connector + .get(_: String)(_: ClassTag[?])) .expects(key, implicitly[ClassTag[T]]) .returning(Future.fromTry(result)) .once() @@ -106,7 +109,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def mGet[T: ClassTag](keys: Seq[String], result: Future[Seq[Option[T]]]): Future[Unit] = Future.successful { - (connector.mGetKeys(_: Seq[String])(_: ClassTag[_])) + (connector + .mGetKeys(_: Seq[String])(_: ClassTag[?])) .expects(keys, implicitly[ClassTag[T]]) .returning(result) .once() @@ -114,15 +118,17 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def set[T](key: String, value: T, duration: Duration = Duration.Inf, setIfNotExists: Boolean = false, result: Future[Boolean]): Future[Unit] = Future.successful { - (connector.set(_: String, _: Any, _: Duration, _: Boolean)) + (connector + .set(_: String, _: Any, _: Duration, _: Boolean)) .expects(key, if (Option(value).isEmpty) * else value, duration, setIfNotExists) .returning(result) .once() } - def mSet(keyValues: Seq[(String, Any)], result: Future[Unit]=Future.unit): Future[Unit] = + def mSet(keyValues: Seq[(String, Any)], result: Future[Unit] = Future.unit): Future[Unit] = Future.successful { - (connector.mSetValues(_: Seq[(String, Any)])) + (connector + .mSetValues(_: Seq[(String, Any)])) .expects(keyValues) .returning(result) .once() @@ -130,7 +136,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def mSetIfNotExist(keyValues: Seq[(String, Any)], result: Future[Boolean]): Future[Unit] = Future.successful { - (connector.mSetIfNotExistValues(_: Seq[(String, Any)])) + (connector + .mSetIfNotExistValues(_: Seq[(String, Any)])) .expects(keyValues) .returning(result) .once() @@ -138,7 +145,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def expire(key: String, duration: Duration, result: Future[Unit] = Future.unit): Future[Unit] = Future.successful { - (connector.expire(_: String, _: Duration)) + (connector + .expire(_: String, _: Duration)) .expects(key, duration) .returning(result) .once() @@ -146,7 +154,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def expiresIn(key: String, result: Future[Option[Duration]]): Future[Unit] = Future.successful { - (connector.expiresIn(_: String)) + (connector + .expiresIn(_: String)) .expects(key) .returning(result) .once() @@ -157,7 +166,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def remove(keys: Seq[String], result: Future[Unit]): Future[Unit] = Future.successful { - (connector.removeValues(_: Seq[String])) + (connector + .removeValues(_: Seq[String])) .expects(keys) .returning(result) .once() @@ -173,7 +183,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def exists(key: String, result: Future[Boolean]): Future[Unit] = Future.successful { - (connector.exists(_: String)) + (connector + .exists(_: String)) .expects(key) .returning(result) .once() @@ -181,7 +192,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def increment(key: String, by: Long, result: Future[Long]): Future[Unit] = Future.successful { - (connector.increment(_: String, _: Long)) + (connector + .increment(_: String, _: Long)) .expects(key, by) .returning(result) .once() @@ -189,7 +201,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def append(key: String, value: String, result: Future[Long]): Future[Unit] = Future.successful { - (connector.append(_: String, _: String)) + (connector + .append(_: String, _: String)) .expects(key, value) .returning(result) .once() @@ -197,39 +210,44 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def matching(pattern: String, result: Future[Seq[String]]): Future[Unit] = Future.successful { - (connector.matching(_: String)) + (connector + .matching(_: String)) .expects(pattern) .returning(result) .once() } - def listPrepend(key: String, values: Seq[String], result: Future[Long]= Future.successful(5L)): Future[Unit] = + def listPrepend(key: String, values: Seq[String], result: Future[Long] = Future.successful(5L)): Future[Unit] = Future.successful { - (connector.listPrependValues(_: String, _: Seq[Any])) + (connector + .listPrependValues(_: String, _: Seq[Any])) .expects(key, values) .returning(result) .once() } - def listAppend[T:ClassTag](key: String, values: Seq[T], result: Future[Long] = Future.successful(5L)): Future[Unit] = + def listAppend[T](key: String, values: Seq[T], result: Future[Long] = Future.successful(5L)): Future[Unit] = Future.successful { - (connector.listAppendValues(_: String, _: Seq[Any])) + (connector + .listAppendValues(_: String, _: Seq[Any])) .expects(key, values) .returning(result) .once() } - def listSlice[T: ClassTag](key: String, start: Int, end: Int, result: Future[Seq[T]]): Future[Unit] = + def listSlice[T: ClassTag](key: String, start: Long, end: Long, result: Future[Seq[T]]): Future[Unit] = Future.successful { - (connector.listSlice(_: String, _: Int, _: Int)(_: ClassTag[_])) + (connector + .listSlice(_: String, _: Long, _: Long)(_: ClassTag[?])) .expects(key, start, end, implicitly[ClassTag[T]]) .returning(result) .once() } - def listHeadPop[T:ClassTag](key: String, result: Future[Option[T]]): Future[Unit] = + def listHeadPop[T: ClassTag](key: String, result: Future[Option[T]]): Future[Unit] = Future.successful { - (connector.listHeadPop(_: String)(_: ClassTag[_])) + (connector + .listHeadPop(_: String)(_: ClassTag[?])) .expects(key, implicitly[ClassTag[T]]) .returning(result) .once() @@ -237,7 +255,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def listSize(key: String, result: Future[Long]): Future[Unit] = Future.successful { - (connector.listSize(_: String)) + (connector + .listSize(_: String)) .expects(key) .returning(result) .once() @@ -245,43 +264,49 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def listInsert(key: String, pivot: String, value: String, result: Future[Option[Long]]): Future[Unit] = Future.successful { - (connector.listInsert(_: String, _: String, _: Any)) + (connector + .listInsert(_: String, _: String, _: Any)) .expects(key, pivot, value) .returning(result) .once() } - def listSetAt(key: String, index: Int, value: String, result: Future[Unit]): Future[Unit] = + def listSetAt(key: String, index: Long, value: String, result: Future[Unit]): Future[Unit] = Future.successful { - (connector.listSetAt(_: String, _: Int, _: Any)) + (connector + .listSetAt(_: String, _: Long, _: Any)) .expects(key, index, value) .returning(result) .once() } - def listRemove(key: String, value: String, count: Int, result: Future[Long]): Future[Unit] = + def listRemove(key: String, value: String, count: Long, result: Future[Long]): Future[Unit] = Future.successful { - (connector.listRemove(_: String, _:Any, _: Int)) + (connector + .listRemove(_: String, _: Any, _: Long)) .expects(key, value, count) .returning(result) .once() } - def listRemoveAt(key: String, index: Int, result: Future[Long]): Future[Unit] = + def listRemoveAt(key: String, index: Long, result: Future[Long]): Future[Unit] = Future.successful { - (connector.listSetAt(_: String, _: Int, _: Any)) + (connector + .listSetAt(_: String, _: Long, _: Any)) .expects(key, index, "play-redis:DELETED") .returning(Future.unit) .once() - (connector.listRemove(_: String, _: Any, _: Int)) + (connector + .listRemove(_: String, _: Any, _: Long)) .expects(key, "play-redis:DELETED", 0) .returning(result) .once() } - def listTrim(key: String, start: Int, end: Int, result: Future[Unit] = Future.unit): Future[Unit] = + def listTrim(key: String, start: Long, end: Long, result: Future[Unit] = Future.unit): Future[Unit] = Future.successful { - (connector.listTrim(_: String, _: Int, _: Int)) + (connector + .listTrim(_: String, _: Long, _: Long)) .expects(key, start, end) .returning(result) .once() @@ -289,7 +314,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def setAdd(key: String, values: Seq[String], result: Future[Long] = Future.successful(5L)): Future[Unit] = Future.successful { - (connector.setAddValues(_: String, _: Seq[Any])) + (connector + .setAddValues(_: String, _: Seq[Any])) .expects(key, values) .returning(result) .once() @@ -297,7 +323,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def setIsMember(key: String, value: String, result: Future[Boolean]): Future[Unit] = Future.successful { - (connector.setIsMember(_: String, _: Any)) + (connector + .setIsMember(_: String, _: Any)) .expects(key, value) .returning(result) .once() @@ -305,7 +332,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def setRemove(key: String, values: Seq[String], result: Future[Long] = Future.successful(1L)): Future[Unit] = Future.successful { - (connector.setRemoveValues(_: String, _: Seq[Any])) + (connector + .setRemoveValues(_: String, _: Seq[Any])) .expects(key, values) .returning(result) .once() @@ -313,7 +341,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def setMembers[T: ClassTag](key: String, result: Future[Set[Any]]): Future[Unit] = Future.successful { - (connector.setMembers(_: String)(_: ClassTag[_])) + (connector + .setMembers(_: String)(_: ClassTag[?])) .expects(key, implicitly[ClassTag[T]]) .returning(result) .once() @@ -321,7 +350,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def setSize(key: String, result: Future[Long]): Future[Unit] = Future.successful { - (connector.setSize(_: String)) + (connector + .setSize(_: String)) .expects(key) .returning(result) .once() @@ -329,7 +359,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def sortedSetAdd(key: String, values: Seq[(Double, String)], result: Future[Long] = Future.successful(1L)): Future[Unit] = Future.successful { - (connector.sortedSetAddValues(_: String, _: Seq[(Double, Any)])) + (connector + .sortedSetAddValues(_: String, _: Seq[(Double, Any)])) .expects(key, values) .returning(result) .once() @@ -337,7 +368,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def sortedSetScore(key: String, value: String, result: Future[Option[Double]]): Future[Unit] = Future.successful { - (connector.sortedSetScore(_: String, _: Any)) + (connector + .sortedSetScore(_: String, _: Any)) .expects(key, value) .returning(result) .once() @@ -345,7 +377,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def sortedSetRemove(key: String, values: Seq[String], result: Future[Long] = Future.successful(1L)): Future[Unit] = Future.successful { - (connector.sortedSetRemoveValues(_: String, _: Seq[Any])) + (connector + .sortedSetRemoveValues(_: String, _: Seq[Any])) .expects(key, values) .returning(result) .once() @@ -353,7 +386,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def sortedSetRange[T: ClassTag](key: String, start: Long, end: Long, result: Future[Seq[String]]): Future[Unit] = Future.successful { - (connector.sortedSetRange(_: String, _: Long, _: Long)(_: ClassTag[_])) + (connector + .sortedSetRange(_: String, _: Long, _: Long)(_: ClassTag[?])) .expects(key, start, end, implicitly[ClassTag[T]]) .returning(result) .once() @@ -361,7 +395,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def sortedSetReverseRange[T: ClassTag](key: String, start: Long, end: Long, result: Future[Seq[String]]): Future[Unit] = Future.successful { - (connector.sortedSetReverseRange(_: String, _: Long, _: Long)(_: ClassTag[_])) + (connector + .sortedSetReverseRange(_: String, _: Long, _: Long)(_: ClassTag[?])) .expects(key, start, end, implicitly[ClassTag[T]]) .returning(result) .once() @@ -369,7 +404,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def sortedSetSize(key: String, result: Future[Long]): Future[Unit] = Future.successful { - (connector.sortedSetSize(_: String)) + (connector + .sortedSetSize(_: String)) .expects(key) .returning(result) .once() @@ -377,7 +413,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def hashSet(key: String, field: String, value: String, result: Future[Boolean]): Future[Unit] = Future.successful { - (connector.hashSet(_: String, _: String, _: Any)) + (connector + .hashSet(_: String, _: String, _: Any)) .expects(key, field, value) .returning(result) .once() @@ -385,7 +422,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def hashGet[T: ClassTag](key: String, field: String, result: Future[Option[T]]): Future[Unit] = Future.successful { - (connector.hashGetField(_: String, _: String)(_: ClassTag[_])) + (connector + .hashGetField(_: String, _: String)(_: ClassTag[?])) .expects(key, field, implicitly[ClassTag[T]]) .returning(result) .once() @@ -393,7 +431,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def hashGet[T: ClassTag](key: String, fields: Seq[String], result: Future[Seq[Option[T]]]): Future[Unit] = Future.successful { - (connector.hashGetFields(_: String, _: Seq[String])(_: ClassTag[_])) + (connector + .hashGetFields(_: String, _: Seq[String])(_: ClassTag[?])) .expects(key, fields, implicitly[ClassTag[T]]) .returning(result) .once() @@ -401,7 +440,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def hashExists(key: String, field: String, result: Future[Boolean]): Future[Unit] = Future.successful { - (connector.hashExists(_: String, _: String)) + (connector + .hashExists(_: String, _: String)) .expects(key, field) .returning(result) .once() @@ -409,7 +449,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def hashRemove(key: String, fields: Seq[String], result: Future[Long] = Future.successful(1L)): Future[Unit] = Future.successful { - (connector.hashRemoveValues(_: String, _: Seq[String])) + (connector + .hashRemoveValues(_: String, _: Seq[String])) .expects(key, fields) .returning(result) .once() @@ -417,7 +458,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def hashIncrement(key: String, field: String, by: Long, result: Future[Long]): Future[Unit] = Future.successful { - (connector.hashIncrement(_: String, _: String, _: Long)) + (connector + .hashIncrement(_: String, _: String, _: Long)) .expects(key, field, by) .returning(result) .once() @@ -425,7 +467,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def hashGetAll[T: ClassTag](key: String, result: Future[Map[String, T]]): Future[Unit] = Future.successful { - (connector.hashGetAllValues(_: String)(_: ClassTag[_])) + (connector + .hashGetAllValues(_: String)(_: ClassTag[?])) .expects(key, implicitly[ClassTag[T]]) .returning(result) .once() @@ -433,7 +476,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def hashKeys(key: String, result: Future[Set[String]]): Future[Unit] = Future.successful { - (connector.hashKeys(_: String)) + (connector + .hashKeys(_: String)) .expects(key) .returning(result) .once() @@ -441,7 +485,8 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def hashValues[T: ClassTag](key: String, result: Future[Set[T]]): Future[Unit] = Future.successful { - (connector.hashValues[T](_: String)(_: ClassTag[T])) + (connector + .hashValues[T](_: String)(_: ClassTag[T])) .expects(key, implicitly[ClassTag[T]]) .returning(result) .once() @@ -449,10 +494,13 @@ private[impl] trait RedisConnectorMock { this: AsyncMockFactoryBase => def hashSize(key: String, result: Future[Long]): Future[Unit] = Future.successful { - (connector.hashSize(_: String)) + (connector + .hashSize(_: String)) .expects(key) .returning(result) .once() } + } -} \ No newline at end of file + +} diff --git a/src/test/scala/play/api/cache/redis/impl/RedisJavaListSpec.scala b/src/test/scala/play/api/cache/redis/impl/RedisJavaListSpec.scala index 1ad2cb89..aa4f25fe 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisJavaListSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisJavaListSpec.scala @@ -201,10 +201,10 @@ class RedisJavaListSpec extends AsyncUnitSpec with RedisListJavaMock with RedisR private def test( name: String, - policy: RecoveryPolicy = recoveryPolicy.default + policy: RecoveryPolicy = recoveryPolicy.default, )( - f: (AsyncRedisList[String], RedisListMock) => Future[Assertion] - ): Unit = { + f: (AsyncRedisList[String], RedisListMock) => Future[Assertion], + ): Unit = name in { implicit val runtime: RedisRuntime = redisRuntime( invocationPolicy = LazyInvocation, @@ -220,5 +220,5 @@ class RedisJavaListSpec extends AsyncUnitSpec with RedisListJavaMock with RedisR f(list, internal) } - } + } diff --git a/src/test/scala/play/api/cache/redis/impl/RedisJavaMapSpec.scala b/src/test/scala/play/api/cache/redis/impl/RedisJavaMapSpec.scala index c860e31e..a7c60ce2 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisJavaMapSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisJavaMapSpec.scala @@ -75,10 +75,10 @@ class RedisJavaMapSpec extends AsyncUnitSpec with RedisMapJavaMock with RedisRun private def test( name: String, - policy: RecoveryPolicy = recoveryPolicy.default + policy: RecoveryPolicy = recoveryPolicy.default, )( - f: (AsyncRedisMap[String], RedisMapMock) => Future[Assertion] - ): Unit = { + f: (AsyncRedisMap[String], RedisMapMock) => Future[Assertion], + ): Unit = name in { implicit val runtime: RedisRuntime = redisRuntime( invocationPolicy = LazyInvocation, @@ -86,8 +86,8 @@ class RedisJavaMapSpec extends AsyncUnitSpec with RedisMapJavaMock with RedisRun ) val internal: RedisMapMock = mock[RedisMapMock] val map: AsyncRedisMap[String] = new RedisMapJavaImpl(internal) - + f(map, internal) } - } + } diff --git a/src/test/scala/play/api/cache/redis/impl/RedisJavaSetSpec.scala b/src/test/scala/play/api/cache/redis/impl/RedisJavaSetSpec.scala index bc836b44..5569e31a 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisJavaSetSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisJavaSetSpec.scala @@ -2,11 +2,10 @@ package play.api.cache.redis.impl import play.api.cache.redis._ import play.api.cache.redis.test._ -import play.cache.redis.{AsyncRedisList, AsyncRedisSet} +import play.cache.redis.AsyncRedisSet import scala.concurrent.Future import scala.jdk.CollectionConverters._ -import scala.jdk.OptionConverters._ class RedisJavaSetSpec extends AsyncUnitSpec with RedisSetJavaMock with RedisRuntimeMock { @@ -17,7 +16,6 @@ class RedisJavaSetSpec extends AsyncUnitSpec with RedisSetJavaMock with RedisRun } yield Passed } - test("contains") { (set, internal) => for { _ <- internal.expect.contains(cacheKey, result = true) @@ -41,10 +39,10 @@ class RedisJavaSetSpec extends AsyncUnitSpec with RedisSetJavaMock with RedisRun private def test( name: String, - policy: RecoveryPolicy = recoveryPolicy.default + policy: RecoveryPolicy = recoveryPolicy.default, )( - f: (AsyncRedisSet[String], RedisSetMock) => Future[Assertion] - ): Unit = { + f: (AsyncRedisSet[String], RedisSetMock) => Future[Assertion], + ): Unit = name in { implicit val runtime: RedisRuntime = redisRuntime( invocationPolicy = LazyInvocation, @@ -55,5 +53,5 @@ class RedisJavaSetSpec extends AsyncUnitSpec with RedisSetJavaMock with RedisRun f(set, internal) } - } + } diff --git a/src/test/scala/play/api/cache/redis/impl/RedisListJavaMock.scala b/src/test/scala/play/api/cache/redis/impl/RedisListJavaMock.scala index 55de8dc6..a86b5976 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisListJavaMock.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisListJavaMock.scala @@ -9,30 +9,34 @@ private[impl] trait RedisListJavaMock { this: AsyncMockFactoryBase => protected[impl] trait RedisListMock extends RedisList[String, Future] - final protected implicit class RedisListOps(list: RedisListMock) { + implicit final protected class RedisListOps(list: RedisListMock) { + def expect: RedisListExpectation = new RedisListExpectation(list) + } - protected final class RedisListExpectation(list: RedisListMock) { + final protected class RedisListExpectation(list: RedisListMock) { - def apply(index: Int, value: Option[String]): Future[Unit] = + def apply(index: Long, value: Option[String]): Future[Unit] = Future.successful { - (list.apply(_: Int)) + (list + .apply(_: Long)) .expects(index) .returning( value.fold[Future[String]]( - Future.failed(new NoSuchElementException()) + Future.failed(new NoSuchElementException()), )( - Future.successful - ) + Future.successful, + ), ) .once() } - def get(index: Int, value: Option[String]): Future[Unit] = + def get(index: Long, value: Option[String]): Future[Unit] = Future.successful { - (list.get(_: Int)) + (list + .get(_: Long)) .expects(index) .returning(Future.successful(value)) .once() @@ -40,7 +44,8 @@ private[impl] trait RedisListJavaMock { this: AsyncMockFactoryBase => def prepend(value: String): Future[Unit] = Future.successful { - (list.prepend(_: String)) + (list + .prepend(_: String)) .expects(value) .returning(Future.successful(list)) .once() @@ -48,7 +53,8 @@ private[impl] trait RedisListJavaMock { this: AsyncMockFactoryBase => def append(value: String): Future[Unit] = Future.successful { - (list.append(_: String)) + (list + .append(_: String)) .expects(value) .returning(Future.successful(list)) .once() @@ -64,31 +70,35 @@ private[impl] trait RedisListJavaMock { this: AsyncMockFactoryBase => def insertBefore(pivot: String, value: String, newSize: Option[Long]): Future[Unit] = Future.successful { - (list.insertBefore(_: String, _: String)) + (list + .insertBefore(_: String, _: String)) .expects(pivot, value) .returning(Future.successful(newSize)) .once() } - def set(index: Int, value: String): Future[Unit] = + def set(index: Long, value: String): Future[Unit] = Future.successful { - (list.set(_: Int, _: String)) + (list + .set(_: Long, _: String)) .expects(index, value) .returning(Future.successful(list)) .once() } - def remove(value: String, count: Int = 1): Future[Unit] = + def remove(value: String, count: Long = 1): Future[Unit] = Future.successful { - (list.remove(_: String, _: Int)) + (list + .remove(_: String, _: Long)) .expects(value, count) .returning(Future.successful(list)) .once() } - def removeAt(index: Int): Future[Unit] = + def removeAt(index: Long): Future[Unit] = Future.successful { - (list.removeAt(_: Int)) + (list + .removeAt(_: Long)) .expects(index) .returning(Future.successful(list)) .once() @@ -99,18 +109,21 @@ private[impl] trait RedisListJavaMock { this: AsyncMockFactoryBase => def modify: RedisListModificationExpectation = new RedisListModificationExpectation(list) } - protected final class RedisListViewExpectation(list: RedisListMock) { + final protected class RedisListViewExpectation(list: RedisListMock) { - def slice(from: Int, to: Int, value: List[String]): Future[Unit] = + def slice(from: Long, to: Long, value: List[String]): Future[Unit] = Future.successful { - (list.view.slice(_: Int, _: Int)) + (list + .view + .slice(_: Long, _: Long)) .expects(from, to) .returning(Future.successful(value)) .once() } + } - protected final class RedisListModificationExpectation(list: RedisListMock) { + final protected class RedisListModificationExpectation(list: RedisListMock) { def clear(): Future[Unit] = Future.successful { @@ -120,12 +133,16 @@ private[impl] trait RedisListJavaMock { this: AsyncMockFactoryBase => .once() } - def slice(from: Int, to: Int): Future[Unit] = + def slice(from: Long, to: Long): Future[Unit] = Future.successful { - (list.modify.slice(_: Int, _: Int)) + (list + .modify + .slice(_: Long, _: Long)) .expects(from, to) .returning(Future.successful(list.modify)) .once() } + } -} \ No newline at end of file + +} diff --git a/src/test/scala/play/api/cache/redis/impl/RedisListSpec.scala b/src/test/scala/play/api/cache/redis/impl/RedisListSpec.scala index 8e6b4182..dad3e597 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisListSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisListSpec.scala @@ -6,7 +6,7 @@ import play.api.cache.redis.test._ import scala.concurrent.Future -class RedisListSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnectorMock with ImplicitFutureMaterialization { +class RedisListSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnectorMock with ImplicitFutureMaterialization { test("prepend (all variants)") { (list, connector) => for { @@ -28,9 +28,9 @@ class RedisListSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConne test("append (all variants)") { (list, connector) => for { - _ <- connector.expect.listAppend(otherKey,Seq( cacheValue)) - _ <- connector.expect.listAppend(otherKey,Seq( cacheValue)) - _ <- connector.expect.listAppend(otherKey,Seq( cacheValue, cacheValue)) + _ <- connector.expect.listAppend(otherKey, Seq(cacheValue)) + _ <- connector.expect.listAppend(otherKey, Seq(cacheValue)) + _ <- connector.expect.listAppend(otherKey, Seq(cacheValue, cacheValue)) _ <- list.append(cacheValue).assertingEqual(list) _ <- (list :+ cacheValue).assertingEqual(list) _ <- (list :++ Seq(cacheValue, cacheValue)).assertingEqual(list) @@ -44,12 +44,12 @@ class RedisListSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConne } yield Passed } - test("get (miss)") { (list, connector) => - for { - _ <- connector.expect.listSlice(otherKey, 5, 5, Seq(cacheValue)) - _ <- list.get(5).assertingEqual(Some(cacheValue)) - } yield Passed - } + test("get (miss)") { (list, connector) => + for { + _ <- connector.expect.listSlice(otherKey, 5, 5, Seq(cacheValue)) + _ <- list.get(5).assertingEqual(Some(cacheValue)) + } yield Passed + } test("get (hit)") { (list, connector) => for { @@ -336,10 +336,10 @@ class RedisListSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConne private def test( name: String, - policy: RecoveryPolicy = recoveryPolicy.default + policy: RecoveryPolicy = recoveryPolicy.default, )( - f: (RedisList[String, AsynchronousResult], RedisConnectorMock) => Future[Assertion] - ): Unit = { + f: (RedisList[String, AsynchronousResult], RedisConnectorMock) => Future[Assertion], + ): Unit = name in { implicit val runtime: RedisRuntime = redisRuntime( invocationPolicy = LazyInvocation, @@ -350,5 +350,5 @@ class RedisListSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConne val list: RedisList[String, AsynchronousResult] = new RedisListImpl[String, AsynchronousResult](otherKey, connector) f(list, connector) } - } + } diff --git a/src/test/scala/play/api/cache/redis/impl/RedisMapJavaMock.scala b/src/test/scala/play/api/cache/redis/impl/RedisMapJavaMock.scala index 4e186387..c15095cc 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisMapJavaMock.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisMapJavaMock.scala @@ -9,22 +9,25 @@ private[impl] trait RedisMapJavaMock { this: AsyncMockFactoryBase => protected[impl] trait RedisMapMock extends RedisMap[String, Future] { - override final def remove(field: String*): Future[RedisMap[String, Future]] = + final override def remove(field: String*): Future[RedisMap[String, Future]] = removeValues(field) def removeValues(field: Seq[String]): Future[RedisMap[String, Future]] } - final protected implicit class RedisMapOps(map: RedisMapMock) { + implicit final protected class RedisMapOps(map: RedisMapMock) { + def expect: RedisMapExpectation = new RedisMapExpectation(map) + } - protected final class RedisMapExpectation(map: RedisMapMock) { + final protected class RedisMapExpectation(map: RedisMapMock) { def add(key: String, value: String): Future[Unit] = Future.successful { - (map.add(_: String, _: String)) + (map + .add(_: String, _: String)) .expects(key, value) .returning(Future.successful(map)) .once() @@ -32,7 +35,8 @@ private[impl] trait RedisMapJavaMock { this: AsyncMockFactoryBase => def get(key: String, value: Option[String]): Future[Unit] = Future.successful { - (map.get(_: String)) + (map + .get(_: String)) .expects(key) .returning(Future.successful(value)) .once() @@ -40,7 +44,8 @@ private[impl] trait RedisMapJavaMock { this: AsyncMockFactoryBase => def contains(key: String, result: Boolean): Future[Unit] = Future.successful { - (map.contains(_: String)) + (map + .contains(_: String)) .expects(key) .returning(Future.successful(result)) .once() @@ -48,7 +53,8 @@ private[impl] trait RedisMapJavaMock { this: AsyncMockFactoryBase => def remove(key: String*): Future[Unit] = Future.successful { - (map.removeValues(_: Seq[String])) + (map + .removeValues(_: Seq[String])) .expects(key) .returning(Future.successful(map)) .once() @@ -56,7 +62,8 @@ private[impl] trait RedisMapJavaMock { this: AsyncMockFactoryBase => def increment(key: String, by: Long, result: Long): Future[Unit] = Future.successful { - (map.increment(_: String, _: Long)) + (map + .increment(_: String, _: Long)) .expects(key, by) .returning(Future.successful(result)) .once() @@ -85,5 +92,7 @@ private[impl] trait RedisMapJavaMock { this: AsyncMockFactoryBase => .returning(Future.successful(values.toSet)) .once() } + } -} \ No newline at end of file + +} diff --git a/src/test/scala/play/api/cache/redis/impl/RedisMapSpec.scala b/src/test/scala/play/api/cache/redis/impl/RedisMapSpec.scala index fdbbc50c..4d658b9e 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisMapSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisMapSpec.scala @@ -6,7 +6,7 @@ import play.api.cache.redis.test._ import scala.concurrent.Future -class RedisMapSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnectorMock with ImplicitFutureMaterialization { +class RedisMapSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnectorMock with ImplicitFutureMaterialization { test("set") { (map, connector) => for { @@ -187,10 +187,10 @@ class RedisMapSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnec private def test( name: String, - policy: RecoveryPolicy = recoveryPolicy.default + policy: RecoveryPolicy = recoveryPolicy.default, )( - f: (RedisMap[String, AsynchronousResult], RedisConnectorMock) => Future[Assertion] - ): Unit = { + f: (RedisMap[String, AsynchronousResult], RedisConnectorMock) => Future[Assertion], + ): Unit = name in { implicit val runtime: RedisRuntime = redisRuntime( invocationPolicy = LazyInvocation, @@ -201,5 +201,5 @@ class RedisMapSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnec val map: RedisMap[String, AsynchronousResult] = new RedisMapImpl[String, AsynchronousResult](cacheKey, connector) f(map, connector) } - } - } + +} diff --git a/src/test/scala/play/api/cache/redis/impl/RedisPrefixSpec.scala b/src/test/scala/play/api/cache/redis/impl/RedisPrefixSpec.scala index e0ecc5cb..599f54e6 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisPrefixSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisPrefixSpec.scala @@ -29,16 +29,16 @@ class RedisPrefixSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConn } private def test( - name: String, - prefix: RedisPrefix - )( - f: (RedisConnectorMock, AsyncRedis) => Future[Assertion] - ): Unit = { + name: String, + prefix: RedisPrefix, + )( + f: (RedisConnectorMock, AsyncRedis) => Future[Assertion], + ): Unit = name in { implicit val runtime: RedisRuntime = redisRuntime(prefix = prefix) val connector = mock[RedisConnectorMock] val cache: AsyncRedis = new AsyncRedisImpl(connector) f(connector, cache) } - } + } diff --git a/src/test/scala/play/api/cache/redis/impl/RedisRuntimeMock.scala b/src/test/scala/play/api/cache/redis/impl/RedisRuntimeMock.scala index 0bc693e9..4f44ac39 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisRuntimeMock.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisRuntimeMock.scala @@ -4,19 +4,21 @@ import akka.util.Timeout import org.scalamock.scalatest.AsyncMockFactoryBase import play.api.cache.redis.{FailThrough, RecoverWithDefault, RecoveryPolicy, RedisException} -import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.duration._ +import scala.concurrent.{ExecutionContext, Future} private[impl] trait RedisRuntimeMock { outer: AsyncMockFactoryBase => protected object recoveryPolicy { private class RerunPolicy extends RecoveryPolicy { + override def recoverFrom[T]( - rerun: => Future[T], - default: => Future[T], - failure: RedisException - ): Future[T] = rerun + rerun: => Future[T], + default: => Future[T], + failure: RedisException, + ): Future[T] = rerun + } val failThrough: RecoveryPolicy = new FailThrough {} @@ -38,4 +40,5 @@ private[impl] trait RedisRuntimeMock { outer: AsyncMockFactoryBase => (() => runtime.timeout).expects().returns(Timeout(timeout)).anyNumberOfTimes() runtime } + } diff --git a/src/test/scala/play/api/cache/redis/impl/RedisRuntimeSpec.scala b/src/test/scala/play/api/cache/redis/impl/RedisRuntimeSpec.scala index bd2aef9c..8a902088 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisRuntimeSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisRuntimeSpec.scala @@ -9,22 +9,22 @@ import play.api.cache.redis.test.UnitSpec class RedisRuntimeSpec extends UnitSpec { import RedisRuntime._ - private implicit val recoveryResolver: RecoveryPolicyResolver = + implicit private val recoveryResolver: RecoveryPolicyResolver = new RecoveryPolicyResolverImpl - private implicit val system: ActorSystem = ActorSystem("test") + implicit private val system: ActorSystem = ActorSystem("test") "be build from config (A)" in { val instance = RedisStandalone( name = "standalone", host = RedisHost(localhost, defaultPort), - settings = defaultsSettings + settings = defaultsSettings, ) val runtime = RedisRuntime( instance = instance, recovery = "log-and-fail", invocation = "eager", - prefix = None + prefix = None, ) runtime.timeout mustEqual Timeout(instance.timeout.sync) runtime.policy mustBe a[LogAndFailPolicy] @@ -32,36 +32,37 @@ class RedisRuntimeSpec extends UnitSpec { runtime.prefix mustEqual RedisEmptyPrefix } - "be build from config (B)" in { - val instance = RedisStandalone( - name = "standalone", - host = RedisHost(localhost, defaultPort), - settings = defaultsSettings - ) - val runtime = RedisRuntime( + "be build from config (B)" in { + val instance = RedisStandalone( + name = "standalone", + host = RedisHost(localhost, defaultPort), + settings = defaultsSettings, + ) + val runtime = RedisRuntime( + instance = instance, + recovery = "log-and-default", + invocation = "lazy", + prefix = Some("prefix"), + ) + runtime.policy mustBe a[LogAndDefaultPolicy] + runtime.invocation mustEqual LazyInvocation + runtime.prefix mustEqual new RedisPrefixImpl("prefix") + } + + "be build from config (C)" in { + val instance = RedisStandalone( + name = "standalone", + host = RedisHost(localhost, defaultPort), + settings = defaultsSettings, + ) + assertThrows[IllegalArgumentException] { + RedisRuntime( instance = instance, recovery = "log-and-default", - invocation = "lazy", - prefix = Some("prefix") + invocation = "other", + prefix = Some("prefix"), ) - runtime.policy mustBe a[LogAndDefaultPolicy] - runtime.invocation mustEqual LazyInvocation - runtime.prefix mustEqual new RedisPrefixImpl("prefix") } + } - "be build from config (C)" in { - val instance = RedisStandalone( - name = "standalone", - host = RedisHost(localhost, defaultPort), - settings = defaultsSettings - ) - assertThrows[IllegalArgumentException] { - RedisRuntime( - instance = instance, - recovery = "log-and-default", - invocation = "other", - prefix = Some("prefix") - ) - } - } } diff --git a/src/test/scala/play/api/cache/redis/impl/RedisSetJavaMock.scala b/src/test/scala/play/api/cache/redis/impl/RedisSetJavaMock.scala index 728e69aa..e524966d 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisSetJavaMock.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisSetJavaMock.scala @@ -9,27 +9,30 @@ private[impl] trait RedisSetJavaMock { this: AsyncMockFactoryBase => protected[impl] trait RedisSetMock extends RedisSet[String, Future] { - override final def add(values: String*): Future[RedisSet[String, Future]] = + final override def add(values: String*): Future[RedisSet[String, Future]] = addValues(values) def addValues(value: Seq[String]): Future[RedisSet[String, Future]] - override final def remove(values: String*): Future[RedisSet[String, Future]] = + final override def remove(values: String*): Future[RedisSet[String, Future]] = removeValues(values) def removeValues(value: Seq[String]): Future[RedisSet[String, Future]] } - final protected implicit class RedisSetOps(set: RedisSetMock) { + implicit final protected class RedisSetOps(set: RedisSetMock) { + def expect: RedisSetExpectation = new RedisSetExpectation(set) + } - protected final class RedisSetExpectation(set: RedisSetMock) { + final protected class RedisSetExpectation(set: RedisSetMock) { def add(value: String*): Future[Unit] = Future.successful { - (set.addValues(_: Seq[String])) + (set + .addValues(_: Seq[String])) .expects(value) .returning(Future.successful(set)) .once() @@ -37,7 +40,8 @@ private[impl] trait RedisSetJavaMock { this: AsyncMockFactoryBase => def contains(value: String, result: Boolean): Future[Unit] = Future.successful { - (set.contains(_: String)) + (set + .contains(_: String)) .expects(value) .returning(Future.successful(result)) .once() @@ -45,7 +49,8 @@ private[impl] trait RedisSetJavaMock { this: AsyncMockFactoryBase => def remove(value: String*): Future[Unit] = Future.successful { - (set.removeValues(_: Seq[String])) + (set + .removeValues(_: Seq[String])) .expects(value) .returning(Future.successful(set)) .once() @@ -58,5 +63,7 @@ private[impl] trait RedisSetJavaMock { this: AsyncMockFactoryBase => .returning(Future.successful(values.toSet)) .once() } + } -} \ No newline at end of file + +} diff --git a/src/test/scala/play/api/cache/redis/impl/RedisSetSpec.scala b/src/test/scala/play/api/cache/redis/impl/RedisSetSpec.scala index add20ac6..c732141b 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisSetSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisSetSpec.scala @@ -6,7 +6,7 @@ import play.api.cache.redis.test._ import scala.concurrent.Future -class RedisSetSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnectorMock with ImplicitFutureMaterialization { +class RedisSetSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnectorMock with ImplicitFutureMaterialization { test("add") { (set, connector) => for { @@ -113,10 +113,10 @@ class RedisSetSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnec private def test( name: String, - policy: RecoveryPolicy = recoveryPolicy.default + policy: RecoveryPolicy = recoveryPolicy.default, )( - f: (RedisSet[String, AsynchronousResult], RedisConnectorMock) => Future[Assertion] - ): Unit = { + f: (RedisSet[String, AsynchronousResult], RedisConnectorMock) => Future[Assertion], + ): Unit = name in { implicit val runtime: RedisRuntime = redisRuntime( invocationPolicy = LazyInvocation, @@ -127,5 +127,5 @@ class RedisSetSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnec val set: RedisSet[String, AsynchronousResult] = new RedisSetImpl[String, AsynchronousResult](cacheKey, connector) f(set, connector) } - } + } diff --git a/src/test/scala/play/api/cache/redis/impl/RedisSortedSetSpec.scala b/src/test/scala/play/api/cache/redis/impl/RedisSortedSetSpec.scala index 22cdbdb7..ae0c82e2 100644 --- a/src/test/scala/play/api/cache/redis/impl/RedisSortedSetSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/RedisSortedSetSpec.scala @@ -29,7 +29,7 @@ class RedisSortedSetSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisC test("contains (hit)") { (set, connector) => for { - _ <- connector.expect.sortedSetScore(cacheKey, otherValue, result = Some(1D)) + _ <- connector.expect.sortedSetScore(cacheKey, otherValue, result = Some(1d)) _ <- set.contains(otherValue).assertingEqual(true) } yield Passed } @@ -116,10 +116,10 @@ class RedisSortedSetSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisC private def test( name: String, - policy: RecoveryPolicy = recoveryPolicy.default + policy: RecoveryPolicy = recoveryPolicy.default, )( - f: (RedisSortedSet[String, AsynchronousResult], RedisConnectorMock) => Future[Assertion] - ): Unit = { + f: (RedisSortedSet[String, AsynchronousResult], RedisConnectorMock) => Future[Assertion], + ): Unit = name in { implicit val runtime: RedisRuntime = redisRuntime( invocationPolicy = LazyInvocation, @@ -130,5 +130,5 @@ class RedisSortedSetSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisC val set: RedisSortedSet[String, AsynchronousResult] = new RedisSortedSetImpl[String, AsynchronousResult](cacheKey, connector) f(set, connector) } - } - } + +} diff --git a/src/test/scala/play/api/cache/redis/impl/SyncRedisSpec.scala b/src/test/scala/play/api/cache/redis/impl/SyncRedisSpec.scala index abc7109b..c103ec7f 100644 --- a/src/test/scala/play/api/cache/redis/impl/SyncRedisSpec.scala +++ b/src/test/scala/play/api/cache/redis/impl/SyncRedisSpec.scala @@ -5,15 +5,15 @@ import play.api.cache.redis.test._ import scala.concurrent.Future -class SyncRedisSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnectorMock with ImplicitFutureMaterialization { +class SyncRedisSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConnectorMock with ImplicitFutureMaterialization { import Helpers._ test("get or else (hit)") { (cache, connector) => for { _ <- connector.expect.get[String](cacheKey, result = Some(cacheValue)) orElse = probe.orElse.const(otherValue) - _ = cache.getOrElse(cacheKey)(orElse.execute()) mustEqual cacheValue - _ = orElse.calls mustEqual 0 + _ = cache.getOrElse(cacheKey)(orElse.execute()) mustEqual cacheValue + _ = orElse.calls mustEqual 0 } yield Passed } @@ -22,8 +22,8 @@ class SyncRedisSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConne _ <- connector.expect.get[String](cacheKey, result = None) _ <- connector.expect.set(cacheKey, cacheValue, result = true) orElse = probe.orElse.const(cacheValue) - _ = cache.getOrElse(cacheKey)(orElse.execute()) mustEqual cacheValue - _ = orElse.calls mustEqual 1 + _ = cache.getOrElse(cacheKey)(orElse.execute()) mustEqual cacheValue + _ = orElse.calls mustEqual 1 } yield Passed } @@ -32,8 +32,8 @@ class SyncRedisSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConne _ <- connector.expect.get[String](cacheKey, result = failure) _ <- connector.expect.set(cacheKey, cacheValue, result = true) orElse = probe.orElse.const(cacheValue) - _ = cache.getOrElse(cacheKey)(orElse.execute()) mustEqual cacheValue - _ = orElse.calls mustEqual 1 + _ = cache.getOrElse(cacheKey)(orElse.execute()) mustEqual cacheValue + _ = orElse.calls mustEqual 1 } yield Passed } @@ -42,8 +42,8 @@ class SyncRedisSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConne _ <- connector.expect.get[String](cacheKey, result = None) _ <- connector.expect.set(cacheKey, cacheValue, result = failure) orElse = probe.orElse.const(cacheValue) - _ = cache.getOrElse(cacheKey)(orElse.execute()) mustEqual cacheValue - _ = orElse.calls mustEqual 1 + _ = cache.getOrElse(cacheKey)(orElse.execute()) mustEqual cacheValue + _ = orElse.calls mustEqual 1 } yield Passed } @@ -52,8 +52,8 @@ class SyncRedisSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConne _ <- connector.expect.get[String](cacheKey, result = failure) _ <- connector.expect.set(cacheKey, cacheValue, result = true) orElse = probe.orElse.const(cacheValue) - _ = cache.getOrElse(cacheKey)(orElse.execute()) mustEqual cacheValue - _ = orElse.calls mustEqual 1 + _ = cache.getOrElse(cacheKey)(orElse.execute()) mustEqual cacheValue + _ = orElse.calls mustEqual 1 } yield Passed } @@ -62,8 +62,8 @@ class SyncRedisSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConne _ <- connector.expect.get[String](s"the-prefix:$cacheKey", result = None) _ <- connector.expect.set(s"the-prefix:$cacheKey", cacheValue, result = true) orElse = probe.orElse.const(cacheValue) - _ = cache.getOrElse(cacheKey)(orElse.execute()) mustEqual cacheValue - _ = orElse.calls mustEqual 1 + _ = cache.getOrElse(cacheKey)(orElse.execute()) mustEqual cacheValue + _ = orElse.calls mustEqual 1 } yield Passed } @@ -72,8 +72,8 @@ class SyncRedisSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConne policy: RecoveryPolicy = recoveryPolicy.default, prefix: Option[String] = None, )( - f: (RedisCache[SynchronousResult], RedisConnectorMock) => Future[Assertion] - ): Unit = { + f: (RedisCache[SynchronousResult], RedisConnectorMock) => Future[Assertion], + ): Unit = name in { implicit val runtime: RedisRuntime = redisRuntime( invocationPolicy = LazyInvocation, @@ -84,5 +84,5 @@ class SyncRedisSpec extends AsyncUnitSpec with RedisRuntimeMock with RedisConne val cache: RedisCache[SynchronousResult] = new SyncRedis(connector) f(cache, connector) } - } - } + +} diff --git a/src/test/scala/play/api/cache/redis/test/BaseSpec.scala b/src/test/scala/play/api/cache/redis/test/BaseSpec.scala index aef52b13..00d7e189 100644 --- a/src/test/scala/play/api/cache/redis/test/BaseSpec.scala +++ b/src/test/scala/play/api/cache/redis/test/BaseSpec.scala @@ -3,53 +3,52 @@ package play.api.cache.redis.test import akka.Done import org.scalactic.source.Position import org.scalamock.scalatest.AsyncMockFactory +import org.scalatest._ import org.scalatest.matchers.must.Matchers import org.scalatest.wordspec.{AnyWordSpecLike, AsyncWordSpecLike} -import org.scalatest._ import play.api.cache.redis.RedisException import play.api.cache.redis.configuration._ import java.util.concurrent.{CompletionStage, TimeoutException} import scala.concurrent.duration._ import scala.concurrent.{Await, ExecutionContext, Future} -import scala.language.implicitConversions import scala.reflect.ClassTag import scala.util.{Failure, Success, Try} trait DefaultValues { - protected final val defaultCacheName: String = "play" - protected final val localhost = "localhost" - protected final val defaultPort: Int = 6379 - - val defaultsSettings: RedisSettingsTest = - RedisSettingsTest( - invocationContext = "akka.actor.default-dispatcher", - invocationPolicy = "lazy", - timeout = RedisTimeouts(1.second, None, Some(500.millis)), - recovery = "log-and-default", - source = "standalone" - ) + final protected val defaultCacheName: String = "play" + final protected val localhost = "localhost" + final protected val defaultPort: Int = 6379 + + val defaultsSettings: RedisSettingsTest = + RedisSettingsTest( + invocationContext = "akka.actor.default-dispatcher", + invocationPolicy = "lazy", + timeout = RedisTimeouts(1.second, None, Some(500.millis)), + recovery = "log-and-default", + source = "standalone", + ) - protected final val cacheKey: String = "cache-key" - protected final val cacheValue: String = "cache-value" - protected final val otherKey: String = "other-key" - protected final val otherValue: String = "other-value" - protected final val field: String = "field" + final protected val cacheKey: String = "cache-key" + final protected val cacheValue: String = "cache-value" + final protected val otherKey: String = "other-key" + final protected val otherValue: String = "other-value" + final protected val field: String = "field" - protected final val cacheExpiration: FiniteDuration = 1.minute + final protected val cacheExpiration: FiniteDuration = 1.minute - protected final val failure: RedisException = SimulatedException.asRedis + final protected val failure: RedisException = SimulatedException.asRedis } trait ImplicitOptionMaterialization { - protected implicit def implicitlyAny2Some[T](value: T): Option[T] = Some(value) + implicit protected def implicitlyAny2Some[T](value: T): Option[T] = Some(value) } trait ImplicitFutureMaterialization { - protected implicit def implicitlyThrowable2Future[T](cause: Throwable): Future[T] = Future.failed(cause) - protected implicit def implicitlyAny2Future[T](value: T): Future[T] = Future.successful(value) + implicit protected def implicitlyThrowable2Future[T](cause: Throwable): Future[T] = Future.failed(cause) + implicit protected def implicitlyAny2Future[T](value: T): Future[T] = Future.successful(value) } trait TimeLimitedSpec extends AsyncTestSuiteMixin with AsyncUtilities { @@ -72,25 +71,27 @@ trait TimeLimitedSpec extends AsyncTestSuiteMixin with AsyncUtilities { val result: Future[Outcome] = Future.firstCompletedOf( Seq( Future.after(testTimeout, ()).map(_ => fail(s"Test didn't finish within $testTimeout.")), - test.toFuture - ) + test.toFuture, + ), ) new FutureOutcome(result) } - abstract override def withFixture(test: NoArgAsyncTest): FutureOutcome = { + abstract override def withFixture(test: NoArgAsyncTest): FutureOutcome = super.withFixture(new TimeLimitedTest(test)) - } + } trait AsyncUtilities { this: AsyncTestSuite => implicit class FutureAsyncUtilities(future: Future.type) { + def after[T](duration: FiniteDuration, value: => T): Future[T] = Future(Await.result(Future.never, duration)).recover(_ => value) def waitFor(duration: FiniteDuration): Future[Unit] = after(duration, ()) + } } @@ -118,10 +119,10 @@ trait FutureAssertions extends AsyncUtilities { this: BaseSpec => def assertingTry(f: Try[T] => Assertion): Future[Assertion] = future.map(Success.apply).recover { case ex => Failure(ex) }.map(f) - def assertingFailure[Cause <: Throwable : ClassTag]: Future[Assertion] = + def assertingFailure[Cause <: Throwable: ClassTag]: Future[Assertion] = future.map(value => fail(s"Expected exception but got $value")).recover { case ex => ex mustBe a[Cause] } - def assertingFailure[Cause <: Throwable : ClassTag, InnerCause <: Throwable : ClassTag]: Future[Assertion] = + def assertingFailure[Cause <: Throwable: ClassTag, InnerCause <: Throwable: ClassTag]: Future[Assertion] = future.map(value => fail(s"Expected exception but got $value")).recover { case ex => ex mustBe a[Cause] ex.getCause mustBe a[InnerCause] @@ -131,21 +132,24 @@ trait FutureAssertions extends AsyncUtilities { this: BaseSpec => future.map(value => fail(s"Expected exception but got $value")).recover { case ex => ex mustEqual cause } def assertingSuccess: Future[Assertion] = - future.recover(cause => fail(s"Got unexpected exception", cause)).map(_ => Passed) - - def assertTimeout(timeout: FiniteDuration): Future[Assertion] = { - Future.firstCompletedOf( - Seq( - Future.after(timeout, throw new TimeoutException(s"Expected timeout after $timeout")), - future.map(value => fail(s"Expected timeout but got $value")) + future.recover(cause => fail("Got unexpected exception", cause)).map(_ => Passed) + + def assertTimeout(timeout: FiniteDuration): Future[Assertion] = + Future + .firstCompletedOf( + Seq( + Future.after(timeout, throw new TimeoutException(s"Expected timeout after $timeout")), + future.map(value => fail(s"Expected timeout but got $value")), + ), ) - ).assertingFailure[TimeoutException] - } + .assertingFailure[TimeoutException] + } implicit class FutureAssertionDoneOps(future: Future[Done]) { def assertingDone: Future[Assertion] = future.assertingEqual(Done) } + } trait BaseSpec extends Matchers with AsyncMockFactory { @@ -153,7 +157,7 @@ trait BaseSpec extends Matchers with AsyncMockFactory { protected type Assertion = org.scalatest.Assertion protected val Passed: Assertion = org.scalatest.Succeeded - override implicit def executionContext: ExecutionContext = ExecutionContext.global + implicit override def executionContext: ExecutionContext = ExecutionContext.global } trait AsyncBaseSpec extends BaseSpec with AsyncWordSpecLike with FutureAssertions with AsyncUtilities with TimeLimitedSpec diff --git a/src/test/scala/play/api/cache/redis/test/FakeApplication.scala b/src/test/scala/play/api/cache/redis/test/FakeApplication.scala index f496134c..7836ce72 100644 --- a/src/test/scala/play/api/cache/redis/test/FakeApplication.scala +++ b/src/test/scala/play/api/cache/redis/test/FakeApplication.scala @@ -4,6 +4,7 @@ import akka.actor.ActorSystem import play.api.inject.Injector trait FakeApplication extends StoppableApplication { + import play.api.Application import play.api.inject.guice.GuiceApplicationBuilder diff --git a/src/test/scala/play/api/cache/redis/test/ForAllTestContainer.scala b/src/test/scala/play/api/cache/redis/test/ForAllTestContainer.scala index eddb85e9..1a92f9cc 100644 --- a/src/test/scala/play/api/cache/redis/test/ForAllTestContainer.scala +++ b/src/test/scala/play/api/cache/redis/test/ForAllTestContainer.scala @@ -3,17 +3,16 @@ package play.api.cache.redis.test import com.dimafeng.testcontainers.SingleContainer import org.scalatest.{BeforeAndAfterAll, Suite} -trait ForAllTestContainer extends BeforeAndAfterAll {this: Suite => +trait ForAllTestContainer extends BeforeAndAfterAll { this: Suite => - protected def newContainer: SingleContainer[_] + protected def newContainer: SingleContainer[?] final protected lazy val container = newContainer - override def beforeAll(): Unit = { + override def beforeAll(): Unit = container.start() - } - override def afterAll(): Unit = { + override def afterAll(): Unit = container.stop() - } + } diff --git a/src/test/scala/play/api/cache/redis/test/Helpers.scala b/src/test/scala/play/api/cache/redis/test/Helpers.scala index 6d78fb82..d6623cc5 100644 --- a/src/test/scala/play/api/cache/redis/test/Helpers.scala +++ b/src/test/scala/play/api/cache/redis/test/Helpers.scala @@ -3,22 +3,24 @@ package play.api.cache.redis.test object Helpers { object configuration { + import com.typesafe.config.ConfigFactory import play.api.Configuration - def default: Configuration = { + def default: Configuration = Configuration(ConfigFactory.load()) - } def fromHocon(hocon: String): Configuration = { val reference = ConfigFactory.load() val local = ConfigFactory.parseString(hocon.stripMargin) Configuration(local.withFallback(reference)) } + } object probe { val orElse: OrElseProbe.type = OrElseProbe } + } diff --git a/src/test/scala/play/api/cache/redis/test/OrElseProbe.scala b/src/test/scala/play/api/cache/redis/test/OrElseProbe.scala index cfe9c490..0522a2d6 100644 --- a/src/test/scala/play/api/cache/redis/test/OrElseProbe.scala +++ b/src/test/scala/play/api/cache/redis/test/OrElseProbe.scala @@ -4,7 +4,6 @@ import java.util.concurrent.CompletionStage import scala.concurrent.Future import scala.jdk.FutureConverters.FutureOps - final class OrElseProbe[T](queue: LazyList[T]) { private var called: Int = 0 @@ -18,6 +17,7 @@ final class OrElseProbe[T](queue: LazyList[T]) { next = next.tail result } + } object OrElseProbe { @@ -36,4 +36,5 @@ object OrElseProbe { def generic[T](values: T*): OrElseProbe[T] = new OrElseProbe(LazyList(values: _*)) + } diff --git a/src/test/scala/play/api/cache/redis/test/RedisClusterContainer.scala b/src/test/scala/play/api/cache/redis/test/RedisClusterContainer.scala index bdc62aee..6b6276ff 100644 --- a/src/test/scala/play/api/cache/redis/test/RedisClusterContainer.scala +++ b/src/test/scala/play/api/cache/redis/test/RedisClusterContainer.scala @@ -7,13 +7,13 @@ import scala.concurrent.duration._ trait RedisClusterContainer extends RedisContainer { this: Suite => - protected val log = Logger("play.api.cache.redis.test") + protected val log: Logger = Logger("play.api.cache.redis.test") protected def redisMaster = 4 protected def redisSlaves = 1 - protected final def initialPort = 7000 + final protected def initialPort = 7000 private val waitForStart = 6.seconds @@ -23,9 +23,9 @@ trait RedisClusterContainer extends RedisContainer { this: Suite => redisMappedPorts = Seq.empty, redisFixedPorts = 0.until(redisMaster * (redisSlaves + 1)).map(initialPort + _), redisEnvironment = Map( - "IP" -> "0.0.0.0", - "INITIAL_PORT" -> initialPort.toString, - "MASTERS" -> redisMaster.toString, + "IP" -> "0.0.0.0", + "INITIAL_PORT" -> initialPort.toString, + "MASTERS" -> redisMaster.toString, "SLAVES_PER_MASTER" -> redisSlaves.toString, ), ) @@ -37,4 +37,5 @@ trait RedisClusterContainer extends RedisContainer { this: Suite => Thread.sleep(waitForStart.toMillis) log.info(s"Finished waiting for Redis Cluster to start on ${container.containerIpAddress}") } + } diff --git a/src/test/scala/play/api/cache/redis/test/RedisContainer.scala b/src/test/scala/play/api/cache/redis/test/RedisContainer.scala index 322d93bf..2ef26b98 100644 --- a/src/test/scala/play/api/cache/redis/test/RedisContainer.scala +++ b/src/test/scala/play/api/cache/redis/test/RedisContainer.scala @@ -15,13 +15,13 @@ trait RedisContainer extends ForAllTestContainer { this: Suite => @nowarn("cat=deprecation") @SuppressWarnings(Array("org.wartremover.warts.ForeachEntry")) - protected override final val newContainer: GenericContainer = { - val container: FixedHostPortGenericContainer[_] = new FixedHostPortGenericContainer(config.redisDockerImage) + final override protected val newContainer: GenericContainer = { + val container: FixedHostPortGenericContainer[?] = new FixedHostPortGenericContainer(config.redisDockerImage) container.withExposedPorts(config.redisMappedPorts.map(int2Integer): _*) config.redisEnvironment.foreach { case (k, v) => container.withEnv(k, v) } container.waitingFor(Wait.forListeningPorts(config.redisMappedPorts ++ config.redisFixedPorts: _*)) - config.redisFixedPorts.foreach { port => container.withFixedExposedPort(port, port) } + config.redisFixedPorts.foreach(port => container.withFixedExposedPort(port, port)) new GenericContainer(container) } -} +} diff --git a/src/test/scala/play/api/cache/redis/test/RedisLogger.scala b/src/test/scala/play/api/cache/redis/test/RedisLogger.scala index 536b794f..1af106da 100644 --- a/src/test/scala/play/api/cache/redis/test/RedisLogger.scala +++ b/src/test/scala/play/api/cache/redis/test/RedisLogger.scala @@ -9,11 +9,11 @@ import akka.event.slf4j.Slf4jLogger */ class RedisLogger extends Slf4jLogger { - private val doReceive: PartialFunction[Any, Unit] = { - case InitializeLogger(_) => sender() ! LoggerInitialized + private val doReceive: PartialFunction[Any, Unit] = { case InitializeLogger(_) => + sender() ! LoggerInitialized } - override def receive: PartialFunction[Any, Unit] = { + override def receive: PartialFunction[Any, Unit] = doReceive.orElse(super.receive) - } + } diff --git a/src/test/scala/play/api/cache/redis/test/RedisSentinelContainer.scala b/src/test/scala/play/api/cache/redis/test/RedisSentinelContainer.scala index e7a422db..fd65e74e 100644 --- a/src/test/scala/play/api/cache/redis/test/RedisSentinelContainer.scala +++ b/src/test/scala/play/api/cache/redis/test/RedisSentinelContainer.scala @@ -12,9 +12,9 @@ trait RedisSentinelContainer extends RedisContainer { protected def nodes: Int = 3 - protected final def initialPort: Int = 7000 + final protected def initialPort: Int = 7000 - protected final def sentinelPort: Int = initialPort - 2000 + final protected def sentinelPort: Int = initialPort - 2000 protected def master: String = s"sentinel$initialPort" @@ -23,12 +23,12 @@ trait RedisSentinelContainer extends RedisContainer { override protected lazy val redisConfig: RedisContainerConfig = RedisContainerConfig( redisDockerImage = "grokzen/redis-cluster:7.0.10", - redisMappedPorts = Seq.empty, + redisMappedPorts = Seq.empty, redisFixedPorts = 0.until(nodes).flatMap(i => Seq(initialPort + i, sentinelPort + i)), redisEnvironment = Map( - "IP" -> "0.0.0.0", + "IP" -> "0.0.0.0", "INITIAL_PORT" -> initialPort.toString, - "SENTINEL" -> "true" + "SENTINEL" -> "true", ), ) @@ -39,4 +39,5 @@ trait RedisSentinelContainer extends RedisContainer { Thread.sleep(waitForStart.toMillis) log.info(s"Finished waiting for Redis Sentinel to start on ${container.containerIpAddress}") } + } diff --git a/src/test/scala/play/api/cache/redis/test/RedisSettingsTest.scala b/src/test/scala/play/api/cache/redis/test/RedisSettingsTest.scala index 7e46d76a..556370f9 100644 --- a/src/test/scala/play/api/cache/redis/test/RedisSettingsTest.scala +++ b/src/test/scala/play/api/cache/redis/test/RedisSettingsTest.scala @@ -8,6 +8,5 @@ final case class RedisSettingsTest( timeout: RedisTimeouts, recovery: String, source: String, - prefix: Option[String] = None - + prefix: Option[String] = None, ) extends RedisSettings diff --git a/src/test/scala/play/api/cache/redis/test/RedisStandaloneContainer.scala b/src/test/scala/play/api/cache/redis/test/RedisStandaloneContainer.scala index e09ea24f..010680b9 100644 --- a/src/test/scala/play/api/cache/redis/test/RedisStandaloneContainer.scala +++ b/src/test/scala/play/api/cache/redis/test/RedisStandaloneContainer.scala @@ -11,6 +11,7 @@ trait RedisStandaloneContainer extends RedisContainer { this: Suite => redisDockerImage = "redis:latest", redisMappedPorts = Seq(defaultPort), redisFixedPorts = Seq.empty, - redisEnvironment = Map.empty + redisEnvironment = Map.empty, ) + } diff --git a/src/test/scala/play/api/cache/redis/test/StoppableApplication.scala b/src/test/scala/play/api/cache/redis/test/StoppableApplication.scala index 6c2c392b..5fbd2f58 100644 --- a/src/test/scala/play/api/cache/redis/test/StoppableApplication.scala +++ b/src/test/scala/play/api/cache/redis/test/StoppableApplication.scala @@ -10,29 +10,29 @@ import scala.util.{Failure, Success} trait StoppableApplication extends ApplicationLifecycle { - private var hooks: List[() => Future[_]] = Nil + private var hooks: List[() => Future[?]] = Nil protected def system: ActorSystem def shutdownAsync(): Future[Done] = CoordinatedShutdown(system).run(CoordinatedShutdown.UnknownReason) - def runAsyncInApplication(block: => Future[Assertion])(implicit ec: ExecutionContext): Future[Assertion] = { - block.map(Success(_)).recover(Failure(_)) + def runAsyncInApplication(block: => Future[Assertion])(implicit ec: ExecutionContext): Future[Assertion] = + block + .map(Success(_)) + .recover(Failure(_)) .flatMap(result => Future.sequence(hooks.map(_.apply())).map(_ => result)) .flatMap(result => shutdownAsync().map(_ => result)) .flatMap(result => system.terminate().map(_ => result)) .flatMap(Future.fromTry) - } - final def runInApplication(block: => Assertion)(implicit ec: ExecutionContext): Future[Assertion] = { + final def runInApplication(block: => Assertion)(implicit ec: ExecutionContext): Future[Assertion] = runAsyncInApplication(Future(block)) - } - final override def addStopHook(hook: () => Future[_]): Unit = + final override def addStopHook(hook: () => Future[?]): Unit = hooks = hook :: hooks - final override def stop(): Future[_] = Future.unit + final override def stop(): Future[?] = Future.unit } @@ -42,4 +42,5 @@ object StoppableApplication { new StoppableApplication { override protected def system: ActorSystem = actorSystem } + }