diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 151942a57..3f31d5301 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -3,3 +3,6 @@ ca5770e327fea2fad0a3bf2f79f896e7a507d2d5 # Scala Steward: Reformat with scalafmt 3.7.14 dbe84f92afbd6d78b05ccea685e852c49f30803e + +# Scala Steward: Reformat with scalafmt 3.7.15 +b3311c285e229dcb096fd9e89d9a3c5b7d95345c diff --git a/.github/ISSUE_TEMPLATE/issue_report.md b/.github/ISSUE_TEMPLATE/issue_report.md new file mode 100644 index 000000000..4bc8c8121 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue_report.md @@ -0,0 +1,32 @@ +--- +name: Issue Report +about: Report an issue to help us improve +title: '[ISSUE] ' +--- + +**Article and Module Links** +A link to the affected article and the affected module. You can find the link to the module in the Conclusion section in the "on Github" standard phase. + +**Describe the Issue** +A clear and concise description of what the issue is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected Behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Environment (please complete the following information):** +- OS: [e.g. Windows] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] + +**Additional Context** +Add any other context about the issue here. diff --git a/.gitignore b/.gitignore index bb0e28e6c..dba43e2bd 100644 --- a/.gitignore +++ b/.gitignore @@ -11,12 +11,15 @@ lib_managed/ src_managed/ project/boot/ project/plugins/project/ +**/.scala-build/ # Scala-IDE specific .scala_dependencies +.scala-build .worksheet .idea !/project/Versions.scala +!/project/LibraryVersions.scala /project/ !project/build.properties out diff --git a/.mergify.yml b/.mergify.yml index 78e527464..e58c45f83 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -3,7 +3,11 @@ pull_request_rules: conditions: - label!=early-semver-major - label!=semver-spec-major - - "#files=1" + - or: + - files~=\.sbt$ + - files~=ScalaVersions.scala + - files~=build.properties + - files~=LibraryVersions.scala - "#approved-reviews-by>=1" - check-success=CI - Jenkins - author=scala-steward-baeldung[bot] @@ -13,3 +17,4 @@ pull_request_rules: actions: merge: method: merge + delete_head_branch: diff --git a/.scala-steward.conf b/.scala-steward.conf index 6e8fc39a1..4990ecd64 100644 --- a/.scala-steward.conf +++ b/.scala-steward.conf @@ -25,7 +25,7 @@ # Default: @asap # #pullRequests.frequency = "0 0 ? * 3" # every thursday on midnight -pullRequests.frequency = "20 days" +pullRequests.frequency = "7 days" # The dependencies which match the given version pattern are updated. # Dependencies that are not listed will be updated. @@ -33,9 +33,11 @@ pullRequests.frequency = "20 days" # Each pattern must have `groupId`, `version` and optional `artifactId`. # Defaults to empty `[]` which mean Scala Steward will update all dependencies. # the following example will allow to update foo when version is 1.1.x -updates.pin = [ { groupId = "ch.qos.logback", artifactId="logback-classic", version = "1.3." } ] +updates.pin = [] # If set, Scala Steward will use this message template for the commit messages and PR titles. # Supported variables: ${artifactName}, ${currentVersion}, ${nextVersion} and ${default} # Default: "${default}" which is equivalent to "Update ${artifactName} to ${nextVersion}" commits.message = "Update ${artifactName} from ${currentVersion} to ${nextVersion}" + +buildRoots = [".", "scalatra"] diff --git a/.scalafmt.conf b/.scalafmt.conf index 41cf16b80..ef0b7960b 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = 3.7.14 +version = 3.7.15 runner.dialect = scala3 fileOverride { "glob:**/scala-2-modules/**" { diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..642f4c670 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,11 @@ +# Contributing to Baeldung Tutorials +First off, thank you for considering contributing to Baeldung Tutorials. + +## Reporting Issues +Before you submit an issue, please review the guidelines below: + +1. **No Custom Modifications:** If your issue arises from any custom modifications you've made to the code in the repository, we won't be able to assist. We can only help if the issue is reproducible with the untouched codebase from this repo. If you're working with a modified version, consider asking for help on StackOverflow or other relevant forums. +2. **Use a clear and descriptive title** for the issue to identify the problem. +3. **Include a link to the article** you're having issues with. +4. **Describe the exact steps which reproduce the problem** in as many details as possible. +5. **Additional Details:** Offer any other context or descriptions that could be useful. Screenshots, error messages, copy/pasteable snippets, or logs can be immensely helpful. \ No newline at end of file diff --git a/README.md b/README.md index 94ddc9432..97f93197a 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,9 @@ # scala-tutorials This is the main repo for all the sample code used in the scala tutorials. +# Pre-requisites +Suggested JDK version : JDK 17 + # Compiling and Running Tests This repo uses a multi-module build with many sub modules. To compile the entire module, you may use the command `sbt compile`. However, this loads all the modules and compiles all of them, which might take some time. diff --git a/build.sbt b/build.sbt index 6549e5fd5..f24ca4a3f 100644 --- a/build.sbt +++ b/build.sbt @@ -1,201 +1,315 @@ val scalaV = ScalaVersions.scala2Version val scala3Version = ScalaVersions.scala3Version -ThisBuild / scalaVersion := scalaV -ThisBuild / version := "1.0-SNAPSHOT" +ThisBuild / scalaVersion := scala3Version ThisBuild / organization := "com.baeldung" ThisBuild / organizationName := "core-scala" - val jUnitInterface = "com.github.sbt" % "junit-interface" % "0.13.3" % "test" -val catsEffect = "org.typelevel" %% "cats-effect" % "3.5.2" -val catEffectTest = "org.typelevel" %% "cats-effect-testkit" % "3.5.2" % Test +val catsEffect = "org.typelevel" %% "cats-effect" % "3.6.3" +val catEffectTest = "org.typelevel" %% "cats-effect-testkit" % "3.6.3" % Test val scalaReflection = "org.scala-lang" % "scala-reflect" % scalaV -val logback = "ch.qos.logback" % "logback-classic" % "1.3.11" -val embedMongoVersion = "4.9.2" +val logback = "ch.qos.logback" % "logback-classic" % "1.5.21" +val embedMongoVersion = "4.21.0" +val AkkaVersion = "2.9.3" +val AlpakkaVersion = "8.0.0" +val AkkaHttpVersion = "10.6.3" val scalaTestDeps = Seq( - "org.scalatest" %% "scalatest" % "3.2.17" % Test, - "org.scalatest" %% "scalatest-shouldmatchers" % "3.2.17" % Test, - "org.scalatest" %% "scalatest-wordspec" % "3.2.17" % Test, - "org.scalatest" %% "scalatest-flatspec" % "3.2.17" % Test + "org.scalatest" %% "scalatest" % "3.2.19" % Test, + "org.scalatest" %% "scalatest-shouldmatchers" % "3.2.19" % Test, + "org.scalatest" %% "scalatest-wordspec" % "3.2.19" % Test, + "org.scalatest" %% "scalatest-flatspec" % "3.2.19" % Test ) -val scalaMock = "org.scalamock" %% "scalamock" % "5.2.0" % Test -val zioVersion = "2.0.18" +val scalaMock = "org.scalamock" %% "scalamock" % "7.5.2" % Test +val zioVersion = "2.1.23" -lazy val scala_core = (project in file("scala-core")) +lazy val scala_core = (project in file("scala-core-modules/scala-core")) .settings( name := "scala-core", + scalaVersion := scala3Version, libraryDependencies ++= Seq( - jUnitInterface, - catsEffect + jUnitInterface ) ++ scalaTestDeps ) -lazy val scala_core_2 = (project in file("scala-core-2")) +lazy val scala_core_2 = (project in file("scala-core-modules/scala-core-2")) .settings( name := "scala-core-2", libraryDependencies ++= scalaTestDeps, - libraryDependencies += scalaMock, - libraryDependencies += jUnitInterface + libraryDependencies += jUnitInterface, + scalaVersion := scala3Version ) -lazy val scala_core_3 = (project in file("scala-core-3")) +val scalaXmlDep = "org.scala-lang.modules" %% "scala-xml" % "2.4.0" + +lazy val scala_core_3 = (project in file("scala-core-modules/scala-core-3")) .settings( name := "scala-core-3", + scalaVersion := scala3Version, + libraryDependencies ++= scalaTestDeps, + libraryDependencies += jUnitInterface, + libraryDependencies += scalaXmlDep + ) + +lazy val scala2_core = (project in file("scala-2-modules/scala2-core")) + .settings( + name := "scala2-core", libraryDependencies ++= scalaTestDeps, libraryDependencies += jUnitInterface, libraryDependencies += scalaReflection, - libraryDependencies += "org.scala-lang.modules" %% "scala-xml" % "2.2.0" + libraryDependencies += scalaXmlDep, + libraryDependencies += "com.github.scopt" %% "scopt" % "4.1.0", + libraryDependencies += "org.rogach" %% "scallop" % "5.3.0", + libraryDependencies += "org.backuity.clist" %% "clist-core" % "3.5.1", + libraryDependencies += "org.backuity.clist" %% "clist-macros" % "3.5.1" % "provided", + libraryDependencies += "args4j" % "args4j" % "2.37", + libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value % "test", + libraryDependencies += catsEffect ) -lazy val scala_core_4 = (project in file("scala-core-4")) +lazy val scala_core_4 = (project in file("scala-core-modules/scala-core-4")) .settings( name := "scala-core-4", + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps, libraryDependencies += jUnitInterface, libraryDependencies += scalaReflection ) -lazy val scala_core_5 = (project in file("scala-core-5")) +lazy val scala_core_5 = (project in file("scala-core-modules/scala-core-5")) .settings( name := "scala-core-5", + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps, - libraryDependencies += jUnitInterface, - libraryDependencies += scalaReflection, - libraryDependencies += "joda-time" % "joda-time" % "2.12.5", - libraryDependencies += "org.joda" % "joda-convert" % "2.2.3", - libraryDependencies += "com.github.nscala-time" %% "nscala-time" % "2.32.0" + libraryDependencies += jUnitInterface ) -lazy val scala_core_6 = (project in file("scala-core-6")) +lazy val scala_core_6 = (project in file("scala-core-modules/scala-core-6")) .settings( name := "scala-core-6", + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps, libraryDependencies += jUnitInterface ) -lazy val scala_core_7 = (project in file("scala-core-7")) +lazy val scala_core_7 = (project in file("scala-core-modules/scala-core-7")) .settings( name := "scala-core-7", + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps, - libraryDependencies += jUnitInterface, - libraryDependencies += "com.github.scopt" %% "scopt" % "4.1.0", - libraryDependencies += "org.rogach" %% "scallop" % "5.0.0", - libraryDependencies += "org.backuity.clist" %% "clist-core" % "3.5.1", - libraryDependencies += "org.backuity.clist" %% "clist-macros" % "3.5.1" % "provided", - libraryDependencies += "args4j" % "args4j" % "2.33" + libraryDependencies += jUnitInterface ) -lazy val scala_core_8 = (project in file("scala-core-8")) +lazy val scala_core_8 = (project in file("scala-core-modules/scala-core-8")) .settings( name := "scala-core-8", - libraryDependencies += scalaReflection, libraryDependencies ++= scalaTestDeps, - libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value % "test", + scalaVersion := scala3Version, libraryDependencies += "org.scala-lang.modules" %% "scala-java8-compat" % "1.0.2", - libraryDependencies += "com.typesafe" % "config" % "1.2.1" + libraryDependencies += "com.typesafe" % "config" % "1.4.5" // scalacOptions += "-Ymacro-debug-lite" ) -lazy val scala_core_io = (project in file("scala-core-io")) +lazy val scala_core_9 = (project in file("scala-core-modules/scala-core-9")) + .settings( + name := "scala-core-9", + libraryDependencies ++= scalaTestDeps, + scalaVersion := scala3Version, + libraryDependencies += "org.scalatestplus" %% "scalacheck-1-17" % "3.2.18.0" % Test + ) + +lazy val scala_core_10 = (project in file("scala-core-modules/scala-core-10")) + .settings( + name := "scala-core-10", + libraryDependencies ++= scalaTestDeps, + scalaVersion := scala3Version, + libraryDependencies += "org.scalatestplus" %% "scalacheck-1-17" % "3.2.18.0" % Test + ) + +lazy val scala_core_numbers = + (project in file("scala-core-modules/scala-core-numbers")) + .settings( + name := "scala-core-numbers", + libraryDependencies ++= scalaTestDeps, + scalaVersion := scala3Version, + libraryDependencies += "org.scalatestplus" %% "scalacheck-1-17" % "3.2.18.0" % Test + ) + +lazy val scala_core_numbers_2 = + (project in file("scala-core-modules/scala-core-numbers-2")) + .settings( + name := "scala-core-numbers", + libraryDependencies ++= scalaTestDeps, + scalaVersion := scala3Version + ) + +lazy val scala_core_io = (project in file("scala-core-modules/scala-core-io")) .settings( name := "scala-core-io", + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps, - libraryDependencies += jUnitInterface, - libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value + libraryDependencies += jUnitInterface ) -lazy val scala_core_oop = (project in file("scala-core-oop")) +lazy val scala_core_oop = (project in file("scala-core-modules/scala-core-oop")) .settings( name := "scala-core-oop", + scalaVersion := scala3Version, libraryDependencies ++= Seq(catsEffect, jUnitInterface) ++ scalaTestDeps ) -lazy val scala_core_fp = (project in file("scala-core-fp")) +lazy val scala_strings_2 = + (project in file("scala-core-modules/scala-strings-2")) + .settings( + name := "scala-core-strings", + libraryDependencies ++= scalaTestDeps, + libraryDependencies += jUnitInterface, + scalaVersion := scala3Version + ) + +lazy val scala_core_fp = (project in file("scala-core-modules/scala-core-fp")) .settings( name := "scala-core-fp", + scalaVersion := scala3Version, libraryDependencies ++= Seq(catsEffect, jUnitInterface) ++ scalaTestDeps ) -lazy val scala_lang = (project in file("scala-lang")) +lazy val scala_core_dates = + (project in file("scala-core-modules/scala-core-dates")) + .settings( + name := "scala-core-dates", + scalaVersion := scala3Version, + libraryDependencies ++= scalaTestDeps, + libraryDependencies += "joda-time" % "joda-time" % "2.14.0", + libraryDependencies += "com.github.nscala-time" %% "nscala-time" % "3.0.0", + libraryDependencies += "com.typesafe" % "config" % "1.4.5" + ) + +lazy val scala_lang = (project in file("scala-lang-modules/scala-lang")) .settings( name := "scala-lang", + scalaVersion := scala3Version, libraryDependencies ++= Seq(jUnitInterface) ++ scalaTestDeps ) -lazy val scala_lang_2 = (project in file("scala-lang-2")) +lazy val scala_lang_2 = (project in file("scala-lang-modules/scala-lang-2")) .settings( - name := "scala-lang", + name := "scala-lang-2", + scalaVersion := scala3Version, libraryDependencies ++= Seq(jUnitInterface) ++ scalaTestDeps ) -lazy val scala_core_collections = (project in file("scala-core-collections")) - .settings( - name := "scala-core-collections", - libraryDependencies ++= Seq( - "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.4" - ) ++ scalaTestDeps - ) +val scalaParColDep = + "org.scala-lang.modules" %% "scala-parallel-collections" % "1.2.0" + +lazy val scala_core_collections = + (project in file("scala-core-collections-modules/scala-core-collections")) + .settings( + name := "scala-core-collections", + scalaVersion := scala3Version, + libraryDependencies ++= Seq( + scalaParColDep + ) ++ scalaTestDeps + ) lazy val scala_core_collections_2 = - (project in file("scala-core-collections-2")) + (project in file("scala-core-collections-modules/scala-core-collections-2")) .settings( name := "scala-core-collections-2", + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps ) + lazy val scala_core_collections_3 = - (project in file("scala-core-collections-3")) + (project in file("scala-core-collections-modules/scala-core-collections-3")) + .settings( + scalaVersion := scala3Version, + libraryDependencies ++= scalaTestDeps + ) + +lazy val scala_core_collections_4 = + (project in file("scala-core-collections-modules/scala-core-collections-4")) + .settings( + scalaVersion := scala3Version, + libraryDependencies ++= scalaTestDeps + ) + +lazy val scala_core_map = + (project in file("scala-core-collections-modules/scala-core-map")) .settings( + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps ) lazy val scala_test = (project in file("scala-test")) .settings( name := "scala-test", + scalaVersion := scala3Version, libraryDependencies ++= Seq( - "org.scalatestplus" %% "mockito-3-4" % "3.2.10.0" % Test, - jUnitInterface, - scalaMock + scalaTestPlusMockito, + jUnitInterface ) ++ scalaTestDeps ) +lazy val scala_test_2 = (project in file("scala-test-2")) + .settings( + name := "scala-test-2", + scalaVersion := scala3Version, + libraryDependencies ++= scalaTestDeps + ) + val embeddedMongo = "de.flapdoodle.embed" % "de.flapdoodle.embed.mongo" % embedMongoVersion +val akkaTypedTestkit = + "com.typesafe.akka" %% "akka-actor-testkit-typed" % AkkaVersion % "it,test" +val akkaStreamDep = "com.typesafe.akka" %% "akka-stream" % AkkaVersion +val akkaHttpDep = "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion +val akkaActorTyped = "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion +val akkaHttpTestkitDep = + "com.typesafe.akka" %% "akka-http-testkit" % AkkaHttpVersion + lazy val scala_akka_dependencies: Seq[ModuleID] = Seq( - "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion, - "com.typesafe.akka" %% "akka-actor-testkit-typed" % AkkaVersion % Test, - "com.lightbend.akka" %% "akka-stream-alpakka-mongodb" % "5.0.0", - "com.typesafe.akka" %% "akka-stream" % AkkaVersion, - "org.mongodb.scala" %% "mongo-scala-driver" % "4.10.2", - "com.lightbend.akka" %% "akka-stream-alpakka-file" % "5.0.0", + akkaActorTyped, + akkaTypedTestkit, + "com.lightbend.akka" %% "akka-stream-alpakka-mongodb" % AlpakkaVersion, + akkaStreamDep, + "org.mongodb.scala" %% "mongo-scala-driver" % "5.6.1", + "com.lightbend.akka" %% "akka-stream-alpakka-file" % AlpakkaVersion, jUnitInterface, embeddedMongo % Test, - "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion + akkaHttpDep ) ++ scalaTestDeps lazy val scala_test_junit4 = (project in file("scala-test-junit4")) .settings( name := "scala-test-junit4", + scalaVersion := scala3Version, libraryDependencies ++= Seq( - "org.scalatestplus" %% "junit-4-13" % "3.2.17.0" % Test, + "org.scalatestplus" %% "junit-4-13" % "3.2.19.1" % Test, jUnitInterface ) ) +lazy val scala_test_junit5 = (project in file("scala-test-junit5")) + .settings( + name := "scala-test-junit5", + scalaVersion := scala3Version + ) + lazy val scala_akka = (project in file("scala-akka")) .configs(IntegrationTest) .settings( name := "scala-akka", libraryDependencies ++= scala_akka_dependencies ++ Seq( - "ch.qos.logback" % "logback-classic" % "1.2.3", // scala-steward:off + "ch.qos.logback" % "logback-classic" % "1.4.11", // scala-steward:off embeddedMongo % "it,compile" ) ++ scalaTestDeps.map(_.withConfigurations(Some("it,test"))), Defaults.itSettings @@ -208,263 +322,451 @@ lazy val scala_akka_2 = (project in file("scala-akka-2")) name := "scala-akka-2", Defaults.itSettings, libraryDependencies ++= Seq( - "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion, - "com.typesafe.akka" %% "akka-stream" % AkkaVersion, - "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion, + akkaActorTyped, + akkaStreamDep, + akkaHttpDep, "com.typesafe.akka" %% "akka-discovery" % AkkaVersion, "com.typesafe.akka" %% "akka-http-spray-json" % AkkaHttpVersion, - "com.typesafe.akka" %% "akka-http-testkit" % AkkaHttpVersion, - "com.lightbend.akka" %% "akka-stream-alpakka-sse" % "5.0.0", + akkaHttpTestkitDep, + "com.lightbend.akka" %% "akka-stream-alpakka-csv" % AlpakkaVersion, + "com.lightbend.akka" %% "akka-stream-alpakka-sse" % AlpakkaVersion, "com.typesafe.akka" %% "akka-persistence-typed" % AkkaVersion, - "com.typesafe.akka" %% "akka-actor-testkit-typed" % AkkaVersion % "it,test", - "com.typesafe.akka" %% "akka-http-testkit" % AkkaHttpVersion % "it,test", + akkaTypedTestkit, + akkaHttpTestkitDep % "it,test", "com.typesafe.akka" %% "akka-stream-testkit" % AkkaVersion % Test ) ++ scalaTestDeps.map(_.withConfigurations(Some("it,test"))) ) -val monocleVersion = "2.1.0" -val slickVersion = "3.4.1" -val shapelessVersion = "2.3.10" -val scalazVersion = "7.3.7" -val fs2Version = "3.9.2" -val AkkaVersion = "2.8.0" -val AkkaHttpVersion = "10.5.0" -val reactiveMongo = "1.0.10" -lazy val scala_libraries = (project in file("scala-libraries")) +lazy val scala_akka_3 = (project in file("scala-akka-3")) + .enablePlugins(AkkaGrpcPlugin) + .configs(IntegrationTest) .settings( - name := "scala-libraries", - libraryDependencies ++= scalaTestDeps, + name := "scala-akka-3", + Defaults.itSettings, libraryDependencies ++= Seq( - "com.github.julien-truffaut" %% "monocle-core" % monocleVersion, - "com.github.julien-truffaut" %% "monocle-macro" % monocleVersion, - "com.github.julien-truffaut" %% "monocle-law" % monocleVersion % "test", - "com.typesafe.slick" %% "slick" % slickVersion, - "com.h2database" % "h2" % "2.2.224", - "com.chuusai" %% "shapeless" % shapelessVersion, - "org.scalaz" %% "scalaz-core" % scalazVersion, - "co.fs2" %% "fs2-core" % fs2Version, - "co.fs2" %% "fs2-io" % fs2Version, - "junit" % "junit" % "4.13.2" % Test, - "org.reactivemongo" %% "reactivemongo" % reactiveMongo, - "org.reactivemongo" %% "reactivemongo-akkastream" % reactiveMongo, - "de.flapdoodle.embed" % "de.flapdoodle.embed.mongo" % embedMongoVersion % Test, - logback % Test, - "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion, "com.typesafe.akka" %% "akka-stream" % AkkaVersion, - catEffectTest, - "org.typelevel" %% "cats-effect-testing-scalatest" % "1.5.0" % Test - ) + "com.typesafe.akka" %% "akka-discovery" % AkkaVersion, + "org.slf4j" % "slf4j-api" % "2.0.17", + "org.slf4j" % "slf4j-simple" % "2.0.17", + "com.typesafe.akka" %% "akka-stream-testkit" % AkkaVersion % Test, + akkaActorTyped, + akkaStreamDep, + akkaTypedTestkit + ) ++ scalaTestDeps.map(_.withConfigurations(Some("it,test"))) ) -val circeVersion = "0.14.6" +val monocleVersion = "2.1.0" +val slickVersion = "3.6.1" +val shapelessVersion = "2.3.13" +val scalazVersion = "7.3.8" +val fs2Version = "3.12.2" +val reactiveMongo = "1.1.0-RC19" +val slickPgVersion = "0.23.1" +val scalaTestContainersVersion = "0.43.6" +val postgresqlVersion = "42.7.8" +val json4sVersion = "4.1.0" + +lazy val scala2_libraries = + (project in file("scala-2-modules/scala2-libraries")) + .configs(IntegrationTest) + .settings( + name := "scala2-libraries", + scalaVersion := scalaV, + libraryDependencies ++= scalaTestDeps + .map(_.withConfigurations(Some("it,test"))), + resolvers += "Kafka avro serializer" at "https://packages.confluent.io/maven", + libraryDependencies ++= Seq( + "com.github.julien-truffaut" %% "monocle-core" % monocleVersion, + "com.github.julien-truffaut" %% "monocle-macro" % monocleVersion, + "com.github.julien-truffaut" %% "monocle-law" % monocleVersion % "test", + "com.chuusai" %% "shapeless" % shapelessVersion, + "junit" % "junit" % "4.13.2" % Test, + logback % Test, + akkaActorTyped, + akkaStreamDep, + "com.github.cb372" %% "scalacache-core" % "0.28.0", + "com.github.cb372" %% "scalacache-guava" % "0.28.0", + "com.github.cb372" %% "scalacache-cats-effect" % "0.28.0", + "com.github.cb372" %% "scalacache-caffeine" % "0.28.0", + enumeratumDep, + "io.monix" %% "monix" % monixVersion, + pureConfigDep, + "com.github.pureconfig" %% "pureconfig-enumeratum" % "0.17.9", + "com.typesafe" % "config" % "1.4.5", + "org.scala-lang.modules" %% "scala-async" % "1.0.1", + "com.clever-cloud.pulsar4s" %% "pulsar4s-core" % "2.11.0", + "com.clever-cloud.pulsar4s" %% "pulsar4s-jackson" % "2.11.0", + "org.testcontainers" % "pulsar" % "1.21.3" % IntegrationTest, + "org.apache.kafka" % "kafka-clients" % kafkaVersion, + "com.fasterxml.jackson.core" % "jackson-databind" % jackSonVersion, + "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % jackSonVersion, + "com.fasterxml.jackson.module" %% "jackson-module-scala" % jackSonVersion, + "com.sksamuel.avro4s" %% "avro4s-core" % avro4sVersion, + "io.confluent" % "kafka-avro-serializer" % kafkaAvroSerializer, + log4jApiScalaDep, + "org.apache.logging.log4j" % "log4j-core" % log4jVersion % Runtime + ), + libraryDependencies ++= Seq( + "com.typesafe.slick" %% "slick" % slickVersion, + "com.h2database" % "h2" % "2.4.240" + ), + scalacOptions += "-Xasync", + Defaults.itSettings + ) + +val circeVersion = "0.14.15" val monixVersion = "3.4.1" -val elastic4sVersion = "8.9.3" -val sparkVersion = "3.5.0" + +val sparkVersion = "4.0.1" +val elastic4sVersion = "9.1.1" val sparkCoreDep = "org.apache.spark" %% "spark-core" % sparkVersion val sparkSqlDep = "org.apache.spark" %% "spark-sql" % sparkVersion -lazy val scala_libraries_2 = (project in file("scala-libraries-2")) +val enumeratumDep = "com.beachape" %% "enumeratum" % "1.9.1" +val circeDep = "io.circe" %% "circe-generic" % circeVersion +val circeParserDep = "io.circe" %% "circe-parser" % circeVersion + +lazy val scala_libraries = (project in file("scala-libraries")) .configs(IntegrationTest) .settings( name := "scala-libraries", + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps .map(_.withConfigurations(Some("it,test"))), libraryDependencies ++= Seq( "io.circe" %% "circe-core" % circeVersion, - "io.circe" %% "circe-generic" % circeVersion, - "io.circe" %% "circe-parser" % circeVersion, - "com.github.cb372" %% "scalacache-core" % "0.28.0", - "com.github.cb372" %% "scalacache-guava" % "0.28.0", - "com.github.cb372" %% "scalacache-cats-effect" % "0.28.0", - "com.github.cb372" %% "scalacache-caffeine" % "0.28.0", - "com.beachape" %% "enumeratum" % "1.7.3" + circeDep, + circeParserDep, + "com.softwaremill.retry" %% "retry" % "0.3.6", + log4jApiScalaDep, + "org.apache.logging.log4j" % "log4j-core" % "2.25.0" % Runtime, + "com.typesafe.scala-logging" %% "scala-logging" % "3.9.6", + "software.amazon.awssdk" % "s3" % "2.25.9", + "com.github.seratch" %% "awscala" % "0.9.2", + "com.opencsv" % "opencsv" % "5.12.0", + "com.github.tototoshi" %% "scala-csv" % "2.0.0", + "org.apache.commons" % "commons-csv" % "1.14.1" ), libraryDependencies ++= Seq( - "com.typesafe.play" %% "play-slick" % "5.1.0", - "org.postgresql" % "postgresql" % "42.6.0" - ), - libraryDependencies ++= Seq( - "io.monix" %% "monix" % monixVersion + "org.playframework" %% "play-slick" % LibraryVersions.playSlickVersion, + "org.postgresql" % "postgresql" % postgresqlVersion ), dependencyOverrides := Seq( "com.typesafe.akka" %% "akka-protobuf-v3" % AkkaVersion, - "com.typesafe.akka" %% "akka-stream" % AkkaVersion, + akkaStreamDep, "com.typesafe.akka" %% "akka-serialization-jackson" % AkkaVersion ), libraryDependencies ++= Seq( - "com.typesafe.akka" %% "akka-actor-testkit-typed" % AkkaVersion % Test, - "org.scalacheck" %% "scalacheck" % "1.17.0" % Test, - "com.lihaoyi" %% "requests" % "0.8.0" + akkaTypedTestkit, + "com.lihaoyi" %% "requests" % "0.9.0" ), libraryDependencies ++= Seq( - "com.sksamuel.elastic4s" %% "elastic4s-client-esjava" % elastic4sVersion, - "com.sksamuel.elastic4s" %% "elastic4s-core" % elastic4sVersion, + "nl.gn0s1s" %% "elastic4s-client-esjava" % elastic4sVersion, + "nl.gn0s1s" %% "elastic4s-core" % elastic4sVersion, logback ), + libraryDependencies ++= Seq( + "org.elasticmq" %% "elasticmq-core" % "1.6.15", + "org.elasticmq" %% "elasticmq-server" % "1.6.15", + "org.elasticmq" %% "elasticmq-rest-sqs" % "1.6.15" + ), + libraryDependencies ++= Seq( + "software.amazon.awssdk" % "sqs" % "2.40.2" + ), Defaults.itSettings ) -val http4sBlaze = "0.23.15" -val http4sVersion = "0.23.23" -val osLibVersion = "0.9.1" -lazy val scala_libraries_3 = (project in file("scala-libraries-3")) +lazy val scala_libraries_2 = (project in file("scala-libraries-2")) + .configs(IntegrationTest) + .settings( + name := "scala-libraries-2", + scalaVersion := scala3Version, + libraryDependencies ++= scalaTestDeps + .map(_.withConfigurations(Some("it,test"))), + libraryDependencies += "io.scalaland" %% "chimney" % "1.8.2", + Defaults.itSettings + ) + +val http4sBlaze = "0.23.17" +val http4sVersion = "0.23.33" +val osLibVersion = "0.11.6" + +val osLibDep = "com.lihaoyi" %% "os-lib" % osLibVersion + +val log4jApiScalaDep = + "org.apache.logging.log4j" %% "log4j-api-scala" % "13.1.0" + +val munitDep = "org.scalameta" %% "munit" % "1.2.1" % Test + +lazy val scala_libraries_os = (project in file("scala-libraries-os")) .settings( name := "scala-libraries", + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps, libraryDependencies ++= Seq( - "org.http4s" %% "http4s-dsl" % http4sVersion, - "org.http4s" %% "http4s-blaze-server" % http4sBlaze, - "org.http4s" %% "http4s-blaze-client" % http4sBlaze, - "com.beachape" %% "enumeratum" % "1.7.3", - "com.github.pureconfig" %% "pureconfig" % "0.17.4", - "com.github.pureconfig" %% "pureconfig-enumeratum" % "0.17.4", - "com.typesafe" % "config" % "1.4.2", - "org.scalameta" %% "munit" % "0.7.29" % Test + log4jApiScalaDep, + "org.apache.logging.log4j" % "log4j-core" % "2.25.2" % Runtime ), - libraryDependencies += scalaMock, - libraryDependencies += "com.softwaremill.retry" %% "retry" % "0.3.6", - libraryDependencies ++= Seq( - "org.apache.logging.log4j" %% "log4j-api-scala" % "12.0", - "org.apache.logging.log4j" % "log4j-core" % "2.20.0" % Runtime - ), - libraryDependencies += "com.lihaoyi" %% "os-lib" % osLibVersion + libraryDependencies += osLibDep ) -lazy val scala_libraries_os = (project in file("scala-libraries-os")) +lazy val redis_intro = + (project in file("scala-libraries-standalone/redis-intro")) + .configs(IntegrationTest) + .settings( + name := "redis-intro", + scalaVersion := scala3Version, + libraryDependencies ++= scalaTestDeps + .map(_.withConfigurations(Some("it,test"))), + libraryDependencies ++= Seq( + "redis.clients" % "jedis" % "7.1.0", + "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.20.1", + "org.scalatestplus" %% "mockito-3-4" % "3.2.10.0" % "it" + ), + Defaults.itSettings + ) + +lazy val tapir = (project in file("scala-libraries-standalone/tapir")) .settings( - name := "scala-libraries", + name := "tapir", + scalaVersion := scala3Version + ) + +lazy val refined = (project in file("scala-libraries-standalone/refined")) + .settings( + name := "refined", + scalaVersion := scalaV, + libraryDependencies += "eu.timepit" %% "refined" % "0.11.3", + libraryDependencies ++= scalaTestDeps + ) + +lazy val nscalatime = (project in file("scala-libraries-standalone/nscalatime")) + .settings( + name := "nscalatime", + scalaVersion := scala3Version, + libraryDependencies += "com.github.nscala-time" %% "nscala-time" % "3.0.0", + libraryDependencies ++= scalaTestDeps + ) + +val spireVersion = "0.18.0" +val kafkaVersion = "8.1.1-ce" +val pureconfigVersion = "0.17.9" +val jackSonVersion = "2.20.1" +val log4jApiScalaVersion = "13.1.0" +val log4jVersion = "2.25.2" +val avro4sVersion = "4.1.2" +val kafkaAvroSerializer = "8.1.1" + +val pureConfigDep = "com.github.pureconfig" %% "pureconfig" % pureconfigVersion + +lazy val scala_libraries_fp = (project in file("scala-libraries-fp")) + .settings( + name := "scala-libraries-fp", + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps, libraryDependencies ++= Seq( - "org.apache.logging.log4j" %% "log4j-api-scala" % "12.0", - "org.apache.logging.log4j" % "log4j-core" % "2.20.0" % Runtime - ), - libraryDependencies += "com.lihaoyi" %% "os-lib" % osLibVersion + "co.fs2" %% "fs2-core" % fs2Version, + "co.fs2" %% "fs2-io" % fs2Version, + "org.typelevel" %% "cats-core" % "2.13.0", + "org.http4s" %% "http4s-dsl" % http4sVersion, + "org.http4s" %% "http4s-blaze-server" % http4sBlaze, + "org.http4s" %% "http4s-blaze-client" % http4sBlaze, + catEffectTest, + "org.typelevel" %% "cats-effect-testing-scalatest" % "1.7.0" % Test, + "org.scalaz" %% "scalaz-core" % scalazVersion, + "junit" % "junit" % "4.13.2" % Test, + "org.typelevel" %% "spire" % spireVersion + ) ) -lazy val scala_libraries_4 = (project in file("scala-libraries-4")) +lazy val scala_libraries_testing = (project in file("scala-libraries-testing")) .configs(IntegrationTest) .settings( - name := "scala-libraries-4", - scalaVersion := scalaV, - libraryDependencies += "com.lihaoyi" %% "utest" % "0.8.1" % "test", + name := "scala-libraries-testing", + scalaVersion := scala3Version, testFrameworks += new TestFramework("utest.runner.Framework"), libraryDependencies ++= scalaTestDeps .map(_.withConfigurations(Some("it,test"))), libraryDependencies ++= Seq( - "org.scala-lang.modules" %% "scala-async" % "1.0.1", - scalaReflection % Provided, - "org.tpolecat" %% "skunk-core" % "0.6.0", - logback, - "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5", - "org.typelevel" %% "cats-core" % "2.10.0" + "org.scalacheck" %% "scalacheck" % "1.19.0" % Test, + scalaMock, + "com.lihaoyi" %% "utest" % "0.8.9" % "test", + munitDep, + "com.amazonaws" % "aws-java-sdk-s3" % "1.12.794" % IntegrationTest, + "com.dimafeng" %% "testcontainers-scala-scalatest" % scalaTestContainersVersion % IntegrationTest, + "com.dimafeng" %% "testcontainers-scala-localstack-v2" % scalaTestContainersVersion % IntegrationTest, + "software.amazon.awssdk" % "s3" % "2.40.2" ), - libraryDependencies ++= Seq( - "com.clever-cloud.pulsar4s" %% "pulsar4s-core" % "2.9.0", - "com.clever-cloud.pulsar4s" %% "pulsar4s-jackson" % "2.9.0", - "org.testcontainers" % "pulsar" % "1.19.1" % IntegrationTest - ), - libraryDependencies ++= Seq( - "software.amazon.awssdk" % "s3" % "2.20.158", - "com.amazonaws" % "aws-java-sdk-s3" % "1.12.566" % IntegrationTest, - "com.dimafeng" %% "testcontainers-scala-scalatest" % "0.41.0" % IntegrationTest, - "com.dimafeng" %% "testcontainers-scala-localstack-v2" % "0.41.0" % IntegrationTest - ), - libraryDependencies ++= Seq( - "com.github.seratch" %% "awscala" % "0.9.2" - ), - scalacOptions += "-Xasync", Defaults.itSettings, IntegrationTest / fork := true ) -val spireVersion = "0.18.0" -val kafkaVersion = "7.5.0-ce" -val pureconfigVersion = "0.17.4" -val jackSonVersion = "2.15.2" -val log4jApiScalaVersion = "12.0" -val log4jVersion = "2.20.0" -val avro4sVersion = "4.1.1" -val kafkaAvroSerializer = "6.2.11" - -lazy val scala_libraries_5 = (project in file("scala-libraries-5")) - .settings( - name := "scala-libraries-5", - resolvers += "Kafka avro serializer" at "https://packages.confluent.io/maven", - scalaVersion := scalaV, +lazy val scala_libraries_persistence = + (project in file("scala-libraries-persistence")) + .configs(IntegrationTest) + .settings( + name := "scala-libraries-persistence", + scalaVersion := scala3Version, + Defaults.itSettings, + libraryDependencies ++= scalaTestDeps + .map(_.withConfigurations(Some("it,test"))), + libraryDependencies ++= Seq( + "com.typesafe.slick" %% "slick" % slickVersion, + "com.h2database" % "h2" % "2.4.240", + "org.tpolecat" %% "skunk-core" % "0.6.4", + doobieCore, + doobiePGDep, + "org.reactivemongo" %% "reactivemongo" % reactiveMongo, + "org.reactivemongo" %% "reactivemongo-akkastream" % reactiveMongo exclude ("org.scala-lang.modules", "scala-parser-combinators_2.13"), + "de.flapdoodle.embed" % "de.flapdoodle.embed.mongo" % embedMongoVersion % IntegrationTest, + logback, + "com.typesafe.slick" %% "slick-hikaricp" % slickVersion, + "org.postgresql" % "postgresql" % postgresqlVersion, + "com.github.tminglei" %% "slick-pg" % slickPgVersion, + "io.github.json4s" %% "json4s-native" % json4sVersion, + "com.github.tminglei" %% "slick-pg_json4s" % slickPgVersion, + "com.dimafeng" %% "testcontainers-scala-scalatest" % scalaTestContainersVersion % IntegrationTest, + "com.dimafeng" %% "testcontainers-scala-postgresql" % scalaTestContainersVersion % IntegrationTest + ) + ) + +lazy val scala_libraries_config = (project in file("scala-libraries-config")) + .settings( + name := "scala-libraries-config", + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps, libraryDependencies ++= Seq( - "org.typelevel" %% "spire" % spireVersion, - "org.apache.kafka" % "kafka-clients" % kafkaVersion, - "com.github.pureconfig" %% "pureconfig" % pureconfigVersion, - "com.fasterxml.jackson.core" % "jackson-databind" % jackSonVersion, - "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % jackSonVersion, - "com.fasterxml.jackson.module" %% "jackson-module-scala" % jackSonVersion, - "com.sksamuel.avro4s" %% "avro4s-core" % avro4sVersion, - "io.confluent" % "kafka-avro-serializer" % kafkaAvroSerializer, - "org.apache.logging.log4j" %% "log4j-api-scala" % log4jApiScalaVersion, - "org.apache.logging.log4j" % "log4j-core" % log4jVersion % Runtime - ) + "com.typesafe" % "config" % "1.4.5", + munitDep, + "com.github.japgolly.clearconfig" %% "core" % "3.1.0", + catsEffect, + "io.circe" %% "circe-yaml" % "0.15.3", + circeDep, + circeParserDep + ), + libraryDependencies += "is.cir" %% "ciris" % "3.7.0", + libraryDependencies += "is.cir" %% "ciris-circe" % "3.7.0", + libraryDependencies += "is.cir" %% "ciris-circe-yaml" % "3.7.0" ) lazy val scala_strings = (project in file("scala-strings")) .settings( name := "scala-strings", + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps, libraryDependencies += jUnitInterface, - libraryDependencies += "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.4" + libraryDependencies += scalaParColDep, + libraryDependencies += "org.scalatestplus" %% "scalacheck-1-17" % "3.2.18.0" % Test ) +val scalaTestPlusMockito = + "org.scalatestplus" %% "mockito-5-10" % "3.2.18.0" % Test + lazy val scala_design_patterns = (project in file("scala-design-patterns")) .settings( name := "scala-design-patterns", + scalaVersion := scala3Version, libraryDependencies ++= scalaTestDeps, - libraryDependencies += scalaMock, + libraryDependencies += scalaTestPlusMockito, libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test ) -lazy val scala3_lang = (project in file("scala3-lang")).settings( - libraryDependencies ++= scalaTestDeps -) +lazy val scala_lang_3 = + (project in file("scala-lang-modules/scala-lang-3")).settings( + libraryDependencies ++= scalaTestDeps, + scalaVersion := scala3Version + ) -lazy val scala3_lang_2 = (project in file("scala3-lang-2")).settings( - libraryDependencies ++= scalaTestDeps -) +lazy val scala_lang_4 = + (project in file("scala-lang-modules/scala-lang-4")).settings( + libraryDependencies ++= scalaTestDeps, + scalaVersion := scala3Version + ) -lazy val scala3_lang_3 = (project in file("scala3-lang-3")).settings( - libraryDependencies ++= scalaTestDeps -) +lazy val scala_lang_5 = + (project in file("scala-lang-modules/scala-lang-5")).settings( + libraryDependencies ++= scalaTestDeps, + scalaVersion := scala3Version + ) lazy val cats_effects = (project in file("cats-effects")) .settings( name := "cats-effects", + scalaVersion := scala3Version, libraryDependencies += catsEffect, - libraryDependencies += "org.typelevel" %% "munit-cats-effect-3" % "1.0.7" % Test, + libraryDependencies += "org.typelevel" %% "munit-cats-effect" % "2.1.0" % Test, + libraryDependencies ++= scalaTestDeps, libraryDependencies += "junit" % "junit" % "4.13.2" % Test ) +val zioJsonDep = "dev.zio" %% "zio-json" % "0.7.45" +val zioTestSbt = "dev.zio" %% "zio-test-sbt" % zioVersion % Test + lazy val zio = (project in file("zio")) .settings( name := "zio", + scalaVersion := scala3Version, libraryDependencies += "dev.zio" %% "zio" % zioVersion, libraryDependencies += "dev.zio" %% "zio-streams" % zioVersion, + libraryDependencies += zioTestSbt, + libraryDependencies += "dev.zio" %% "zio-kafka" % "2.12.0", + libraryDependencies += zioJsonDep, + libraryDependencies += "dev.zio" %% "zio-prelude" % "1.0.0-RC42", + libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % Test, + testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework") + ) + +lazy val zio2 = (project in file("zio-2")) + .settings( + name := "zio-2", + scalaVersion := scala3Version, + libraryDependencies += "dev.zio" %% "zio" % zioVersion, + libraryDependencies += "dev.zio" %% "zio-json" % "0.7.45", + libraryDependencies += "dev.zio" %% "zio-test" % zioVersion % Test, libraryDependencies += "dev.zio" %% "zio-test-sbt" % zioVersion % Test, + libraryDependencies += "dev.zio" %% "zio-logging" % "2.1.17", + libraryDependencies += "dev.zio" %% "zio-logging-slf4j2" % "2.1.17", + libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.5.6", +// used in the article, but can't work with zio-logging-slf4j2 dependency +// libraryDependencies += "dev.zio" %% "zio-logging-slf4j2-bridge" % "2.1.10", + libraryDependencies += "dev.zio" %% "zio-metrics-connectors" % "2.4.1", + libraryDependencies += "dev.zio" %% "zio-metrics-connectors-prometheus" % "2.4.1", + libraryDependencies ++= scalaTestDeps, testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework") ) -lazy val doobie = (project in file("doobie")) +lazy val zio3 = (project in file("zio3")) .settings( - name := "doobie", - libraryDependencies += "org.tpolecat" %% "doobie-core" % "1.0.0-RC2", - libraryDependencies += "org.tpolecat" %% "doobie-postgres" % "1.0.0-RC2" + libraryDependencies ++= Seq( + zioJsonDep, + "dev.zio" %% "zio-http" % "3.7.1", + "io.getquill" %% "quill-zio" % "4.8.5", + "io.getquill" %% "quill-jdbc-zio" % "4.8.5", + "com.h2database" % "h2" % "2.4.240" + ), + libraryDependencies ++= Seq( + "dev.zio" %% "zio-test" % zioVersion % Test, + zioTestSbt, + "dev.zio" %% "zio-test-magnolia" % zioVersion % Test, + "dev.zio" %% "zio-http-testkit" % "3.7.1" % Test + ), + testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"), + run / fork := true ) +val doobieVersion = "1.0.0-RC2" + +val doobiePGDep = "org.tpolecat" %% "doobie-postgres" % doobieVersion +val doobieCore = "org.tpolecat" %% "doobie-core" % doobieVersion + // Scala Native Project is disabled as it needs clang to installed in the target machine. // To test the scala-native code, install clang and then uncomment this build // lazy val scala_native = (project in file("scala-native")) // .settings( // name := "scala-native", -// libraryDependencies += "com.lihaoyi" %%% "fansi" % "0.3.0" +// scalaVersion := scala3Version, +// libraryDependencies += "com.lihaoyi" %%% "fansi" % "0.4.0" // ) // ScalaPy Python Project is disabled as it needs clang and python to installed in the target machine. @@ -472,8 +774,9 @@ lazy val doobie = (project in file("doobie")) // lazy val scala_python = (project in file("scala-python")) // .settings( // name := "scala-python", -// libraryDependencies += "me.shadaj" %% "scalapy-core" % "0.5.2", -// fork := true +// libraryDependencies += "dev.scalapy" %%% "scalapy-core" % "0.5.3", +// fork := true, +// scalaVersion := scala3Version // ) lazy val reflection = (project in file("reflection")) @@ -483,16 +786,6 @@ lazy val reflection = (project in file("reflection")) libraryDependencies += "junit" % "junit" % "4.13.2" % Test ) -lazy val scala3_libraries = (project in file("scala3-libraries")) - .settings( - scalaVersion := scala3Version, - name := "scala3-libraries", - libraryDependencies ++= Seq( - "com.github.japgolly.clearconfig" %% "core" % "3.1.0", - "org.scalameta" %% "munit" % "0.7.29" % Test - ) - ) - Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-eG") lazy val scala212 = (project in file("scala-2-modules/scala212")) @@ -507,12 +800,20 @@ lazy val spark_scala = (project in file("spark-scala")) libraryDependencies ++= Seq( sparkSqlDep, sparkCoreDep - ) ++ scalaTestDeps + ) ++ scalaTestDeps, + fork := true, + javaOptions ++= Seq( + "--add-exports=java.base/sun.nio.ch=ALL-UNNAMED" // Added for JDK 17 issue with Spark + ) ) +// This is here to prevent the accidental addition of the stand-alone module for SCALA-156 +// Do not uncomment the next line +// lazy val spark_scala = (project in file("sbt-standalone")) + addCommandAlias( "ci", - ";compile;test:compile;it:compile;scalafmtCheckAll;test" + ";compile;test:compile;it:compile;scalafmtCheckAll;validateUnitTestNames;test" ) addCommandAlias( @@ -538,6 +839,13 @@ addCommandAlias( lazy val playGroup = (project in file("play-scala")) //Uncomment this to enable scala-js module. It needs nodejs module as well in local machine -//lazy val scalajs = project in file("scala-js") +//lazy val scalajs = (project in file("scala-js")) +// .settings( +// scalaVersion := scala3Version +// ) lazy val scalatra = project in file("scalatra") lazy val benchmark = project in file("specialized-benchmark") + +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always + +ThisBuild / resolvers += "Akka dependencies" at "https://repo.akka.io/maven/" diff --git a/cats-effects/README.md b/cats-effects/README.md index 98d8ae4c0..b9e239bf3 100644 --- a/cats-effects/README.md +++ b/cats-effects/README.md @@ -5,3 +5,6 @@ - [Difference Between delay, defer, and deferred in Cats Effect](https://www.baeldung.com/scala/cats-effect-delay-defer-vs-deferred) - [Resource Handling in Cats Effect](https://www.baeldung.com/scala/cats-effect-resource-handling) - [Difference Between flatMap(), flatTap(), evalMap() and evalTap() in Cats Effect](https://www.baeldung.com/scala/cats-effect) +- [Error Handling in Cats Effect](https://www.baeldung.com/scala/cats-effect-error-handling) +- [Cancellation in Cats Effect](https://www.baeldung.com/scala/cats-effect-cancellation) +- [Configuration Loading With Ciris](https://www.baeldung.com/scala/ciris-configuration-loading) diff --git a/cats-effects/src/main/scala/com/baeldung/scala/catseffects/Cancellation.scala b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/Cancellation.scala new file mode 100644 index 000000000..61658d34c --- /dev/null +++ b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/Cancellation.scala @@ -0,0 +1,99 @@ +package com.baeldung.scala.catseffects + +import cats.effect.{IO, Resource, Deferred} +import cats.implicits.{catsSyntaxParallelTraverse1, toTraverseOps} + +import java.io.{InputStream, OutputStream} +import java.util.concurrent.atomic.AtomicBoolean +import scala.concurrent.duration.DurationInt + +object Cancellation { + + def cancelFiberDirectly[A](io: IO[A], onCancellation: => IO[Unit]): IO[Unit] = + for { + fiber <- io.onCancel(onCancellation).start + _ <- fiber.cancel + _ <- fiber.join + } yield () + + def cancelFiberDirectlySafe[A]( + io: IO[A], + onCancellation: => IO[Unit] + ): IO[Unit] = + for { + completionSignal <- Deferred[IO, Unit] + fiber <- io + .onCancel(onCancellation >> completionSignal.complete(()).void) + .start + _ <- fiber.cancel + _ <- completionSignal.get + _ <- IO.println(s"joining the fiber") + _ <- fiber.join + _ <- IO.println(s"fiber is joined") + } yield () + + def naiveParMap_1[A, B, C]( + ioa: IO[A], + iob: IO[B], + onCancelA: IO[A], + onCancelB: IO[B], + f: (A, B) => C + ): IO[C] = + for { + fiberA <- ioa.start + fiberB <- iob.start + a <- fiberA.joinWith(onCancelA).onError(_ => fiberB.cancel) + b <- fiberB.joinWith(onCancelB).onError(_ => fiberA.cancel) + } yield f(a, b) + + def naiveParMap_2[A, B, C]( + ioa: IO[A], + iob: IO[B], + onCancelA: IO[A], + onCancelB: IO[B], + f: (A, B) => C + ): IO[C] = + for { + fiberA <- ioa.start + fiberB <- iob.start + fiberAj = fiberA.joinWith(onCancelA).onError(_ => fiberB.cancel) + fiberBj = fiberB.joinWith(onCancelB).onError(_ => fiberA.cancel) + regA <- fiberAj.start + regB <- fiberBj.start + a <- regA.joinWith(onCancelA) + b <- regB.joinWith(onCancelB) + } yield f(a, b) + + def getEmails(): IO[List[String]] = IO( + List("gmail@bob.com", "alice@tech.org", "john@doe.org") + ) + + def send(message: String)(email: String): IO[Unit] = IO.sleep( + 1.seconds + ) >> IO.println(s"sending email to $email with $message") + + def sendEmailsUncancelable(message: String): IO[Unit] = + for { + emails <- getEmails() + _ <- IO.println("ready to send emails") + _ <- IO.uncancelable(_ => emails.traverse(send(message))) + _ <- IO.println("emails are sent") + } yield () + + def sendEmailsUncancelableResource(message: String) = + Resource + .make(getEmails())(emails => IO.println(s"emails $emails are released")) + .use(emails => + IO.println("ready to send emails") >> + IO.uncancelable(_ => emails.traverse(send(message))) >> + IO.println("emails are sent") + ) + + def sendEmailsUncancelableBracket(message: String): IO[Unit] = + (getEmails()).bracket(emails => + IO.println("ready to send emails") >> + IO.uncancelable(_ => emails.traverse(send(message))) >> + IO.println("emails are sent") + )(emails => IO.println(s"[bracket] emails $emails are released")) + +} diff --git a/cats-effects/src/main/scala/com/baeldung/scala/catseffects/CancellationApp.scala b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/CancellationApp.scala new file mode 100644 index 000000000..907429757 --- /dev/null +++ b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/CancellationApp.scala @@ -0,0 +1,31 @@ +package com.baeldung.scala.catseffects + +import cats.effect.IOApp +import cats.effect.IO +import scala.concurrent.duration.DurationInt +import java.util.concurrent.atomic.AtomicBoolean +import cats.effect.ExitCode + +object CancellationApp extends IOApp { + + def example() = { + val flag = new AtomicBoolean(false) + var counter = 0 + val ioa = IO.blocking { + while (!flag.get()) { + Thread.sleep(500) + println(s"counter = $counter") + counter += 1 + } + } + + ioa.cancelable( + IO.sleep(3.seconds) >> IO.println("executing the finalizer...") >> IO + .delay(flag.set(true)) + ) + } + + override def run(args: List[String]) = + example().as(ExitCode.Success) + +} diff --git a/cats-effects/src/main/scala/com/baeldung/scala/catseffects/ErrorHandling.scala b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/ErrorHandling.scala new file mode 100644 index 000000000..89e9124ba --- /dev/null +++ b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/ErrorHandling.scala @@ -0,0 +1,24 @@ +package com.baeldung.scala.catseffects + +import cats.syntax.applicative._ +import cats.{Applicative, ApplicativeError} + +import scala.util.{Failure, Success, Try} + +object ErrorHandling { + + trait Calculator[F[_]] { + def calculate(f: => Int): F[Int] + } + + class CalculatorImpl[F[_]: Applicative]()(implicit + m: ApplicativeError[F, Throwable] + ) extends Calculator[F] { + override def calculate(f: => Int): F[Int] = Try(f) match { + case Success(res) => res.pure[F] + case Failure(_) => + m.raiseError[Int](new RuntimeException("Calculation failed")) + } + } + +} diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/LazyIO.scala b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/LazyIO.scala similarity index 100% rename from cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/LazyIO.scala rename to cats-effects/src/main/scala/com/baeldung/scala/catseffects/LazyIO.scala diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/MissileLaucher.scala b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/MissileLaucher.scala similarity index 100% rename from cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/MissileLaucher.scala rename to cats-effects/src/main/scala/com/baeldung/scala/catseffects/MissileLaucher.scala diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/NotParallelApp.scala b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/NotParallelApp.scala similarity index 100% rename from cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/NotParallelApp.scala rename to cats-effects/src/main/scala/com/baeldung/scala/catseffects/NotParallelApp.scala diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/ParallelApp.scala b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/ParallelApp.scala similarity index 100% rename from cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/ParallelApp.scala rename to cats-effects/src/main/scala/com/baeldung/scala/catseffects/ParallelApp.scala diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/SequenceApp.scala b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/SequenceApp.scala similarity index 91% rename from cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/SequenceApp.scala rename to cats-effects/src/main/scala/com/baeldung/scala/catseffects/SequenceApp.scala index 5ef90977d..e74cf2d67 100644 --- a/cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/SequenceApp.scala +++ b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/SequenceApp.scala @@ -12,5 +12,5 @@ object SequenceApp extends IOApp { sequenceAllTasks.map(_.mkString(", ")).flatMap(putStr) override def run(args: List[String]): IO[ExitCode] = - sequenceAllTasks.as(ExitCode.Success) + printTaskSequence.as(ExitCode.Success) } diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/Substitution.scala b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/Substitution.scala similarity index 100% rename from cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/Substitution.scala rename to cats-effects/src/main/scala/com/baeldung/scala/catseffects/Substitution.scala diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/TraverseApp.scala b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/TraverseApp.scala similarity index 100% rename from cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/TraverseApp.scala rename to cats-effects/src/main/scala/com/baeldung/scala/catseffects/TraverseApp.scala diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/Utils.scala b/cats-effects/src/main/scala/com/baeldung/scala/catseffects/Utils.scala similarity index 100% rename from cats-effects/src/main/scala-2/com/baeldung/scala/catseffects/Utils.scala rename to cats-effects/src/main/scala/com/baeldung/scala/catseffects/Utils.scala diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/differences/Differences.scala b/cats-effects/src/main/scala/com/baeldung/scala/differences/Differences.scala similarity index 100% rename from cats-effects/src/main/scala-2/com/baeldung/scala/differences/Differences.scala rename to cats-effects/src/main/scala/com/baeldung/scala/differences/Differences.scala diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/differences/FlatEvalDiff.scala b/cats-effects/src/main/scala/com/baeldung/scala/differences/FlatEvalDiff.scala similarity index 97% rename from cats-effects/src/main/scala-2/com/baeldung/scala/differences/FlatEvalDiff.scala rename to cats-effects/src/main/scala/com/baeldung/scala/differences/FlatEvalDiff.scala index 188c90b18..c83f231f0 100644 --- a/cats-effects/src/main/scala-2/com/baeldung/scala/differences/FlatEvalDiff.scala +++ b/cats-effects/src/main/scala/com/baeldung/scala/differences/FlatEvalDiff.scala @@ -32,7 +32,7 @@ object FlatEvalDiff extends IOApp.Simple { } val resource: Resource[IO, SimpleConnection] = - Resource.make(acquireResource)(releaseResource) + Resource.make(acquireResource())(releaseResource) val simpleResourceData: IO[Unit] = resource.use(simpleConn => IO.println("Result using Simple Resource")) diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/fibers/Fibers.scala b/cats-effects/src/main/scala/com/baeldung/scala/fibers/Fibers.scala similarity index 100% rename from cats-effects/src/main/scala-2/com/baeldung/scala/fibers/Fibers.scala rename to cats-effects/src/main/scala/com/baeldung/scala/fibers/Fibers.scala diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/fibers/IOExtensions.scala b/cats-effects/src/main/scala/com/baeldung/scala/fibers/IOExtensions.scala similarity index 100% rename from cats-effects/src/main/scala-2/com/baeldung/scala/fibers/IOExtensions.scala rename to cats-effects/src/main/scala/com/baeldung/scala/fibers/IOExtensions.scala diff --git a/cats-effects/src/main/scala/com/baeldung/scala/ioapp/ArgumentIOApp.scala b/cats-effects/src/main/scala/com/baeldung/scala/ioapp/ArgumentIOApp.scala new file mode 100644 index 000000000..b06f6d09b --- /dev/null +++ b/cats-effects/src/main/scala/com/baeldung/scala/ioapp/ArgumentIOApp.scala @@ -0,0 +1,15 @@ +package com.baeldung.scala.ioapp + +import cats.effect.{ExitCode, IO, IOApp} + +object ArgumentIOApp extends IOApp { + + def run(args: List[String]): IO[ExitCode] = { + if (args.nonEmpty) { + IO.println(s"Running with args: ${args.mkString(",")}") + .as(ExitCode.Success) + } else { + IO.println("No args provided. Aborting").as(ExitCode(2)) + } + } +} diff --git a/cats-effects/src/main/scala/com/baeldung/scala/ioapp/ContextIOApp.scala b/cats-effects/src/main/scala/com/baeldung/scala/ioapp/ContextIOApp.scala new file mode 100644 index 000000000..0a08947e2 --- /dev/null +++ b/cats-effects/src/main/scala/com/baeldung/scala/ioapp/ContextIOApp.scala @@ -0,0 +1,21 @@ +package com.baeldung.scala.ioapp + +import cats.effect._ +import cats.effect.unsafe.IORuntimeConfig + +import java.util.concurrent.Executors +import scala.concurrent.ExecutionContext + +object ContextIOApp extends IOApp { + + private val customExecutionContext: ExecutionContext = + ExecutionContext.fromExecutor(Executors.newWorkStealingPool(1)) + + override def run(args: List[String]): IO[ExitCode] = { + val computation = IO { + println(s"Running on thread: ${Thread.currentThread().getName}") + } + + computation.evalOn(customExecutionContext).as(ExitCode.Success) + } +} diff --git a/cats-effects/src/main/scala/com/baeldung/scala/ioapp/ResourceIOApp.scala b/cats-effects/src/main/scala/com/baeldung/scala/ioapp/ResourceIOApp.scala new file mode 100644 index 000000000..64a129b5a --- /dev/null +++ b/cats-effects/src/main/scala/com/baeldung/scala/ioapp/ResourceIOApp.scala @@ -0,0 +1,22 @@ +package com.baeldung.scala.ioapp + +import cats.effect.{ExitCode, IO, IOApp, Resource} + +import java.util.concurrent.Executors +import scala.concurrent.ExecutionContext + +object ResourceIOApp extends IOApp { + + val customExecutionContext: Resource[IO, ExecutionContext] = + Resource.make( + IO(ExecutionContext.fromExecutorService(Executors.newWorkStealingPool(1))) + ) { ec => + IO.println("Shutting down execution contest").as(ec.shutdown()) + } + + def run(args: List[String]): IO[ExitCode] = customExecutionContext + .use { ec => + IO.println(s"Running on thread: ${Thread.currentThread().getName}") + } + .as(ExitCode.Success) +} diff --git a/cats-effects/src/main/scala/com/baeldung/scala/ioapp/SimpleIOApp.scala b/cats-effects/src/main/scala/com/baeldung/scala/ioapp/SimpleIOApp.scala new file mode 100644 index 000000000..706b59f7d --- /dev/null +++ b/cats-effects/src/main/scala/com/baeldung/scala/ioapp/SimpleIOApp.scala @@ -0,0 +1,9 @@ +package com.baeldung.scala.ioapp + +import cats.effect.{IO, IOApp} + +object SimpleIOApp extends IOApp.Simple { + val run: IO[Unit] = { + IO.println("Running with simple app") + } +} diff --git a/cats-effects/src/main/scala-2/com/baeldung/scala/resources/ResourceHandling.scala b/cats-effects/src/main/scala/com/baeldung/scala/resources/ResourceHandling.scala similarity index 100% rename from cats-effects/src/main/scala-2/com/baeldung/scala/resources/ResourceHandling.scala rename to cats-effects/src/main/scala/com/baeldung/scala/resources/ResourceHandling.scala diff --git a/cats-effects/src/test/scala/com/baeldung/scala/catseffects/CancellationUnitTest.scala b/cats-effects/src/test/scala/com/baeldung/scala/catseffects/CancellationUnitTest.scala new file mode 100644 index 000000000..21c24f332 --- /dev/null +++ b/cats-effects/src/test/scala/com/baeldung/scala/catseffects/CancellationUnitTest.scala @@ -0,0 +1,140 @@ +package com.baeldung.scala.catseffects + +import cats.effect.kernel.CancelScope.Cancelable +import cats.effect.{IO, Resource} +import cats.effect.unsafe.implicits.global +import cats.implicits.catsSyntaxTuple2Parallel +import org.scalatest.{Failed, Outcome, Retries, Canceled} +import org.scalatest.matchers.should.Matchers +import org.scalatest.tags.Retryable +import org.scalatest.wordspec.AnyWordSpec + +import java.util.concurrent.atomic.AtomicBoolean +import scala.concurrent.duration.DurationInt + +@Retryable +class CancellationUnitTest extends AnyWordSpec with Matchers with Retries { + + val retries = 5 + + override def withFixture(test: NoArgTest): Outcome = { + if (isRetryable(test)) withFixture(test, retries) + else super.withFixture(test) + } + + def withFixture(test: NoArgTest, count: Int): Outcome = { + val outcome = super.withFixture(test) + outcome match { + case Failed(_) | Canceled(_) => + if (count == 1) super.withFixture(test) + else { + println( + s"Retrying SchedulerUnitTest flaky test `${test.name}`, Attempts remaining: ${count - 1}" + ) + // scheduling the retry after 1 second + withRetry(1.seconds)(withFixture(test, count - 1)) + } + case other => other + } + } + + "Cancellation" should { + "cancel the fiber directly and execute the action on cancellation" in { + val io = IO.println(s"ready to cancel the fiber") >> IO.sleep( + 15.millisecond + ) >> IO.println("Hello, World!") + def onCancel(flag: AtomicBoolean) = + IO.println(s"cancellation signal received") >> IO.delay( + flag.set(true) + ) >> + IO.println(s"flag after the cancellation = ${flag.get()}") + val flag = new AtomicBoolean(false) + + val cancelFiber = Cancellation.cancelFiberDirectlySafe(io, onCancel(flag)) + val res = cancelFiber + .flatMap(_ => + IO.println(s"checking the flag: flag = ${flag.get()}") >> + IO.delay(flag.get()) + ) + .unsafeRunSync() + res shouldBe true + } + + "naive parMap works" in { + def tickingClock: IO[Unit] = + for { + _ <- IO.println(s"current time = ${System.currentTimeMillis()}") + _ <- IO.sleep(15.millisecond) + _ <- tickingClock + } yield () + val error: IO[Unit] = + IO.sleep(60.millisecond) *> IO.raiseError(new RuntimeException("boom!")) + + val parMapNaive_2 = Cancellation.naiveParMap_2( + tickingClock, + error, + IO.println("[2] tickingClock was cancelled"), + IO.println("[2] error was cancelled"), + (_: Unit, _: Unit) => println("[2] Exit") + ) + + val res = parMapNaive_2.handleError(e => e.getMessage).unsafeRunSync() + res shouldBe "boom!" + } + + "send emails if the cancellation signal came during the sending process" in { + val sendEmails: IO[Unit] = + Cancellation + .sendEmailsUncancelable("Exam on FP is shifted to Jan 11, 2024") + .onCancel(IO.println("cancellation signal received")) + IO.race(IO.sleep(1.seconds), sendEmails).unsafeRunSync() + } + + "if one of the effects running in parallel throws an error, the second one in cancelled, but it won't affect uncancelalble regions" in { + val flag = new AtomicBoolean(false) + val res = ( + IO.sleep(15.millisecond) >> IO.raiseError( + new RuntimeException("Boom!") + ), + IO.uncancelable(_ => + IO.sleep(30.millisecond) >> IO.println("Hey there") >> IO.delay( + flag.set(true) + ) + ) + ).parTupled + .handleError(_ => flag.get) + .unsafeRunSync() + res shouldBe true + } + + "partially cancel the unancelable IO" in { + var counter = 0 + val example: IO[Unit] = { + val flag = new AtomicBoolean(false) + val ioa = IO.blocking { + while (!flag.get()) { + Thread.sleep(25) + println(s"counter = $counter") + counter += 1 + } + } + + ioa.cancelable( + IO.println("executing the finalizer...") >> IO.delay(flag.set(true)) + ) + } + + (for { + fiber <- example.start + _ <- IO.sleep(75.millisecond) + _ <- IO.println("cancelling the fiber") + _ <- fiber.cancel + _ <- fiber.join + } yield ()).unsafeRunSync() + + counter <= 6 shouldBe true + } + + } + +} diff --git a/cats-effects/src/test/scala/com/baeldung/scala/catseffects/ErrorHandlingUnitTest.scala b/cats-effects/src/test/scala/com/baeldung/scala/catseffects/ErrorHandlingUnitTest.scala new file mode 100644 index 000000000..359f7d675 --- /dev/null +++ b/cats-effects/src/test/scala/com/baeldung/scala/catseffects/ErrorHandlingUnitTest.scala @@ -0,0 +1,323 @@ +package com.baeldung.scala.catseffects + +import cats.effect.IO +import cats.effect.unsafe.implicits.global +import cats.{Applicative, ApplicativeError, MonadError, MonadThrow} +import com.baeldung.scala.catseffects.ErrorHandling.{Calculator, CalculatorImpl} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +import scala.util.Try +import scala.util.control.NoStackTrace; + +class ErrorHandlingUnitTest extends AnyWordSpec with Matchers { + + "Calculator" should { + "return left value using attempt in case of division by zero" in { + val calculator = new CalculatorImpl[IO]() + val res = calculator.calculate(5 / 0).attempt.unsafeRunSync() + res.isLeft shouldBe true + } + } + + import ErrorHandlingSpec._ + + "IO error handling built in methods" should { + import cats.syntax.monadError._ + "adaptError" in { + def calculate(f: => Int): IO[Int] = + IO(f).adaptError { case _: ArithmeticException => + new RuntimeException("Calculation failed") + } + + val res = calculate(5 / 0) + res + .handleError(e => e.getMessage) + .unsafeRunSync() shouldBe "Calculation failed" + } + + "recover from the errors" in { + def calculate(f: => Int): IO[Int] = { + IO(f).recover { + case _: ArithmeticException => -1 + case _: NumberFormatException => -2 + } + } + + val res_0 = calculate(5 / 0) + res_0.unsafeRunSync() shouldBe -1 + + val res_1 = calculate("5 * 3".toInt / 1) + res_1.unsafeRunSync() shouldBe -2 + + val res_2 = calculate(15 / 1) + res_2.unsafeRunSync() shouldBe 15 + } + + "execute some actions onError" in { + var status = 0 + def calculate(f: => Int): IO[Int] = + IO(f) + .onError { + case _: ArithmeticException => IO { status = -1 } + case _: NumberFormatException => IO { status = -2 } + } + .handleError { + case _: ArithmeticException => -1 + case _: NumberFormatException => -2 + } + calculate(5 / 0).unsafeRunSync() shouldBe -1 + status shouldBe -1 + + calculate("5 * 3".toInt).unsafeRunSync() shouldBe -2 + status shouldBe -2 + } + "execute action in the middle of error handling" in { + var errorCounter = 0 + def calculateAndExecuteAction(f: => Int, action: => IO[Unit]): IO[Int] = + IO(f).handleErrorWith { + case _: ArithmeticException => action *> IO.pure(-1) + case _: NumberFormatException => action *> IO.pure(-2) + } + + val res_0 = calculateAndExecuteAction(5 / 0, IO { errorCounter += 1 }) + res_0.unsafeRunSync() shouldBe -1 + errorCounter shouldBe 1 + + val res_1 = + calculateAndExecuteAction("n".toInt / 1, IO { errorCounter += 1 }) + res_1.unsafeRunSync() shouldBe -2 + errorCounter shouldBe 2 + } + } + + "Error handling for Either with ApplicativeError" should { + import cats.syntax.applicative._ + import cats.syntax.applicativeError._ + + "handle error if it occurred right away" in { + type ErrorOr[A] = Either[Throwable, A] + val calculator = new CalculatorImplAE[ErrorOr] + val res = calculator.calculate(5 / 0).handleErrorWith { + case _: ArithmeticException => (-1).pure[ErrorOr] + case _: NumberFormatException => (-2).pure[ErrorOr] + } + res shouldBe Right(-1) + } + + "if the error handling is suspended, then the error handling fails" in { + type ErrorOr[A] = Either[Throwable, A] + val calculator = new CalculatorImplAE[ErrorOr] + val failed = calculator.calculate(5 / 0) + failed.handleErrorWith { + case _: ArithmeticException => (-1).pure[ErrorOr] + case _: NumberFormatException => (-2).pure[ErrorOr] + } + + val recovered = calculator.calculate(5 / 0).handleErrorWith { + case _: ArithmeticException => (-1).pure[ErrorOr] + case _: NumberFormatException => (-2).pure[ErrorOr] + } + + failed.isLeft shouldBe true + recovered shouldBe Right(-1) + } + } + + "Error handling for Either with MonadError" should { + import cats.syntax.applicative._ + import cats.syntax.applicativeError._ + import cats.syntax.monadError._ + + "raise error if the predicate fails via ensure" in { + type ErrorOr[A] = Either[Throwable, A] + val calculator = new CalculatorImplME[ErrorOr] + + val resValid = calculator + .calculate(5 / 1) + .ensure(new RuntimeException("Bad result"))(_ > 0) + val resInvalid = calculator + .calculate(-1) + .ensure(new RuntimeException("Bad result"))(_ > 0) + + resValid shouldBe Right(5) + resInvalid.isLeft shouldBe true + } + + "raise error if the predicate fails via flatMap" in { + type ErrorOr[A] = Either[Throwable, A] + val calculator = new CalculatorImplME[ErrorOr] + + val resValid = calculator.calculate(5 / 1).flatMap { + case x if x > 0 => x.pure[ErrorOr] + case _ => new RuntimeException("Bad result").raiseError[ErrorOr, Int] + } + val resInvalid = calculator + .calculate(-1) + .ensure(new RuntimeException("Bad result"))(_ > 0) + + resValid shouldBe Right(5) + resInvalid.isLeft shouldBe true + } + + "rethrow the error if it is inside the Either" in { + val calculator = new CalculatorImplME[IO] + def calculateSafely(f: => Int): IO[Int] = + calculator.calculate(f).attempt.rethrow.handleErrorWith { + case _: ArithmeticException => IO.pure(-1) + case _: NumberFormatException => IO.pure(-2) + } + + val resInvalid = calculateSafely(5 / 0) + val resValid = calculateSafely(5 / 1) + + resInvalid.unsafeRunSync() shouldBe -1 + resValid.unsafeRunSync() shouldBe 5 + } + + "rethrow the error" in { + import cats.syntax.applicative._ + import cats.syntax.applicativeError._ + def calculate[F[_]: Applicative](f: => Int)(implicit + ae: ApplicativeError[F, Throwable] + ): F[Either[Throwable, Int]] = + ae.fromTry(Try(f)).attempt + + def calculateSafely[F[_]](f: => Int)(implicit me: MonadThrow[F]): F[Int] = + calculate(f).rethrow.handleErrorWith { + case _: ArithmeticException => (-1).pure[F] + case _: NumberFormatException => (-2).pure[F] + } + + val resInvalid = calculateSafely[IO](5 / 0) + val resValid = calculateSafely[IO](5 / 1) + + resInvalid.unsafeRunSync() shouldBe -1 + resValid.unsafeRunSync() shouldBe 5 + } + + "attemptTap returns the error if it occurs and returns the value otherwise" in { + import cats.syntax.applicative._ + def calculate[F[_]: Applicative](f: => Int)(implicit + ae: ApplicativeError[F, Throwable] + ): F[Int] = + ae.fromTry(Try(f)) + + def calculateOrRaise[F[_]](f: => Int)(implicit + me: MonadThrow[F] + ): F[Int] = { + calculate(f).attemptTap { + case Left(_) => + new RuntimeException("Calculation failed").raiseError[F, Unit] + case Right(_) => ().pure[F] + } + } + + val resInvalid = calculateOrRaise[IO](5 / 0).attempt + val resValid = calculateOrRaise[IO](5 / 1) + + resInvalid.unsafeRunSync().isLeft shouldBe true + resValid.unsafeRunSync() shouldBe 5 + } + + "if recover throws the error, then this error will be returned" in { + def calculate[F[_]: Applicative](f: => Int)(implicit + ae: ApplicativeError[F, Throwable] + ): F[Int] = + ae.fromTry(Try(f)) + + def recover[F[_]]()(implicit ae: ApplicativeError[F, Throwable]): F[Int] = + new RuntimeException("Calculation failed").raiseError[F, Int] + + def calculateOrRaise[F[_]](f: => Int)(implicit + me: MonadThrow[F] + ): F[Int] = + calculate(f).recoverWith { + case _: ArithmeticException => recover() + case _: NumberFormatException => recover() + } + + val resInvalid = calculateOrRaise[IO](5 / 0).handleErrorWith { + case _: RuntimeException => IO.pure(-1) + case _ => IO.pure(-2) + } + val resValid = calculateOrRaise[IO](5 / 1) + + resInvalid.unsafeRunSync() shouldBe -1 + resValid.unsafeRunSync() shouldBe 5 + } + + } + + "Domain errors " should { + "be thrown if there's an implicit instance of MonadError[F, DomainError]" in { + object ErrorHandlingSpec { + import cats.syntax.applicative._ + + sealed trait DomainError extends NoStackTrace + case object NotFound extends DomainError + case object InvalidInput extends DomainError + + trait RaiseCustomError[F[_]] { + def raiseCustomError[A](e: DomainError): F[A] + } + + object RaiseCustomError { + implicit def instance[F[_]](implicit + M: MonadError[F, Throwable] + ): RaiseCustomError[F] = + new RaiseCustomError[F] { + def raiseCustomError[A](e: DomainError): F[A] = M.raiseError(e) + } + } + + def serve[F[_]: Applicative](inOpt: Option[String])(implicit + R: RaiseCustomError[F] + ): F[String] = + inOpt match { + case None => R.raiseCustomError(NotFound) + case Some(in) if in.isEmpty => R.raiseCustomError(InvalidInput) + case Some(in) => in.pure[F] + } + } + import ErrorHandlingSpec._ + val resNone = serve[IO](None).handleErrorWith { + case NotFound => IO.pure("Not found") + case InvalidInput => IO.pure("Invalid input") + } + val resEmpty = serve[IO](Some("")).handleErrorWith { + case NotFound => IO.pure("Not found") + case InvalidInput => IO.pure("Invalid input") + } + + resNone.unsafeRunSync() shouldBe "Not found" + resEmpty.unsafeRunSync() shouldBe "Invalid input" + } + } + +} + +object ErrorHandlingSpec { + + class CalculatorImplAE[F[_]](implicit ae: ApplicativeError[F, Throwable]) + extends Calculator[F] { + override def calculate(f: => Int): F[Int] = ae.fromTry(Try(f)) + } + + class CalculatorImplME[F[_]](implicit me: MonadError[F, Throwable]) + extends Calculator[F] { + import cats.syntax.applicative._ + import cats.syntax.applicativeError._ + import cats.syntax.functor._ + + override def calculate(f: => Int): F[Int] = { + me.fromTry(Try(f)).handleErrorWith { + case e: ArithmeticException => + println(e.getMessage).pure[F].map(_ => -1) + case e: NumberFormatException => + println(e.getMessage).pure[F].map(_ => -2) + } + } + } + +} diff --git a/doobie/README.md b/doobie/README.md deleted file mode 100644 index aeb6d1b89..000000000 --- a/doobie/README.md +++ /dev/null @@ -1,3 +0,0 @@ -### Relevant Articles: - -- [Introduction to doobie – a JDBC Layer for Scala](https://www.baeldung.com/scala/doobie-intro) diff --git a/internal-scripts/ClassRenamer.scala b/internal-scripts/ClassRenamer.scala new file mode 100644 index 000000000..21c21b0aa --- /dev/null +++ b/internal-scripts/ClassRenamer.scala @@ -0,0 +1,75 @@ +/** This is an internal scala-cli script written just to reduce manual work of + * renaming files and class names to follow naming standards. This doesn't + * fully correct all the class names, however reduces the manual effort by 90%. + */ +//> using toolkit default +import os._ + +object RenameClassNames { + def main(args: Array[String]): Unit = { + val directory = os.pwd / os.up + + os.walk(directory) + .filter(_.ext == "scala") + .filter(_.toString.contains("src/test/scala")) + .filterNot(_.toString.endsWith("UnitTest.scala")) + .filter(f => + f.toString.endsWith("Test.scala") || f.toString.endsWith( + "Spec.scala" + ) || f.toString.endsWith("Tests.scala") || f.toString + .endsWith("Suite.scala") + ) + .foreach { _filePath => + val _fileName = _filePath.last + + val newFileName = _filePath.toString + .replace("Test.scala", "UnitTest.scala") + .replace("Spec.scala", "UnitTest.scala") + .replace("Suite.scala", "UnitTest.scala") + .replace("Tests.scala", "UnitTest.scala") + + // rename file + val newFilePath = os.Path(newFileName) + os.move(_filePath, newFilePath) + + val content = os.read(newFilePath) + val existingClassName = getClassFromContent(content) + + val fileNameWithoutExtension = newFilePath.last.dropRight(6) + + def isTestClass(existingClassName: String): Boolean = { + existingClassName + .endsWith("Spec") || existingClassName.endsWith("Test") + } + + // Rename the class if it doesn't match the filename + if ( + isTestClass( + existingClassName + ) && existingClassName.toLowerCase != fileNameWithoutExtension.toLowerCase + ) { + // Update the content with the new class name + val updatedContent = content.replaceAll( + s"class\\s+$existingClassName", + s"class $fileNameWithoutExtension" + ) + + // Now, rename the class + os.write.over(newFilePath, updatedContent) + println( + s"Renamed class in ${newFilePath.last} to $fileNameWithoutExtension" + ) + } + } + } + + // Extract class name from file content + def getClassFromContent(content: String): String = { + val classNameRegex = """class\s+(\w+)""".r + val matchResult = classNameRegex.findFirstMatchIn(content) + matchResult match { + case Some(m) => m.group(1) + case None => "" + } + } +} diff --git a/play-scala/application-tests/app/com/baeldung/arrival/db/manager/DbManager.scala b/play-scala/application-tests/app/com/baeldung/arrival/db/manager/DbManager.scala index e901d2585..8fe37f96f 100644 --- a/play-scala/application-tests/app/com/baeldung/arrival/db/manager/DbManager.scala +++ b/play-scala/application-tests/app/com/baeldung/arrival/db/manager/DbManager.scala @@ -7,7 +7,7 @@ import scala.concurrent.Future trait DbManager { - def dbConfig: DatabaseConfig[_] + def dbConfig: DatabaseConfig[?] def execute[T](dbio: DBIO[T]): Future[T] = dbConfig.db.run(dbio) diff --git a/play-scala/application-tests/app/com/baeldung/arrival/db/repository/SlickArrivalRepository.scala b/play-scala/application-tests/app/com/baeldung/arrival/db/repository/SlickArrivalRepository.scala index dfb13c55e..62a9296f5 100644 --- a/play-scala/application-tests/app/com/baeldung/arrival/db/repository/SlickArrivalRepository.scala +++ b/play-scala/application-tests/app/com/baeldung/arrival/db/repository/SlickArrivalRepository.scala @@ -26,7 +26,7 @@ class SlickArrivalRepository @Inject() (val dbProfile: JdbcProfile)(implicit origin, destination, plane - ) <> ((Arrival.apply _).tupled, Arrival.unapply) + ) <> ((Arrival.apply).tupled, Arrival.unapply) } /** The starting point for all queries on the people table. diff --git a/play-scala/application-tests/app/com/baeldung/arrival/service/ArrivalDecoratorService.scala b/play-scala/application-tests/app/com/baeldung/arrival/service/ArrivalDecoratorService.scala index e21795b1e..9e8003fc4 100644 --- a/play-scala/application-tests/app/com/baeldung/arrival/service/ArrivalDecoratorService.scala +++ b/play-scala/application-tests/app/com/baeldung/arrival/service/ArrivalDecoratorService.scala @@ -19,7 +19,7 @@ class ArrivalDecoratorService @Inject() (configuration: Configuration) { private val maximumMediumNameLength = configuration.get[Int]("medium-name-max") - def decorate(undecorated: Arrival): Arrival with Size = new Arrival( + def decorate(undecorated: Arrival): Arrival & Size = new Arrival( undecorated.planeId, undecorated.origin, undecorated.destination, diff --git a/play-scala/application-tests/build.sbt b/play-scala/application-tests/build.sbt index e5b2266a8..1f2b7a172 100644 --- a/play-scala/application-tests/build.sbt +++ b/play-scala/application-tests/build.sbt @@ -5,12 +5,12 @@ version := "1.0-SNAPSHOT" enablePlugins(PlayScala) -scalaVersion := ScalaVersions.scala2Version +scalaVersion := ScalaVersions.scala3Version libraryDependencies += guice libraryDependencies += "com.h2database" % "h2" % "1.4.200" -libraryDependencies += "org.postgresql" % "postgresql" % "42.2.27" -libraryDependencies += "com.typesafe.play" %% "play-slick" % "5.1.0" -libraryDependencies += "com.typesafe.play" %% "play-slick-evolutions" % "5.1.0" -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.1.0" % Test +libraryDependencies += "org.postgresql" % "postgresql" % "42.2.29" +libraryDependencies += "org.playframework" %% "play-slick" % LibraryVersions.playSlickVersion +libraryDependencies += "org.playframework" %% "play-slick-evolutions" % LibraryVersions.playSlickVersion +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % LibraryVersions.scalatestPlayVersion % Test libraryDependencies += "org.scalatestplus" %% "mockito-3-4" % "3.2.10.0" % Test diff --git a/play-scala/application-tests/project/build.properties b/play-scala/application-tests/project/build.properties deleted file mode 100644 index 563a014da..000000000 --- a/play-scala/application-tests/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.7.2 diff --git a/play-scala/application-tests/project/plugins.sbt b/play-scala/application-tests/project/plugins.sbt index 8846622ec..3b68e9317 100644 --- a/play-scala/application-tests/project/plugins.sbt +++ b/play-scala/application-tests/project/plugins.sbt @@ -1,2 +1,2 @@ -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.19") -addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.13.1") +addSbtPlugin("org.playframework" % "sbt-plugin" % LibraryVersions.playVersion) +addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.16.2") diff --git a/play-scala/application-tests/test/com/baeldung/arrival/actions/SourceActionsTest.scala b/play-scala/application-tests/test/com/baeldung/arrival/actions/SourceActionsUnitTest.scala similarity index 78% rename from play-scala/application-tests/test/com/baeldung/arrival/actions/SourceActionsTest.scala rename to play-scala/application-tests/test/com/baeldung/arrival/actions/SourceActionsUnitTest.scala index 1ed3d1b5e..32d3b6398 100644 --- a/play-scala/application-tests/test/com/baeldung/arrival/actions/SourceActionsTest.scala +++ b/play-scala/application-tests/test/com/baeldung/arrival/actions/SourceActionsUnitTest.scala @@ -7,8 +7,9 @@ import play.api.http.Status.{BAD_REQUEST, NO_CONTENT} import play.api.mvc.Headers import play.api.mvc.Results.NoContent import play.api.test.{FakeRequest, Helpers} +import play.api.mvc.{Action, AnyContent} -class SourceActionsTest +class SourceActionsUnitTest extends AnyWordSpec with SourceActions with ScalaFutures { @@ -20,7 +21,8 @@ class SourceActionsTest "SourceAction" should { "return BAD_REQUEST status for missing source header" in { - val testee = SourceAction(anyContentParser)(globalEc) { _ => NoContent } + val testee: Action[AnyContent] = + SourceAction(anyContentParser)(globalEc) { _ => NoContent } whenReady(testee.apply(FakeRequest())) { result => assert(result.header.status === BAD_REQUEST) @@ -28,7 +30,8 @@ class SourceActionsTest } "return NO_CONTENT status for when source header is present" in { - val testee = SourceAction(anyContentParser)(globalEc) { _ => NoContent } + val testee: Action[AnyContent] = + SourceAction(anyContentParser)(globalEc) { _ => NoContent } whenReady( testee.apply(FakeRequest().withHeaders(Headers("source" -> "foo"))) ) { result => diff --git a/play-scala/application-tests/test/com/baeldung/arrival/controller/ArrivalControllerH2Test.scala b/play-scala/application-tests/test/com/baeldung/arrival/controller/ArrivalControllerH2UnitTest.scala similarity index 91% rename from play-scala/application-tests/test/com/baeldung/arrival/controller/ArrivalControllerH2Test.scala rename to play-scala/application-tests/test/com/baeldung/arrival/controller/ArrivalControllerH2UnitTest.scala index 9d0ae0e79..e959b6ac6 100644 --- a/play-scala/application-tests/test/com/baeldung/arrival/controller/ArrivalControllerH2Test.scala +++ b/play-scala/application-tests/test/com/baeldung/arrival/controller/ArrivalControllerH2UnitTest.scala @@ -11,14 +11,14 @@ import play.api.libs.ws.{WSClient, WSResponse} import scala.concurrent.Future -class ArrivalControllerH2Test +class ArrivalControllerH2UnitTest extends AnyWordSpec with WsScalaTestClient with GuiceOneServerPerTest with ScalaFutures with H2ApplicationFactory { - private implicit def wsClient = app.injector.instanceOf[WSClient] + private implicit def wsClient: WSClient = app.injector.instanceOf[WSClient] "ArrivalController#index" should { "return arrivals using h2" in { diff --git a/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalDecoratorServiceTest.scala b/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalDecoratorServiceUnitTest.scala similarity index 60% rename from play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalDecoratorServiceTest.scala rename to play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalDecoratorServiceUnitTest.scala index ad5a6d609..259ea5b77 100644 --- a/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalDecoratorServiceTest.scala +++ b/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalDecoratorServiceUnitTest.scala @@ -5,7 +5,7 @@ import org.scalatestplus.play.MixedPlaySpec import play.api.Configuration import play.api.inject.guice.GuiceApplicationBuilder -class ArrivalDecoratorServiceTest extends MixedPlaySpec { +class ArrivalDecoratorServiceUnitTest extends MixedPlaySpec { "ArrivalDecoratorService#decorate" should { "mark as short an arrival with plane name length = 5" in new App( @@ -17,11 +17,13 @@ class ArrivalDecoratorServiceTest extends MixedPlaySpec { ) .build() ) { - private val testee = app.injector.instanceOf[ArrivalDecoratorService] - private val arrival = Arrival(1L, "Athens", "Heathrow", "12345") - assert(testee.decorate(arrival).short) - assert(!testee.decorate(arrival).medium) - assert(!testee.decorate(arrival).long) + override def running() = { + val testee = app.injector.instanceOf[ArrivalDecoratorService] + val arrival = Arrival(1L, "Athens", "Heathrow", "12345") + assert(testee.decorate(arrival).short) + assert(!testee.decorate(arrival).medium) + assert(!testee.decorate(arrival).long) + } } "mark as medium an arrival with plane name length = 5 with overridden configuration" in new App( @@ -37,11 +39,13 @@ class ArrivalDecoratorServiceTest extends MixedPlaySpec { ) .build() ) { - private val testee = app.injector.instanceOf[ArrivalDecoratorService] - private val arrival = Arrival(1L, "Athens", "Heathrow", "12345") - assert(!testee.decorate(arrival).short) - assert(testee.decorate(arrival).medium) - assert(!testee.decorate(arrival).long) + override def running() = { + val testee = app.injector.instanceOf[ArrivalDecoratorService] + val arrival = Arrival(1L, "Athens", "Heathrow", "12345") + assert(!testee.decorate(arrival).short) + assert(testee.decorate(arrival).medium) + assert(!testee.decorate(arrival).long) + } } } diff --git a/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceH2Test.scala b/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceH2UnitTest.scala similarity index 96% rename from play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceH2Test.scala rename to play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceH2UnitTest.scala index 6c053ea73..f0459fca0 100644 --- a/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceH2Test.scala +++ b/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceH2UnitTest.scala @@ -8,7 +8,7 @@ import org.scalatestplus.play.guice.GuiceOneAppPerTest import scala.language.postfixOps -class ArrivalServiceH2Test +class ArrivalServiceH2UnitTest extends AnyWordSpec with GuiceOneAppPerTest with ScalaFutures diff --git a/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceIsolatedTest.scala b/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceIsolatedUnitTest.scala similarity index 97% rename from play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceIsolatedTest.scala rename to play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceIsolatedUnitTest.scala index e1c8dd578..aff1c16fd 100644 --- a/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceIsolatedTest.scala +++ b/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceIsolatedUnitTest.scala @@ -13,7 +13,7 @@ import org.scalatestplus.play.guice.GuiceOneAppPerTest import play.api.inject.guice.GuiceApplicationBuilder import play.api.{Application, Configuration, inject} -class ArrivalServiceIsolatedTest +class ArrivalServiceIsolatedUnitTest extends AnyWordSpec with GuiceOneAppPerTest with ScalaFutures { diff --git a/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceMocksTest.scala b/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceMocksUnitTest.scala similarity index 98% rename from play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceMocksTest.scala rename to play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceMocksUnitTest.scala index 0ba97efa1..d2c888827 100644 --- a/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceMocksTest.scala +++ b/play-scala/application-tests/test/com/baeldung/arrival/service/ArrivalServiceMocksUnitTest.scala @@ -15,7 +15,7 @@ import slick.dbio.{DBIO, SuccessAction} import scala.concurrent.Future -class ArrivalServiceMocksTest +class ArrivalServiceMocksUnitTest extends AnyWordSpec with GuiceOneAppPerTest with ScalaFutures diff --git a/play-scala/application-tests/test/com/baeldung/arrival/service/isolated/InMemoryDbManager.scala b/play-scala/application-tests/test/com/baeldung/arrival/service/isolated/InMemoryDbManager.scala index 82b08be5f..4b9934cb2 100644 --- a/play-scala/application-tests/test/com/baeldung/arrival/service/isolated/InMemoryDbManager.scala +++ b/play-scala/application-tests/test/com/baeldung/arrival/service/isolated/InMemoryDbManager.scala @@ -7,7 +7,7 @@ import slick.dbio.{DBIO, SuccessAction} import scala.concurrent.Future class InMemoryDbManager extends DbManager { - override def dbConfig: DatabaseConfig[_] = ??? + override def dbConfig: DatabaseConfig[?] = ??? override def execute[T](dbio: DBIO[T]): Future[T] = Future.successful(dbio.asInstanceOf[SuccessAction[T]].value) diff --git a/play-scala/async-tasks/app/actors/ActorsModule.scala b/play-scala/async-tasks/app/actors/ActorsModule.scala index 8492d6c05..d3f91fc27 100644 --- a/play-scala/async-tasks/app/actors/ActorsModule.scala +++ b/play-scala/async-tasks/app/actors/ActorsModule.scala @@ -1,9 +1,9 @@ package actors import com.google.inject.AbstractModule -import play.libs.akka.AkkaGuiceSupport +import play.libs.pekko.PekkoGuiceSupport -class ActorsModule extends AbstractModule with AkkaGuiceSupport { +class ActorsModule extends AbstractModule with PekkoGuiceSupport { override def configure(): Unit = { bindActor(classOf[AsyncTaskInActor], "async-job-actor") } diff --git a/play-scala/async-tasks/app/actors/AsyncTaskInActor.scala b/play-scala/async-tasks/app/actors/AsyncTaskInActor.scala index 679d381bc..fe4159302 100644 --- a/play-scala/async-tasks/app/actors/AsyncTaskInActor.scala +++ b/play-scala/async-tasks/app/actors/AsyncTaskInActor.scala @@ -1,10 +1,11 @@ package actors -import akka.actor.Actor -import org.joda.time.DateTime +import org.apache.pekko.actor.Actor + +import java.time.LocalDateTime class AsyncTaskInActor extends Actor { override def receive: Receive = { case msg: String => - Console.println(s"Message ${msg} received at ${DateTime.now()}") + Console.println(s"Message ${msg} received at ${LocalDateTime.now()}") } } diff --git a/play-scala/async-tasks/app/controllers/AsyncTaskController.scala b/play-scala/async-tasks/app/controllers/AsyncTaskController.scala index 00ed4208e..44667ca7d 100644 --- a/play-scala/async-tasks/app/controllers/AsyncTaskController.scala +++ b/play-scala/async-tasks/app/controllers/AsyncTaskController.scala @@ -1,9 +1,11 @@ package controllers -import akka.actor.{ActorRef, ActorSystem} +import org.apache.pekko.actor.{ActorRef, ActorSystem} + import javax.inject._ -import org.joda.time.DateTime import play.api.mvc.{Action, AnyContent, BaseController, ControllerComponents} + +import java.time.LocalDateTime import scala.concurrent.ExecutionContext import scala.concurrent.duration._ import scala.language.postfixOps @@ -16,9 +18,9 @@ class AsyncTaskController @Inject() ( )(implicit ec: ExecutionContext) extends BaseController { def runAsync(): Action[AnyContent] = Action { - Console.println(s"In route handler: ${DateTime.now()}") + Console.println(s"In route handler: ${LocalDateTime.now()}") actorSystem.scheduler.scheduleOnce(30 seconds) { - Console.println(s"30 seconds later: ${DateTime.now()}") + Console.println(s"30 seconds later: ${LocalDateTime.now()}") } actor ! "YELLING AT ACTOR" actorSystem.scheduler.scheduleOnce( diff --git a/play-scala/async-tasks/build.sbt b/play-scala/async-tasks/build.sbt index 43f5da87d..f70ccaa23 100644 --- a/play-scala/async-tasks/build.sbt +++ b/play-scala/async-tasks/build.sbt @@ -5,6 +5,6 @@ version := "1.0-SNAPSHOT" enablePlugins(PlayScala) -scalaVersion := ScalaVersions.scala2Version +scalaVersion := ScalaVersions.scala3Version libraryDependencies += guice diff --git a/play-scala/async-tasks/project/plugins.sbt b/play-scala/async-tasks/project/plugins.sbt index 0607b1609..6c59903ab 100644 --- a/play-scala/async-tasks/project/plugins.sbt +++ b/play-scala/async-tasks/project/plugins.sbt @@ -1,2 +1,2 @@ -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.2") +addSbtPlugin("org.playframework" % "sbt-plugin" % LibraryVersions.playVersion) addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0") diff --git a/play-scala/build.sbt b/play-scala/build.sbt index 4d6e85ac7..6927d3c47 100644 --- a/play-scala/build.sbt +++ b/play-scala/build.sbt @@ -9,4 +9,4 @@ lazy val resApi = (project in file("rest-api")) lazy val appTests = (project in file("application-tests")) .settings(Defaults.itSettings) .configs(IntegrationTest) -lazy val playStaticAssets = (project in file("play-static-assets")) \ No newline at end of file +lazy val playStaticAssets = (project in file("play-static-assets")) diff --git a/play-scala/caching-in-play/build.sbt b/play-scala/caching-in-play/build.sbt index 0cae60375..03a3b064b 100644 --- a/play-scala/caching-in-play/build.sbt +++ b/play-scala/caching-in-play/build.sbt @@ -5,12 +5,12 @@ version := "1.0-SNAPSHOT" enablePlugins(PlayScala) -scalaVersion := ScalaVersions.scala2Version +scalaVersion := ScalaVersions.scala3Version libraryDependencies += guice libraryDependencies += caffeine libraryDependencies += ws -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test -libraryDependencies += "org.mockito" % "mockito-core" % "3.12.4" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % LibraryVersions.scalatestPlayVersion % Test +libraryDependencies += "org.mockito" % "mockito-core" % "5.20.0" % Test PlayKeys.devSettings += "play.server.http.port" -> "9000" diff --git a/play-scala/caching-in-play/project/build.properties b/play-scala/caching-in-play/project/build.properties deleted file mode 100644 index 0837f7a13..000000000 --- a/play-scala/caching-in-play/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.3.13 diff --git a/play-scala/caching-in-play/project/plugins.sbt b/play-scala/caching-in-play/project/plugins.sbt index c6220baa7..0fff6a333 100644 --- a/play-scala/caching-in-play/project/plugins.sbt +++ b/play-scala/caching-in-play/project/plugins.sbt @@ -1,3 +1,3 @@ -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.2") +addSbtPlugin("org.playframework" % "sbt-plugin" % LibraryVersions.playVersion) addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.0") diff --git a/play-scala/configuration-access/app/services/MyService.scala b/play-scala/configuration-access/app/services/MyService.scala index ff2b6b470..7d17341b7 100644 --- a/play-scala/configuration-access/app/services/MyService.scala +++ b/play-scala/configuration-access/app/services/MyService.scala @@ -1,17 +1,19 @@ package services import java.util.Date - import com.typesafe.config.Config + import javax.inject.Inject import play.api.{ConfigLoader, Configuration} +import java.text.SimpleDateFormat +import java.time.LocalDateTime import scala.util.Try object ISO8601DateConfigLoader { implicit val iso8601DateConfigLoader: ConfigLoader[Date] = { ConfigLoader(_.getString) - .map[Date](javax.xml.bind.DatatypeConverter.parseDateTime(_).getTime) + .map[Date](new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(_)) } } @@ -31,9 +33,8 @@ object PlayerInfo { config.getString("name"), config.getString("email"), config.getInt("age"), - javax.xml.bind.DatatypeConverter - .parseDateTime(config.getString("signUpDate")) - .getTime, + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss") + .parse(config.getString("signUpDate")), Try(config.getString("twitterHandle")).toOption ) } diff --git a/play-scala/configuration-access/build.sbt b/play-scala/configuration-access/build.sbt index 3bca1bfc9..8da627d85 100644 --- a/play-scala/configuration-access/build.sbt +++ b/play-scala/configuration-access/build.sbt @@ -5,11 +5,11 @@ version := "1.0-SNAPSHOT" enablePlugins(PlayScala) -scalaVersion := ScalaVersions.scala2Version +scalaVersion := ScalaVersions.scala3Version libraryDependencies += guice -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test -libraryDependencies += "org.mockito" % "mockito-scala_2.13" % "1.17.27" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % LibraryVersions.scalatestPlayVersion % Test +libraryDependencies += "org.scalatestplus" %% "mockito-3-4" % "3.2.10.0" // Adds additional packages into Twirl //TwirlKeys.templateImports += "com.baeldung.controllers._" diff --git a/play-scala/configuration-access/project/build.properties b/play-scala/configuration-access/project/build.properties deleted file mode 100644 index 0837f7a13..000000000 --- a/play-scala/configuration-access/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.3.13 diff --git a/play-scala/configuration-access/project/plugins.sbt b/play-scala/configuration-access/project/plugins.sbt index c6220baa7..0fff6a333 100644 --- a/play-scala/configuration-access/project/plugins.sbt +++ b/play-scala/configuration-access/project/plugins.sbt @@ -1,3 +1,3 @@ -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.2") +addSbtPlugin("org.playframework" % "sbt-plugin" % LibraryVersions.playVersion) addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.0") diff --git a/play-scala/configuration-access/test/controllers/HomeControllerUnitTest.scala b/play-scala/configuration-access/test/controllers/HomeControllerUnitTest.scala index 542dab5ae..29b84c8cd 100644 --- a/play-scala/configuration-access/test/controllers/HomeControllerUnitTest.scala +++ b/play-scala/configuration-access/test/controllers/HomeControllerUnitTest.scala @@ -1,11 +1,11 @@ package controllers -import org.mockito.MockitoSugar import org.scalatestplus.play._ import org.scalatestplus.play.guice._ import play.api.test.Helpers._ import play.api.test._ import services.MyService +import org.scalatestplus.mockito.MockitoSugar /** Add your spec here. You can mock out a whole application including requests, * plugins etc. diff --git a/play-scala/configuration-access/test/services/MyServiceUnitTest.scala b/play-scala/configuration-access/test/services/MyServiceUnitTest.scala index 5e0252b07..1fcdccacf 100644 --- a/play-scala/configuration-access/test/services/MyServiceUnitTest.scala +++ b/play-scala/configuration-access/test/services/MyServiceUnitTest.scala @@ -2,10 +2,10 @@ package services import java.util.{Calendar, Date, TimeZone} -import org.mockito.MockitoSugar import org.scalatestplus.play._ import org.scalatestplus.play.guice._ import play.api.test._ +import org.scalatestplus.mockito.MockitoSugar class MyServiceUnitTest extends PlaySpec diff --git a/play-scala/custom-error-handling/build.sbt b/play-scala/custom-error-handling/build.sbt index feb3d5af0..87fd91f40 100644 --- a/play-scala/custom-error-handling/build.sbt +++ b/play-scala/custom-error-handling/build.sbt @@ -5,7 +5,7 @@ version := "1.0-SNAPSHOT" enablePlugins(PlayScala) -scalaVersion := ScalaVersions.scala2Version +scalaVersion := ScalaVersions.scala3Version libraryDependencies += guice -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % LibraryVersions.scalatestPlayVersion % Test diff --git a/play-scala/custom-error-handling/project/build.properties b/play-scala/custom-error-handling/project/build.properties deleted file mode 100644 index 797e7ccfd..000000000 --- a/play-scala/custom-error-handling/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.3.10 diff --git a/play-scala/custom-error-handling/project/plugins.sbt b/play-scala/custom-error-handling/project/plugins.sbt index 0607b1609..6c59903ab 100644 --- a/play-scala/custom-error-handling/project/plugins.sbt +++ b/play-scala/custom-error-handling/project/plugins.sbt @@ -1,2 +1,2 @@ -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.2") +addSbtPlugin("org.playframework" % "sbt-plugin" % LibraryVersions.playVersion) addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0") diff --git a/play-scala/custom-error-handling/test/errors/CustomErrorHandlerUnitTest.scala b/play-scala/custom-error-handling/test/errors/CustomErrorHandlerUnitTest.scala index 2f803c464..3e5babcb4 100644 --- a/play-scala/custom-error-handling/test/errors/CustomErrorHandlerUnitTest.scala +++ b/play-scala/custom-error-handling/test/errors/CustomErrorHandlerUnitTest.scala @@ -1,6 +1,6 @@ package errors -import akka.http.scaladsl.model.StatusCodes +import org.apache.pekko.http.scaladsl.model.StatusCodes import org.scalatest.concurrent.Eventually import org.scalatestplus.play.PlaySpec import org.scalatestplus.play.guice.GuiceOneAppPerTest diff --git a/play-scala/dependency-injection/app/guice/filter/LoggingFilter.scala b/play-scala/dependency-injection/app/guice/filter/LoggingFilter.scala index a17471a1e..23f5db584 100644 --- a/play-scala/dependency-injection/app/guice/filter/LoggingFilter.scala +++ b/play-scala/dependency-injection/app/guice/filter/LoggingFilter.scala @@ -1,6 +1,6 @@ package guice.filter -import akka.stream.Materializer +import org.apache.pekko.stream.Materializer import play.api.Logger import play.api.mvc.{Filter, RequestHeader, Result} import play.api.routing.{HandlerDef, Router} diff --git a/play-scala/dependency-injection/app/macwire/components/OrderComponents.scala b/play-scala/dependency-injection/app/macwire/components/OrderComponents.scala index 00ee58f2b..efe52de92 100644 --- a/play-scala/dependency-injection/app/macwire/components/OrderComponents.scala +++ b/play-scala/dependency-injection/app/macwire/components/OrderComponents.scala @@ -12,7 +12,7 @@ import macwire.services.{ OrderService } -trait OrderComponents extends { +trait OrderComponents { lazy val p1: OrderPipelineProcessor = (order: Order) => println("Processor 1 processed") diff --git a/play-scala/dependency-injection/app/models/Order.scala b/play-scala/dependency-injection/app/models/Order.scala index 7c3f2c55f..df07a0337 100644 --- a/play-scala/dependency-injection/app/models/Order.scala +++ b/play-scala/dependency-injection/app/models/Order.scala @@ -3,7 +3,8 @@ package models import play.api.libs.json.Json object Order { - implicit val writes = Json.writes[Order] + implicit val writes: play.api.libs.json.OWrites[models.Order] = + Json.writes[Order] } case class Order(id: Long, userId: Long, date: Long, isEnterprise: Boolean) diff --git a/play-scala/dependency-injection/app/models/User.scala b/play-scala/dependency-injection/app/models/User.scala index 63a93960a..63336411a 100644 --- a/play-scala/dependency-injection/app/models/User.scala +++ b/play-scala/dependency-injection/app/models/User.scala @@ -5,5 +5,6 @@ import play.api.libs.json.Json case class User(id: Long, name: String) object User { - implicit val writes = Json.writes[User] + implicit val writes: play.api.libs.json.OWrites[models.User] = + Json.writes[User] } diff --git a/play-scala/dependency-injection/build.sbt b/play-scala/dependency-injection/build.sbt index e3836abd7..27a9b3b70 100644 --- a/play-scala/dependency-injection/build.sbt +++ b/play-scala/dependency-injection/build.sbt @@ -4,9 +4,9 @@ version := "1.0-SNAPSHOT" enablePlugins(PlayScala) -scalaVersion := ScalaVersions.scala2Version +scalaVersion := ScalaVersions.scala3Version libraryDependencies += guice -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test -libraryDependencies += "com.softwaremill.macwire" %% "macros" % "2.5.9" % Provided -libraryDependencies += "com.softwaremill.macwire" %% "util" % "2.5.9" +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % LibraryVersions.scalatestPlayVersion % Test +libraryDependencies += "com.softwaremill.macwire" %% "macros" % "2.6.7" % Provided +libraryDependencies += "com.softwaremill.macwire" %% "util" % "2.6.7" diff --git a/play-scala/dependency-injection/project/plugins.sbt b/play-scala/dependency-injection/project/plugins.sbt index bb6d511d8..684105b93 100644 --- a/play-scala/dependency-injection/project/plugins.sbt +++ b/play-scala/dependency-injection/project/plugins.sbt @@ -1,3 +1,3 @@ -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.8") +addSbtPlugin("org.playframework" % "sbt-plugin" % LibraryVersions.playVersion) addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.2") diff --git a/play-scala/dependency-injection/test/guice/service/ServiceWithRemoteCallTest.scala b/play-scala/dependency-injection/test/guice/service/ServiceWithRemoteCallUnitTest.scala similarity index 61% rename from play-scala/dependency-injection/test/guice/service/ServiceWithRemoteCallTest.scala rename to play-scala/dependency-injection/test/guice/service/ServiceWithRemoteCallUnitTest.scala index cfb3ec06e..77ecd4866 100644 --- a/play-scala/dependency-injection/test/guice/service/ServiceWithRemoteCallTest.scala +++ b/play-scala/dependency-injection/test/guice/service/ServiceWithRemoteCallUnitTest.scala @@ -6,13 +6,13 @@ import guice.modules.{ ServiceModule, ServiceWithRemoteCall } -import org.scalatest.{WordSpec, fixture} +import org.scalatest.wordspec.FixtureAnyWordSpecLike import org.scalatestplus.play.MixedFixtures import play.api.inject.guice.GuiceApplicationBuilder -class ServiceWithRemoteCallTest +class ServiceWithRemoteCallUnitTest extends MixedFixtures - with fixture.WordSpecLike { + with FixtureAnyWordSpecLike { "ServiceWithRemoteCall call" should { "invoke mock when remote api is mocked" in { @@ -20,8 +20,10 @@ class ServiceWithRemoteCallTest .overrides(new MockApiModule, new ServiceModule) .build() new App(application) { - val srv = app.injector.instanceOf[ServiceWithRemoteCall] - assert(srv.call() == "Mock remote api call") + override def running() = { + val srv = app.injector.instanceOf[ServiceWithRemoteCall] + assert(srv.call() == "Mock remote api call") + } } } @@ -30,8 +32,10 @@ class ServiceWithRemoteCallTest .overrides(new ApiModule, new ServiceModule) .build() new App(application) { - val srv = app.injector.instanceOf[ServiceWithRemoteCall] - assert(srv.call() == "Real remote api call") + override def running() = { + val srv = app.injector.instanceOf[ServiceWithRemoteCall] + assert(srv.call() == "Real remote api call") + } } } diff --git a/play-scala/dependency-injection/test/macwire/service/ServiceWithRemoteCallTest.scala b/play-scala/dependency-injection/test/macwire/service/ServiceWithRemoteCallUnitTest.scala similarity index 83% rename from play-scala/dependency-injection/test/macwire/service/ServiceWithRemoteCallTest.scala rename to play-scala/dependency-injection/test/macwire/service/ServiceWithRemoteCallUnitTest.scala index 444f637a3..44850fc31 100644 --- a/play-scala/dependency-injection/test/macwire/service/ServiceWithRemoteCallTest.scala +++ b/play-scala/dependency-injection/test/macwire/service/ServiceWithRemoteCallUnitTest.scala @@ -1,9 +1,9 @@ package macwire.service import macwire.components.{ApiComponents, MockApiComponents, ServiceComponents} -import org.scalatest.WordSpec +import org.scalatest.wordspec.AnyWordSpec -class ServiceWithRemoteCallTest extends WordSpec { +class ServiceWithRemoteCallUnitTest extends AnyWordSpec { "ServiceWithRemoteCall call" should { diff --git a/play-scala/introduction-to-play/build.sbt b/play-scala/introduction-to-play/build.sbt index 7f836edcd..1b3551d3e 100644 --- a/play-scala/introduction-to-play/build.sbt +++ b/play-scala/introduction-to-play/build.sbt @@ -5,7 +5,7 @@ version := "1.0-SNAPSHOT" enablePlugins(PlayScala) -scalaVersion := ScalaVersions.scala2Version +scalaVersion := ScalaVersions.scala3Version libraryDependencies += guice -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % LibraryVersions.scalatestPlayVersion % Test diff --git a/play-scala/introduction-to-play/conf/routes b/play-scala/introduction-to-play/conf/routes index 575ecef46..81afe0062 100644 --- a/play-scala/introduction-to-play/conf/routes +++ b/play-scala/introduction-to-play/conf/routes @@ -4,7 +4,7 @@ # ~~~~ # An example controller showing a sample home page -GET / controllers.HomeController.index +GET / controllers.HomeController.index() GET /sum/:first/:second controllers.HomeController.printSum(first: Long, second: Long) # Map static resources from the /public folder to the /assets URL path diff --git a/play-scala/introduction-to-play/project/build.properties b/play-scala/introduction-to-play/project/build.properties deleted file mode 100644 index 797e7ccfd..000000000 --- a/play-scala/introduction-to-play/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.3.10 diff --git a/play-scala/introduction-to-play/project/plugins.sbt b/play-scala/introduction-to-play/project/plugins.sbt index 0607b1609..6c59903ab 100644 --- a/play-scala/introduction-to-play/project/plugins.sbt +++ b/play-scala/introduction-to-play/project/plugins.sbt @@ -1,2 +1,2 @@ -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.2") +addSbtPlugin("org.playframework" % "sbt-plugin" % LibraryVersions.playVersion) addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0") diff --git a/play-scala/play-static-assets/build.sbt b/play-scala/play-static-assets/build.sbt index 80a89c06b..8c56cc80c 100644 --- a/play-scala/play-static-assets/build.sbt +++ b/play-scala/play-static-assets/build.sbt @@ -6,11 +6,10 @@ version := "1.0-SNAPSHOT" enablePlugins(PlayScala) enablePlugins(SbtWeb) -// scalaVersion := ScalaVersions.scala2Version -scalaVersion := "2.13.12" +scalaVersion := ScalaVersions.scala3Version libraryDependencies += guice -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.1.0" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % LibraryVersions.scalatestPlayVersion % Test libraryDependencies += "org.webjars.bower" % "bootstrap-sass" % "3.3.7" // See https://github.com/sbt/sbt-js-engine/issues/68 diff --git a/play-scala/play-static-assets/project/build.properties b/play-scala/play-static-assets/project/build.properties deleted file mode 100644 index 797e7ccfd..000000000 --- a/play-scala/play-static-assets/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.3.10 diff --git a/play-scala/play-static-assets/project/plugins.sbt b/play-scala/play-static-assets/project/plugins.sbt index 50727d31b..7867745dd 100644 --- a/play-scala/play-static-assets/project/plugins.sbt +++ b/play-scala/play-static-assets/project/plugins.sbt @@ -1,6 +1,6 @@ -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.2") +addSbtPlugin("org.playframework" % "sbt-plugin" % LibraryVersions.playVersion) addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0") addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.2") addSbtPlugin("com.github.platypii" % "sbt-typescript" % "4.6.4") addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.1.2") -addSbtPlugin("io.github.irundaia" % "sbt-sassify" % "1.5.2") \ No newline at end of file +addSbtPlugin("io.github.irundaia" % "sbt-sassify" % "1.5.2") diff --git a/play-scala/play-templates/app/controllers/FormController.scala b/play-scala/play-templates/app/controllers/FormController.scala new file mode 100644 index 000000000..2b978c448 --- /dev/null +++ b/play-scala/play-templates/app/controllers/FormController.scala @@ -0,0 +1,94 @@ +package controllers + +import com.google.inject.Inject +import models.{ + ComplexFormCustomField, + InputFormWithConstraints, + MultipleFieldsForm, + SimpleForm +} +import play.api.i18n.I18nSupport +import play.api.libs.json.Json +import play.api.mvc.{ + AbstractController, + Action, + AnyContent, + ControllerComponents +} + +class FormController @Inject() (cc: ControllerComponents) + extends AbstractController(cc) + with I18nSupport { + + def simpleForm: Action[AnyContent] = Action { implicit request => + Ok(views.html.Baeldung.FormTemplate(SimpleForm.form)) + } + + def formPost(): Action[AnyContent] = Action { implicit request => + val form = SimpleForm.form.bindFromRequest().get + Ok(form.toString) + } + + def multipleFieldsForm: Action[AnyContent] = Action { implicit request => + Ok(views.html.Baeldung.MultipleFieldsFormTemplate(MultipleFieldsForm.form)) + } + + def multipleFieldsFormPost(): Action[AnyContent] = Action { + implicit request => + val form = MultipleFieldsForm.form.bindFromRequest().get + Ok(form.toString) + } + + def formWithConstraints: Action[AnyContent] = Action { implicit request => + Ok( + views.html.Baeldung.FormTemplateWithConstraints( + InputFormWithConstraints.form + ) + ) + } + + def formWithConstraintsPost(): Action[AnyContent] = Action { + implicit request => + InputFormWithConstraints.form + .bindFromRequest() + .fold( + { formWithError => + BadRequest( + views.html.Baeldung.FormTemplateWithConstraints(formWithError) + ) + }, + { data => Ok(Json.toJson(data)) } + ) + } + + def simpleFormPostWithErrors(): Action[AnyContent] = Action { + implicit request => + SimpleForm.form + .bindFromRequest() + .fold( + { formWithError => + BadRequest( + views.html.Baeldung.FormTemplateWithErrors(formWithError) + ) + }, + { data => Ok(Json.toJson(data)) } + ) + } + + def complexForm: Action[AnyContent] = Action { implicit request => + Ok(views.html.Baeldung.ComplexFormTemplate(ComplexFormCustomField.form)) + } + + def complexFormPostWithErrors(): Action[AnyContent] = Action { + implicit request => + ComplexFormCustomField.form + .bindFromRequest() + .fold( + { formWithError => + BadRequest(views.html.Baeldung.ComplexFormTemplate(formWithError)) + }, + { data => Ok(Json.toJson(data)) } + ) + } + +} diff --git a/play-scala/play-templates/app/controllers/MenuController.scala b/play-scala/play-templates/app/controllers/MenuController.scala index a5c89c086..33b799c22 100644 --- a/play-scala/play-templates/app/controllers/MenuController.scala +++ b/play-scala/play-templates/app/controllers/MenuController.scala @@ -10,7 +10,7 @@ class MenuController @Inject() ( cc: ControllerComponents ) extends AbstractController(cc) { - def availableProducts = Action { implicit request => + def availableProducts = Action { val products = List( Product("coffee", 8.99, true), Product("cake", 12.00, true), diff --git a/play-scala/play-templates/app/controllers/ViewTemplateController.scala b/play-scala/play-templates/app/controllers/ViewTemplateController.scala index 58dcc126b..b990e4535 100644 --- a/play-scala/play-templates/app/controllers/ViewTemplateController.scala +++ b/play-scala/play-templates/app/controllers/ViewTemplateController.scala @@ -8,7 +8,7 @@ case class Article(title: String, url: String) class ViewTemplateController @Inject() (cc: ControllerComponents) extends AbstractController(cc) { - def index = Action { implicit request => + def index = Action { val articles = List( ( "Introduction to Play Framework", @@ -22,7 +22,7 @@ class ViewTemplateController @Inject() (cc: ControllerComponents) Ok(views.html.Baeldung.index(articles)) } - def withClass = Action { implicit request => + def withClass = Action { val articles = List( Article( "Introduction to Play Framework", diff --git a/play-scala/play-templates/app/models/InputForms.scala b/play-scala/play-templates/app/models/InputForms.scala new file mode 100644 index 000000000..802cfed13 --- /dev/null +++ b/play-scala/play-templates/app/models/InputForms.scala @@ -0,0 +1,189 @@ +package models + +import models.Measure.UnitMeasurement +import play.api.data.Form +import play.api.data.Forms._ +import play.api.data.validation.Constraints.minLength +import play.api.libs.json.{Json, Writes} + +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.{Date, UUID} + +case class SimpleForm(i: Int, active: Boolean, msg: String) + +object SimpleForm { + + implicit val simpleFormWrites: Writes[SimpleForm] = Json.writes[SimpleForm] + val form: Form[SimpleForm] = Form( + mapping( + "i" -> number, + "active" -> boolean, + "msg" -> text + )(SimpleForm.apply)(SimpleForm.unapply) + ) + + def unapply(simpleForm: SimpleForm): Option[(Int, Boolean, String)] = { + Some((simpleForm.i, simpleForm.active, simpleForm.msg)) + } + +} + +case class MultipleFieldsForm( + i: Int, + pwd: String, + active: Boolean, + msg: String, + date: Date, + uuid: UUID, + favMovie: String, + favDrink: String +) + +object MultipleFieldsForm { + + implicit val multipleFieldsFormWrites: Writes[MultipleFieldsForm] = + Json.writes[MultipleFieldsForm] + val form: Form[MultipleFieldsForm] = Form( + mapping( + "i" -> number, + "pwd" -> text, + "active" -> boolean, + "msg" -> text, + "date" -> date, + "uuid" -> uuid, + "favMovie" -> text, + "favDrink" -> text + )(MultipleFieldsForm.apply)(MultipleFieldsForm.unapply) + ) + + def unapply( + multipleFieldsForm: MultipleFieldsForm + ): Option[(Int, String, Boolean, String, Date, UUID, String, String)] = { + Some( + ( + multipleFieldsForm.i, + multipleFieldsForm.pwd, + multipleFieldsForm.active, + multipleFieldsForm.msg, + multipleFieldsForm.date, + multipleFieldsForm.uuid, + multipleFieldsForm.favMovie, + multipleFieldsForm.favDrink + ) + ) + } + + object Movies { + val list = List( + "pulpFiction" -> "Pulp Fiction", + "inception" -> "Inception", + "theMatrix" -> "The Matrix", + "titanic" -> "Titanic" + ) + } + + object Drinks { + val list = List( + "vodka" -> "Vodka", + "tequila" -> "Tequila", + "whisky" -> "Whisky", + "wine" -> "Wine", + "beer" -> "Beer" + ) + } + +} + +case class ComplexFormCustomField( + i: Int, + active: Boolean, + msg: String, + measurement: UnitMeasurement +) +object ComplexFormCustomField { + + implicit val complexFormWrites: Writes[ComplexFormCustomField] = + Json.writes[ComplexFormCustomField] + val form: Form[ComplexFormCustomField] = Form( + mapping( + "i" -> number, + "active" -> boolean, + "msg" -> text, + "measurement" -> Measure.unitMeasurementMapping + )(ComplexFormCustomField.apply)(ComplexFormCustomField.unapply) + ) + + def unapply( + complexForm: ComplexFormCustomField + ): Option[(Int, Boolean, String, UnitMeasurement)] = { + Some( + ( + complexForm.i, + complexForm.active, + complexForm.msg, + complexForm.measurement + ) + ) + } +} + +case class InputFormWithConstraints( + i: Int, + msg: String, + msgOpt: Option[String], + email: String, + birthday: Date +) +object InputFormWithConstraints { + + implicit val inputFormWrites: Writes[InputFormWithConstraints] = + Json.writes[InputFormWithConstraints] + val form: Form[InputFormWithConstraints] = Form( + mapping( + "i" -> number(min = 10, max = 20), + "msg" -> text(minLength = 3, maxLength = 12), + "msgOpt" -> optional(text), + "email" -> email, + "birthday" -> date + )(InputFormWithConstraints.apply)(InputFormWithConstraints.unapply) + ) + + def unapply( + inputForm: InputFormWithConstraints + ): Option[(Int, String, Option[String], String, Date)] = { + Some( + ( + inputForm.i, + inputForm.msg, + inputForm.msgOpt, + inputForm.email, + inputForm.birthday + ) + ) + } +} + +case class InputFormWithCustomConstraints(email: String, birthday: Date) +object InputFormWithCustomConstraints { + + implicit val inputFormWrites: Writes[InputFormWithCustomConstraints] = + Json.writes[InputFormWithCustomConstraints] + val form: Form[InputFormWithCustomConstraints] = Form( + mapping( + "email" -> email.verifying(minLength(15)), + "birthday" -> date.verifying( + "Not 18 years old", + d => d.toInstant.isBefore(Instant.now().minus(18, ChronoUnit.YEARS)) + ) + )(InputFormWithCustomConstraints.apply)( + InputFormWithCustomConstraints.unapply + ) + ) + + def unapply( + inputForm: InputFormWithCustomConstraints + ): Option[(String, Date)] = { + Some((inputForm.email, inputForm.birthday)) + } +} diff --git a/play-scala/play-templates/app/models/Measure.scala b/play-scala/play-templates/app/models/Measure.scala new file mode 100644 index 000000000..ce185aec3 --- /dev/null +++ b/play-scala/play-templates/app/models/Measure.scala @@ -0,0 +1,49 @@ +package models + +import play.api.data.format.{Formats, Formatter} +import play.api.data.{FormError, Forms, Mapping} +import play.api.libs.json.{Json, Writes} + +object Measure { + + def unitMeasurementMapping: Mapping[UnitMeasurement] = + Forms.of[UnitMeasurement] + + case class UnitMeasurement(quantity: Int, unit: String) + + implicit def binder: Formatter[UnitMeasurement] = + new Formatter[UnitMeasurement] { + override def bind( + key: String, + data: Map[String, String] + ): Either[Seq[FormError], UnitMeasurement] = Formats.parsing( + d => UnitMeasurement.fromString(d), + "The format is (\\d*)(\\s)(\\D*)- example: \"1 pound\"", + Nil + )(key, data) + + override def unbind( + key: String, + value: UnitMeasurement + ): Map[String, String] = Map(key -> s"${value.quantity} ${value.unit}") + } + + object UnitMeasurement { + + implicit val unitMeasurementFormWrites: Writes[UnitMeasurement] = + Json.writes[UnitMeasurement] + + private val pattern = "(\\d*)(\\s)(\\D*)".r + + def fromString(str: String): UnitMeasurement = { + val matches = pattern.findAllIn(str) + if (matches.hasNext) { + val List(number, space, quantity) = matches.subgroups + UnitMeasurement(number.toInt, quantity) + } else { + throw new RuntimeException(s"Incorrect data: $str") + } + } + } + +} diff --git a/play-scala/play-templates/app/views/Baeldung/ComplexFormTemplate.scala.html b/play-scala/play-templates/app/views/Baeldung/ComplexFormTemplate.scala.html new file mode 100644 index 000000000..8dd416fb9 --- /dev/null +++ b/play-scala/play-templates/app/views/Baeldung/ComplexFormTemplate.scala.html @@ -0,0 +1,12 @@ +@import helper._ + +@(complexFormCustomField: Form[ComplexFormCustomField])(implicit messages: Messages) + + +@form(action = routes.FormController.complexFormPostWithErrors()) { + @inputText(complexFormCustomField("i")) + @inputText(complexFormCustomField("active")) + @inputText(complexFormCustomField("msg")) + @inputText(complexFormCustomField("measurement")) + +} \ No newline at end of file diff --git a/play-scala/play-templates/app/views/Baeldung/FormTemplate.scala.html b/play-scala/play-templates/app/views/Baeldung/FormTemplate.scala.html new file mode 100644 index 000000000..d2e024201 --- /dev/null +++ b/play-scala/play-templates/app/views/Baeldung/FormTemplate.scala.html @@ -0,0 +1,10 @@ +@import helper._ + +@(simpleForm: Form[SimpleForm])(implicit messages: Messages) + +@form(action = routes.FormController.formPost()) { + @inputText(simpleForm("i")) + @inputText(simpleForm("active")) + @inputText(simpleForm("msg")) + +} \ No newline at end of file diff --git a/play-scala/play-templates/app/views/Baeldung/FormTemplateWithConstraints.scala.html b/play-scala/play-templates/app/views/Baeldung/FormTemplateWithConstraints.scala.html new file mode 100644 index 000000000..1b9486893 --- /dev/null +++ b/play-scala/play-templates/app/views/Baeldung/FormTemplateWithConstraints.scala.html @@ -0,0 +1,13 @@ +@import helper._ + +@(formWithConstraints: Form[InputFormWithConstraints])(implicit messages: Messages) + + +@form(action = routes.FormController.formWithConstraintsPost()) { + @inputText(formWithConstraints("i")) + @inputText(formWithConstraints("msg")) + @inputText(formWithConstraints("msgOpt")) + @inputText(formWithConstraints("email")) + @inputDate(formWithConstraints("birthday")) + +} \ No newline at end of file diff --git a/play-scala/play-templates/app/views/Baeldung/FormTemplateWithErrors.scala.html b/play-scala/play-templates/app/views/Baeldung/FormTemplateWithErrors.scala.html new file mode 100644 index 000000000..5c73b9cf1 --- /dev/null +++ b/play-scala/play-templates/app/views/Baeldung/FormTemplateWithErrors.scala.html @@ -0,0 +1,11 @@ +@import helper._ + +@(simpleForm: Form[SimpleForm])(implicit messages: Messages) + + +@form(action = routes.FormController.formPost()) { + @inputText(simpleForm("i")) + @inputText(simpleForm("active")) + @inputText(simpleForm("msg")) + +} \ No newline at end of file diff --git a/play-scala/play-templates/app/views/Baeldung/MultipleFieldsFormTemplate.scala.html b/play-scala/play-templates/app/views/Baeldung/MultipleFieldsFormTemplate.scala.html new file mode 100644 index 000000000..115acb352 --- /dev/null +++ b/play-scala/play-templates/app/views/Baeldung/MultipleFieldsFormTemplate.scala.html @@ -0,0 +1,15 @@ +@import helper._ + +@(multipleFieldsForm: Form[MultipleFieldsForm])(implicit messages: Messages) + +@form(action = routes.FormController.multipleFieldsFormPost()) { + @inputText(multipleFieldsForm("i")) + @inputPassword(multipleFieldsForm("pwd")) + @checkbox(multipleFieldsForm("active")) + @inputText(multipleFieldsForm("msg")) + @inputDate(multipleFieldsForm("date")) + @inputText(multipleFieldsForm("uuid")) + @inputRadioGroup(multipleFieldsForm("favMovie"), MultipleFieldsForm.Movies.list) + @select(multipleFieldsForm("favDrink"), MultipleFieldsForm.Drinks.list) + +} \ No newline at end of file diff --git a/play-scala/play-templates/build.sbt b/play-scala/play-templates/build.sbt index 0fd477d67..d3a29ae60 100644 --- a/play-scala/play-templates/build.sbt +++ b/play-scala/play-templates/build.sbt @@ -5,7 +5,7 @@ version := "1.0-SNAPSHOT" enablePlugins(PlayScala) -scalaVersion := ScalaVersions.scala2Version +scalaVersion := ScalaVersions.scala3Version libraryDependencies += guice -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % LibraryVersions.scalatestPlayVersion % Test diff --git a/play-scala/play-templates/conf/application.conf b/play-scala/play-templates/conf/application.conf index cb94680e7..4af37f63e 100644 --- a/play-scala/play-templates/conf/application.conf +++ b/play-scala/play-templates/conf/application.conf @@ -1 +1,5 @@ # https://www.playframework.com/documentation/latest/Configuration +play.filters.hosts { + # Allow requests to example.com, its subdomains, and localhost:9000. + allowed = ["192.168.1.16:9000", "192.168.1.19:9000", ".example.com", "localhost:9000"] +} \ No newline at end of file diff --git a/play-scala/play-templates/conf/routes b/play-scala/play-templates/conf/routes index 920af4f2a..e590d7d9c 100644 --- a/play-scala/play-templates/conf/routes +++ b/play-scala/play-templates/conf/routes @@ -1,3 +1,12 @@ GET /template controllers.ViewTemplateController.index() GET /withclass controllers.ViewTemplateController.withClass() -GET /menu controllers.MenuController.availableProducts() \ No newline at end of file +GET /menu controllers.MenuController.availableProducts() +GET /simple-form controllers.FormController.simpleForm() +POST /simple-form controllers.FormController.formPost() +GET /multiple-fields-form controllers.FormController.multipleFieldsForm() +POST /multiple-fields-form controllers.FormController.multipleFieldsFormPost() +GET /form-with-constraints controllers.FormController.formWithConstraints() +POST /form-with-constraints controllers.FormController.formWithConstraintsPost() +POST /simple-form-with-errors controllers.FormController.simpleFormPostWithErrors() +GET /complex-form controllers.FormController.complexForm() +POST /complex-form controllers.FormController.complexFormPostWithErrors() \ No newline at end of file diff --git a/play-scala/play-templates/project/build.properties b/play-scala/play-templates/project/build.properties deleted file mode 100644 index 797e7ccfd..000000000 --- a/play-scala/play-templates/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.3.10 diff --git a/play-scala/play-templates/project/plugins.sbt b/play-scala/play-templates/project/plugins.sbt index 0607b1609..6c59903ab 100644 --- a/play-scala/play-templates/project/plugins.sbt +++ b/play-scala/play-templates/project/plugins.sbt @@ -1,2 +1,2 @@ -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.2") +addSbtPlugin("org.playframework" % "sbt-plugin" % LibraryVersions.playVersion) addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0") diff --git a/play-scala/play-templates/test/controllers/MenuControllerUnitTest.scala b/play-scala/play-templates/test/controllers/MenuControllerUnitTest.scala index 071889919..439c343f3 100644 --- a/play-scala/play-templates/test/controllers/MenuControllerUnitTest.scala +++ b/play-scala/play-templates/test/controllers/MenuControllerUnitTest.scala @@ -17,13 +17,12 @@ class MenuControllerUnitTest val controller = new MenuController(template, stubControllerComponents()) - val result = controller - .availableProducts() + val result = controller.availableProducts .apply( FakeRequest(GET, "/menu") ) - contentAsString(result) must include("coffee 8,99 €") + contentAsString(result) `must` include("coffee 8,99 €") } } } diff --git a/play-scala/play-templates/test/controllers/ViewTemplateControllerUnitTest.scala b/play-scala/play-templates/test/controllers/ViewTemplateControllerUnitTest.scala index f8ec7d11d..1917aa9fc 100644 --- a/play-scala/play-templates/test/controllers/ViewTemplateControllerUnitTest.scala +++ b/play-scala/play-templates/test/controllers/ViewTemplateControllerUnitTest.scala @@ -13,8 +13,7 @@ class ViewTemplateControllerUnitTest "should return two articles" in { val controller = new ViewTemplateController(stubControllerComponents()) - val result = controller - .index() + val result = controller.index .apply( FakeRequest(GET, "/template") ) @@ -27,8 +26,7 @@ class ViewTemplateControllerUnitTest "with_class function returns the same two articles" in { val controller = new ViewTemplateController(stubControllerComponents()) - val result = controller - .withClass() + val result = controller.withClass .apply( FakeRequest(GET, "/withclass") ) diff --git a/play-scala/project/build.properties b/play-scala/project/build.properties deleted file mode 100644 index 875b706a8..000000000 --- a/play-scala/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.9.2 diff --git a/play-scala/rest-api/app/controllers/TodoListController.scala b/play-scala/rest-api/app/controllers/TodoListController.scala index c299f94fb..b624e68cc 100644 --- a/play-scala/rest-api/app/controllers/TodoListController.scala +++ b/play-scala/rest-api/app/controllers/TodoListController.scala @@ -14,8 +14,11 @@ class TodoListController @Inject() ( todoList += TodoListItem(1, "test", true) todoList += TodoListItem(2, "some other value", false) - implicit val todoListJson = Json.format[TodoListItem] - implicit val newTodoListJson = Json.format[NewTodoListItem] + implicit val todoListJson: play.api.libs.json.OFormat[models.TodoListItem] = + Json.format[TodoListItem] + implicit val newTodoListJson + : play.api.libs.json.OFormat[models.NewTodoListItem] = + Json.format[NewTodoListItem] // curl localhost:9000/todo def getAll(): Action[AnyContent] = Action { @@ -51,7 +54,7 @@ class TodoListController @Inject() ( } // curl -v -d '{"description": "some new item"}' -H 'Content-Type: application/json' -X POST localhost:9000/todo - def addNewItem() = Action { implicit request => + def addNewItem(): Action[AnyContent] = Action { request => val content = request.body val jsonObject = content.asJson diff --git a/play-scala/rest-api/build.sbt b/play-scala/rest-api/build.sbt index 970cfb2b3..46e450f99 100644 --- a/play-scala/rest-api/build.sbt +++ b/play-scala/rest-api/build.sbt @@ -5,10 +5,10 @@ version := "1.0-SNAPSHOT" enablePlugins(PlayScala) -scalaVersion := ScalaVersions.scala2Version +scalaVersion := ScalaVersions.scala3Version libraryDependencies += guice -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % LibraryVersions.scalatestPlayVersion % Test // Adds additional packages into Twirl //TwirlKeys.templateImports += "com.baeldung.controllers._" diff --git a/play-scala/rest-api/conf/routes b/play-scala/rest-api/conf/routes index 521a8d3db..f933d081a 100644 --- a/play-scala/rest-api/conf/routes +++ b/play-scala/rest-api/conf/routes @@ -7,8 +7,8 @@ # Map static resources from the /public folder to the /assets URL path -GET /todo controllers.TodoListController.getAll +GET /todo controllers.TodoListController.getAll() GET /todo/:itemId controllers.TodoListController.getById(itemId: Long) PUT /todo/done/:itemId controllers.TodoListController.markAsDone(itemId: Long) -DELETE /todo/done controllers.TodoListController.deleteAllDone -POST /todo controllers.TodoListController.addNewItem \ No newline at end of file +DELETE /todo/done controllers.TodoListController.deleteAllDone() +POST /todo controllers.TodoListController.addNewItem() \ No newline at end of file diff --git a/play-scala/rest-api/project/build.properties b/play-scala/rest-api/project/build.properties deleted file mode 100644 index 797e7ccfd..000000000 --- a/play-scala/rest-api/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.3.10 diff --git a/play-scala/rest-api/project/plugins.sbt b/play-scala/rest-api/project/plugins.sbt index 0607b1609..6c59903ab 100644 --- a/play-scala/rest-api/project/plugins.sbt +++ b/play-scala/rest-api/project/plugins.sbt @@ -1,2 +1,2 @@ -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.2") +addSbtPlugin("org.playframework" % "sbt-plugin" % LibraryVersions.playVersion) addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0") diff --git a/project/LibraryVersions.scala b/project/LibraryVersions.scala new file mode 100644 index 000000000..af25d8c4b --- /dev/null +++ b/project/LibraryVersions.scala @@ -0,0 +1,5 @@ +object LibraryVersions { + val playVersion = "3.0.4" + val playSlickVersion = "6.2.0" + val scalatestPlayVersion = "7.0.2" +} diff --git a/project/ScalaVersions.scala b/project/ScalaVersions.scala index 3ec8a49fd..389d35ad1 100644 --- a/project/ScalaVersions.scala +++ b/project/ScalaVersions.scala @@ -1,8 +1,8 @@ object ScalaVersions { - val scala2Version = "2.13.11" - val scala3Version = "3.3.0" + val scala2Version = "2.13.18" + val scala3Version = "3.7.4" @deprecated( "Use Scala 2.13 or Scala 3. Don't use 2.12 unless it is a specific feature in 2.12." ) - val scala212Version = "2.12.18" + val scala212Version = "2.12.20" } diff --git a/project/build.properties b/project/build.properties index 6cd347fab..b1b10405c 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1,2 +1,2 @@ -sbt.version=1.8.2 +sbt.version=1.11.7 diff --git a/project/plugins.sbt b/project/plugins.sbt index 62316333b..c3d2a15cc 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,19 +1,22 @@ -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.0") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.6") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.10") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.0") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.9") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.20.1") addSbtPlugin("com.lightbend.akka.grpc" % "sbt-akka-grpc" % "2.2.1") -libraryDependencies += "ai.kien" %% "python-native-libs" % "0.2.4" -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.19") -ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always +libraryDependencies += "ai.kien" %% "python-native-libs" % "0.2.5" + +// Make sure to keep the play plugin in sync with the sub modules under play-scala group +addSbtPlugin("org.playframework" % "sbt-plugin" % "3.0.9") -addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.4.2") +addSbtPlugin("org.playframework.twirl" % "sbt-twirl" % "2.0.1") addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.1") -addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") -libraryDependencies += "org.scala-js" %% "scalajs-env-jsdom-nodejs" % "1.0.0" +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.8") +libraryDependencies += "org.scala-js" %% "scalajs-env-jsdom-nodejs" % "1.1.1" resolvers += Resolver.jcenterRepo -addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.8.3") +addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.11.1") + +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always diff --git a/reflection/src/test/scala-2/com/baeldung/reflection/JavaReflectionTest.scala b/reflection/src/test/scala-2/com/baeldung/reflection/JavaReflectionUnitTest.scala similarity index 92% rename from reflection/src/test/scala-2/com/baeldung/reflection/JavaReflectionTest.scala rename to reflection/src/test/scala-2/com/baeldung/reflection/JavaReflectionUnitTest.scala index eb8ebd85d..9eb874f6b 100644 --- a/reflection/src/test/scala-2/com/baeldung/reflection/JavaReflectionTest.scala +++ b/reflection/src/test/scala-2/com/baeldung/reflection/JavaReflectionUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.reflection import org.junit.Test -class JavaReflectionTest { +class JavaReflectionUnitTest { @Test def invoke_method_dynamically(): Unit = { val person = Person("John", 20) diff --git a/reflection/src/test/scala-2/com/baeldung/reflection/ScalaReflectionTest.scala b/reflection/src/test/scala-2/com/baeldung/reflection/ScalaReflectionUnitTest.scala similarity index 98% rename from reflection/src/test/scala-2/com/baeldung/reflection/ScalaReflectionTest.scala rename to reflection/src/test/scala-2/com/baeldung/reflection/ScalaReflectionUnitTest.scala index 3046d8234..08e58cc03 100644 --- a/reflection/src/test/scala-2/com/baeldung/reflection/ScalaReflectionTest.scala +++ b/reflection/src/test/scala-2/com/baeldung/reflection/ScalaReflectionUnitTest.scala @@ -4,7 +4,7 @@ import org.junit.Test import scala.reflect.runtime.{universe => ru} -class ScalaReflectionTest { +class ScalaReflectionUnitTest { @Test def instantiate_a_class_at_runtime_v1: Unit = { val mirror: ru.Mirror = ru.runtimeMirror(getClass.getClassLoader) diff --git a/sbt-release/README.md b/sbt-release/README.md new file mode 100644 index 000000000..b108e24dd --- /dev/null +++ b/sbt-release/README.md @@ -0,0 +1,2 @@ +### Relevant Articles +- [Customizable Release Process Using sbt-release](https://www.baeldung.com/scala/sbt-release) diff --git a/sbt-release/build.sbt b/sbt-release/build.sbt new file mode 100644 index 000000000..1e6b04d38 --- /dev/null +++ b/sbt-release/build.sbt @@ -0,0 +1,42 @@ +import ReleaseTransformations.* + +lazy val root = (project in file(".")) + .settings( + name := "sbt-release" + ) + +releaseVersionFile := file("version/version.sbt") +releaseCrossBuild := true +releaseUseGlobalVersion := false +releaseVersionBump := sbtrelease.Version.Bump.NextStable +publish / skip := true + +releaseTagComment := s"Releasing ${(ThisBuild / version).value} using sbt-release" +releaseCommitMessage := s"Setting version to ${(ThisBuild / version).value} using sbt-release" +releaseNextCommitMessage := s"Setting version to ${(ThisBuild / version).value} using sbt-release" + +releaseNextVersion := (releaseVersion => releaseVersion.split("\\.") match { + case Array(major, minor, bugfix) => + s"$major.$minor.${bugfix.toInt + 1}" +}) + +val customReleaseStep = ReleaseStep(action = step => { + val extracted = Project.extract(step) + val v = extracted.get(Keys.version) + println(s"Custom release step that prints the new version: $v") + step +}) +releaseProcess := Seq[ReleaseStep]( + checkSnapshotDependencies, // : ReleaseStep + inquireVersions, // : ReleaseStep + runClean, // : ReleaseStep + runTest, // : ReleaseStep + setReleaseVersion, // : ReleaseStep + commitReleaseVersion, // : ReleaseStep, performs the initial git checks + tagRelease, // : ReleaseStep + // publishArtifacts, // : ReleaseStep, checks whether `publishTo` is properly set up + setNextVersion, // : ReleaseStep + commitNextVersion, // : ReleaseStep + customReleaseStep + // pushChanges // : ReleaseStep, also checks that an upstream branch is properly configured +) diff --git a/sbt-release/project/build.properties b/sbt-release/project/build.properties new file mode 100644 index 000000000..a360ccac1 --- /dev/null +++ b/sbt-release/project/build.properties @@ -0,0 +1 @@ +sbt.version = 1.11.7 diff --git a/sbt-release/project/plugins.sbt b/sbt-release/project/plugins.sbt new file mode 100644 index 000000000..80fcb2486 --- /dev/null +++ b/sbt-release/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.github.sbt" % "sbt-release" % "1.4.0") \ No newline at end of file diff --git a/sbt-release/src/main/scala/Main.scala b/sbt-release/src/main/scala/Main.scala new file mode 100644 index 000000000..6650aa243 --- /dev/null +++ b/sbt-release/src/main/scala/Main.scala @@ -0,0 +1,5 @@ +object Main { + def main(args: Array[String]): Unit = { + println("Hello world!") + } +} \ No newline at end of file diff --git a/sbt-release/version/version.sbt b/sbt-release/version/version.sbt new file mode 100644 index 000000000..a5f65fc71 --- /dev/null +++ b/sbt-release/version/version.sbt @@ -0,0 +1 @@ +ThisBuild / version := "1.0.0-SNAPSHOT" diff --git a/sbt-standalone/README.md b/sbt-standalone/README.md new file mode 100644 index 000000000..3c3bb4d84 --- /dev/null +++ b/sbt-standalone/README.md @@ -0,0 +1,2 @@ +### Relevant Articles +- [Enforcing the JVM Version in an SBT Project](https://www.baeldung.com/scala/sbt-enforcing-jvm-version) diff --git a/sbt-standalone/build.sbt b/sbt-standalone/build.sbt new file mode 100644 index 000000000..6a03bd976 --- /dev/null +++ b/sbt-standalone/build.sbt @@ -0,0 +1,24 @@ +name := "SCALA-156" + +version := "0.1" + +scalaVersion := "3.4.1" + +// Setting target for Scala +scalacOptions += "-target:17" + +// Setting source and target for Java +javacOptions ++= Seq("-source", "17", "-target", "17") + +// Enforcing the minimum JVM version +initialize := { + val _ = initialize.value // Ensure previous initializations are run + + val required = VersionNumber("17") + val current = VersionNumber(sys.props("java.specification.version")) + println(current) + assert( + CompatibleJavaVersion(current, required), + s"Java $required or above is required to run this project." + ) +} diff --git a/sbt-standalone/project/CompatibleJavaVersion.scala b/sbt-standalone/project/CompatibleJavaVersion.scala new file mode 100644 index 000000000..b9b61fe24 --- /dev/null +++ b/sbt-standalone/project/CompatibleJavaVersion.scala @@ -0,0 +1,16 @@ +import sbt._ + +// Define a custom object for Java specification version compatibility +object CompatibleJavaVersion extends VersionNumberCompatibility { + def name = "Java specification compatibility" + + def isCompatible(current: VersionNumber, required: VersionNumber): Boolean = + current.numbers + .zip(required.numbers) + .foldRight(required.numbers.size <= current.numbers.size) { + case ((curr, req), acc) => (curr > req) || (curr == req && acc) + } + + def apply(current: VersionNumber, required: VersionNumber): Boolean = + isCompatible(current, required) +} diff --git a/sbt-standalone/project/build.properties b/sbt-standalone/project/build.properties new file mode 100644 index 000000000..01a16ed14 --- /dev/null +++ b/sbt-standalone/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.11.7 diff --git a/sbt-standalone/src/main/scala/Main.scala b/sbt-standalone/src/main/scala/Main.scala new file mode 100644 index 000000000..7a5149ffd --- /dev/null +++ b/sbt-standalone/src/main/scala/Main.scala @@ -0,0 +1,3 @@ +object Main extends App { + println("Hello from Scala!") +} diff --git a/scala-2-modules/scala2-core/README.md b/scala-2-modules/scala2-core/README.md new file mode 100644 index 000000000..818ad117f --- /dev/null +++ b/scala-2-modules/scala2-core/README.md @@ -0,0 +1,12 @@ +### Relevant Articles: + +- [Preserving Type Information at Runtime in Scala](https://www.baeldung.com/scala/type-information-at-runtime) +- [Type Disjunction (Union Types) in Scala](https://www.baeldung.com/scala/type-disjunction) +- [Implicit Conversions](https://www.baeldung.com/scala/implicit-conversions) +- [Reading Command-Line Arguments in Scala](https://www.baeldung.com/scala/read-command-line-arguments) +- [Introduction to Scala Macros](https://www.baeldung.com/scala/scala2-macros) +- [Introduction to Macros in Scala 2](https://www.baeldung.com/scala/scala2-macros) +- [Demystifying View and Context Bounds](https://www.baeldung.com/scala/view-context-bounds) +- [Introduction to Scala](https://www.baeldung.com/scala/scala-intro) +- [Exception Handling in Scala](https://www.baeldung.com/scala/exception-handling) +- [Usages of Underscore (_) in Scala](https://www.baeldung.com/scala/underscore) \ No newline at end of file diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/Args4J.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/Args4J.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/Args4J.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/Args4J.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/Clist.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/Clist.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/Clist.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/Clist.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/PatternMatching.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/PatternMatching.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/PatternMatching.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/PatternMatching.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/Scallop.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/Scallop.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/Scallop.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/Scallop.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/ScallopPatternMatching.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/ScallopPatternMatching.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/ScallopPatternMatching.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/ScallopPatternMatching.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/Scopt.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/Scopt.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/Scopt.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/Scopt.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/Sliding.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/Sliding.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/argumentparsing/Sliding.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/argumentparsing/Sliding.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/contextbound.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/contextbound.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/contextbound.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/contextbound.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/conversion.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/conversion.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/conversion.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/conversion.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/implicitconversion.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/implicitconversion.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/implicitconversion.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/implicitconversion.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/implicitobject.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/implicitobject.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/implicitobject.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/implicitobject.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/implicitvalues.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/implicitvalues.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/implicitvalues.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/implicitvalues.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/multipleparameterlists.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/multipleparameterlists.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/multipleparameterlists.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/multipleparameterlists.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/richobject.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/richobject.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/richobject.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/richobject.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/viewbound.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/viewbound.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/bounds/viewbound.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/bounds/viewbound.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Calculator.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Calculator.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Calculator.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Calculator.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Examples.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Examples.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Examples.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Examples.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithEither.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithEither.scala similarity index 90% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithEither.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithEither.scala index bf85eec31..d705cb97b 100644 --- a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithEither.scala +++ b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithEither.scala @@ -1,10 +1,6 @@ package com.baeldung.scala.exceptionhandling -import com.baeldung.scala.exceptionhandling.LegacyErrors.{ - ResourceNotFound, - ServerError, - UserNotFound -} +import com.baeldung.scala.exceptionhandling.LegacyErrors._ import scala.util.{Failure, Success, Try} diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithMonadError.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithMonadError.scala similarity index 92% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithMonadError.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithMonadError.scala index ee1009207..d488150d2 100644 --- a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithMonadError.scala +++ b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithMonadError.scala @@ -3,11 +3,7 @@ package com.baeldung.scala.exceptionhandling import cats.MonadError import cats.syntax.flatMap._ import cats.syntax.functor._ -import com.baeldung.scala.exceptionhandling.LegacyErrors.{ - ResourceNotFound, - ServerError, - UserNotFound -} +import com.baeldung.scala.exceptionhandling.LegacyErrors._ import scala.util.{Failure, Success, Try} diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithOption.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithOption.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithOption.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithOption.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithTry.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithTry.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithTry.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithTry.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithTryOption.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithTryOption.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithTryOption.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithTryOption.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithValidated.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithValidated.scala similarity index 90% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithValidated.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithValidated.scala index 1dcce5aca..f476fb134 100644 --- a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithValidated.scala +++ b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/HandlingWithValidated.scala @@ -3,10 +3,7 @@ package com.baeldung.scala.exceptionhandling import cats.data.ValidatedNel import cats.syntax.apply._ import cats.syntax.validated._ -import com.baeldung.scala.exceptionhandling.ValidationErrors.{ - IllegalLogin, - IllegalPassword -} +import com.baeldung.scala.exceptionhandling.ValidationErrors._ object HandlingWithValidated { type InvalidOr[T] = ValidatedNel[ValidationErrors, T] diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/LegacyErrors.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/LegacyErrors.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/LegacyErrors.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/LegacyErrors.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/LegacyService.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/LegacyService.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/LegacyService.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/LegacyService.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Resource.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Resource.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Resource.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Resource.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Session.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Session.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Session.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/Session.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/User.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/User.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/User.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/User.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/ValidationErrors.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/ValidationErrors.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/ValidationErrors.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/exceptionhandling/ValidationErrors.scala diff --git a/scala-core-6/src/main/scala-2/com/baeldung/scala/implicitconversions/LengthUnit.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/implicitconversions/LengthUnit.scala similarity index 100% rename from scala-core-6/src/main/scala-2/com/baeldung/scala/implicitconversions/LengthUnit.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/implicitconversions/LengthUnit.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/introduction/ControlStructuresDemo.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/introduction/ControlStructuresDemo.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/introduction/ControlStructuresDemo.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/introduction/ControlStructuresDemo.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/introduction/Employee.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/introduction/Employee.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/introduction/Employee.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/introduction/Employee.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/introduction/HelloWorld.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/introduction/HelloWorld.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/introduction/HelloWorld.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/introduction/HelloWorld.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/introduction/IntSet.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/introduction/IntSet.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/introduction/IntSet.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/introduction/IntSet.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/introduction/Utils.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/introduction/Utils.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/introduction/Utils.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/introduction/Utils.scala diff --git a/scala-core-8/src/main/scala-2/com/baeldung/scala/macros/GenericMacros.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/macros/GenericMacros.scala similarity index 100% rename from scala-core-8/src/main/scala-2/com/baeldung/scala/macros/GenericMacros.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/macros/GenericMacros.scala diff --git a/scala-core-8/src/main/scala-2/com/baeldung/scala/macros/OddEvenMacros.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/macros/OddEvenMacros.scala similarity index 100% rename from scala-core-8/src/main/scala-2/com/baeldung/scala/macros/OddEvenMacros.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/macros/OddEvenMacros.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/typtag/ClassTagExample.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/typtag/ClassTagExample.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/typtag/ClassTagExample.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/typtag/ClassTagExample.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/typtag/TypeTagExample.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/typtag/TypeTagExample.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/typtag/TypeTagExample.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/typtag/TypeTagExample.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/typtag/WeakTypeTagExample.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/typtag/WeakTypeTagExample.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/typtag/WeakTypeTagExample.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/typtag/WeakTypeTagExample.scala diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/underscore/UnderscoreUsages.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/underscore/UnderscoreUsages.scala similarity index 88% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/underscore/UnderscoreUsages.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/underscore/UnderscoreUsages.scala index 7068166b0..157b6cda5 100644 --- a/scala-core-2/src/main/scala-2/com/baeldung/scala/underscore/UnderscoreUsages.scala +++ b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/underscore/UnderscoreUsages.scala @@ -36,11 +36,11 @@ object UnderscoreUsages { def list_++(list: List[_]): List[_] = List.concat(list, list) trait ObjectContainer[T[_]] { // higher kinded type parameter - def checkIfEmpty(collection: T[_]): Boolean + def checkIfEmpty[A](collection: T[A]): Boolean } object SeqContainer extends ObjectContainer[Seq] { - override def checkIfEmpty(collection: Seq[_]): Boolean = + override def checkIfEmpty[A](collection: Seq[A]): Boolean = !collection.nonEmpty } diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/uniontypes/ArbitraryArityUnionType.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/uniontypes/ArbitraryArityUnionType.scala similarity index 86% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/uniontypes/ArbitraryArityUnionType.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/uniontypes/ArbitraryArityUnionType.scala index 41c974013..f7f44b6da 100644 --- a/scala-core-4/src/main/scala-2/com/baeldung/scala/uniontypes/ArbitraryArityUnionType.scala +++ b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/uniontypes/ArbitraryArityUnionType.scala @@ -1,5 +1,7 @@ package com.baeldung.scala.uniontypes +import scala.language.implicitConversions + object ArbitraryArityUnionType extends App { def isIntOrStringOrBool[T: IntOrStringOrBool](t: T): String = t match { @@ -23,8 +25,8 @@ object ArbitraryArityUnionType extends App { sealed trait AOrB[A, B] object AOrB { - implicit def aInstance[A, B](a: A) = new AOrB[A, B] {} - implicit def bInstance[A, B](b: B) = new AOrB[A, B] {} + implicit def aInstance[A, B](a: A): AOrB[A, B] = new AOrB[A, B] {} + implicit def bInstance[A, B](b: B): AOrB[A, B] = new AOrB[A, B] {} } def isIntOrString[T <% String AOrB Int](t: T): String = t match { diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/uniontypes/EitherDisjointUnion.scala b/scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/uniontypes/EitherDisjointUnion.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/uniontypes/EitherDisjointUnion.scala rename to scala-2-modules/scala2-core/src/main/scala-2/com/baeldung/scala/uniontypes/EitherDisjointUnion.scala diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/exceptionhandling/ExamplesUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/exceptionhandling/ExamplesUnitTest.scala similarity index 100% rename from scala-core/src/test/scala-2/com/baeldung/scala/exceptionhandling/ExamplesUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/exceptionhandling/ExamplesUnitTest.scala diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/exceptionhandling/IdiomaticExceptionHandlingUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/exceptionhandling/IdiomaticExceptionHandlingUnitTest.scala similarity index 98% rename from scala-core/src/test/scala-2/com/baeldung/scala/exceptionhandling/IdiomaticExceptionHandlingUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/exceptionhandling/IdiomaticExceptionHandlingUnitTest.scala index 573144130..f64fdc6f6 100644 --- a/scala-core/src/test/scala-2/com/baeldung/scala/exceptionhandling/IdiomaticExceptionHandlingUnitTest.scala +++ b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/exceptionhandling/IdiomaticExceptionHandlingUnitTest.scala @@ -2,15 +2,8 @@ package com.baeldung.scala.exceptionhandling import cats.data.NonEmptyList import cats.data.Validated.{Invalid, Valid} -import com.baeldung.scala.exceptionhandling.LegacyErrors.{ - ResourceNotFound, - ServerError, - UserNotFound -} -import com.baeldung.scala.exceptionhandling.ValidationErrors.{ - IllegalLogin, - IllegalPassword -} +import com.baeldung.scala.exceptionhandling.LegacyErrors._ +import com.baeldung.scala.exceptionhandling.ValidationErrors._ import org.scalatest.GivenWhenThen import org.scalatest.featurespec.AnyFeatureSpec import org.scalatest.matchers.should.Matchers diff --git a/scala-core-6/src/test/scala-2/com/baeldung/scala/implicitconversions/LengthUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/implicitconversions/LengthUnitTest.scala similarity index 100% rename from scala-core-6/src/test/scala-2/com/baeldung/scala/implicitconversions/LengthUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/implicitconversions/LengthUnitTest.scala diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/introduction/ControlStructuresDemoUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/introduction/ControlStructuresDemoUnitTest.scala similarity index 100% rename from scala-core/src/test/scala-2/com/baeldung/scala/introduction/ControlStructuresDemoUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/introduction/ControlStructuresDemoUnitTest.scala diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/introduction/EmployeeUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/introduction/EmployeeUnitTest.scala similarity index 100% rename from scala-core/src/test/scala-2/com/baeldung/scala/introduction/EmployeeUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/introduction/EmployeeUnitTest.scala diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/introduction/IntSetUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/introduction/IntSetUnitTest.scala similarity index 100% rename from scala-core/src/test/scala-2/com/baeldung/scala/introduction/IntSetUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/introduction/IntSetUnitTest.scala diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/introduction/UtilsUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/introduction/UtilsUnitTest.scala similarity index 87% rename from scala-core/src/test/scala-2/com/baeldung/scala/introduction/UtilsUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/introduction/UtilsUnitTest.scala index 734ac08e7..432ff8782 100644 --- a/scala-core/src/test/scala-2/com/baeldung/scala/introduction/UtilsUnitTest.scala +++ b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/introduction/UtilsUnitTest.scala @@ -1,11 +1,6 @@ package com.baeldung.scala.introduction -import com.baeldung.scala.introduction.Utils.{ - average, - fibonacci, - power, - randomLessThan -} +import com.baeldung.scala.introduction.Utils._ import org.junit.Assert.{assertEquals, assertTrue} import org.junit.Test diff --git a/scala-core-8/src/test/scala-2/com/baeldung/scala/macros/GenericMacrosTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/macros/GenericMacrosUnitTest.scala similarity index 89% rename from scala-core-8/src/test/scala-2/com/baeldung/scala/macros/GenericMacrosTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/macros/GenericMacrosUnitTest.scala index f918b6d62..41e6816d1 100644 --- a/scala-core-8/src/test/scala-2/com/baeldung/scala/macros/GenericMacrosTest.scala +++ b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/macros/GenericMacrosUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.macros import org.scalatest.matchers.must.Matchers import org.scalatest.wordspec.AnyWordSpec -class GenericMacrosTest extends AnyWordSpec with Matchers { +class GenericMacrosUnitTest extends AnyWordSpec with Matchers { "generic macro" should { "return String for string argument" in { GenericMacros.getType("this is a string") mustBe "String" diff --git a/scala-core-8/src/test/scala-2/com/baeldung/scala/macros/OddEvenMacrosTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/macros/OddEvenMacrosUnitTest.scala similarity index 94% rename from scala-core-8/src/test/scala-2/com/baeldung/scala/macros/OddEvenMacrosTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/macros/OddEvenMacrosUnitTest.scala index 66373cd7e..64305d1a0 100644 --- a/scala-core-8/src/test/scala-2/com/baeldung/scala/macros/OddEvenMacrosTest.scala +++ b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/macros/OddEvenMacrosUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.macros import org.scalatest.matchers.must.Matchers import org.scalatest.wordspec.AnyWordSpec -class OddEvenMacrosTest extends AnyWordSpec with Matchers { +class OddEvenMacrosUnitTest extends AnyWordSpec with Matchers { "def macros" should { "return literal odd for odd number" in { diff --git a/scala-core-3/src/test/scala-2/com/baeldung/scala/typetag/ClassTagExampleUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/typetag/ClassTagExampleUnitTest.scala similarity index 92% rename from scala-core-3/src/test/scala-2/com/baeldung/scala/typetag/ClassTagExampleUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/typetag/ClassTagExampleUnitTest.scala index fab1cd120..9ced16b4a 100644 --- a/scala-core-3/src/test/scala-2/com/baeldung/scala/typetag/ClassTagExampleUnitTest.scala +++ b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/typetag/ClassTagExampleUnitTest.scala @@ -1,4 +1,4 @@ -package scala.com.baeldung.scala.typetag +package com.baeldung.scala.typetag import com.baeldung.scala.typtag.ClassTagExample.makeArrayFrom import org.scalatest.wordspec.AnyWordSpec diff --git a/scala-core-3/src/test/scala-2/com/baeldung/scala/typetag/TypeTagExampleUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/typetag/TypeTagExampleUnitTest.scala similarity index 91% rename from scala-core-3/src/test/scala-2/com/baeldung/scala/typetag/TypeTagExampleUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/typetag/TypeTagExampleUnitTest.scala index b91a0a64d..3d3d4875b 100644 --- a/scala-core-3/src/test/scala-2/com/baeldung/scala/typetag/TypeTagExampleUnitTest.scala +++ b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/typetag/TypeTagExampleUnitTest.scala @@ -1,4 +1,5 @@ -package scala.com.baeldung.scala.typetag +package com.baeldung.scala.typetag + import com.baeldung.scala.typtag.TypeTagExample.checkType import org.scalatest.wordspec.AnyWordSpec diff --git a/scala-core-3/src/test/scala-2/com/baeldung/scala/typetag/WeakTypeTagExampleUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/typetag/WeakTypeTagExampleUnitTest.scala similarity index 89% rename from scala-core-3/src/test/scala-2/com/baeldung/scala/typetag/WeakTypeTagExampleUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/typetag/WeakTypeTagExampleUnitTest.scala index 3d61dd131..5ab778f07 100644 --- a/scala-core-3/src/test/scala-2/com/baeldung/scala/typetag/WeakTypeTagExampleUnitTest.scala +++ b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/typetag/WeakTypeTagExampleUnitTest.scala @@ -1,4 +1,4 @@ -package scala.com.baeldung.scala.typetag +package com.baeldung.scala.typetag import com.baeldung.scala.typtag.WeakTypeTagExample.Foo import org.scalatest.wordspec.AnyWordSpec diff --git a/scala-core-2/src/test/scala-2/com/baeldung/scala/underscore/UnderscoreUsagesUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/underscore/UnderscoreUsagesUnitTest.scala similarity index 96% rename from scala-core-2/src/test/scala-2/com/baeldung/scala/underscore/UnderscoreUsagesUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/underscore/UnderscoreUsagesUnitTest.scala index 0f19873b1..d72fb0391 100644 --- a/scala-core-2/src/test/scala-2/com/baeldung/scala/underscore/UnderscoreUsagesUnitTest.scala +++ b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/underscore/UnderscoreUsagesUnitTest.scala @@ -1,8 +1,9 @@ package com.baeldung.scala.underscore -import com.baeldung.scala.underscore.UnderscoreUsages._ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import UnderscoreUsages._ + class UnderscoreUsagesUnitTest extends AnyWordSpec with Matchers { "The underscore" should { @@ -48,7 +49,7 @@ class UnderscoreUsagesUnitTest extends AnyWordSpec with Matchers { b shouldBe "b" text = "a,b,c,d,e" - val Array(a2, _*) = text.split(",") + val Array(a2, _*) = text.split(","): @unchecked a2 shouldBe "a" val Array(a3, b3, _, d, e) = text.split(",") diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/uniontypes/ArbitraryArityUnionTypeUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/uniontypes/ArbitraryArityUnionTypeUnitTest.scala similarity index 99% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/uniontypes/ArbitraryArityUnionTypeUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/uniontypes/ArbitraryArityUnionTypeUnitTest.scala index 560fc6114..39f3d5dad 100644 --- a/scala-core-4/src/test/scala-2/com/baeldung/scala/uniontypes/ArbitraryArityUnionTypeUnitTest.scala +++ b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/uniontypes/ArbitraryArityUnionTypeUnitTest.scala @@ -1,4 +1,5 @@ package com.baeldung.scala.uniontypes + import com.baeldung.scala.uniontypes.ArbitraryArityUnionType._ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/uniontypes/EitherDisjointUnionUnitTest.scala b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/uniontypes/EitherDisjointUnionUnitTest.scala similarity index 99% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/uniontypes/EitherDisjointUnionUnitTest.scala rename to scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/uniontypes/EitherDisjointUnionUnitTest.scala index 39ec68abb..1a4d3f5a4 100644 --- a/scala-core-4/src/test/scala-2/com/baeldung/scala/uniontypes/EitherDisjointUnionUnitTest.scala +++ b/scala-2-modules/scala2-core/src/test/scala-2/com/baeldung/scala/uniontypes/EitherDisjointUnionUnitTest.scala @@ -1,7 +1,8 @@ package com.baeldung.scala.uniontypes -import com.baeldung.scala.uniontypes.EitherDisjointUnion._ + import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers +import com.baeldung.scala.uniontypes.EitherDisjointUnion._ class EitherDisjointUnionUnitTest extends AnyFlatSpec with Matchers { "isIntOrString" should "be able to take an integer parameter" in { diff --git a/scala-2-modules/scala2-libraries/README.md b/scala-2-modules/scala2-libraries/README.md new file mode 100644 index 000000000..4af05b5f8 --- /dev/null +++ b/scala-2-modules/scala2-libraries/README.md @@ -0,0 +1,12 @@ +### Relevant Articles: + +- [Introduction to Optics in Scala Using Monocle](https://www.baeldung.com/scala/monocle-optics) +- [Building REST APIs in Scala with Finch](https://www.baeldung.com/scala/finch-rest-apis) +- [Introduction to Generic Programming in Scala with shapeless](https://www.baeldung.com/scala/generic-programming) +- [Asynchronous and Reactive Programming With Monix in Scala](https://www.baeldung.com/scala/monix) +- [ScalaCache: A Caching Library To Rule Them All](https://www.baeldung.com/scala/scalacache) +- [Better Enumerations in Scala Using Enumeratum](https://www.baeldung.com/scala/enumeratum) +- [Load Configuration Files In Scala Using PureConfig](https://www.baeldung.com/scala/pureconfig-load-config-files) +- [Introduction to scala-async](https://www.baeldung.com/scala/scala-async) +- [Apache Pulsar Scala Client – pulsar4s](https://www.baeldung.com/scala/pulsar4s) +- [Introduction to Kafka With Scala](https://www.baeldung.com/scala/kafka) diff --git a/scala-libraries/src/finch/hello-world/build.sbt b/scala-2-modules/scala2-libraries/src/finch/hello-world/build.sbt similarity index 100% rename from scala-libraries/src/finch/hello-world/build.sbt rename to scala-2-modules/scala2-libraries/src/finch/hello-world/build.sbt diff --git a/scala-libraries/src/finch/hello-world/project/build.properties b/scala-2-modules/scala2-libraries/src/finch/hello-world/project/build.properties similarity index 100% rename from scala-libraries/src/finch/hello-world/project/build.properties rename to scala-2-modules/scala2-libraries/src/finch/hello-world/project/build.properties diff --git a/scala-libraries/src/finch/hello-world/src/main/scala/com/baeldung/finch/helloWorld/Main.scala b/scala-2-modules/scala2-libraries/src/finch/hello-world/src/main/scala/com/baeldung/finch/helloWorld/Main.scala similarity index 100% rename from scala-libraries/src/finch/hello-world/src/main/scala/com/baeldung/finch/helloWorld/Main.scala rename to scala-2-modules/scala2-libraries/src/finch/hello-world/src/main/scala/com/baeldung/finch/helloWorld/Main.scala diff --git a/scala-libraries/src/finch/todo/build.sbt b/scala-2-modules/scala2-libraries/src/finch/todo/build.sbt similarity index 100% rename from scala-libraries/src/finch/todo/build.sbt rename to scala-2-modules/scala2-libraries/src/finch/todo/build.sbt diff --git a/scala-libraries/src/finch/todo/project/build.properties b/scala-2-modules/scala2-libraries/src/finch/todo/project/build.properties similarity index 100% rename from scala-libraries/src/finch/todo/project/build.properties rename to scala-2-modules/scala2-libraries/src/finch/todo/project/build.properties diff --git a/scala-libraries/src/finch/todo/src/main/scala/com/baeldung/finch/todo/Main.scala b/scala-2-modules/scala2-libraries/src/finch/todo/src/main/scala/com/baeldung/finch/todo/Main.scala similarity index 90% rename from scala-libraries/src/finch/todo/src/main/scala/com/baeldung/finch/todo/Main.scala rename to scala-2-modules/scala2-libraries/src/finch/todo/src/main/scala/com/baeldung/finch/todo/Main.scala index 119ea51af..f3fb1036d 100644 --- a/scala-libraries/src/finch/todo/src/main/scala/com/baeldung/finch/todo/Main.scala +++ b/scala-2-modules/scala2-libraries/src/finch/todo/src/main/scala/com/baeldung/finch/todo/Main.scala @@ -55,17 +55,16 @@ object Main extends IOApp { } yield Created(created) } - val getTodo: Endpoint[IO, Todo] = get(todosPath :: path[Int]) { - id: Int => - for { - todos <- sql"select * from todo where id = $id" - .query[Todo] - .to[Set] - .transact(xa) - } yield todos.headOption match { - case None => NotFound(new Exception("Record not found")) - case Some(todo) => Ok(todo) - } + val getTodo: Endpoint[IO, Todo] = get(todosPath :: path[Int]) { id: Int => + for { + todos <- sql"select * from todo where id = $id" + .query[Todo] + .to[Set] + .transact(xa) + } yield todos.headOption match { + case None => NotFound(new Exception("Record not found")) + case Some(todo) => Ok(todo) + } } val updateTodo: Endpoint[IO, Todo] = diff --git a/scala-libraries/src/finch/todo/src/main/scala/com/baeldung/finch/todo/models/Todo.scala b/scala-2-modules/scala2-libraries/src/finch/todo/src/main/scala/com/baeldung/finch/todo/models/Todo.scala similarity index 100% rename from scala-libraries/src/finch/todo/src/main/scala/com/baeldung/finch/todo/models/Todo.scala rename to scala-2-modules/scala2-libraries/src/finch/todo/src/main/scala/com/baeldung/finch/todo/models/Todo.scala diff --git a/scala-libraries-4/src/it/scala-2/com/baeldung/scala/pulsar4s/PulsarJsonSchemaManualTest.scala b/scala-2-modules/scala2-libraries/src/it/scala/com/baeldung/scala/pulsar4s/PulsarJsonSchemaManualTest.scala similarity index 100% rename from scala-libraries-4/src/it/scala-2/com/baeldung/scala/pulsar4s/PulsarJsonSchemaManualTest.scala rename to scala-2-modules/scala2-libraries/src/it/scala/com/baeldung/scala/pulsar4s/PulsarJsonSchemaManualTest.scala diff --git a/scala-libraries-4/src/it/scala-2/com/baeldung/scala/pulsar4s/PulsarStringSchemaManualTest.scala b/scala-2-modules/scala2-libraries/src/it/scala/com/baeldung/scala/pulsar4s/PulsarStringSchemaManualTest.scala similarity index 100% rename from scala-libraries-4/src/it/scala-2/com/baeldung/scala/pulsar4s/PulsarStringSchemaManualTest.scala rename to scala-2-modules/scala2-libraries/src/it/scala/com/baeldung/scala/pulsar4s/PulsarStringSchemaManualTest.scala diff --git a/scala-2-modules/scala2-libraries/src/main/resources/application.conf b/scala-2-modules/scala2-libraries/src/main/resources/application.conf new file mode 100644 index 000000000..2272bb414 --- /dev/null +++ b/scala-2-modules/scala2-libraries/src/main/resources/application.conf @@ -0,0 +1,17 @@ +h2mem { + url = "jdbc:h2:mem:testDB" + driver = org.h2.Driver + keepAliveConnection = true + connectionPool = disabled +} + +#postgres { +# dataSourceClass = "org.postgresql.ds.PGSimpleDataSource" +# properties = { +# serverName = "localhost" +# portNumber = "5432" +# databaseName = "slick-tutorial" +# user = "postgres" +# password = "admin" +# } +#} \ No newline at end of file diff --git a/scala-libraries-5/src/main/resources/docker-compose.yml b/scala-2-modules/scala2-libraries/src/main/resources/docker-compose.yml similarity index 100% rename from scala-libraries-5/src/main/resources/docker-compose.yml rename to scala-2-modules/scala2-libraries/src/main/resources/docker-compose.yml diff --git a/scala-libraries-5/src/main/resources/kafka-intro-avro.conf b/scala-2-modules/scala2-libraries/src/main/resources/kafka-intro-avro.conf similarity index 100% rename from scala-libraries-5/src/main/resources/kafka-intro-avro.conf rename to scala-2-modules/scala2-libraries/src/main/resources/kafka-intro-avro.conf diff --git a/scala-libraries-5/src/main/resources/kafka-intro.conf b/scala-2-modules/scala2-libraries/src/main/resources/kafka-intro.conf similarity index 100% rename from scala-libraries-5/src/main/resources/kafka-intro.conf rename to scala-2-modules/scala2-libraries/src/main/resources/kafka-intro.conf diff --git a/scala-libraries-3/src/main/resources/log4j2.xml b/scala-2-modules/scala2-libraries/src/main/resources/log4j2.xml similarity index 100% rename from scala-libraries-3/src/main/resources/log4j2.xml rename to scala-2-modules/scala2-libraries/src/main/resources/log4j2.xml diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/AsyncQueryMemoizeService.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/AsyncQueryMemoizeService.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/AsyncQueryMemoizeService.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/AsyncQueryMemoizeService.scala diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/CacheFlagService.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/CacheFlagService.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/CacheFlagService.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/CacheFlagService.scala diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/CaffeineCacheConfig.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/CaffeineCacheConfig.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/CaffeineCacheConfig.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/CaffeineCacheConfig.scala diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/CatsService.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/CatsService.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/CatsService.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/CatsService.scala diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/GenericCacheService.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/GenericCacheService.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/GenericCacheService.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/GenericCacheService.scala diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/Models.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/Models.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/Models.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/Models.scala diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/ScalaCacheCachingBlock.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/ScalaCacheCachingBlock.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/ScalaCacheCachingBlock.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/ScalaCacheCachingBlock.scala diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/SyncQueryCustomMemoizeKeyService.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/SyncQueryCustomMemoizeKeyService.scala similarity index 89% rename from scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/SyncQueryCustomMemoizeKeyService.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/SyncQueryCustomMemoizeKeyService.scala index e06317ea7..6c8d03fe7 100644 --- a/scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/SyncQueryCustomMemoizeKeyService.scala +++ b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/SyncQueryCustomMemoizeKeyService.scala @@ -28,9 +28,10 @@ object GuavaCacheCustomMemoizationKeyConfig { val memoizedUnderlyingCustomKeyGuavaCache = CacheBuilder.newBuilder().maximumSize(10000L).build[String, Entry[User]] - implicit val customKeyCacheConfig = CacheConfig(memoization = - MemoizationConfig(toStringConverter = CustomKeyGenerator) - ) + implicit val customKeyCacheConfig: scalacache.CacheConfig = + CacheConfig(memoization = + MemoizationConfig(toStringConverter = CustomKeyGenerator) + ) implicit val guavaCache: Cache[User] = GuavaCache( memoizedUnderlyingCustomKeyGuavaCache diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/SyncQueryMemoizeService.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/SyncQueryMemoizeService.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/SyncQueryMemoizeService.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/SyncQueryMemoizeService.scala diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/SyncQueryService.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/SyncQueryService.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/cache/service/SyncQueryService.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/cache/service/SyncQueryService.scala diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/enumeratum/Enums.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/enumeratum/Enums.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/enumeratum/Enums.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/enumeratum/Enums.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/common/Article.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/common/Article.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/common/Article.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/common/Article.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/common/ClientConfig.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/common/ClientConfig.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/common/ClientConfig.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/common/ClientConfig.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/common/SerdeConfig.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/common/SerdeConfig.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/common/SerdeConfig.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/common/SerdeConfig.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/ArticleAvroConsumer.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/ArticleAvroConsumer.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/ArticleAvroConsumer.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/ArticleAvroConsumer.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/ArticleJsonStringConsumer.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/ArticleJsonStringConsumer.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/ArticleJsonStringConsumer.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/ArticleJsonStringConsumer.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/AvroDeSerializer.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/AvroDeSerializer.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/AvroDeSerializer.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/AvroDeSerializer.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/ConsumerConfig.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/ConsumerConfig.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/ConsumerConfig.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/ConsumerConfig.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/ConsumerUtils.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/ConsumerUtils.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/ConsumerUtils.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/ConsumerUtils.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/JsonStringDeSerializer.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/JsonStringDeSerializer.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/JsonStringDeSerializer.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/consumer/common/JsonStringDeSerializer.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/ArticleAvroProducer.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/ArticleAvroProducer.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/ArticleAvroProducer.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/ArticleAvroProducer.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/ArticleJsonStringProducer.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/ArticleJsonStringProducer.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/ArticleJsonStringProducer.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/ArticleJsonStringProducer.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/Generator.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/Generator.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/Generator.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/Generator.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/AvroSerializer.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/AvroSerializer.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/AvroSerializer.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/AvroSerializer.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/JsonStringSerializer.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/JsonStringSerializer.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/JsonStringSerializer.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/JsonStringSerializer.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/ProducerConfig.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/ProducerConfig.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/ProducerConfig.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/ProducerConfig.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/ProducerUtils.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/ProducerUtils.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/ProducerUtils.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/kafka/intro/producer/common/ProducerUtils.scala diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/scala/monix/MonixTask.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/monix/MonixTask.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/scala/monix/MonixTask.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/monix/MonixTask.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/monocle/OpticsExamples.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/monocle/OpticsExamples.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/monocle/OpticsExamples.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/monocle/OpticsExamples.scala diff --git a/scala-libraries-4/src/main/scala-2/com/baeldung/scala/pulsar4s/JsonPulsarConsumer.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pulsar4s/JsonPulsarConsumer.scala similarity index 100% rename from scala-libraries-4/src/main/scala-2/com/baeldung/scala/pulsar4s/JsonPulsarConsumer.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pulsar4s/JsonPulsarConsumer.scala diff --git a/scala-libraries-4/src/main/scala-2/com/baeldung/scala/pulsar4s/JsonPulsarProducer.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pulsar4s/JsonPulsarProducer.scala similarity index 100% rename from scala-libraries-4/src/main/scala-2/com/baeldung/scala/pulsar4s/JsonPulsarProducer.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pulsar4s/JsonPulsarProducer.scala diff --git a/scala-libraries-4/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarClient.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarClient.scala similarity index 100% rename from scala-libraries-4/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarClient.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarClient.scala diff --git a/scala-libraries-4/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarConsumer.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarConsumer.scala similarity index 100% rename from scala-libraries-4/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarConsumer.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarConsumer.scala diff --git a/scala-libraries-4/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarMessage.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarMessage.scala similarity index 100% rename from scala-libraries-4/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarMessage.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarMessage.scala diff --git a/scala-libraries-4/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarProducer.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarProducer.scala similarity index 100% rename from scala-libraries-4/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarProducer.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pulsar4s/PulsarProducer.scala diff --git a/scala-libraries-3/src/main/scala-2/com/baeldung/scala/pureconfig/Configs.scala b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pureconfig/Configs.scala similarity index 83% rename from scala-libraries-3/src/main/scala-2/com/baeldung/scala/pureconfig/Configs.scala rename to scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pureconfig/Configs.scala index 4ab46e027..3b6ef44bf 100644 --- a/scala-libraries-3/src/main/scala-2/com/baeldung/scala/pureconfig/Configs.scala +++ b/scala-2-modules/scala2-libraries/src/main/scala-2/com/baeldung/scala/pureconfig/Configs.scala @@ -18,12 +18,15 @@ object Protocol { } object impl { - implicit val localDateConvert = localDateConfigConvert( - DateTimeFormatter.ISO_DATE - ) - implicit val localDateTimeConvert = localDateTimeConfigConvert( - DateTimeFormatter.ISO_DATE_TIME - ) + implicit val localDateConvert: pureconfig.ConfigConvert[java.time.LocalDate] = + localDateConfigConvert( + DateTimeFormatter.ISO_DATE + ) + implicit val localDateTimeConvert + : pureconfig.ConfigConvert[java.time.LocalDateTime] = + localDateTimeConfigConvert( + DateTimeFormatter.ISO_DATE_TIME + ) } final case class Port(number: Int) extends AnyVal diff --git a/scala-libraries-3/src/test/resources/application-default.conf b/scala-2-modules/scala2-libraries/src/test/resources/application-default.conf similarity index 100% rename from scala-libraries-3/src/test/resources/application-default.conf rename to scala-2-modules/scala2-libraries/src/test/resources/application-default.conf diff --git a/scala-2-modules/scala2-libraries/src/test/resources/application.conf b/scala-2-modules/scala2-libraries/src/test/resources/application.conf new file mode 100644 index 000000000..3496ee306 --- /dev/null +++ b/scala-2-modules/scala2-libraries/src/test/resources/application.conf @@ -0,0 +1,59 @@ +mongo-async-driver { + akka { + loggers = ["akka.event.slf4j.Slf4jLogger"] + loglevel = DEBUG + } +} +akka { + # Loggers to register at boot time (akka.event.Logging$DefaultLogger logs + # to STDOUT) + loggers = ["akka.event.Logging$DefaultLogger"] + # Options: OFF, ERROR, WARNING, INFO, DEBUG + loglevel = "INFO" +} + +kafka { + port = 8090 + bootstrap-server = "kafka.mydomain.com" + protocol = "https" + timeout = 2s +} + +graphite { + enabled : true + servers : [ + { + host = "graphite.monitoring.com" + port = 6666 + } + ] +} + +app-name = "baeldung-app" +env = Prod +baseDate = "2022-03-17" + +# Samples used in config blog examples + +id = 100 +name = "baeldung" +price = 2.0 +status = false +mem = 1k +delay = 1 second + +app.database { + postgres { + url = "localhost:5432", + username = "user" + } +} + +complex-types { + duration = 5 seconds + heap-size = 1k +} + +sub { + desc = "This is a "${name}" project" +} diff --git a/scala-libraries-3/src/test/resources/database.conf b/scala-2-modules/scala2-libraries/src/test/resources/database.conf similarity index 100% rename from scala-libraries-3/src/test/resources/database.conf rename to scala-2-modules/scala2-libraries/src/test/resources/database.conf diff --git a/scala-libraries/src/test/resources/fs2data.txt b/scala-2-modules/scala2-libraries/src/test/resources/fs2data.txt similarity index 100% rename from scala-libraries/src/test/resources/fs2data.txt rename to scala-2-modules/scala2-libraries/src/test/resources/fs2data.txt diff --git a/scala-libraries-3/src/test/resources/http.conf b/scala-2-modules/scala2-libraries/src/test/resources/http.conf similarity index 100% rename from scala-libraries-3/src/test/resources/http.conf rename to scala-2-modules/scala2-libraries/src/test/resources/http.conf diff --git a/scala-libraries/src/test/resources/logback-test.xml b/scala-2-modules/scala2-libraries/src/test/resources/logback-test.xml similarity index 100% rename from scala-libraries/src/test/resources/logback-test.xml rename to scala-2-modules/scala2-libraries/src/test/resources/logback-test.xml diff --git a/scala-libraries-3/src/test/resources/main.conf b/scala-2-modules/scala2-libraries/src/test/resources/main.conf similarity index 100% rename from scala-libraries-3/src/test/resources/main.conf rename to scala-2-modules/scala2-libraries/src/test/resources/main.conf diff --git a/scala-libraries-3/src/test/resources/notification.conf b/scala-2-modules/scala2-libraries/src/test/resources/notification.conf similarity index 100% rename from scala-libraries-3/src/test/resources/notification.conf rename to scala-2-modules/scala2-libraries/src/test/resources/notification.conf diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/cache/GenericCacheUnitTest.scala b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/cache/GenericCacheUnitTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/cache/GenericCacheUnitTest.scala rename to scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/cache/GenericCacheUnitTest.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/cache/ScalaCacheAsyncUnitTest.scala b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/cache/ScalaCacheAsyncUnitTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/cache/ScalaCacheAsyncUnitTest.scala rename to scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/cache/ScalaCacheAsyncUnitTest.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/cache/ScalaCacheCachingBlockUnitTest.scala b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/cache/ScalaCacheCachingBlockUnitTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/cache/ScalaCacheCachingBlockUnitTest.scala rename to scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/cache/ScalaCacheCachingBlockUnitTest.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/cache/ScalaCacheCatsServiceUnitTest.scala b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/cache/ScalaCacheCatsServiceUnitTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/cache/ScalaCacheCatsServiceUnitTest.scala rename to scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/cache/ScalaCacheCatsServiceUnitTest.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/cache/ScalaCacheSyncUnitTest.scala b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/cache/ScalaCacheSyncUnitTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/cache/ScalaCacheSyncUnitTest.scala rename to scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/cache/ScalaCacheSyncUnitTest.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/enumeratum/EnumeratumUnitTest.scala b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/enumeratum/EnumeratumUnitTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/enumeratum/EnumeratumUnitTest.scala rename to scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/enumeratum/EnumeratumUnitTest.scala diff --git a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/async/ScalaAsyncTest.scala b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/async/ScalaAsyncUnitTest.scala similarity index 96% rename from scala-libraries-4/src/test/scala-2/com/baeldung/scala/async/ScalaAsyncTest.scala rename to scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/async/ScalaAsyncUnitTest.scala index ef797d564..e487cf75d 100644 --- a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/async/ScalaAsyncTest.scala +++ b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/async/ScalaAsyncUnitTest.scala @@ -59,7 +59,7 @@ object ScalaAsyncTest { } yield r1 + r2 } -class ScalaAsyncTest extends AsyncWordSpec with Matchers with ScalaFutures { +class ScalaAsyncUnitTest extends AsyncWordSpec with Matchers with ScalaFutures { import ScalaAsyncTest._ diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/scala/monix/MonixTaskUnitTest.scala b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/monix/MonixTaskUnitTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/scala/monix/MonixTaskUnitTest.scala rename to scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/monix/MonixTaskUnitTest.scala diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/monocle/OpticsExamplesUnitTest.scala b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/monocle/OpticsExamplesUnitTest.scala similarity index 100% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/monocle/OpticsExamplesUnitTest.scala rename to scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/monocle/OpticsExamplesUnitTest.scala diff --git a/scala-libraries-3/src/test/scala-2/com/baeldung/scala/pureconfig/SampleConfigLoader.scala b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/pureconfig/SampleConfigLoaderUnitTest.scala similarity index 96% rename from scala-libraries-3/src/test/scala-2/com/baeldung/scala/pureconfig/SampleConfigLoader.scala rename to scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/pureconfig/SampleConfigLoaderUnitTest.scala index 6a67d949d..c4b860db3 100644 --- a/scala-libraries-3/src/test/scala-2/com/baeldung/scala/pureconfig/SampleConfigLoader.scala +++ b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/pureconfig/SampleConfigLoaderUnitTest.scala @@ -12,7 +12,7 @@ import pureconfig.generic.ProductHint import pureconfig.error.ConfigReaderException import scala.concurrent.duration._ -class SampleConfigLoader extends AnyWordSpec with Matchers { +class SampleConfigLoaderUnitTest extends AnyWordSpec with Matchers { "pureconfig" should { "load config successfully" in { diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/shapeless/ShapelessUnitTest.scala b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/shapeless/ShapelessUnitTest.scala similarity index 93% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/shapeless/ShapelessUnitTest.scala rename to scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/shapeless/ShapelessUnitTest.scala index fd7339df7..6b91c7ad2 100644 --- a/scala-libraries/src/test/scala-2/com/baeldung/scala/shapeless/ShapelessUnitTest.scala +++ b/scala-2-modules/scala2-libraries/src/test/scala-2/com/baeldung/scala/shapeless/ShapelessUnitTest.scala @@ -138,9 +138,12 @@ class ShapelessUnitTest extends AnyWordSpec with Matchers { "calculate length of variant input types" in { import shapeless._ object polyLength extends Poly1 { - implicit val listCase = at[List[Int]](i => i.length) - implicit val stringCase = at[String](d => d.length) - implicit val arrayCase = at[Array[Int]](d => d.length) + implicit val listCase: polyLength.Case.Aux[List[Int], Int] = + at[List[Int]](i => i.length) + implicit val stringCase: polyLength.Case.Aux[String, Int] = + at[String](d => d.length) + implicit val arrayCase: polyLength.Case.Aux[Array[Int], Int] = + at[Array[Int]](d => d.length) } val list = List(1, 2) :: "123" :: Array(1, 2, 3, 4) :: HNil diff --git a/scala-akka-2/README.md b/scala-akka-2/README.md index b028345b4..75ca827f4 100644 --- a/scala-akka-2/README.md +++ b/scala-akka-2/README.md @@ -9,3 +9,4 @@ - [Difference Between tell and forward in Akka Actor](https://www.baeldung.com/scala/akka-actor-tell-vs-forward) - [Testing an Akka HTTP Application](https://www.baeldung.com/scala/akka-http-testing) - [Introduction to Akka-Streams in Scala](https://www.baeldung.com/scala/akka-streams) +- [Reading CSV Files Using Akka-Streams](https://www.baeldung.com/scala/akka-streams-read-csv-file) diff --git a/scala-akka-2/src/main/readingcsv/ReadingCSV.scala b/scala-akka-2/src/main/readingcsv/ReadingCSV.scala new file mode 100644 index 000000000..4f223ade1 --- /dev/null +++ b/scala-akka-2/src/main/readingcsv/ReadingCSV.scala @@ -0,0 +1,45 @@ +import akka.NotUsed +import akka.stream.{IOResult, Materializer} +import akka.stream.scaladsl.{FileIO, Framing, Source} +import akka.util.ByteString +import akka.stream.alpakka.csv.scaladsl.{CsvParsing, CsvToMap} + +import java.nio.file.Paths +import scala.concurrent.Future + +object ReadingCSV { + + def read( + path: String + )(implicit mat: Materializer): Source[String, Future[IOResult]] = { + FileIO + .fromPath(Paths.get(path)) + .via(Framing.delimiter(ByteString("\n"), 256, true).map(_.utf8String)) + } + + def readFromString( + csvString: String + )(implicit mat: Materializer): Source[String, NotUsed] = { + Source + .single(ByteString(csvString)) + .via(Framing.delimiter(ByteString("\n"), 256, true).map(_.utf8String)) + } + + def readWithAlpakka( + path: String + )(implicit mat: Materializer): Source[List[String], Future[IOResult]] = { + FileIO + .fromPath(Paths.get(path)) + .via(CsvParsing.lineScanner()) + .map(_.map(_.utf8String)) + } + + def readWithAlpakkaAsMap(path: String)(implicit + mat: Materializer + ): Source[Map[String, String], Future[IOResult]] = { + FileIO + .fromPath(Paths.get(path)) + .via(CsvParsing.lineScanner()) + .via(CsvToMap.toMapAsStrings()) + } +} diff --git a/scala-akka-2/src/main/scala-2/com/baeldung/scala/akka/http/MovieServer.scala b/scala-akka-2/src/main/scala-2/com/baeldung/scala/akka/http/MovieServer.scala index eb934ff68..6f3ab509e 100644 --- a/scala-akka-2/src/main/scala-2/com/baeldung/scala/akka/http/MovieServer.scala +++ b/scala-akka-2/src/main/scala-2/com/baeldung/scala/akka/http/MovieServer.scala @@ -13,9 +13,11 @@ import scala.util._ object MovieServer extends App { - implicit val system = ActorSystem("MoviesServer") + implicit val system: akka.actor.ActorSystem = ActorSystem("MoviesServer") val movieService = new MovieService() - implicit val movieFormat = jsonFormat3(Movie) + implicit val movieFormat + : spray.json.RootJsonFormat[com.baeldung.scala.akka.http.Movie] = + jsonFormat3(Movie) val route = path("movies" / "heartbeat") { get { diff --git a/scala-akka-2/src/test/readingcsv/ReadingCSVTest.scala b/scala-akka-2/src/test/readingcsv/ReadingCSVTest.scala new file mode 100644 index 000000000..06c6d06a8 --- /dev/null +++ b/scala-akka-2/src/test/readingcsv/ReadingCSVTest.scala @@ -0,0 +1,72 @@ +import akka.actor.ActorSystem +import akka.stream.Materializer +import akka.stream.testkit.scaladsl.TestSink +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ReadingCSVTest extends AnyFlatSpec with Matchers { + implicit val system: ActorSystem = ActorSystem("ReadingCSVTest") + implicit val materializer = Materializer.createMaterializer(system) + + "read" should "read csv correctly" in { + ReadingCSV + .read("src/test/resources/readingcsv.csv") + .runWith(TestSink[String]()) + .request(5) + .expectNext( + """"Name","Age"""", + """"Bob",24""", + """"Jane",47""", + """"Imran",32""", + """"Trisha",50""" + ) + .expectComplete() + } + + "readFromString" should "read csv correctly" in { + ReadingCSV + .readFromString(""""Name","Age" + |"Bob",24 + |"Jane",47 + |"Imran",32 + |"Trisha",50""".stripMargin) + .runWith(TestSink[String]()) + .request(5) + .expectNext( + """"Name","Age"""", + """"Bob",24""", + """"Jane",47""", + """"Imran",32""", + """"Trisha",50""" + ) + .expectComplete() + } + + "readWithAlpakka" should "read csv correctly" in { + ReadingCSV + .readWithAlpakka("src/test/resources/readingcsv.csv") + .runWith(TestSink[List[String]]()) + .request(5) + .expectNext( + List("Name", "Age"), + List("Bob", "24"), + List("Jane", "47"), + List("Imran", "32"), + List("Trisha", "50") + ) + .expectComplete() + } + "readWithAlpakkaAsMap" should "read csv correctly" in { + ReadingCSV + .readWithAlpakkaAsMap("src/test/resources/readingcsv.csv") + .runWith(TestSink[Map[String, String]]()) + .request(5) + .expectNext( + Map("Name" -> "Bob", "Age" -> "24"), + Map("Name" -> "Jane", "Age" -> "47"), + Map("Name" -> "Imran", "Age" -> "32"), + Map("Name" -> "Trisha", "Age" -> "50") + ) + .expectComplete() + } +} diff --git a/scala-akka-2/src/test/resources/readingcsv.csv b/scala-akka-2/src/test/resources/readingcsv.csv new file mode 100644 index 000000000..aba2614ba --- /dev/null +++ b/scala-akka-2/src/test/resources/readingcsv.csv @@ -0,0 +1,5 @@ +"Name","Age" +"Bob",24 +"Jane",47 +"Imran",32 +"Trisha",50 \ No newline at end of file diff --git a/scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/grpc/MessageExchangeServiceTest.scala b/scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/grpc/MessageExchangeServiceUnitTest.scala similarity index 98% rename from scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/grpc/MessageExchangeServiceTest.scala rename to scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/grpc/MessageExchangeServiceUnitTest.scala index 367b113a5..1720973f9 100644 --- a/scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/grpc/MessageExchangeServiceTest.scala +++ b/scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/grpc/MessageExchangeServiceUnitTest.scala @@ -6,7 +6,7 @@ import akka.testkit.TestKit import org.scalatest.BeforeAndAfterAll import org.scalatest.wordspec.AsyncWordSpecLike -class MessageExchangeServiceTest +class MessageExchangeServiceUnitTest extends TestKit(ActorSystem("Akka-gRPC")) with AsyncWordSpecLike with BeforeAndAfterAll { diff --git a/scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/persistence/BankAccountTest.scala b/scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/persistence/BankAccountUnitTest.scala similarity index 97% rename from scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/persistence/BankAccountTest.scala rename to scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/persistence/BankAccountUnitTest.scala index f3fa4061a..7e30da328 100644 --- a/scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/persistence/BankAccountTest.scala +++ b/scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/persistence/BankAccountUnitTest.scala @@ -4,7 +4,9 @@ import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.pattern.StatusReply import org.scalatest.wordspec.AnyWordSpecLike -class BankAccountTest extends ScalaTestWithActorTestKit with AnyWordSpecLike { +class BankAccountUnitTest + extends ScalaTestWithActorTestKit + with AnyWordSpecLike { "A Bank Account" should { "deposit money to a bank account" in { diff --git a/scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/query_parameters/QueryParametersTest.scala b/scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/query_parameters/QueryParametersUnitTest.scala similarity index 98% rename from scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/query_parameters/QueryParametersTest.scala rename to scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/query_parameters/QueryParametersUnitTest.scala index 18f1d4798..7e0d50299 100644 --- a/scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/query_parameters/QueryParametersTest.scala +++ b/scala-akka-2/src/test/scala-2/com/baeldung/scala/akka_2/query_parameters/QueryParametersUnitTest.scala @@ -7,7 +7,7 @@ import akka.http.scaladsl.testkit.ScalatestRouteTest import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class QueryParametersTest +class QueryParametersUnitTest extends AnyWordSpec with ScalatestRouteTest with Matchers { diff --git a/scala-akka-2/src/test/scala/com/baeldung/scala/akka/http/routetesting/AkkaRoutesTest.scala b/scala-akka-2/src/test/scala/com/baeldung/scala/akka/http/routetesting/AkkaRoutesUnitTest.scala similarity index 94% rename from scala-akka-2/src/test/scala/com/baeldung/scala/akka/http/routetesting/AkkaRoutesTest.scala rename to scala-akka-2/src/test/scala/com/baeldung/scala/akka/http/routetesting/AkkaRoutesUnitTest.scala index a3e2cd735..2de25508d 100644 --- a/scala-akka-2/src/test/scala/com/baeldung/scala/akka/http/routetesting/AkkaRoutesTest.scala +++ b/scala-akka-2/src/test/scala/com/baeldung/scala/akka/http/routetesting/AkkaRoutesUnitTest.scala @@ -7,7 +7,10 @@ import com.baeldung.scala.akka.http.routetesting.AkkaRoutes.routes import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class AkkaRoutesTest extends AnyFlatSpec with Matchers with ScalatestRouteTest { +class AkkaRoutesUnitTest + extends AnyFlatSpec + with Matchers + with ScalatestRouteTest { "/hello" should "return Hello World!" in { Get("/hello") ~> routes ~> check { status shouldEqual StatusCodes.OK diff --git a/scala-akka-2/src/test/scala/com/baeldung/scala/akka/stream/StreamTest.scala b/scala-akka-2/src/test/scala/com/baeldung/scala/akka/stream/StreamUnitTest.scala similarity index 95% rename from scala-akka-2/src/test/scala/com/baeldung/scala/akka/stream/StreamTest.scala rename to scala-akka-2/src/test/scala/com/baeldung/scala/akka/stream/StreamUnitTest.scala index 9827c322e..ec7169d4e 100644 --- a/scala-akka-2/src/test/scala/com/baeldung/scala/akka/stream/StreamTest.scala +++ b/scala-akka-2/src/test/scala/com/baeldung/scala/akka/stream/StreamUnitTest.scala @@ -7,7 +7,7 @@ import org.scalatest.concurrent.ScalaFutures.convertScalaFuture import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class StreamTest extends AnyFlatSpec with Matchers { +class StreamUnitTest extends AnyFlatSpec with Matchers { implicit val system: ActorSystem = ActorSystem("baeldung") "The parse flow" should "parse pairs of integers" in { diff --git a/scala-akka-3/README.md b/scala-akka-3/README.md new file mode 100644 index 000000000..0336ab1c3 --- /dev/null +++ b/scala-akka-3/README.md @@ -0,0 +1,4 @@ +### Relevant Articles +- [Testing Akka Streams](https://www.baeldung.com/scala/akka-streams-tests) +- [Actor Lifecycle in Akka](https://www.baeldung.com/scala/akka-actor-lifecycle) +- [Testing Akka Actors](https://www.baeldung.com/scala/testing-akka-actors) diff --git a/scala-akka-3/src/main/scala/com/baeldung/scala/akka/stream/errors/Main.scala b/scala-akka-3/src/main/scala/com/baeldung/scala/akka/stream/errors/Main.scala new file mode 100644 index 000000000..fe6a6e0e6 --- /dev/null +++ b/scala-akka-3/src/main/scala/com/baeldung/scala/akka/stream/errors/Main.scala @@ -0,0 +1,42 @@ +package com.baeldung.scala.akka.stream.errors + +import akka.actor.ActorSystem +import akka.stream.RestartSettings +import akka.stream.scaladsl.RestartSource + +import scala.concurrent.duration.DurationInt +import scala.language.postfixOps +import scala.util.{Failure, Success} + +object Main extends App { + implicit val system: ActorSystem = ActorSystem("baeldung") + + private val backoffSettings = RestartSettings( + minBackoff = 3 seconds, + maxBackoff = 30 seconds, + randomFactor = 0.2 + ).withMaxRestarts(3, 5.minutes) + + RestartSource + .withBackoff(backoffSettings) { () => source } + .via(parse) + .via(compare) + .runWith(sink) + .andThen { + case Failure(exception) => println(exception) + case Success((correct, total)) => + println(s"$correct/$total correct answers") + }(system.dispatcher) + .onComplete(_ => system.terminate())(system.dispatcher) + + /*source + .via(parseWithLogging) + .via(compare) + .runWith(sink) + .andThen { + case Failure(exception) => println(exception) + case Success((correct, total)) => + println(s"$correct/$total correct answers") + }(system.dispatcher) + .onComplete(_ => system.terminate())(system.dispatcher)*/ +} diff --git a/scala-akka-3/src/main/scala/com/baeldung/scala/akka/stream/errors/package.scala b/scala-akka-3/src/main/scala/com/baeldung/scala/akka/stream/errors/package.scala new file mode 100644 index 000000000..6fe74b795 --- /dev/null +++ b/scala-akka-3/src/main/scala/com/baeldung/scala/akka/stream/errors/package.scala @@ -0,0 +1,53 @@ +package com.baeldung.scala.akka.stream + +import akka.NotUsed +import akka.stream.Attributes +import akka.stream.scaladsl.{Flow, Sink, Source} + +import scala.concurrent.Future + +package object errors { + val source: Source[String, NotUsed] = Source( + Seq("5,10", "15,15", "78,79", "12,12", "0,0", "456,456") + ) + + val parse: Flow[String, (Int, Int), NotUsed] = + Flow[String] + .map { pair => + val parts = pair.split(",") + (parts(0).toInt, parts(1).toInt) + } + + val parseWithRecover: Flow[String, Either[String, (Int, Int)], NotUsed] = + Flow[String] + .map { pair => + val parts = pair.split(",") + Right((parts(0).toInt, parts(1).toInt)) + } + .recover({ case e: ArrayIndexOutOfBoundsException => + Left(e.getMessage) + }) + + val parseWithLogging: Flow[String, (Int, Int), NotUsed] = + Flow[String] + .map { pair => + val parts = pair.split(",") + (parts(0).toInt, parts(1).toInt) + } + .log(name = "Baeldung stream") + .addAttributes( + Attributes.logLevels( + onElement = Attributes.LogLevels.Info + ) + ) + + val compare: Flow[(Int, Int), Boolean, NotUsed] = + Flow[(Int, Int)] + .map { case (userAnswer, correctAnswer) => userAnswer == correctAnswer } + + val sink: Sink[Boolean, Future[(Int, Int)]] = Sink.fold((0, 0)) { + case ((correctCount, total), wasCorrect) => + if (wasCorrect) (correctCount + 1, total + 1) + else (correctCount, total + 1) + } +} diff --git a/scala-akka-3/src/main/scala/com/baeldung/scala/akka/stream/package.scala b/scala-akka-3/src/main/scala/com/baeldung/scala/akka/stream/package.scala new file mode 100644 index 000000000..889b951e7 --- /dev/null +++ b/scala-akka-3/src/main/scala/com/baeldung/scala/akka/stream/package.scala @@ -0,0 +1,29 @@ +package com.baeldung.scala.akka + +import akka.NotUsed +import akka.stream.scaladsl.{Flow, Sink, Source} + +import scala.concurrent.Future + +package object stream { + val source: Source[String, NotUsed] = Source( + Seq("5,10", "15,15", "78,79", "12,12", "0,0", "456,456") + ) + + val parse: Flow[String, (Int, Int), NotUsed] = + Flow[String] + .map { pair => + val parts = pair.split(",") + (parts(0).toInt, parts(1).toInt) + } + + val compare: Flow[(Int, Int), Boolean, NotUsed] = + Flow[(Int, Int)] + .map { case (userAnswer, correctAnswer) => userAnswer == correctAnswer } + + val sink: Sink[Boolean, Future[(Int, Int)]] = Sink.fold((0, 0)) { + case ((correctCount, total), wasCorrect) => + if (wasCorrect) (correctCount + 1, total + 1) + else (correctCount, total + 1) + } +} diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/typedactors/AkkaTyped.scala b/scala-akka-3/src/main/scala/com/baeldung/scala/typedactors/AkkaTyped.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/typedactors/AkkaTyped.scala rename to scala-akka-3/src/main/scala/com/baeldung/scala/typedactors/AkkaTyped.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/akka/TestService.scala b/scala-akka-3/src/test/scala/com/baeldung/scala/akka/TestServiceUnitTest.scala similarity index 62% rename from scala-libraries-2/src/test/scala-2/com/baeldung/akka/TestService.scala rename to scala-akka-3/src/test/scala/com/baeldung/scala/akka/TestServiceUnitTest.scala index 2353d831a..a3b078546 100644 --- a/scala-libraries-2/src/test/scala-2/com/baeldung/akka/TestService.scala +++ b/scala-akka-3/src/test/scala/com/baeldung/scala/akka/TestServiceUnitTest.scala @@ -1,13 +1,16 @@ -package com.baeldung.akka +package com.baeldung.scala.akka -import akka.actor.testkit.typed.scaladsl.ActorTestKit import org.scalatest.BeforeAndAfterAll import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import akka.actor.testkit.typed.scaladsl.ActorTestKit -class TestService extends AnyWordSpec with BeforeAndAfterAll with Matchers { +class TestServiceUnitTest + extends AnyWordSpec + with BeforeAndAfterAll + with Matchers { val testKit = ActorTestKit() - implicit val system = testKit.system + implicit val system: akka.actor.typed.ActorSystem[Nothing] = testKit.system // responsible for shutting down the ActorSystem override def afterAll(): Unit = testKit.shutdownTestKit() diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/akka/Tests.scala b/scala-akka-3/src/test/scala/com/baeldung/scala/akka/UnitTest.scala similarity index 93% rename from scala-libraries-2/src/test/scala-2/com/baeldung/akka/Tests.scala rename to scala-akka-3/src/test/scala/com/baeldung/scala/akka/UnitTest.scala index 8d5e94cff..16dd4181f 100644 --- a/scala-libraries-2/src/test/scala-2/com/baeldung/akka/Tests.scala +++ b/scala-akka-3/src/test/scala/com/baeldung/scala/akka/UnitTest.scala @@ -1,4 +1,4 @@ -package com.baeldung.akka +package com.baeldung.scala.akka import akka.actor.typed.scaladsl.AskPattern.{Askable, schedulerFromActorSystem} import akka.actor.typed.{ActorRef, Behavior} @@ -68,7 +68,7 @@ object ActorTest { } } -class GreeterTest extends TestService { +class GreeterUnitTest extends TestServiceUnitTest { import scala.concurrent.duration._ val greeting = "Hello there" val sender = testKit.spawn(Greeter(), "greeter") @@ -80,7 +80,7 @@ class GreeterTest extends TestService { probe.expectNoMessage(50.millis) } -class TrafficLightTest extends TestService { +class TrafficLightUnitTest extends TestServiceUnitTest { import scala.concurrent.duration._ val sender = testKit.spawn(TrafficLight(), "traffic") val probe = testKit.createTestProbe[TrafficLight.CurrentSignal]() @@ -106,11 +106,11 @@ class TrafficLightTest extends TestService { probe.expectNoMessage(50.millis) } -class TrafficLightTestFut extends TestService { +class TrafficLightTestFutUnitTest extends TestServiceUnitTest { import scala.concurrent.duration._ val sender = testKit.spawn(TrafficLight(), "traffic") val duration = 300.millis - implicit val timeout = Timeout(duration) + implicit val timeout: akka.util.Timeout = Timeout(duration) val signalFut = sender.ask(replyTo => TrafficLight.SignalCommand.GetSignal(replyTo)) diff --git a/scala-akka-3/src/test/scala/com/baeldung/scala/akka/stream/StreamUnitTest.scala b/scala-akka-3/src/test/scala/com/baeldung/scala/akka/stream/StreamUnitTest.scala new file mode 100644 index 000000000..04d75a40c --- /dev/null +++ b/scala-akka-3/src/test/scala/com/baeldung/scala/akka/stream/StreamUnitTest.scala @@ -0,0 +1,119 @@ +package com.baeldung.scala.akka.stream + +import akka.actor.ActorSystem +import akka.stream.scaladsl.Keep +import akka.stream.testkit.scaladsl.{TestSink, TestSource} +import org.scalatest.concurrent.ScalaFutures.convertScalaFuture +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class StreamUnitTest extends AnyFlatSpec with Matchers { + implicit val system: ActorSystem = ActorSystem("baeldung") + + "The \"parse\" flow" should "parse pairs of integers" in { + val (pub, sub) = TestSource[String]() + .via(parse) + .toMat(TestSink[(Int, Int)]())(Keep.both) + .run() + + pub.sendNext("1,1") + pub.sendNext("145,146") + pub.sendComplete() + + sub.requestNext((1, 1)) + sub.requestNext((145, 146)) + sub.expectComplete() + } + + "The \"parse\" flow" should "reject pairs with the wrong delimiter" in { + val (pub, sub) = TestSource[String]() + .via(parse) + .toMat(TestSink[(Int, Int)]())(Keep.both) + .run() + + pub.sendNext("2,3") + pub.sendNext("1;1") + pub.sendComplete() + + sub.request(2) + sub.expectNext((2, 3)) + val exc = sub.expectError() + exc.getMessage should be("For input string: \"1;1\"") + } + + "The \"parse\" flow" should "reject pairs with more than two numbers" in { + val (pub, sub) = TestSource[String]() + .via(parse) + .toMat(TestSink[(Int, Int)]())(Keep.both) + .run() + + pub.sendNext("(1,3),(4,5)") + pub.sendComplete() + + val exc = sub.expectSubscriptionAndError() + exc.getMessage should be("For input string: \"(1\"") + } + + "The \"parse\" flow" should "reject pairs with only one number" in { + val (pub, sub) = TestSource[String]() + .via(parse) + .toMat(TestSink[(Int, Int)]())(Keep.both) + .run() + + pub.sendNext("1") + pub.sendComplete() + + val exc = sub.expectSubscriptionAndError() + exc.getMessage should be("Index 1 out of bounds for length 1") + } + + "The \"parse\" flow" should "reject pairs with the wrong types" in { + val (pub, sub) = TestSource[String]() + .via(parse) + .toMat(TestSink[(Int, Int)]())(Keep.both) + .run() + + pub.sendNext("2,3") + pub.sendNext("1,a") + pub.sendComplete() + + sub.request(2) + sub.expectNext((2, 3)) + val exc = sub.expectError() + exc.getMessage should be("For input string: \"a\"") + } + + "The \"compare\" flow" should "compare pairs of integers" in { + val (pub, sub) = TestSource[(Int, Int)]() + .via(compare) + .toMat(TestSink[Boolean]())(Keep.both) + .run() + + pub.sendNext((1, 1)) + pub.sendNext((145, 146)) + pub.sendComplete() + + sub.requestNext(true) + sub.requestNext(false) + sub.expectComplete() + } + + "The \"sink\" sink" should "count the number of trues" in { + val (probe, result) = TestSource[Boolean]().toMat(sink)(Keep.both).run() + + probe.sendNext(true) + probe.sendNext(false) + probe.sendNext(false) + probe.sendComplete() + + result.futureValue shouldBe (1, 3) + } + + "The \"source\" source" should "emit pairs of integers" in { + source + .runWith(TestSink[String]()) + .request(6) + .expectNext("5,10", "15,15", "78,79", "12,12", "0,0", "456,456") + .expectComplete() + } +} diff --git a/scala-akka-3/src/test/scala/com/baeldung/scala/akka/stream/errors/ErrorRecoveryUnitTest.scala b/scala-akka-3/src/test/scala/com/baeldung/scala/akka/stream/errors/ErrorRecoveryUnitTest.scala new file mode 100644 index 000000000..9dc70b78e --- /dev/null +++ b/scala-akka-3/src/test/scala/com/baeldung/scala/akka/stream/errors/ErrorRecoveryUnitTest.scala @@ -0,0 +1,53 @@ +package com.baeldung.scala.akka.stream.errors + +import akka.actor.ActorSystem +import akka.stream.{ActorAttributes, Supervision} +import akka.stream.scaladsl.Keep +import akka.stream.testkit.scaladsl.{TestSink, TestSource} +import org.scalatest.concurrent.ScalaFutures.convertScalaFuture +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ErrorRecoveryUnitTest extends AnyFlatSpec with Matchers { + implicit val system: ActorSystem = ActorSystem("baeldung") + + "The \"parseWithRecover\" flow" should "parse recover from a parsing error" in { + val (pub, sub) = TestSource[String]() + .via(parseWithRecover) + .toMat(TestSink[Either[String, (Int, Int)]]())(Keep.both) + .run() + + pub.sendNext("1,1") + pub.sendNext("145146") + pub.sendComplete() + + sub.requestNext(Right(1, 1)) + sub.requestNext(Left("Index 1 out of bounds for length 1")) + sub.expectComplete() + } + + "The \"Resume\" supervision strategy" should "ignore parsing errors" in { + val runnableGraph = TestSource[String]() + .via(parseWithRecover) + .toMat(TestSink[Either[String, (Int, Int)]]())(Keep.both) + + val decider: Supervision.Decider = { + case _: ArrayIndexOutOfBoundsException => Supervision.Resume + case _ => Supervision.Stop + } + + val graphWithResumeSupervision = + runnableGraph.withAttributes(ActorAttributes.supervisionStrategy(decider)) + + val (pub, sub) = graphWithResumeSupervision.run() + + pub.sendNext("1,1") + pub.sendNext("145146") + pub.sendNext("1,2") + pub.sendComplete() + + sub.requestNext(Right(1, 1)) + sub.requestNext(Right(1, 2)) + sub.expectComplete() + } +} diff --git a/scala-akka/src/it/scala-2/com/baeldung/scala/akka/alpakka/AlpakkaIntegrationTest.scala b/scala-akka/src/it/scala-2/com/baeldung/scala/akka/alpakka/AlpakkaIntegrationTest.scala index 75bd33371..362e24acf 100644 --- a/scala-akka/src/it/scala-2/com/baeldung/scala/akka/alpakka/AlpakkaIntegrationTest.scala +++ b/scala-akka/src/it/scala-2/com/baeldung/scala/akka/alpakka/AlpakkaIntegrationTest.scala @@ -36,7 +36,7 @@ class AlpakkaIntegrationTest .build() override def beforeAll() = { - mongodInstance.start(Version.Main.V4_4) + mongodInstance.start(Version.Main.V7_0) } "Alpakka MongoDB integration service" must { diff --git a/scala-akka/src/main/scala-2/com/baeldung/scala/akka/alpakka/Configs.scala b/scala-akka/src/main/scala-2/com/baeldung/scala/akka/alpakka/Configs.scala index 5d899f691..a93434a4f 100644 --- a/scala-akka/src/main/scala-2/com/baeldung/scala/akka/alpakka/Configs.scala +++ b/scala-akka/src/main/scala-2/com/baeldung/scala/akka/alpakka/Configs.scala @@ -5,8 +5,8 @@ import akka.stream.ActorMaterializer object Configs { - implicit val actorSystem = ActorSystem("Alpakka") - implicit val materializer = ActorMaterializer() + implicit val actorSystem: akka.actor.ActorSystem = ActorSystem("Alpakka") + implicit val materializer: akka.stream.ActorMaterializer = ActorMaterializer() val filePath = "vehicle_data.log" diff --git a/scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/GracefulStopActorTest.scala b/scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/GracefulStopActorUnitTest.scala similarity index 97% rename from scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/GracefulStopActorTest.scala rename to scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/GracefulStopActorUnitTest.scala index 58f76b2f2..53ca3e0f9 100644 --- a/scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/GracefulStopActorTest.scala +++ b/scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/GracefulStopActorUnitTest.scala @@ -11,7 +11,7 @@ import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await import scala.concurrent.duration._ -class GracefulStopActorTest +class GracefulStopActorUnitTest extends TestKit(ActorSystem("test_system")) with AnyWordSpecLike with Matchers diff --git a/scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/PoisonPillActorTest.scala b/scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/PoisonPillActorUnitTest.scala similarity index 97% rename from scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/PoisonPillActorTest.scala rename to scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/PoisonPillActorUnitTest.scala index 42a612255..915df9a4e 100644 --- a/scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/PoisonPillActorTest.scala +++ b/scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/PoisonPillActorUnitTest.scala @@ -7,7 +7,7 @@ import com.baeldung.scala.akka.stopping.MessageProcessorActor._ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike -class PoisonPillActorTest +class PoisonPillActorUnitTest extends TestKit(ActorSystem("test_system")) with AnyWordSpecLike with Matchers diff --git a/scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/StopActorTest.scala b/scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/StopActorUnitTest.scala similarity index 97% rename from scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/StopActorTest.scala rename to scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/StopActorUnitTest.scala index b10d31c16..60d773065 100644 --- a/scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/StopActorTest.scala +++ b/scala-akka/src/test/scala-2/com/baeldung/scala/akka/stopping/StopActorUnitTest.scala @@ -8,7 +8,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.duration._ -class StoppingActorTest +class StopActorUnitTest extends TestKit(ActorSystem("test_system")) with AnyWordSpecLike with Matchers diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/adt/Examples.scala b/scala-core-5/src/main/scala-2/com/baeldung/scala/adt/Examples.scala deleted file mode 100644 index e75be00f4..000000000 --- a/scala-core-5/src/main/scala-2/com/baeldung/scala/adt/Examples.scala +++ /dev/null @@ -1,35 +0,0 @@ -package com.baeldung.scala.adt - -object Examples { - sealed trait Color - final case object White extends Color - final case object Black extends Color - - sealed trait Name - final case object Pawn extends Name - final case object Rook extends Name - final case object Knight extends Name - final case object Bishop extends Name - final case object Queen extends Name - final case object King extends Name - - case class ChessPiece(color: Color, name: Name) - - def isTheMostImportantPiece(c: ChessPiece): Boolean = c match { - case ChessPiece(_, King) => true - case _ => false - } - - trait Semaphore { - val color: SemaphoreColor - } - - sealed trait SemaphoreColor - final case object Green extends SemaphoreColor - final case object Amber extends SemaphoreColor - final case object Red extends SemaphoreColor - - trait Feline - trait Animal - trait Cat extends Animal with Feline -} diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/datesandtimes/JavaUtilDateUnitTest.scala b/scala-core-5/src/test/scala-2/com/baeldung/scala/datesandtimes/JavaUtilDateUnitTest.scala deleted file mode 100644 index a9c558586..000000000 --- a/scala-core-5/src/test/scala-2/com/baeldung/scala/datesandtimes/JavaUtilDateUnitTest.scala +++ /dev/null @@ -1,20 +0,0 @@ -package com.baeldung.scala.datesandtimes -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers - -import java.text.SimpleDateFormat -import java.util.Date - -class JavaUtilDateUnitTest extends AnyFlatSpec with Matchers { - "toDate" should "be able to parse a date-string and return a Date object" in { - val dateStr = "2021-06-13" - val format: ThreadLocal[SimpleDateFormat] = - new ThreadLocal[SimpleDateFormat] { - override def initialValue = { - new SimpleDateFormat("yyyy-MM-dd") - } - } - val expectedDate: Date = new Date(121, 5, 13) - JavaUtilDate.toDate(dateStr, format) shouldEqual expectedDate - } -} diff --git a/scala-core-collections-3/README.md b/scala-core-collections-3/README.md deleted file mode 100644 index 692760115..000000000 --- a/scala-core-collections-3/README.md +++ /dev/null @@ -1,4 +0,0 @@ -- [Guide to Scala Collections](https://www.baeldung.com/scala/collections) -- [Guide to Scala ListSet](https://www.baeldung.com/scala/listset) -- [Guide to Scala ListMap](https://www.baeldung.com/scala/listmap) -- [Get a List Item by Index in Scala](https://www.baeldung.com/scala/list-get-item-by-index) \ No newline at end of file diff --git a/scala-core-collections-2/README.md b/scala-core-collections-modules/scala-core-collections-2/README.md similarity index 90% rename from scala-core-collections-2/README.md rename to scala-core-collections-modules/scala-core-collections-2/README.md index 0a6456398..33e4b0f61 100644 --- a/scala-core-collections-2/README.md +++ b/scala-core-collections-modules/scala-core-collections-2/README.md @@ -1,7 +1,6 @@ ## Relevant Articles - [Find the Last Occurrence of an Element in a List](https://www.baeldung.com/scala/last-occurrence-in-list) - [Rotating a Scala Collection](https://www.baeldung.com/scala/rotate-collection) -- [Filter “None” Values From a Map](https://www.baeldung.com/scala/filter-none-from-map) - [Check if a List Is a Sublist of Another List](https://www.baeldung.com/scala/check-if-list-is-sublist) - [Get a Subarray in Scala](https://www.baeldung.com/scala/extract-subarray) - [Finding the First Element Matching a Condition in a Collection](https://www.baeldung.com/scala/find-first-match-in-collection) diff --git a/scala-core-collections-2/src/main/scala-2/com/baeldung/scala/firstmatching/Finder.scala b/scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/firstmatching/Finder.scala similarity index 100% rename from scala-core-collections-2/src/main/scala-2/com/baeldung/scala/firstmatching/Finder.scala rename to scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/firstmatching/Finder.scala diff --git a/scala-core-collections-2/src/main/scala-2/com/baeldung/scala/flattening/Flattener.scala b/scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/flattening/Flattener.scala similarity index 100% rename from scala-core-collections-2/src/main/scala-2/com/baeldung/scala/flattening/Flattener.scala rename to scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/flattening/Flattener.scala diff --git a/scala-core-collections-2/src/main/scala-2/com/baeldung/scala/lastelement/Finder.scala b/scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/lastelement/Finder.scala similarity index 100% rename from scala-core-collections-2/src/main/scala-2/com/baeldung/scala/lastelement/Finder.scala rename to scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/lastelement/Finder.scala diff --git a/scala-core-collections-2/src/main/scala-2/com/baeldung/scala/lazylists/SimpleLazyList.scala b/scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/lazylists/SimpleLazyList.scala similarity index 100% rename from scala-core-collections-2/src/main/scala-2/com/baeldung/scala/lazylists/SimpleLazyList.scala rename to scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/lazylists/SimpleLazyList.scala diff --git a/scala-core-collections-2/src/main/scala-2/com/baeldung/scala/lazylists/SimpleList.scala b/scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/lazylists/SimpleList.scala similarity index 100% rename from scala-core-collections-2/src/main/scala-2/com/baeldung/scala/lazylists/SimpleList.scala rename to scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/lazylists/SimpleList.scala diff --git a/scala-core-collections-2/src/main/scala-2/com/baeldung/scala/randomfixedsizesample/RandomFixedSizeSample.scala b/scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/randomfixedsizesample/RandomFixedSizeSample.scala similarity index 100% rename from scala-core-collections-2/src/main/scala-2/com/baeldung/scala/randomfixedsizesample/RandomFixedSizeSample.scala rename to scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/randomfixedsizesample/RandomFixedSizeSample.scala diff --git a/scala-core-collections-2/src/main/scala-2/com/baeldung/scala/rotation/Rotator.scala b/scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/rotation/Rotator.scala similarity index 100% rename from scala-core-collections-2/src/main/scala-2/com/baeldung/scala/rotation/Rotator.scala rename to scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/rotation/Rotator.scala diff --git a/scala-core-collections-2/src/main/scala-2/com/baeldung/scala/sublist/SubListOfList.scala b/scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/sublist/SubListOfList.scala similarity index 100% rename from scala-core-collections-2/src/main/scala-2/com/baeldung/scala/sublist/SubListOfList.scala rename to scala-core-collections-modules/scala-core-collections-2/src/main/scala/com/baeldung/scala/sublist/SubListOfList.scala diff --git a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/array/AppendAnElementToAnArrayUnitTest.scala b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/array/AppendAnElementToAnArrayUnitTest.scala similarity index 100% rename from scala-core-collections-2/src/test/scala-2/com/baeldung/scala/array/AppendAnElementToAnArrayUnitTest.scala rename to scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/array/AppendAnElementToAnArrayUnitTest.scala diff --git a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/firstmatching/FinderSpec.scala b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/firstmatching/FinderUnitTest.scala similarity index 95% rename from scala-core-collections-2/src/test/scala-2/com/baeldung/scala/firstmatching/FinderSpec.scala rename to scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/firstmatching/FinderUnitTest.scala index 2f51a3247..5d1df91ff 100644 --- a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/firstmatching/FinderSpec.scala +++ b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/firstmatching/FinderUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.firstmatching import org.scalatest.wordspec.AnyWordSpec -class FinderSpec extends AnyWordSpec { +class FinderUnitTest extends AnyWordSpec { import Finder._ "A finder" should { diff --git a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/flattening/FlattenerSpec.scala b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/flattening/FlattenerUnitTest.scala similarity index 96% rename from scala-core-collections-2/src/test/scala-2/com/baeldung/scala/flattening/FlattenerSpec.scala rename to scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/flattening/FlattenerUnitTest.scala index b313ca633..657c7d3b3 100644 --- a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/flattening/FlattenerSpec.scala +++ b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/flattening/FlattenerUnitTest.scala @@ -5,7 +5,7 @@ import Flattener._ import scala.collection.immutable.Queue -class FlattenerSpec extends AnyWordSpec { +class FlattenerUnitTest extends AnyWordSpec { "A full flattener" should { "respect the contents of an already flat sequence" in { diff --git a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/lastelement/FinderSpec.scala b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/lastelement/FinderUnitTest.scala similarity index 94% rename from scala-core-collections-2/src/test/scala-2/com/baeldung/scala/lastelement/FinderSpec.scala rename to scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/lastelement/FinderUnitTest.scala index f8d8f0f4b..a81f769e8 100644 --- a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/lastelement/FinderSpec.scala +++ b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/lastelement/FinderUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.lastelement import org.scalatest.wordspec.AnyWordSpec -class FinderSpec extends AnyWordSpec { +class FinderUnitTest extends AnyWordSpec { import Finder._ "A last element finder" should { diff --git a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/lazylists/SimpleLazyListSpec.scala b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/lazylists/SimpleLazyListUnitTest.scala similarity index 96% rename from scala-core-collections-2/src/test/scala-2/com/baeldung/scala/lazylists/SimpleLazyListSpec.scala rename to scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/lazylists/SimpleLazyListUnitTest.scala index 3bb388e9c..a5ae16656 100644 --- a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/lazylists/SimpleLazyListSpec.scala +++ b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/lazylists/SimpleLazyListUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.lazylists import org.scalatest.wordspec.AnyWordSpec -class SimpleLazyListSpec extends AnyWordSpec { +class SimpleLazyListUnitTest extends AnyWordSpec { "A list" should { "Allow the creation of a one Element list" in { assertResult(5)((5 :: SLNil).head) diff --git a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/lazylists/SimpleListSpec.scala b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/lazylists/SimpleListUnitTest.scala similarity index 91% rename from scala-core-collections-2/src/test/scala-2/com/baeldung/scala/lazylists/SimpleListSpec.scala rename to scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/lazylists/SimpleListUnitTest.scala index ba10b63fa..9ef1cf9fd 100644 --- a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/lazylists/SimpleListSpec.scala +++ b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/lazylists/SimpleListUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.lazylists import org.scalatest.wordspec.AnyWordSpec -class SimpleListSpec extends AnyWordSpec { +class SimpleListUnitTest extends AnyWordSpec { "A list" should { "Allow the creation of a one Element list" in { assertResult(5)((5 :: SLNil).head) diff --git a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/randomfixedsizesample/RandomFixedSizeSampleSpec.scala b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/randomfixedsizesample/RandomFixedSizeSampleUnitTest.scala similarity index 96% rename from scala-core-collections-2/src/test/scala-2/com/baeldung/scala/randomfixedsizesample/RandomFixedSizeSampleSpec.scala rename to scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/randomfixedsizesample/RandomFixedSizeSampleUnitTest.scala index 49a0fb527..05ec62550 100644 --- a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/randomfixedsizesample/RandomFixedSizeSampleSpec.scala +++ b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/randomfixedsizesample/RandomFixedSizeSampleUnitTest.scala @@ -5,7 +5,7 @@ import org.scalatest.matchers.should.Matchers import scala.annotation.tailrec; -class RandomFixedSizeSampleSpec extends AnyWordSpec with Matchers { +class RandomFixedSizeSampleUnitTest extends AnyWordSpec with Matchers { "RandomFixedSizeSample" should { "create a random sample out of the initial List" in { diff --git a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/rotation/RotatorSpec.scala b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/rotation/RotatorUnitTest.scala similarity index 96% rename from scala-core-collections-2/src/test/scala-2/com/baeldung/scala/rotation/RotatorSpec.scala rename to scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/rotation/RotatorUnitTest.scala index b83e713af..bc61eefc5 100644 --- a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/rotation/RotatorSpec.scala +++ b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/rotation/RotatorUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.rotation import org.scalatest.wordspec.AnyWordSpec -class RotatorSpec extends AnyWordSpec { +class RotatorUnitTest extends AnyWordSpec { import Rotator._ "A rotator of collections" should { diff --git a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/subarray/GetASubarrayUnitTest.scala b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/subarray/GetASubarrayUnitTest.scala similarity index 100% rename from scala-core-collections-2/src/test/scala-2/com/baeldung/scala/subarray/GetASubarrayUnitTest.scala rename to scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/subarray/GetASubarrayUnitTest.scala diff --git a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/sublist/SubListOfListSpec.scala b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/sublist/SubListOfListUnitTest.scala similarity index 97% rename from scala-core-collections-2/src/test/scala-2/com/baeldung/scala/sublist/SubListOfListSpec.scala rename to scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/sublist/SubListOfListUnitTest.scala index 177934346..8bc38d08c 100644 --- a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/sublist/SubListOfListSpec.scala +++ b/scala-core-collections-modules/scala-core-collections-2/src/test/scala/com/baeldung/scala/sublist/SubListOfListUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.sublist import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class SubListOfListSpec extends AnyWordSpec with Matchers { +class SubListOfListUnitTest extends AnyWordSpec with Matchers { val fullList = "a" :: "b" :: "c" :: "d" :: Nil val subList = "a" :: "b" :: "c" :: Nil val notSubList = "e" :: "f" :: Nil diff --git a/scala-core-collections-modules/scala-core-collections-3/README.md b/scala-core-collections-modules/scala-core-collections-3/README.md new file mode 100644 index 000000000..6968c0213 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/README.md @@ -0,0 +1,12 @@ +## Relevant Articles + +- [Guide to Scala Collections](https://www.baeldung.com/scala/collections) +- [Guide to Scala ListMap](https://www.baeldung.com/scala/listmap) +- [Get a List Item by Index in Scala](https://www.baeldung.com/scala/list-get-item-by-index) +- [Moving Averages in Scala: A Tale of Two Approaches](https://www.baeldung.com/scala/moving-averages) +- [Implement a Fixed-Size List in Scala](https://www.baeldung.com/scala/list-finite-size) +- [Difference Between zip() and lazyZip() in Scala](https://www.baeldung.com/scala/zip-vs-lazyzip) +- [Implementing an LRU Cache in Scala 3: A Step-by-Step Guide](https://www.baeldung.com/scala/least-recently-used-cache) +- [Using the sliding() and grouped() Methods With Scala Collections](https://www.baeldung.com/scala/sliding-grouped-methods-with-collections) +- [Introduction to BitSet in Scala](https://www.baeldung.com/scala/bitiset) +- [zip(), zipAll(), and zipWithIndex() Methods in Scala](https://www.baeldung.com/scala/zip-zipall-zipwithindex) diff --git a/scala-core-collections-modules/scala-core-collections-3/src/main/resources/GOOG.csv b/scala-core-collections-modules/scala-core-collections-3/src/main/resources/GOOG.csv new file mode 100644 index 000000000..bb59948ed --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/main/resources/GOOG.csv @@ -0,0 +1,447 @@ +Date,Open,High,Low,Close,Adj Close,Volume +2022-01-03,144.475494,145.550003,143.502502,145.074493,145.074493,25214000 +2022-01-04,145.550507,146.610001,143.816147,144.416504,144.416504,22928000 +2022-01-05,144.181000,144.298004,137.523499,137.653503,137.653503,49642000 +2022-01-06,137.497498,139.686005,136.763504,137.550995,137.550995,29050000 +2022-01-07,137.904999,138.254745,135.789001,137.004501,137.004501,19408000 +2022-01-10,135.098999,138.639999,133.140503,138.574005,138.574005,34096000 +2022-01-11,138.180496,140.329498,136.813507,140.017502,140.017502,23502000 +2022-01-12,141.554504,142.814255,141.112000,141.647995,141.647995,23642000 +2022-01-13,141.840500,143.185501,138.914001,139.130997,139.130997,26566000 +2022-01-14,137.500000,141.200500,137.500000,139.786499,139.786499,23826000 +2022-01-18,136.600006,137.391495,135.617004,136.290497,136.290497,27382000 +2022-01-19,136.938507,138.399506,135.500000,135.651993,135.651993,20796000 +2022-01-20,136.514008,137.912003,133.144501,133.506500,133.506500,21930000 +2022-01-21,133.011993,134.760498,130.001007,130.091995,130.091995,41920000 +2022-01-24,126.027496,130.778503,124.641953,130.371994,130.371994,55148000 +2022-01-25,128.435501,129.338501,126.377998,126.735497,126.735497,36008000 +2022-01-26,130.592499,132.807495,127.153503,129.240005,129.240005,39630000 +2022-01-27,131.360992,132.609955,128.945007,129.121002,129.121002,30248000 +2022-01-28,130.000000,133.370499,128.694504,133.289505,133.289505,30518000 +2022-01-31,134.197998,135.843506,132.274002,135.698502,135.698502,34056000 +2022-02-01,137.835007,138.199997,134.568253,137.878494,137.878494,51204000 +2022-02-02,151.863495,152.100006,145.557495,148.036499,148.036499,89750000 +2022-02-03,145.294998,149.117706,142.205002,142.650497,142.650497,56930000 +2022-02-04,143.016998,144.535248,139.817505,143.016006,143.016006,49224000 +2022-02-07,143.709000,143.846497,138.699005,138.938004,138.938004,44610000 +2022-02-08,138.991257,139.837097,136.873001,139.212997,139.212997,34256000 +2022-02-09,140.849747,142.175507,140.376999,141.453003,141.453003,28628000 +2022-02-10,139.500000,141.431000,138.050003,138.602493,138.602493,33018000 +2022-02-11,138.750000,139.283249,133.288498,134.130005,134.130005,38808000 +2022-02-14,133.365494,136.166504,133.302002,135.300003,135.300003,26792000 +2022-02-15,137.471497,137.899994,135.539505,136.425507,136.425507,26578000 +2022-02-16,136.430496,137.945999,134.823654,137.487503,137.487503,25610000 +2022-02-17,136.149994,136.839493,132.201996,132.308502,132.308502,30968000 +2022-02-18,133.037506,133.824005,130.307053,130.467499,130.467499,31858000 +2022-02-22,129.985001,131.900757,127.740997,129.402496,129.402496,38906000 +2022-02-23,131.078506,131.748993,127.503502,127.584999,127.584999,26432000 +2022-02-24,125.000000,133.037003,124.764503,132.673492,132.673492,43166000 +2022-02-25,133.525497,135.389008,131.764999,134.519501,134.519501,26236000 +2022-02-28,133.284500,135.640503,132.825256,134.891006,134.891006,29676000 +2022-03-01,134.479996,136.110992,133.378494,134.167999,134.167999,24640000 +2022-03-02,134.608246,135.615494,133.432495,134.751495,134.751495,23966000 +2022-03-03,135.978500,136.713806,133.431000,134.307999,134.307999,19780000 +2022-03-04,133.382507,134.199005,130.408493,132.121994,132.121994,24446000 +2022-03-07,131.904007,131.904007,126.410004,126.464500,126.464500,39178000 +2022-03-08,126.250504,131.246506,125.860748,127.278503,127.278503,35250000 +2022-03-09,131.399994,134.198502,130.087997,133.865997,133.865997,32258000 +2022-03-10,131.462494,133.538498,131.401001,132.682007,132.682007,24266000 +2022-03-11,133.999496,134.199997,130.296494,130.475494,130.475494,26600000 +2022-03-14,130.572998,131.026001,126.413002,126.740997,126.740997,30254000 +2022-03-15,127.741501,130.517242,126.568001,129.660507,129.660507,30292000 +2022-03-16,131.000000,133.770996,129.201004,133.690506,133.690506,32058000 +2022-03-17,133.320999,134.739502,132.718994,134.600494,134.600494,23994000 +2022-03-18,133.884003,136.913498,132.932007,136.801498,136.801498,45900000 +2022-03-21,136.847504,137.582504,134.611496,136.478500,136.478500,26632000 +2022-03-22,136.500000,141.500000,136.500000,140.277496,140.277496,29776000 +2022-03-23,139.138504,140.024994,138.166504,138.503494,138.503494,25302000 +2022-03-24,139.272507,141.396500,138.039398,141.311996,141.311996,20544000 +2022-03-25,141.753998,141.959503,139.699493,141.521500,141.521500,19270000 +2022-03-28,140.684494,141.976501,139.828156,141.949997,141.949997,23774000 +2022-03-29,143.160507,144.162506,142.483994,143.250000,143.250000,28678000 +2022-03-30,142.869995,143.480499,142.167999,142.644501,142.644501,21046000 +2022-03-31,142.448502,142.644501,139.619003,139.649506,139.649506,29516000 +2022-04-01,140.009995,140.949997,138.796997,140.699997,140.699997,23480000 +2022-04-04,140.824493,144.043747,140.824493,143.642502,143.642502,19076000 +2022-04-05,143.399506,143.589996,140.943497,141.063004,141.063004,19256000 +2022-04-06,139.161499,139.848495,136.418106,137.175995,137.175995,23574000 +2022-04-07,136.617996,137.701508,134.857254,136.464996,136.464996,19448000 +2022-04-08,136.250000,136.250000,133.752502,134.010498,134.010498,16434000 +2022-04-11,132.899994,132.939194,129.617493,129.796494,129.796494,24188000 +2022-04-12,132.423492,132.423492,127.575996,128.374496,128.374496,23004000 +2022-04-13,128.626495,130.655746,128.438599,130.285995,130.285995,19542000 +2022-04-14,130.649506,130.710251,127.111504,127.252998,127.252998,23484000 +2022-04-18,127.410004,128.712006,126.578453,127.960999,127.960999,14918000 +2022-04-19,128.076996,130.903748,127.451500,130.531006,130.531006,22720000 +2022-04-20,131.283997,131.923492,127.894051,128.245499,128.245499,22610000 +2022-04-21,129.350006,130.307495,124.650002,124.937500,124.937500,30158000 +2022-04-22,125.000000,125.452003,119.140503,119.613998,119.613998,46410000 +2022-04-25,119.429497,123.278000,118.769249,123.250000,123.250000,34522000 +2022-04-26,122.750000,122.750000,119.161850,119.505997,119.505997,49394000 +2022-04-27,114.373001,117.500000,113.124252,115.020500,115.020500,62238000 +2022-04-28,117.114998,120.438499,115.143898,119.411499,119.411499,36790000 +2022-04-29,117.578003,118.959999,114.694000,114.966499,114.966499,33694000 +2022-05-02,113.906502,117.339500,113.399498,117.156998,117.156998,30280000 +2022-05-03,116.764999,119.300003,116.626999,118.129501,118.129501,21216000 +2022-05-04,118.003502,123.142998,115.738503,122.574997,122.574997,33232000 +2022-05-05,120.220497,121.233253,115.182503,116.746498,116.746498,43090000 +2022-05-06,115.518997,117.498497,114.142998,115.660004,115.660004,35310000 +2022-05-09,113.303497,115.562897,112.551498,113.084000,113.084000,34520000 +2022-05-10,116.040497,116.691002,113.383301,114.584503,114.584503,31158000 +2022-05-11,113.710503,116.670998,113.650002,113.960999,113.960999,36502000 +2022-05-12,111.938004,114.856499,110.113503,113.161003,113.161003,41464000 +2022-05-13,114.845497,118.084999,114.000000,116.515503,116.515503,29738000 +2022-05-16,115.384003,116.607498,114.334999,114.792503,114.792503,23282000 +2022-05-17,117.227501,117.227501,115.337502,116.701500,116.701500,21576000 +2022-05-18,115.237503,115.695648,112.141998,112.401001,112.401001,27982000 +2022-05-19,111.841003,113.587502,110.468002,110.745499,110.745499,29192000 +2022-05-20,112.085503,112.550003,106.373001,109.313004,109.313004,37586000 +2022-05-23,110.103996,112.005501,109.154251,111.666496,111.666496,31558000 +2022-05-24,106.377502,106.394997,102.208000,105.926003,105.926003,60386000 +2022-05-25,105.141998,106.544701,104.211250,105.839500,105.839500,37900000 +2022-05-26,106.050499,108.955254,105.487999,108.295998,108.295998,30288000 +2022-05-27,109.788498,112.867996,109.550003,112.799004,112.799004,29924000 +2022-05-31,113.079002,116.433502,112.572502,114.039001,114.039001,51302000 +2022-06-01,114.931503,117.399002,113.550499,114.137001,114.137001,28630000 +2022-06-02,114.188004,117.898003,113.307999,117.746002,117.746002,27472000 +2022-06-03,115.992500,116.364502,113.667999,114.564003,114.564003,25052000 +2022-06-06,116.742500,119.398499,116.528297,117.010498,117.010498,23786000 +2022-06-07,115.648003,117.748650,115.125504,117.229500,117.229500,26414000 +2022-06-08,116.876503,118.646004,116.696747,117.237999,117.237999,22544000 +2022-06-09,116.341499,118.349998,114.866997,114.917999,114.917999,23142000 +2022-06-10,112.781250,113.497002,110.861000,111.427498,111.427498,31324000 +2022-06-13,107.445999,109.218498,106.588051,106.876503,106.876503,36756000 +2022-06-14,106.889999,108.457497,106.351997,107.194000,107.194000,25480000 +2022-06-15,108.899498,112.063004,108.118752,110.390503,110.390503,33192000 +2022-06-16,108.149498,109.290497,105.792503,106.636002,106.636002,35314000 +2022-06-17,106.535004,109.249496,105.628548,107.865501,107.865501,43516000 +2022-06-21,109.702003,112.672997,109.293503,112.014999,112.014999,39010000 +2022-06-22,111.163002,113.769501,110.724297,112.033997,112.033997,23922000 +2022-06-23,112.949997,113.196503,111.028999,112.684502,112.684502,24710000 +2022-06-24,113.602997,118.637497,113.602997,118.538002,118.538002,39122000 +2022-06-27,118.934998,119.250000,116.000748,116.622498,116.622498,32840000 +2022-06-28,116.350998,117.856499,112.444000,112.571503,112.571503,28232000 +2022-06-29,112.148499,113.664497,111.554001,112.256500,112.256500,18628000 +2022-06-30,110.499496,111.329803,107.309998,109.372498,109.372498,38046000 +2022-07-01,108.336998,109.806351,107.105003,109.081001,109.081001,31028000 +2022-07-05,107.514503,114.052597,106.249496,113.887001,113.887001,36398000 +2022-07-06,114.092003,116.351997,112.250504,115.213501,115.213501,28852000 +2022-07-07,116.008003,119.862000,115.533997,119.306000,119.306000,32184000 +2022-07-08,117.550003,120.434998,117.514000,120.168503,120.168503,29082000 +2022-07-11,118.650002,118.794502,116.234497,116.522499,116.522499,26718000 +2022-07-12,116.838501,117.849503,114.614998,114.849503,114.849503,24970000 +2022-07-13,112.639000,115.156998,111.822998,112.186996,112.186996,38958000 +2022-07-14,110.825996,111.987503,109.325500,111.440002,111.440002,32366000 +2022-07-15,112.962997,114.000504,111.822502,112.766998,112.766998,34330000 +2022-07-18,113.440002,114.800003,109.300003,109.910004,109.910004,33354000 +2022-07-19,111.730003,114.809998,110.500000,114.620003,114.620003,30992300 +2022-07-20,114.059998,116.330002,113.260002,114.699997,114.699997,26780100 +2022-07-21,115.089996,115.209999,111.910004,115.040001,115.040001,27267800 +2022-07-22,111.809998,113.180000,107.599998,108.360001,108.360001,44455300 +2022-07-25,108.879997,110.580002,107.010002,108.209999,108.209999,28289900 +2022-07-26,107.430000,107.739998,104.760002,105.440002,105.440002,36626600 +2022-07-27,109.599998,114.400002,108.419998,113.599998,113.599998,41474600 +2022-07-28,112.800003,114.699997,111.850998,114.589996,114.589996,23303800 +2022-07-29,113.400002,116.900002,113.230003,116.639999,116.639999,31336200 +2022-08-01,115.529999,117.120003,114.690002,115.480003,115.480003,22856200 +2022-08-02,114.430000,117.080002,114.260002,115.900002,115.900002,17911000 +2022-08-03,116.339996,119.419998,116.150002,118.779999,118.779999,25302800 +2022-08-04,118.300003,119.500000,117.709999,118.870003,118.870003,15757700 +2022-08-05,116.930000,118.860001,116.709999,118.220001,118.220001,15615700 +2022-08-08,119.120003,120.860001,117.830002,118.139999,118.139999,17061100 +2022-08-09,117.989998,118.199997,116.559998,117.500000,117.500000,15424300 +2022-08-10,119.589996,121.779999,119.360001,120.650002,120.650002,20497000 +2022-08-11,122.080002,122.339996,119.550003,119.820000,119.820000,16671600 +2022-08-12,121.160004,122.650002,120.400002,122.650002,122.650002,16121100 +2022-08-15,122.209999,123.260002,121.570000,122.879997,122.879997,15525000 +2022-08-16,122.320000,123.227997,121.535004,122.510002,122.510002,15626200 +2022-08-17,120.930000,122.150002,120.199997,120.320000,120.320000,17589200 +2022-08-18,120.230003,121.690002,119.550003,120.860001,120.860001,15652000 +2022-08-19,119.870003,120.000000,117.669998,118.120003,118.120003,20187000 +2022-08-22,116.099998,116.500000,114.669998,115.070000,115.070000,19316000 +2022-08-23,114.320000,115.930000,114.300003,114.769997,114.769997,14390700 +2022-08-24,114.449997,115.717003,113.779999,114.699997,114.699997,16051200 +2022-08-25,115.150002,117.779999,115.050003,117.699997,117.699997,14874700 +2022-08-26,115.809998,116.599998,111.220001,111.300003,111.300003,31698700 +2022-08-29,110.779999,111.959999,109.809998,110.339996,110.339996,20386100 +2022-08-30,111.029999,111.370003,108.800003,109.910004,109.910004,20548200 +2022-08-31,111.629997,111.769997,109.050003,109.150002,109.150002,25898000 +2022-09-01,109.199997,111.220001,108.190002,110.550003,110.550003,22784400 +2022-09-02,111.339996,111.675003,108.129997,108.680000,108.680000,20618100 +2022-09-06,108.135002,108.879997,106.510002,107.480003,107.480003,20565100 +2022-09-07,107.760002,110.989998,107.614998,110.480003,110.480003,22987200 +2022-09-08,109.180000,110.580002,108.059998,109.419998,109.419998,21660700 +2022-09-09,110.050003,112.000000,110.000000,111.779999,111.779999,21732900 +2022-09-12,111.989998,112.639999,110.930000,111.870003,111.870003,19732900 +2022-09-13,108.889999,109.370003,105.000000,105.309998,105.309998,33015000 +2022-09-14,105.440002,106.099998,104.500000,105.870003,105.870003,22115800 +2022-09-15,105.010002,106.209999,103.309998,103.900002,103.900002,26494900 +2022-09-16,102.970001,104.029999,101.855003,103.629997,103.629997,64540100 +2022-09-19,102.540001,104.019997,102.370003,103.849998,103.849998,19738600 +2022-09-20,102.879997,103.169998,101.120003,101.830002,101.830002,24001700 +2022-09-21,102.239998,103.489998,99.989998,100.010002,100.010002,26596800 +2022-09-22,99.449997,101.680000,99.410004,100.570000,100.570000,21272700 +2022-09-23,100.059998,100.110001,98.010002,99.169998,99.169998,25657000 +2022-09-26,98.610001,100.440002,98.379997,98.809998,98.809998,22437900 +2022-09-27,99.910004,100.459999,97.339996,98.089996,98.089996,24225000 +2022-09-28,98.019997,101.400002,97.800003,100.739998,100.739998,24617000 +2022-09-29,99.300003,99.300003,96.519997,98.089996,98.089996,21921500 +2022-09-30,97.730003,99.494003,96.029999,96.150002,96.150002,26277800 +2022-10-03,97.220001,99.970001,97.019997,99.300003,99.300003,24840000 +2022-10-04,101.040001,102.720001,101.040001,102.410004,102.410004,22580900 +2022-10-05,100.690002,102.739998,99.739998,102.220001,102.220001,18475500 +2022-10-06,101.500000,103.730003,101.500000,102.239998,102.239998,17156200 +2022-10-07,100.650002,101.419998,99.209999,99.570000,99.570000,24249900 +2022-10-10,99.849998,99.989998,97.870003,98.709999,98.709999,16529900 +2022-10-11,98.250000,100.120003,97.250000,98.050003,98.050003,21617700 +2022-10-12,98.269997,99.648003,97.669998,98.300003,98.300003,17343400 +2022-10-13,95.930000,100.529999,95.269997,99.709999,99.709999,32812200 +2022-10-14,100.625000,101.290001,97.029999,97.180000,97.180000,22624800 +2022-10-17,99.519997,101.769997,99.510002,100.779999,100.779999,23311600 +2022-10-18,103.940002,104.220001,100.650002,101.389999,101.389999,21610500 +2022-10-19,100.699997,101.658997,99.635002,100.290001,100.290001,21573700 +2022-10-20,100.820000,103.000000,99.970001,100.529999,100.529999,25125100 +2022-10-21,98.459999,101.620003,98.230003,101.480003,101.480003,28988700 +2022-10-24,102.089996,103.099998,100.300003,102.970001,102.970001,24680800 +2022-10-25,103.300003,105.099998,103.019997,104.930000,104.930000,29910200 +2022-10-26,96.760002,98.540001,94.570000,94.820000,94.820000,71504300 +2022-10-27,94.309998,95.169998,91.900002,92.599998,92.599998,54036500 +2022-10-28,92.529999,96.860001,92.322998,96.580002,96.580002,35696900 +2022-10-31,95.779999,96.349998,94.379997,94.660004,94.660004,29868700 +2022-11-01,95.589996,96.165001,90.430000,90.500000,90.500000,43220600 +2022-11-02,90.910004,91.300003,87.010002,87.070000,87.070000,43553600 +2022-11-03,86.345001,86.550003,83.449997,83.489998,83.489998,48510400 +2022-11-04,85.510002,86.730003,83.879997,86.699997,86.699997,40173300 +2022-11-07,87.339996,88.940002,86.959999,88.650002,88.650002,26899900 +2022-11-08,89.160004,90.404999,87.650002,88.910004,88.910004,30172000 +2022-11-09,88.544998,89.489998,87.360001,87.400002,87.400002,26743900 +2022-11-10,92.339996,94.550003,91.650002,94.169998,94.169998,42371200 +2022-11-11,94.709999,97.360001,94.160004,96.730003,96.730003,30569100 +2022-11-14,95.500000,97.180000,95.112999,96.029999,96.029999,24170100 +2022-11-15,98.669998,100.419998,97.019997,98.720001,98.720001,31831000 +2022-11-16,98.019997,99.849998,97.902000,98.989998,98.989998,24660200 +2022-11-17,97.180000,99.480003,97.099998,98.500000,98.500000,21818700 +2022-11-18,99.010002,99.160004,96.739998,97.800003,97.800003,24969900 +2022-11-21,97.559998,98.720001,95.669998,95.830002,95.830002,18696900 +2022-11-22,96.160004,97.547997,94.410004,97.330002,97.330002,18868900 +2022-11-23,97.339996,99.069000,97.339996,98.820000,98.820000,17568900 +2022-11-25,98.464996,98.940002,97.529999,97.599998,97.599998,8567800 +2022-11-28,97.199997,97.830002,95.889999,96.250000,96.250000,19974500 +2022-11-29,96.000000,96.389999,94.389999,95.440002,95.440002,20220000 +2022-11-30,95.120003,101.449997,94.669998,101.449997,101.449997,39888100 +2022-12-01,101.400002,102.589996,100.669998,101.279999,101.279999,21771500 +2022-12-02,99.370003,101.150002,99.169998,100.830002,100.830002,18821500 +2022-12-05,99.815002,101.750000,99.355003,99.870003,99.870003,19955500 +2022-12-06,99.669998,100.209999,96.760002,97.309998,97.309998,20877600 +2022-12-07,96.769997,97.309998,95.025002,95.150002,95.150002,26647900 +2022-12-08,95.690002,95.870003,93.800003,93.949997,93.949997,25593200 +2022-12-09,93.900002,94.489998,93.019997,93.070000,93.070000,21885300 +2022-12-12,93.089996,93.875000,91.900002,93.559998,93.559998,27380900 +2022-12-13,98.070000,99.800003,95.379997,95.849998,95.849998,34788500 +2022-12-14,95.540001,97.220001,93.940002,95.309998,95.309998,26452900 +2022-12-15,93.540001,94.029999,90.430000,91.199997,91.199997,28298800 +2022-12-16,91.199997,91.750000,90.010002,90.860001,90.860001,48485500 +2022-12-19,90.879997,91.199997,88.925003,89.150002,89.150002,23020500 +2022-12-20,88.730003,89.779999,88.040001,89.629997,89.629997,21976800 +2022-12-21,89.730003,90.915001,88.910004,90.250000,90.250000,20336400 +2022-12-22,88.930000,89.180000,86.940002,88.260002,88.260002,23656100 +2022-12-23,87.620003,90.099998,87.620003,89.809998,89.809998,17815000 +2022-12-27,89.309998,89.500000,87.535004,87.930000,87.930000,15470900 +2022-12-28,87.500000,88.519997,86.370003,86.459999,86.459999,17879600 +2022-12-29,87.029999,89.364998,86.989998,88.949997,88.949997,18280700 +2022-12-30,87.364998,88.830002,87.029999,88.730003,88.730003,19190300 +2023-01-03,89.830002,91.550003,89.019997,89.699997,89.699997,20738500 +2023-01-04,91.010002,91.239998,87.800003,88.709999,88.709999,27046500 +2023-01-05,88.070000,88.209999,86.559998,86.769997,86.769997,23136100 +2023-01-06,87.360001,88.470001,85.570000,88.160004,88.160004,26612600 +2023-01-09,89.195000,90.830002,88.580002,88.800003,88.800003,22996700 +2023-01-10,86.720001,89.474998,86.699997,89.239998,89.239998,22855600 +2023-01-11,90.059998,92.449997,89.739998,92.260002,92.260002,25998800 +2023-01-12,92.400002,92.620003,90.570000,91.910004,91.910004,22754200 +2023-01-13,91.528000,92.980003,90.930000,92.800003,92.800003,18630700 +2023-01-17,92.779999,92.970001,90.839996,92.160004,92.160004,22935800 +2023-01-18,92.940002,93.587997,91.400002,91.779999,91.779999,19641600 +2023-01-19,91.389999,94.400002,91.379997,93.910004,93.910004,28707700 +2023-01-20,95.949997,99.419998,95.910004,99.279999,99.279999,53704800 +2023-01-23,99.129997,101.400002,98.750000,101.209999,101.209999,31791800 +2023-01-24,99.550003,101.089996,98.699997,99.209999,99.209999,27391400 +2023-01-25,97.199997,97.720001,95.262001,96.730003,96.730003,31000900 +2023-01-26,98.279999,99.209999,96.820000,99.160004,99.160004,24542100 +2023-01-27,99.050003,101.580002,98.970001,100.709999,100.709999,29020400 +2023-01-30,98.745003,99.408997,97.519997,97.949997,97.949997,24365100 +2023-01-31,97.860001,99.910004,97.790001,99.870003,99.870003,22306800 +2023-02-01,99.739998,102.190002,98.419998,101.430000,101.430000,26392600 +2023-02-02,106.790001,108.820000,106.540001,108.800003,108.800003,46622600 +2023-02-03,103.510002,108.019997,103.300003,105.220001,105.220001,36823400 +2023-02-06,102.684998,104.699997,102.209999,103.470001,103.470001,25573000 +2023-02-07,103.629997,108.669998,103.547997,108.040001,108.040001,33738800 +2023-02-08,102.690002,103.580002,98.455002,100.000000,100.000000,73546000 +2023-02-09,100.540001,100.610001,93.860001,95.459999,95.459999,97798600 +2023-02-10,95.739998,97.019997,94.529999,94.860001,94.860001,49325300 +2023-02-13,95.010002,95.349998,94.050003,95.000000,95.000000,43116600 +2023-02-14,94.660004,95.175003,92.650002,94.949997,94.949997,42513100 +2023-02-15,94.739998,97.339996,94.360001,97.099998,97.099998,36964500 +2023-02-16,95.540001,97.879997,94.970001,95.779999,95.779999,35642100 +2023-02-17,95.070000,95.750000,93.449997,94.589996,94.589996,31095100 +2023-02-21,93.239998,93.415001,92.000000,92.050003,92.050003,28367200 +2023-02-22,91.933998,92.360001,90.870003,91.800003,91.800003,29891100 +2023-02-23,92.129997,92.129997,90.010002,91.070000,91.070000,32423700 +2023-02-24,89.629997,90.129997,88.860001,89.349998,89.349998,31295600 +2023-02-27,90.089996,90.449997,89.610001,90.099998,90.099998,22724300 +2023-02-28,89.540001,91.449997,89.519997,90.300003,90.300003,30546900 +2023-03-01,90.160004,91.199997,89.849998,90.510002,90.510002,26323900 +2023-03-02,89.860001,92.480003,89.769997,92.309998,92.309998,23328600 +2023-03-03,92.739998,94.110001,92.660004,94.019997,94.019997,30242500 +2023-03-06,94.360001,96.300003,94.300003,95.580002,95.580002,28288200 +2023-03-07,95.419998,96.089996,93.844002,94.169998,94.169998,24101500 +2023-03-08,94.404999,96.239998,94.404999,94.650002,94.650002,25395200 +2023-03-09,94.489998,95.919998,92.355003,92.660004,92.660004,24438900 +2023-03-10,92.500000,93.180000,90.800003,91.010002,91.010002,32850100 +2023-03-13,90.565002,93.080002,89.940002,91.660004,91.660004,31508600 +2023-03-14,93.070000,94.830002,92.779999,94.250000,94.250000,32303900 +2023-03-15,93.540001,97.250000,93.040001,96.550003,96.550003,38367300 +2023-03-16,96.570000,101.970001,95.870003,101.070000,101.070000,54499500 +2023-03-17,100.839996,103.489998,100.750000,102.459999,102.459999,76140300 +2023-03-20,101.059998,102.580002,100.790001,101.930000,101.930000,26033900 +2023-03-21,101.980003,105.959999,101.860001,105.839996,105.839996,33122800 +2023-03-22,105.139999,107.510002,104.209999,104.220001,104.220001,32336900 +2023-03-23,105.889999,107.100998,105.410004,106.260002,106.260002,31385800 +2023-03-24,105.739998,106.160004,104.739998,106.059998,106.059998,25236200 +2023-03-27,105.320000,105.400002,102.629997,103.059998,103.059998,25393400 +2023-03-28,103.000000,103.000000,100.279999,101.360001,101.360001,24913500 +2023-03-29,102.720001,102.820000,101.029999,101.900002,101.900002,26148300 +2023-03-30,101.440002,101.610001,100.290001,101.320000,101.320000,25009800 +2023-03-31,101.709999,104.190002,101.440002,104.000000,104.000000,28108000 +2023-04-03,102.669998,104.949997,102.379997,104.910004,104.910004,20719900 +2023-04-04,104.839996,106.099998,104.599998,105.120003,105.120003,20377200 +2023-04-05,106.120003,106.540001,104.101997,104.949997,104.949997,21864200 +2023-04-06,105.769997,109.629997,104.815002,108.900002,108.900002,34684200 +2023-04-10,107.389999,107.970001,105.599998,106.949997,106.949997,19741500 +2023-04-11,106.919998,107.220001,105.279999,106.120003,106.120003,18721300 +2023-04-12,107.389999,107.586998,104.970001,105.220001,105.220001,22761600 +2023-04-13,106.470001,108.264999,106.440002,108.190002,108.190002,21650700 +2023-04-14,107.690002,109.580002,107.589996,109.459999,109.459999,20758700 +2023-04-17,105.430000,106.709999,105.320000,106.419998,106.419998,29043400 +2023-04-18,107.000000,107.050003,104.779999,105.120003,105.120003,17641400 +2023-04-19,104.214996,105.724998,103.800003,105.019997,105.019997,16732000 +2023-04-20,104.650002,106.888000,104.639999,105.900002,105.900002,22515300 +2023-04-21,106.089996,106.639999,105.485001,105.910004,105.910004,22379000 +2023-04-24,106.050003,107.320000,105.360001,106.779999,106.779999,21410900 +2023-04-25,106.610001,107.440002,104.559998,104.610001,104.610001,31408100 +2023-04-26,105.559998,107.019997,103.269997,104.449997,104.449997,37068200 +2023-04-27,105.230003,109.150002,104.419998,108.370003,108.370003,38235200 +2023-04-28,107.800003,108.290001,106.040001,108.220001,108.220001,23957900 +2023-05-01,107.720001,108.680000,107.500000,107.709999,107.709999,20926300 +2023-05-02,107.660004,107.730003,104.500000,105.980003,105.980003,20343100 +2023-05-03,106.220001,108.129997,105.620003,106.120003,106.120003,17116300 +2023-05-04,106.160004,106.300003,104.699997,105.209999,105.209999,19780600 +2023-05-05,105.320000,106.440002,104.738998,106.214996,106.214996,20705300 +2023-05-08,105.794998,108.419998,105.790001,108.239998,108.239998,17266000 +2023-05-09,108.779999,110.595001,107.724998,107.940002,107.940002,24782400 +2023-05-10,108.550003,113.510002,108.480003,112.279999,112.279999,47533500 +2023-05-11,115.860001,118.440002,114.930000,116.900002,116.900002,57115100 +2023-05-12,117.000000,118.260002,116.550003,117.919998,117.919998,31272500 +2023-05-15,116.489998,118.794998,116.480003,116.959999,116.959999,22107900 +2023-05-16,116.830002,121.199997,116.830002,120.089996,120.089996,32370100 +2023-05-17,120.180000,122.279999,119.459999,121.480003,121.480003,26659600 +2023-05-18,121.559998,123.900002,121.489998,123.519997,123.519997,27014500 +2023-05-19,124.199997,126.478996,122.720001,123.250000,123.250000,30251300 +2023-05-22,123.510002,127.050003,123.449997,125.870003,125.870003,29760200 +2023-05-23,124.930000,125.419998,123.050003,123.290001,123.290001,24477900 +2023-05-24,121.879997,122.750000,120.750000,121.639999,121.639999,23087900 +2023-05-25,125.209999,125.980003,122.900002,124.349998,124.349998,33812700 +2023-05-26,124.065002,126.000000,123.290001,125.430000,125.430000,25154700 +2023-05-30,126.290001,126.379997,122.889999,124.639999,124.639999,27230700 +2023-05-31,123.699997,124.900002,123.099998,123.370003,123.370003,41548800 +2023-06-01,123.500000,125.040001,123.300003,124.370003,124.370003,25017700 +2023-06-02,124.489998,126.745003,124.349998,125.230003,125.230003,19362400 +2023-06-05,124.610001,127.989998,124.379997,126.629997,126.629997,22672500 +2023-06-06,126.599998,128.880005,125.970001,127.910004,127.910004,19450100 +2023-06-07,127.574997,129.550003,122.629997,122.940002,122.940002,34179300 +2023-06-08,122.584999,123.730003,122.010002,122.669998,122.669998,24815000 +2023-06-09,122.559998,124.285004,122.419998,122.870003,122.870003,20304500 +2023-06-12,123.394997,124.750000,122.349998,124.349998,124.349998,22255700 +2023-06-13,125.650002,125.860001,123.845001,124.430000,124.430000,19287700 +2023-06-14,123.800003,124.790001,122.160004,124.379997,124.379997,24659600 +2023-06-15,123.879997,126.160004,123.139999,125.790001,125.790001,24517100 +2023-06-16,126.699997,126.699997,123.790001,124.059998,124.059998,56686800 +2023-06-20,123.535004,125.175003,122.830002,123.849998,123.849998,22698000 +2023-06-21,123.235001,123.410004,120.860001,121.260002,121.260002,22612000 +2023-06-22,120.660004,123.934998,119.599998,123.870003,123.870003,20781900 +2023-06-23,122.040001,123.440002,121.860001,123.019997,123.019997,29542900 +2023-06-26,121.466003,122.720001,118.989998,119.089996,119.089996,23185000 +2023-06-27,117.839996,119.894997,116.910004,119.010002,119.010002,27221700 +2023-06-28,117.959999,121.269997,117.599998,121.080002,121.080002,19753100 +2023-06-29,120.089996,120.910004,119.209999,120.010002,120.010002,18517500 +2023-06-30,121.099998,122.029999,120.879997,120.970001,120.970001,23865800 +2023-07-03,120.320000,121.019997,119.705002,120.559998,120.559998,13888300 +2023-07-05,120.059998,123.370003,120.059998,122.629997,122.629997,17830300 +2023-07-06,120.639999,121.150002,119.250000,120.930000,120.930000,17732500 +2023-07-07,120.889999,121.750000,120.089996,120.139999,120.139999,20982400 +2023-07-10,119.070000,119.070000,116.639999,116.870003,116.870003,32960100 +2023-07-11,116.760002,118.224998,115.830002,117.709999,117.709999,18286600 +2023-07-12,119.300003,120.959999,119.000000,119.620003,119.620003,22059600 +2023-07-13,121.540001,125.334999,121.059998,124.830002,124.830002,31535900 +2023-07-14,125.129997,127.089996,124.900002,125.699997,125.699997,20482800 +2023-07-17,126.059998,127.279999,124.500000,125.059998,125.059998,20675300 +2023-07-18,124.904999,124.989998,123.300003,124.080002,124.080002,21071200 +2023-07-19,124.790001,125.470001,122.470001,122.779999,122.779999,22313800 +2023-07-20,122.120003,124.699997,118.684998,119.529999,119.529999,27541700 +2023-07-21,120.870003,121.300003,119.070000,120.309998,120.309998,56498100 +2023-07-24,121.926003,123.349998,121.379997,121.879997,121.879997,22276100 +2023-07-25,121.879997,123.690002,121.529999,122.790001,122.790001,31820800 +2023-07-26,130.360001,131.369995,128.710007,129.660004,129.660004,46216900 +2023-07-27,131.800003,133.600006,129.179993,129.869995,129.869995,35931600 +2023-07-28,130.970001,134.070007,130.919998,133.009995,133.009995,26971000 +2023-07-31,133.009995,133.830002,132.130005,133.110001,133.110001,18381900 +2023-08-01,130.854996,132.919998,130.750000,131.889999,131.889999,22154300 +2023-08-02,129.839996,130.419998,127.849998,128.639999,128.639999,22705800 +2023-08-03,128.369995,129.770004,127.775002,128.770004,128.770004,15018100 +2023-08-04,129.600006,131.929993,128.315002,128.539993,128.539993,20509500 +2023-08-07,129.509995,132.059998,129.429993,131.940002,131.940002,17621000 +2023-08-08,130.979996,131.940002,130.130005,131.839996,131.839996,16836000 +2023-08-09,132.190002,132.470001,129.505005,130.149994,130.149994,17745200 +2023-08-10,131.970001,132.647003,130.035004,130.210007,130.210007,17855700 +2023-08-11,129.201996,130.440002,128.750000,130.169998,130.169998,15191500 +2023-08-14,129.850006,131.910004,129.589996,131.830002,131.830002,17526200 +2023-08-15,131.589996,131.990005,129.819000,130.270004,130.270004,14769200 +2023-08-16,129.279999,130.897995,128.460007,129.110001,129.110001,17548400 +2023-08-17,130.449997,132.490997,129.850006,130.460007,130.460007,23665600 +2023-08-18,129.059998,129.830002,127.000000,128.110001,128.110001,23619400 +2023-08-21,127.849998,129.259995,127.160004,128.929993,128.929993,21851100 +2023-08-22,129.130005,130.949997,128.925003,129.690002,129.690002,15569400 +2023-08-23,130.850006,134.070007,130.509995,133.210007,133.210007,26497000 +2023-08-24,134.727005,134.970001,130.300003,130.419998,130.419998,18680400 +2023-08-25,130.139999,131.399994,128.039993,130.690002,130.690002,20678100 +2023-08-28,132.080002,133.240005,130.850006,131.789993,131.789993,16715500 +2023-08-29,132.998001,137.294998,132.979996,135.490005,135.490005,30803300 +2023-08-30,135.570007,137.250000,135.020996,136.929993,136.929993,21773400 +2023-08-31,137.050003,138.399994,136.820007,137.350006,137.350006,28147900 +2023-09-01,138.429993,138.580002,135.940002,136.800003,136.800003,16665700 +2023-09-05,136.440002,137.369995,135.559998,136.710007,136.710007,17730200 +2023-09-06,137.014999,137.479996,134.690002,135.369995,135.369995,15814300 +2023-09-07,134.600006,136.580002,133.960007,136.199997,136.199997,16976000 +2023-09-08,135.869995,137.514999,135.869995,137.199997,137.199997,17810700 +2023-09-11,137.380005,138.264008,136.550003,137.740005,137.740005,17180800 +2023-09-12,137.130005,137.639999,135.929993,136.070007,136.070007,15212900 +2023-09-13,135.899994,137.699997,134.929993,137.500000,137.500000,16394900 +2023-09-14,138.389999,139.550003,137.059998,138.990005,138.990005,19064600 +2023-09-15,138.800003,139.360001,137.179993,138.300003,138.300003,48947600 +2023-09-18,137.630005,139.929993,137.630005,138.960007,138.960007,16233600 +2023-09-19,138.250000,139.175003,137.500000,138.830002,138.830002,15479100 +2023-09-20,138.830002,138.839996,134.520004,134.589996,134.589996,21473500 +2023-09-21,132.389999,133.190002,131.089996,131.360001,131.360001,22042700 +2023-09-22,131.679993,133.009995,130.509995,131.250000,131.250000,17348700 +2023-09-25,130.770004,132.220001,130.029999,132.169998,132.169998,14650000 +2023-09-26,130.914001,131.404999,128.190002,129.449997,129.449997,20378800 +2023-09-27,129.440002,131.720001,129.380005,131.460007,131.460007,18764200 +2023-09-28,130.690002,134.179993,130.690002,133.130005,133.130005,18201400 +2023-09-29,134.080002,134.889999,131.320007,131.850006,131.850006,23224200 +2023-10-02,132.154999,135.360001,132.065002,135.169998,135.169998,19210400 +2023-10-03,134.929993,135.240005,132.815002,133.300003,133.300003,19628700 +2023-10-04,133.660004,136.570007,133.429993,136.270004,136.270004,22848000 +2023-10-05,136.130005,136.500000,134.455002,135.990005,135.990005,15922900 +2023-10-06,134.940002,139.186005,134.940002,138.729996,138.729996,20819300 +2023-10-09,137.990005,139.970001,136.699997,139.500000,139.500000,16599100 +2023-10-10,139.509995,140.740005,138.429993,139.199997,139.199997,19554900 +2023-10-11,139.850006,142.220001,139.839996,141.699997,141.699997,19203633 \ No newline at end of file diff --git a/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/caching/LRUCache.scala b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/caching/LRUCache.scala new file mode 100644 index 000000000..15337eb25 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/caching/LRUCache.scala @@ -0,0 +1,32 @@ +package com.baeldung.scala.caching + +import scala.collection.mutable + +class LRUCache[K, V](val capacity: Int): + require(capacity >= 1, "A Cache of negative or 0 capacity makes no sense") + private val cache = mutable.LinkedHashMap.empty[K, V] + + private def evict(): Unit = { + cache.remove(cache.head._1) // Evict the least recently used item + } + + def get(key: K): Option[V] = synchronized { + // When reading, we attempt to remove the value + cache.remove(key) match + case Some(value) => + cache.put( + key, + value + ) // Put it back at the end to indicate recent access + Some(value) + case None => None + } + + def put(key: K, value: V): Unit = synchronized { + cache.remove(key) match + case _ if cache.size >= capacity => + evict() + cache.put(key, value) // Add new element at the end + case _ => + cache.put(key, value) + } diff --git a/scala-core-collections-3/src/main/scala-2/com/baeldung/scala/commoncollections/ScalaCollections.scala b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/commoncollections/ScalaCollections.scala similarity index 93% rename from scala-core-collections-3/src/main/scala-2/com/baeldung/scala/commoncollections/ScalaCollections.scala rename to scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/commoncollections/ScalaCollections.scala index f8083d32f..078296450 100644 --- a/scala-core-collections-3/src/main/scala-2/com/baeldung/scala/commoncollections/ScalaCollections.scala +++ b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/commoncollections/ScalaCollections.scala @@ -10,7 +10,7 @@ object ScalaCollections { val numbersListWithOperator: List[Int] = 1 :: 2 :: 3 :: 4 :: Nil val emptyListWithNil: List[Int] = Nil - val x :: xs = numbersList + val x :: xs = numbersList: @unchecked // Scala Set val emptySet: Set[Int] = Set() diff --git a/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/fixedsizelist/CircularBufferFixedSizeList.scala b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/fixedsizelist/CircularBufferFixedSizeList.scala new file mode 100644 index 000000000..bb5223275 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/fixedsizelist/CircularBufferFixedSizeList.scala @@ -0,0 +1,48 @@ +package com.baeldung.scala.fixedsizelist + +class CircularBufferFixedSizeList[A](val maxSize: Int) extends Iterable[A] { + private val buffer = Array.ofDim[Any](maxSize) + private var start = 0 + private var end = 0 + + def add(element: A): Unit = { + buffer(end) = element + end = (end + 1) % maxSize + + if (end == start) { + start = (start + 1) % maxSize + } + } + + def get(index: Int): Option[A] = { + if (index >= 0 && index < size) { + Some(buffer((start + index) % maxSize).asInstanceOf[A]) + } else { + None + } + } + + override def size: Int = { + if (end >= start) end - start + else maxSize - start + end + } + + override def isEmpty: Boolean = start == end + + override def iterator: Iterator[A] = new Iterator[A] { + private var pos = start + + override def hasNext: Boolean = pos != end + + override def next(): A = { + val elem = buffer(pos).asInstanceOf[A] + pos = (pos + 1) % maxSize + elem + } + } +} + +object CircularBufferFixedSizeList { + def apply[A](maxSize: Int): CircularBufferFixedSizeList[A] = + new CircularBufferFixedSizeList[A](maxSize) +} diff --git a/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/fixedsizelist/FixedSizeList.scala b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/fixedsizelist/FixedSizeList.scala new file mode 100644 index 000000000..be234f816 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/fixedsizelist/FixedSizeList.scala @@ -0,0 +1,27 @@ +package com.baeldung.scala.fixedsizelist + +class FixedSizeList[A](val maxSize: Int) extends Iterable[A] { + private val internalList = scala.collection.mutable.Queue[A]() + + def add(element: A): Unit = { + internalList.enqueue(element) + if (internalList.size > maxSize) { + internalList.dequeue() + } + } + + def get(index: Int): Option[A] = { + if (index >= 0 && index < internalList.size) Some(internalList(index)) + else None + } + + override def size: Int = internalList.size + + override def isEmpty: Boolean = internalList.isEmpty + + override def iterator: Iterator[A] = internalList.iterator +} + +object FixedSizeList { + def apply[A](maxSize: Int): FixedSizeList[A] = new FixedSizeList[A](maxSize) +} diff --git a/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/movingaverage/OptimizedMovingAverage.scala b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/movingaverage/OptimizedMovingAverage.scala new file mode 100644 index 000000000..69286f131 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/movingaverage/OptimizedMovingAverage.scala @@ -0,0 +1,46 @@ +package com.baeldung.scala.movingaverage + +import scala.collection.immutable.Queue +import scala.io.Source + +object OptimizedMovingAverage { + def main(args: Array[String]): Unit = { + val stream = getClass.getResourceAsStream("/GOOG.csv") + val lines = Source.fromInputStream(stream).getLines().drop(1) + val stockPrices = lines.map { line => + val Array(date, _, _, _, _, close, _) = line.split(",") + StockPrice(date, close.toDouble) + }.toList + + val windowSize = 60 + + val optimizedSMA = stockPrices + .foldLeft((Queue.empty[Double], List.empty[StockPrice], 0.0)) { + case ((queue, averages, sum), StockPrice(date, price)) => + val newQueue = queue.enqueue(price) + val newSum = sum + price + + if (newQueue.size > windowSize) { + val (oldest, remainingQueue) = newQueue.dequeue + val newAverage = (newSum - oldest) / windowSize + ( + remainingQueue, + StockPrice(date, newAverage) :: averages, + newSum - oldest + ) + } else if (newQueue.size == windowSize) { + val newAverage = newSum / windowSize + (newQueue, StockPrice(date, newAverage) :: averages, newSum) + } else { + (newQueue, averages, newSum) + } + } + ._2 + .reverse + + println("Optimized 30-day Simple Moving Averages:") + optimizedSMA.foreach { case StockPrice(date, average) => + println(s"$date: $average") + } + } +} diff --git a/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/movingaverage/SlidingMovingAverage.scala b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/movingaverage/SlidingMovingAverage.scala new file mode 100644 index 000000000..d70581e41 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/movingaverage/SlidingMovingAverage.scala @@ -0,0 +1,48 @@ +package com.baeldung.scala.movingaverage + +case class StockPrice(date: String, price: Double) + +object SlidingMovingAverage extends App { + + // Sample stock prices + val stockPrices = List( + StockPrice("2021-01-01", 1.2), + StockPrice("2021-01-02", 1.3), + StockPrice("2021-01-03", 1.4), + StockPrice("2021-01-04", 1.5), + StockPrice("2021-01-05", 1.6) + ) + + val windowSize = 3 + + // Simple Moving Average (SMA) + val sma = stockPrices + .sliding(windowSize) + .map { window => + val average = window.map(_.price).sum / window.size + val lastDate = window.last.date + StockPrice(lastDate, average) + } + .toList + + // Weighted Moving Average (WMA) + val weights = List(0.1, 0.2, 0.7) + val wma = stockPrices + .sliding(windowSize) + .map { window => + val weightedSum = (window.map(_.price) zip weights).map { + case (price, weight) => + price * weight + }.sum + val lastDate = window.last.date + StockPrice(lastDate, weightedSum) + } + .toList + + // Output the results + println("Simple Moving Average:") + sma.foreach(println) + + println("\nWeighted Moving Average:") + wma.foreach(println) +} diff --git a/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/zip/ZipPerformanceApp.scala b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/zip/ZipPerformanceApp.scala new file mode 100644 index 000000000..f540b90e8 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/main/scala/com/baeldung/scala/zip/ZipPerformanceApp.scala @@ -0,0 +1,31 @@ +package com.baeldung.scala.zip + +object ZipPerformanceApp { + + def timed[T](f: => T): T = { + val startTime = System.nanoTime() + val res = f + val endTime = System.nanoTime() + println( + s"Time taken for operation: ${(endTime - startTime) / 1000000} milliseconds" + ) + res + } + + @main + def main(): Unit = { + val largeList = (1 to 10000000).toList + println("--- zip ---") + timed(largeList.zip(largeList).take(100)) // eager evaluation + println("--- lazyZip without eval ---") + timed(largeList.lazyZip(largeList)) // lazy evaluation of lazyZip + println("--- lazyZip with partial eval ---") + timed( + largeList.lazyZip(largeList).take(100).toList + ) // force partial evaluation of lazyZip + println("--- lazyZip with full eval ---") + timed( + largeList.lazyZip(largeList).toList + ) // force full evaluation of lazyZip + } +} diff --git a/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/bitset/BitSetUnitTest.scala b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/bitset/BitSetUnitTest.scala new file mode 100644 index 000000000..5e755b3e6 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/bitset/BitSetUnitTest.scala @@ -0,0 +1,94 @@ +package com.baeldung.scala.bitset + +import org.scalatest.Assertions +import org.scalatest.wordspec.AnyWordSpec + +import scala.collection.immutable.BitSet + +class BitSetUnitTest extends AnyWordSpec with Assertions { + + "BitSet.apply" should { + "create an empty instance" in { + assert(BitSet() === BitSet.empty) + } + + "create an non-empty instance when arguments are provided" in { + assert(BitSet(2) === BitSet() + 2) + assert(BitSet(2) === BitSet().incl(2)) + } + } + + "BitSet.excl" should { + "remove an element if present" in { + assert(BitSet(3, 4).excl(3) === BitSet(4)) + assert(BitSet(4).excl(3) === BitSet(4)) + } + } + + "BitSet.incl" should { + "include an element if not present" in { + assert(BitSet(3).incl(4) === BitSet(3, 4)) + assert(BitSet(3, 4).incl(3) === BitSet(3, 4)) + } + } + + "BitSet.contains" should { + "return true for existing elements" in { + assert(BitSet(3, 4).contains(3)) + } + + "return false for non-existing elements" in { + assert(!BitSet(3, 4).contains(12)) + } + } + + "BitSet.diff" should { + "return the difference of two BitSet instances" in { + assert(BitSet(3, 4).diff(BitSet(2, 3)) == BitSet(4)) + } + + "return an empty BitSet for equal sets" in { + assert(BitSet(3, 4).diff(BitSet(3, 4)) == BitSet.empty) + } + } + + "BitSet.concat" should { + "return a BitSet containing the elements of both sets" in { + assert(BitSet(3, 4).concat(BitSet(2, 3)) == BitSet(2, 3, 4)) + } + } + + "BitSet.union" should { + "return a BitSet containing the elements of both sets" in { + assert(BitSet(3, 4).union(BitSet(2, 3)) == BitSet(2, 3, 4)) + assert( + BitSet(3, 4).union(BitSet(2, 3)) == BitSet(3, 4).concat(BitSet(2, 3)) + ) + } + } + + "BitSet.intersect" should { + "return a BitSet containing only elements existing in both sets" in { + assert(BitSet(3, 4).intersect(BitSet(2, 3)) == BitSet(3)) + } + } + + "BitSet.xor" should { + "return a BitSet containing only elements existing in either set1 or set2" in { + assert(BitSet(3, 4).xor(BitSet(2, 3)) == BitSet(2, 4)) + } + } + + "BitSet.toBitMask" should { + "return an array containing the words that are stored internally" in { + assert(BitSet(3, 4).toBitMask.sameElements(List(24))) + } + } + + "BitSet.fromBitMask" should { + "return a BitSet containing internally as words the given arguments" in { + assert(BitSet.fromBitMaskNoCopy(Array(25L)) == BitSet(0, 3, 4)) + } + } + +} diff --git a/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/caching/LRUCacheUnitTest.scala b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/caching/LRUCacheUnitTest.scala new file mode 100644 index 000000000..98baa437c --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/caching/LRUCacheUnitTest.scala @@ -0,0 +1,82 @@ +package com.baeldung.scala.caching + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class LRUCacheUnitTest extends AnyFlatSpec with Matchers: + + "An LRU Cache" should "throw IllegalArgumentException if created with zero or negative size" in { + an[IllegalArgumentException] should be thrownBy { + new LRUCache[Int, String](0) + } + + an[IllegalArgumentException] should be thrownBy { + new LRUCache[Int, String](-1) + } + } + + it should "handle a cache size of 1 correctly" in { + val cache = new LRUCache[String, Int](1) + cache.put("key1", 1) + cache.get("key1") should be(Some(1)) + + // Adding another element should evict "key1" + cache.put("key2", 2) + cache.get("key1") should be(None) + cache.get("key2") should be(Some(2)) + + // Accessing "key2" and then adding a new key should evict "key2" + cache.get("key2") + cache.put("key3", 3) + cache.get("key2") should be(None) + cache.get("key3") should be(Some(3)) + } + + it should "return None for missing values" in { + val cache = new LRUCache[String, Int](2) + cache.get("missing") should be(None) + } + + it should "return the value for a key if present" in { + val cache = new LRUCache[String, Int](2) + cache.put("key1", 1) + cache.get("key1") should be(Some(1)) + } + + it should "evict the least recently used item when exceeding maxSize" in { + val cache = new LRUCache[String, Int](2) + cache.put("key1", 1) + cache.put("key2", 2) + cache.put("key3", 3) // This should evict "key1" + + cache.get("key1") should be(None) + cache.get("key2") should be(Some(2)) + cache.get("key3") should be(Some(3)) + } + + it should "update the position of a key when accessed" in { + val cache = new LRUCache[String, Int](2) + cache.put("key1", 1) + cache.put("key2", 2) + cache.get("key1") // This should move "key1" to the end + cache.put("key3", 3) // This should evict "key2" + + cache.get("key1") should be(Some(1)) + cache.get("key2") should be(None) + cache.get("key3") should be(Some(3)) + } + + it should "update the position of a key when updated" in { + val cache = new LRUCache[String, Int](2) + cache.put("key1", 1) + cache.put("key2", 2) + cache.put( + "key1", + 10 + ) // This should update "key1" value and move it to the end + cache.put("key3", 3) // This should evict "key2" + + cache.get("key1") should be(Some(10)) + cache.get("key2") should be(None) + cache.get("key3") should be(Some(3)) + } diff --git a/scala-core-collections-3/src/test/scala-2/com/baeldung/scala/commoncollections/ArrayBufferUnitTest.scala b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/commoncollections/ArrayBufferUnitTest.scala similarity index 100% rename from scala-core-collections-3/src/test/scala-2/com/baeldung/scala/commoncollections/ArrayBufferUnitTest.scala rename to scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/commoncollections/ArrayBufferUnitTest.scala diff --git a/scala-core-collections-3/src/test/scala-2/com/baeldung/scala/commoncollections/GetListItemsUnitTest.scala b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/commoncollections/GetListItemsUnitTest.scala similarity index 100% rename from scala-core-collections-3/src/test/scala-2/com/baeldung/scala/commoncollections/GetListItemsUnitTest.scala rename to scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/commoncollections/GetListItemsUnitTest.scala diff --git a/scala-core-collections-3/src/test/scala-2/com/baeldung/scala/commoncollections/ListSetOperationsUnitTest.scala b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/commoncollections/ListSetOperationsUnitTest.scala similarity index 100% rename from scala-core-collections-3/src/test/scala-2/com/baeldung/scala/commoncollections/ListSetOperationsUnitTest.scala rename to scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/commoncollections/ListSetOperationsUnitTest.scala diff --git a/scala-core-collections-3/src/test/scala-2/com/baeldung/scala/commoncollections/ScalaCollectionsUnitTest.scala b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/commoncollections/ScalaCollectionsUnitTest.scala similarity index 100% rename from scala-core-collections-3/src/test/scala-2/com/baeldung/scala/commoncollections/ScalaCollectionsUnitTest.scala rename to scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/commoncollections/ScalaCollectionsUnitTest.scala diff --git a/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/fixedsizelist/CircularBufferFixedSizeListUnitTest.scala b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/fixedsizelist/CircularBufferFixedSizeListUnitTest.scala new file mode 100644 index 000000000..d18a06cb2 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/fixedsizelist/CircularBufferFixedSizeListUnitTest.scala @@ -0,0 +1,41 @@ +package com.baeldung.scala.fixedsizelist + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class CircularBufferFixedSizeListUnitTest extends AnyFlatSpec with Matchers { + "A CircularBufferFixedSizeListSpec" should "limit its size to the specified maximum" in { + val list = FixedSizeList[Int](3) + list.add(1) + list.add(2) + list.add(3) + list.add(4) + list.size should be(3) + list.get(0) should be(Some(2)) + } + + it should "be empty when created" in { + val list = FixedSizeList[Int](3) + list.isEmpty should be(true) + } + + it should "be mappable" in { + val list = FixedSizeList[Int](3) + list.add(1) + list.add(2) + list.add(3) + list.map(_ * 2) should contain theSameElementsAs Seq(2, 4, 6) + } + + it should "be iterable" in { + val expected = List(1, 2, 3) + val list = FixedSizeList[Int](3) + list.add(1) + list.add(2) + list.add(3) + + for ((e, i) <- list.zipWithIndex) { + e should be(expected(i)) + } + } +} diff --git a/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/fixedsizelist/FixedSizeListUnitTest.scala b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/fixedsizelist/FixedSizeListUnitTest.scala new file mode 100644 index 000000000..b3e5ffe3d --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/fixedsizelist/FixedSizeListUnitTest.scala @@ -0,0 +1,41 @@ +package com.baeldung.scala.fixedsizelist + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class FixedSizeListUnitTest extends AnyFlatSpec with Matchers { + "A FixedSizeList" should "limit its size to the specified maximum" in { + val list = FixedSizeList[Int](3) + list.add(1) + list.add(2) + list.add(3) + list.add(4) + list.size should be(3) + list.get(0) should be(Some(2)) + } + + it should "be empty when created" in { + val list = FixedSizeList[Int](3) + list.isEmpty should be(true) + } + + it should "be mappable" in { + val list = FixedSizeList[Int](3) + list.add(1) + list.add(2) + list.add(3) + list.map(_ * 2) should contain theSameElementsAs Seq(2, 4, 6) + } + + it should "be iterable" in { + val expected = List(1, 2, 3) + val list = FixedSizeList[Int](3) + list.add(1) + list.add(2) + list.add(3) + + for ((e, i) <- list.zipWithIndex) { + e should be(expected(i)) + } + } +} diff --git a/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/slidinggrouped/SlidingGroupedUnitTest.scala b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/slidinggrouped/SlidingGroupedUnitTest.scala new file mode 100644 index 000000000..0155b2972 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/slidinggrouped/SlidingGroupedUnitTest.scala @@ -0,0 +1,58 @@ +package com.baeldung.scala.slidinggrouped + +import org.scalatest.wordspec.AnyWordSpec + +class SlidingGroupedUnitTest extends AnyWordSpec { + + "sliding method" should { + "creating a sliding window with 2 elements" in { + val numbers: List[Int] = List(1, 2, 3, 4, 5, 6) + val slidingWindow: Iterator[List[Int]] = numbers.sliding(2, 1) + assert( + slidingWindow.toList == List( + List(1, 2), + List(2, 3), + List(3, 4), + List(4, 5), + List(5, 6) + ) + ) + } + "without providing sliding window should use 1 by default" in { + val numbers = List(1, 2, 3, 4, 5, 6) + val window = numbers.sliding(2) + assert( + window.toList == List( + List(1, 2), + List(2, 3), + List(3, 4), + List(4, 5), + List(5, 6) + ) + ) + } + + "create a group with lesser number than the group size provided" in { + val numbers = List(1, 2, 3, 4) + assert(numbers.sliding(3, 2).toList == List(List(1, 2, 3), List(3, 4))) + } + + } + + "grouped method" should { + "group the elements into equal groupings if possible" in { + val numbers = List(1, 2, 3, 4) + assert(numbers.grouped(2).toList == List(List(1, 2), List(3, 4))) + } + + "be same as sliding if the sliding window and group size is same" in { + val numbers = List(1, 2, 3, 4, 5, 6) + val slidingList = + numbers.sliding(2, 2).toList // List(List(1, 2), List(3, 4), List(5, 6)) + val groupedList = + numbers.grouped(2).toList // List(List(1, 2), List(3, 4), List(5, 6)) + assert(slidingList == groupedList) + } + } + +} diff --git a/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/zip/ZipUnitTest.scala b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/zip/ZipUnitTest.scala new file mode 100644 index 000000000..ccbb45d39 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/zip/ZipUnitTest.scala @@ -0,0 +1,51 @@ +package com.baeldung.scala.zip + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +import scala.collection.LazyZip2 + +class ZipUnitTest extends AnyWordSpec with Matchers { + + "Zip" should { + "zip two lists" in { + val list1 = List(1, 2, 3) + val list2 = List("a", "b", "c") + val zipped = list1.zip(list2) + zipped shouldBe List((1, "a"), (2, "b"), (3, "c")) + Map(1 -> 2) zip Map(3 -> 4) + Set(1, 2) zip Set(3, 4) + } + + } + + "Lazy Zip" should { + "zip two lists" in { + val list1 = List(1, 2, 3) + val list2 = List("a", "b", "c") + val zipped = list1.lazyZip(list2) + zipped.toList shouldBe List((1, "a"), (2, "b"), (3, "c")) + } + + "zip two potentially infinite sequences" in { + val infiniteNumbers: LazyList[Int] = LazyList.from(1) + val infiniteStrings: LazyList[String] = LazyList.iterate("a")(_ + "a") + val result = infiniteNumbers.lazyZip(infiniteStrings) + result.take(3).toList shouldBe List((1, "a"), (2, "aa"), (3, "aaa")) + } + + "automatically flattens upto 4 levels" in { + val list = List(1, 2, 3) + val level4Res = list.lazyZip(list).lazyZip(list).lazyZip(list).toList + level4Res shouldBe List((1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3)) + val level5Res = + list.lazyZip(list).lazyZip(list).lazyZip(list).lazyZip(list).toList + level5Res shouldBe List( + ((1, 1, 1, 1), 1), + ((2, 2, 2, 2), 2), + ((3, 3, 3, 3), 3) + ) + } + } + +} diff --git a/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/zipvariants/ZipVariantUnitTest.scala b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/zipvariants/ZipVariantUnitTest.scala new file mode 100644 index 000000000..9e7d6466c --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-3/src/test/scala/com/baeldung/scala/zipvariants/ZipVariantUnitTest.scala @@ -0,0 +1,48 @@ +package com.baeldung.scala.zipvariants + +import org.scalatest.wordspec.AnyWordSpec + +class ZipVariantUnitTest extends AnyWordSpec { + + "zip method" should { + "combine two collections of equal size correctly" in { + val numbers = List(1, 2, 3) + val words = List("one", "two", "three") + val zipped = numbers.zip(words) + assert(zipped == List((1, "one"), (2, "two"), (3, "three"))) + } + + "drop elements from larger collection" in { + val numbers = List(1, 2, 3) + val words = List("one", "two", "three", "four") + val zipped = numbers.zip(words) + assert(zipped == List((1, "one"), (2, "two"), (3, "three"))) + } + } + + "zipAll" should { + "combine two collections of unequal size with default values" in { + val numbers = List(1, 2, 3) + val words = List("one", "two", "three", "four") + val zipped = numbers.zipAll(words, 0, "unknown") + assert(zipped == List((1, "one"), (2, "two"), (3, "three"), (0, "four"))) + val zipped2 = numbers.zipAll(words.take(2), 0, "unknown") + assert(zipped2 == List((1, "one"), (2, "two"), (3, "unknown"))) + } + } + + "zipWithIndex" should { + "combine each element of a list with its corresponding index" in { + val words = List("one", "two", "three", "four") + assert( + words.zipWithIndex == List( + ("one", 0), + ("two", 1), + ("three", 2), + ("four", 3) + ) + ) + } + } + +} diff --git a/scala-core-collections-modules/scala-core-collections-4/README.md b/scala-core-collections-modules/scala-core-collections-4/README.md new file mode 100644 index 000000000..b8c34ac81 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-4/README.md @@ -0,0 +1,5 @@ +## Relevant Articles +- [Using filter() and takeWhile() With Collections in Scala](https://www.baeldung.com/scala/filter-takewhile) +- [Finding Elements in a List using takeWhile() and dropWhile() in Scala](https://www.baeldung.com/scala/list-find-takewhile-dropwhile) +- [Calculate the Average of a List in Scala](https://www.baeldung.com/scala/list-compute-mean-value) +- [Convert Java List to Scala Seq](https://www.baeldung.com/scala/java-list-seq-conversion) diff --git a/scala-core-collections-modules/scala-core-collections-4/src/main/scala/com/baeldung/scala/javatoscalaseq/ConvertJavaAListToScalaSeq.scala b/scala-core-collections-modules/scala-core-collections-4/src/main/scala/com/baeldung/scala/javatoscalaseq/ConvertJavaAListToScalaSeq.scala new file mode 100644 index 000000000..3164fbca5 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-4/src/main/scala/com/baeldung/scala/javatoscalaseq/ConvertJavaAListToScalaSeq.scala @@ -0,0 +1,45 @@ +package com.baeldung.scala.javatoscalaseq + +import java.util +import scala.jdk.javaapi.CollectionConverters +import scala.collection.JavaConverters + +object ConvertJavaAListToScalaSeq { + + private def takeSeq(seq: Seq[String]): String = { + seq.mkString + } + + def in212(list: util.List[String]): String = { + val scalaSeq: Seq[String] = + JavaConverters.collectionAsScalaIterable(list).toSeq + takeSeq(scalaSeq) + } + + def in213(list: util.List[String]): String = { + val scalaSeq: Seq[String] = CollectionConverters.asScala(list).toSeq + takeSeq(scalaSeq) + } + + def convertToList212(list: util.List[String]): String = { + val scalaList: List[String] = + JavaConverters.collectionAsScalaIterable(list).toList + val scalaSet: Set[String] = + JavaConverters.collectionAsScalaIterable(list).toSet + val scalaIndexedSeq: IndexedSeq[String] = + JavaConverters.collectionAsScalaIterable(list).toIndexedSeq + val scalaVector: Vector[String] = + JavaConverters.collectionAsScalaIterable(list).toVector + takeSeq(scalaList) + } + + def convertToList213(list: util.List[String]): String = { + val scalaList: List[String] = CollectionConverters.asScala(list).toList + val scalaSet: Set[String] = CollectionConverters.asScala(list).toSet + val scalaIndexedSeq: IndexedSeq[String] = + CollectionConverters.asScala(list).toIndexedSeq + val scalaVector: Vector[String] = + CollectionConverters.asScala(list).toVector + takeSeq(scalaList) + } +} diff --git a/scala-core-collections-modules/scala-core-collections-4/src/main/scala/com/baeldung/scala/listaverage/ListAverage.scala b/scala-core-collections-modules/scala-core-collections-4/src/main/scala/com/baeldung/scala/listaverage/ListAverage.scala new file mode 100644 index 000000000..4bd39d137 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-4/src/main/scala/com/baeldung/scala/listaverage/ListAverage.scala @@ -0,0 +1,44 @@ +package com.baeldung.scala.listaverage + +object ListAverage { + + def naive(lst: List[Int]): Int = { + var total = 0 + var nrElements = 0 + + for (elem <- lst) { + total += elem + nrElements += 1 + } + + total / nrElements + } + + def averageDouble(lst: List[Int]): Double = { + var total = 0 + + // here we use double to ensure we dont round final number + var nrElements = 0.0 + + for (elem <- lst) { + total += elem + nrElements += 1 + } + + total / nrElements + } + + def averageWithListMethods(lst: List[Int]): Int = { + lst.sum / lst.size + } + + def averageWithListMethodsDouble(lst: List[Int]): Double = { + lst.sum / lst.size.toDouble + } + + def averageWithFold(lst: List[Int]): Double = { + val (sum, size) = + lst.foldLeft((0.0, 0))((pair, elem) => (pair._1 + elem, pair._2 + 1)) + sum / size + } +} diff --git a/scala-core-collections-modules/scala-core-collections-4/src/main/scala/com/baeldung/scala/listtotuple/ConvertListToTuple.scala b/scala-core-collections-modules/scala-core-collections-4/src/main/scala/com/baeldung/scala/listtotuple/ConvertListToTuple.scala new file mode 100644 index 000000000..c652dcd18 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-4/src/main/scala/com/baeldung/scala/listtotuple/ConvertListToTuple.scala @@ -0,0 +1,24 @@ +package com.baeldung.scala.listtotuple + +object ConvertListToTuple { + def twoElementsToTuple(list: List[String]): (String, String) = { + val first :: second :: _ = list + (first, second) + } + + def twoElementsToTupleUsingMatch(list: List[String]): (String, String) = { + list match { + case first :: second :: _ => (first, second) + case _ => ("", "") + } + } + + def unknownSizeToTuple(list: List[String]): Tuple = { + list match { + case first :: second :: third :: _ => (first, second, third) + case first :: second :: _ => (first, second) + case first :: _ => Tuple1(first) + case _ => ("", "") + } + } +} diff --git a/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/filtertakewhile/FilterAndTakeWhileUnitTest.scala b/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/filtertakewhile/FilterAndTakeWhileUnitTest.scala new file mode 100644 index 000000000..23cddb503 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/filtertakewhile/FilterAndTakeWhileUnitTest.scala @@ -0,0 +1,46 @@ +package com.baeldung.scala.filtertakewhile + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +import java.time.{LocalDate, LocalDateTime} +import scala.concurrent.{Await, Future, TimeoutException} +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration.* + +class FilterAndTakeWhileUnitTest extends AnyWordSpec with Matchers { + + "filter" should { + "filter all elements matching the condition" in { + val numbers = List(1, 2, 3, 4, 5, 6) + val oddNumbers = numbers.filter(_ % 2 != 0) + oddNumbers shouldBe List(1, 3, 5) + } + + "not complete the execution due to infinite collection" ignore { + val infiniteNumbers = LazyList.from(1) + val lessThan100 = + infiniteNumbers.filter(_ < 100).toList // never completes + } + } + + "takeWhile" should { + "select elements until the predicate fails" in { + val numbers = List(1, 2, 3, 4, 5, 6) + val numbersBeforeFirstEven = numbers.takeWhile(_ % 2 != 0) + numbersBeforeFirstEven shouldBe List(1) + } + + "select different list of elements if the list item changes" in { + val numbers = List(1, 3, 4, 5, 6) + val numbersBeforeFirstEven = numbers.takeWhile(_ % 2 != 0) + numbersBeforeFirstEven shouldBe List(1, 3) + } + + "successfully get result from an infinite collection" in { + val infiniteNumbers = LazyList.from(1) + val first100 = infiniteNumbers.takeWhile(_ < 100).toList + first100 shouldBe (1 to 99).toList + } + } +} diff --git a/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/javatoscalaseq/ConvertJavaAListToScalaSeqUnitTest.scala b/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/javatoscalaseq/ConvertJavaAListToScalaSeqUnitTest.scala new file mode 100644 index 000000000..8bbf82166 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/javatoscalaseq/ConvertJavaAListToScalaSeqUnitTest.scala @@ -0,0 +1,27 @@ +package com.baeldung.scala.javatoscalaseq + +import java.util +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ConvertJavaAListToScalaSeqUnitTest extends AnyFlatSpec with Matchers { + val list: util.List[String] = util.ArrayList[String]() + list.add("hello") + list.add(" world") + + "212" should "return String" in { + ConvertJavaAListToScalaSeq.in212(list) shouldBe "hello world" + } + + "213" should "return String" in { + ConvertJavaAListToScalaSeq.in213(list) shouldBe "hello world" + } + + "convertToList212" should "return String" in { + ConvertJavaAListToScalaSeq.convertToList212(list) shouldBe "hello world" + } + + "convertToList213" should "return String" in { + ConvertJavaAListToScalaSeq.convertToList213(list) shouldBe "hello world" + } +} diff --git a/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/listaverage/ListAverageUnitTest.scala b/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/listaverage/ListAverageUnitTest.scala new file mode 100644 index 000000000..485410619 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/listaverage/ListAverageUnitTest.scala @@ -0,0 +1,29 @@ +package com.baeldung.scala.listaverage + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ListAverageUnitTest extends AnyFlatSpec with Matchers { + val lst = List(1, 2, 3, 4) + + "naive" should "return rounded average 2" in { + ListAverage.naive(lst) shouldBe 2 + } + + "naive with double" should "return average 2.5" in { + ListAverage.averageDouble(lst) shouldBe 2.5 + } + + "averageWithListMethods" should "return rounded average 2" in { + ListAverage.averageWithListMethods(lst) shouldBe 2 + } + + "averageWithListMethodsDouble" should "return average 2.5" in { + ListAverage.averageWithListMethodsDouble(lst) shouldBe 2.5 + } + + "averageWithFold" should "return average 2.5" in { + ListAverage.averageWithFold(lst) shouldBe 2.5 + } + +} diff --git a/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/listtotuple/ConvertListToTupleUnitTest.scala b/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/listtotuple/ConvertListToTupleUnitTest.scala new file mode 100644 index 000000000..ff709a92e --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/listtotuple/ConvertListToTupleUnitTest.scala @@ -0,0 +1,61 @@ +package com.baeldung.scala.listtotuple + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +class ConvertListToTupleUnitTest extends AnyFlatSpec with Matchers { + "twoElementsToTuple" should "convert list with 2 elements" in { + val testList = List("Hello", "world") + ConvertListToTuple.twoElementsToTuple(testList) shouldBe ("Hello", "world") + } + + "twoElementsToTuple" should "convert list with 3 elements to Tuple3" in { + val testList = List("Hello", "world", "!") + ConvertListToTuple.twoElementsToTuple(testList) shouldBe ("Hello", "world") + } + + "twoElementsToTupleUsingMatch" should "convert list with 2 elements" in { + val testList = List("Hello", "world") + ConvertListToTuple.twoElementsToTupleUsingMatch( + testList + ) shouldBe ("Hello", "world") + } + + "twoElementsToTupleUsingMatch" should "convert list with 3 elements to tuple2 ignoring extra elements" in { + val testList = List("Hello", "world", "!") + ConvertListToTuple.twoElementsToTupleUsingMatch( + testList + ) shouldBe ("Hello", "world") + } + + "twoElementsToTupleUsingMatch" should "return empty Strings for 1 element" in { + val testList = List("Hello") + ConvertListToTuple.twoElementsToTupleUsingMatch(testList) shouldBe ("", "") + } + + "twoElementsToTupleUsingMatch" should "return empty Strings for Nil" in { + val testList = Nil + ConvertListToTuple.twoElementsToTupleUsingMatch(testList) shouldBe ("", "") + } + + "unknownSizeToTuple" should "return empty Strings for Nil" in { + val testList = Nil + ConvertListToTuple.unknownSizeToTuple(testList) shouldBe ("", "") + } + + "unknownSizeToTuple" should "convert list of 1 element to tuple1" in { + val testList = List("Hello") + ConvertListToTuple.unknownSizeToTuple(testList) shouldBe Tuple1("Hello") + } + + "unknownSizeToTuple" should "convert list of 2 elements to tuple2" in { + val testList = List("Hello", "world") + ConvertListToTuple.unknownSizeToTuple(testList) shouldBe ("Hello", "world") + } + + "unknownSizeToTuple" should "convert list of 3 elements to tuple3" in { + val testList = List("Hello", "world", "!") + ConvertListToTuple.unknownSizeToTuple( + testList + ) shouldBe ("Hello", "world", "!") + } +} diff --git a/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/removeelement/RemoveMatchingElementUnitTest.scala b/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/removeelement/RemoveMatchingElementUnitTest.scala new file mode 100644 index 000000000..16eeb273a --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/removeelement/RemoveMatchingElementUnitTest.scala @@ -0,0 +1,118 @@ +package com.baeldung.scala.removeelement + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks + +import scala.collection.mutable.{ArrayBuffer, ListBuffer} + +class RemoveMatchingElementUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + //format: off + val firstMatchTable = Table( + ("input", "elementToRemove", "expected"), + (List("abc", "def", "xyz", "abc"), "abc" ,List("def", "xyz", "abc")), + (List("abc", "def", "xyz", "abc"), "abcd" ,List("abc", "def", "xyz", "abc")), + (List.empty[String], "abcd" ,List.empty[String]), + ) + + val allMatchTable = Table( + ("input", "elementToRemove", "expected"), + (List("abc", "def", "xyz", "abc"), "abc", List("def", "xyz")), + (List("abc", "def", "xyz", "abc"), "abcd", List("abc", "def", "xyz", "abc")), + (List.empty[String], "abcd", List.empty[String]), + ) + //format on + + // on immutable collections + it should "remove first match using span" in { + forAll(firstMatchTable) { (input, ele, exp) => + val (part1, part2)= input.span(_ != ele) + val result = part1 ++ part2.drop(1) + result shouldBe exp + } + } + + it should "remove first match using pattern matching" in { + forAll(firstMatchTable) { (input, ele, exp) => + val result = input match { + case `ele` :: sub => sub + case sub => sub + } + result shouldBe exp + } + } + + it should "remove all matches using partition" in { + forAll(allMatchTable) { (input, ele, exp) => + val (part1, _) = input.partition(_ != ele) + val result = part1 + result shouldBe exp + } + } + + // on mutable collections + it should "remove first match from mutable list" in { + val buffer = ListBuffer[String]("abc", "def", "xyz", "def") + buffer -= "def" + buffer shouldBe ListBuffer[String]("abc", "xyz", "def") + } + + it should "remove all matches from mutable list using filterInPlace" in { + val buffer = ListBuffer[String]("abc", "def", "xyz", "def") + buffer.filterInPlace(_ != "def") + buffer shouldBe ListBuffer[String]("abc", "xyz") + } + + it should "remove all matches from mutable list using filter" in { + val list = List("abc", "def", "xyz", "def") + val filteredList = list.filter(_ != "def") + filteredList shouldBe List("abc", "xyz") + list.filterNot(_ == "def") shouldBe List("abc", "xyz") + // on ListBuffer + val buffer = ListBuffer[String]("abc", "def", "xyz", "def") + val newBuffer = buffer.filter(_ != "def") + newBuffer shouldBe ListBuffer[String]("abc", "xyz") + } + + // remove multiple matches + it should "remove all matches using collect" in { + forAll(allMatchTable) { (input, ele, exp) => + val result = input.collect { + case e if e != ele => e + } + result shouldBe exp + } + } + + it should "remove all matches using filter" in { + forAll(allMatchTable) { (input, ele, exp) => + val result = input.filter(_ != ele) + result shouldBe exp + val result2 = input.filterNot(_ == ele) + result2 shouldBe exp + } + } + + it should "remove all matches using recursion" in { + forAll(allMatchTable) { (input, ele, exp) => + val result = input.foldLeft(List.empty[String])((acc,i) => if(i != ele) acc :+ i else acc) + result shouldBe exp + } + } + + it should "remove all matches from mutable ListBuffer" in { + val buffer = ListBuffer[String]("abc", "def", "xyz", "def") + buffer.filterInPlace(_ != "def") + buffer shouldBe ListBuffer[String]("abc", "xyz") + } + + it should "remove all matches from mutable ArrayBuffer" in { + val buffer = ArrayBuffer[String]("abc", "def", "xyz", "def") + buffer.filterInPlace(_ != "def") + buffer shouldBe ListBuffer[String]("abc", "xyz") + } +} diff --git a/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/takewhiledropwhile/TakeWhileDropWhileUnitTest.scala b/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/takewhiledropwhile/TakeWhileDropWhileUnitTest.scala new file mode 100644 index 000000000..f6fa3fe07 --- /dev/null +++ b/scala-core-collections-modules/scala-core-collections-4/src/test/scala/com/baeldung/scala/takewhiledropwhile/TakeWhileDropWhileUnitTest.scala @@ -0,0 +1,47 @@ +package com.baeldung.scala.takewhiledropwhile + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +class TakeWhileDropWhileUnitTest extends AnyWordSpec with Matchers { + + private val numbersAscending = List(1, 2, 3, 4, 5, 6, 7, 8) + private val numbersDescending = List(8, 7, 6, 5, 4, 3, 2, 1) + private val numbersMixed = List(1, 2, 3, 4, 3, 2, 1) + + "takeWhile" should { + "take element from list till condition satisfies" in { + val lessThanFive = numbersAscending.takeWhile(_ < 5) + lessThanFive shouldBe List(1, 2, 3, 4) + } + + "return different elements when the element order changes" in { + val lessThanFive = numbersDescending.takeWhile(_ < 5) + lessThanFive shouldBe Nil + } + + "stop taking as soon as the first failure in predicate" in { + val lessThanThree = numbersMixed.takeWhile(_ < 3) + lessThanThree shouldBe List(1, 2) + } + + "take all elements if the predicate is satisfied for all" in { + val positive = numbersAscending.takeWhile(_ > 0) + positive shouldBe numbersAscending + } + + } + + "dropWhile" should { + "drop the elements from the list until the predicate is true" in { + val dropLessThan5 = numbersAscending.dropWhile(_ < 5) + dropLessThan5 shouldBe List(5, 6, 7, 8) + } + + "dropWhile behavior changes with the order" in { + val dropLessThan5 = numbersDescending.dropWhile(_ < 5) + dropLessThan5 shouldBe numbersDescending + } + } + +} diff --git a/scala-core-collections/README.md b/scala-core-collections-modules/scala-core-collections/README.md similarity index 77% rename from scala-core-collections/README.md rename to scala-core-collections-modules/scala-core-collections/README.md index ca65111c7..8832b7506 100644 --- a/scala-core-collections/README.md +++ b/scala-core-collections-modules/scala-core-collections/README.md @@ -4,10 +4,7 @@ - [Converting Java Collections to Scala Collections](https://www.baeldung.com/scala/convert-java-collections) - [Folding Lists in Scala](https://www.baeldung.com/scala/folding-lists) - [List Concatenation Operators in Scala](https://www.baeldung.com/scala/list-concatenation-operators) -- [Iterating Over a Scala Map](https://www.baeldung.com/scala/iterate-map) - [Remove Duplicates in a Scala List](https://www.baeldung.com/scala/list-remove-duplicates) - [Guide to Arrays in Scala](https://www.baeldung.com/scala/arrays-guide) -- [Map Both Keys and Values of a Scala Map](https://www.baeldung.com/scala/map-keys-values) - [Split List by Fixed Number of Elements](https://www.baeldung.com/scala/split-list-by-fixed-number-of-elements) -- [Merge Two Maps in Scala](https://www.baeldung.com/scala/merge-two-maps) - [Different Ways To Iterate Over Collections in Scala](https://www.baeldung.com/scala/collections-iterators) diff --git a/scala-core-collections/src/main/scala-2/com/baeldung/scala/collections/Arrays.scala b/scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/collections/Arrays.scala similarity index 100% rename from scala-core-collections/src/main/scala-2/com/baeldung/scala/collections/Arrays.scala rename to scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/collections/Arrays.scala diff --git a/scala-core-collections/src/main/scala-2/com/baeldung/scala/collections/FoldingLists.scala b/scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/collections/FoldingLists.scala similarity index 100% rename from scala-core-collections/src/main/scala-2/com/baeldung/scala/collections/FoldingLists.scala rename to scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/collections/FoldingLists.scala diff --git a/scala-core-collections/src/main/scala-2/com/baeldung/scala/collections/IterateCollections.scala b/scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/collections/IterateCollections.scala similarity index 100% rename from scala-core-collections/src/main/scala-2/com/baeldung/scala/collections/IterateCollections.scala rename to scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/collections/IterateCollections.scala diff --git a/scala-core-collections/src/main/scala-2/com/baeldung/scala/collections/ParallelCollections.scala b/scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/collections/ParallelCollections.scala similarity index 100% rename from scala-core-collections/src/main/scala-2/com/baeldung/scala/collections/ParallelCollections.scala rename to scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/collections/ParallelCollections.scala diff --git a/scala-core-collections/src/main/scala-2/com/baeldung/scala/flattening/Flattener.scala b/scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/flattening/Flattener.scala similarity index 100% rename from scala-core-collections/src/main/scala-2/com/baeldung/scala/flattening/Flattener.scala rename to scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/flattening/Flattener.scala diff --git a/scala-core-collections/src/main/scala-2/com/baeldung/scala/removeduplicates/DuplicatesRemover.scala b/scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/removeduplicates/DuplicatesRemover.scala similarity index 100% rename from scala-core-collections/src/main/scala-2/com/baeldung/scala/removeduplicates/DuplicatesRemover.scala rename to scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/removeduplicates/DuplicatesRemover.scala diff --git a/scala-core-collections/src/main/scala-2/com/baeldung/scala/splitlist/ListSplitter.scala b/scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/splitlist/ListSplitter.scala similarity index 100% rename from scala-core-collections/src/main/scala-2/com/baeldung/scala/splitlist/ListSplitter.scala rename to scala-core-collections-modules/scala-core-collections/src/main/scala/com/baeldung/scala/splitlist/ListSplitter.scala diff --git a/scala-core-collections/src/test/java/com/baedung/scala/conversions/JavaApi.java b/scala-core-collections-modules/scala-core-collections/src/test/java/com/baedung/scala/conversions/JavaApi.java similarity index 100% rename from scala-core-collections/src/test/java/com/baedung/scala/conversions/JavaApi.java rename to scala-core-collections-modules/scala-core-collections/src/test/java/com/baedung/scala/conversions/JavaApi.java diff --git a/scala-core-collections/src/test/scala-2/com/baedung/scala/collections/FoldingListsUnitTest.scala b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/collections/FoldingListsUnitTest.scala similarity index 100% rename from scala-core-collections/src/test/scala-2/com/baedung/scala/collections/FoldingListsUnitTest.scala rename to scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/collections/FoldingListsUnitTest.scala diff --git a/scala-core-collections/src/test/scala-2/com/baedung/scala/collections/ListConcatenationUnitTest.scala b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/collections/ListConcatenationUnitTest.scala similarity index 100% rename from scala-core-collections/src/test/scala-2/com/baedung/scala/collections/ListConcatenationUnitTest.scala rename to scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/collections/ListConcatenationUnitTest.scala diff --git a/scala-core-collections/src/test/scala-2/com/baedung/scala/collections/ParallelCollectionTest.scala b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/collections/ParallelCollectionUnitTest.scala similarity index 93% rename from scala-core-collections/src/test/scala-2/com/baedung/scala/collections/ParallelCollectionTest.scala rename to scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/collections/ParallelCollectionUnitTest.scala index f9c2ce5f7..010b251ff 100644 --- a/scala-core-collections/src/test/scala-2/com/baedung/scala/collections/ParallelCollectionTest.scala +++ b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/collections/ParallelCollectionUnitTest.scala @@ -3,7 +3,7 @@ package com.baedung.scala.collections import com.baeldung.scala.collections.ParallelCollections._ import org.scalatest.flatspec.AnyFlatSpec -class ParallelCollectionTest extends AnyFlatSpec { +class ParallelCollectionUnitTest extends AnyFlatSpec { val ZERO_TO_HUNDRED_SUM = 5050 val ZERO_TO_HUNDRED_SUM_TIMES2 = 10100 diff --git a/scala-core-collections/src/test/scala-2/com/baedung/scala/conversions/ConversionWrappersUnitTest.scala b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/conversions/ConversionWrappersUnitTest.scala similarity index 100% rename from scala-core-collections/src/test/scala-2/com/baedung/scala/conversions/ConversionWrappersUnitTest.scala rename to scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/conversions/ConversionWrappersUnitTest.scala diff --git a/scala-core-collections/src/test/scala-2/com/baedung/scala/conversions/JavaToScalaConversionsUnitTest.scala b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/conversions/JavaToScalaConversionsUnitTest.scala similarity index 100% rename from scala-core-collections/src/test/scala-2/com/baedung/scala/conversions/JavaToScalaConversionsUnitTest.scala rename to scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/conversions/JavaToScalaConversionsUnitTest.scala diff --git a/scala-core-collections/src/test/scala-2/com/baedung/scala/conversions/ScalaToJavaConversionsUnitTest.scala b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/conversions/ScalaToJavaConversionsUnitTest.scala similarity index 100% rename from scala-core-collections/src/test/scala-2/com/baedung/scala/conversions/ScalaToJavaConversionsUnitTest.scala rename to scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/conversions/ScalaToJavaConversionsUnitTest.scala diff --git a/scala-core-collections/src/test/scala-2/com/baedung/scala/flattening/FlattenerSpec.scala b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/flattening/FlattenerUnitTest.scala similarity index 96% rename from scala-core-collections/src/test/scala-2/com/baedung/scala/flattening/FlattenerSpec.scala rename to scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/flattening/FlattenerUnitTest.scala index 424efcfc3..49996e43a 100644 --- a/scala-core-collections/src/test/scala-2/com/baedung/scala/flattening/FlattenerSpec.scala +++ b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/flattening/FlattenerUnitTest.scala @@ -4,7 +4,7 @@ import org.scalatest.wordspec.AnyWordSpec import scala.collection.immutable.Queue -class FlattenerSpec extends AnyWordSpec { +class FlattenerUnitTest extends AnyWordSpec { import com.baeldung.scala.flattening.Flattener.sequenceFlattener diff --git a/scala-core-collections/src/test/scala-2/com/baedung/scala/splitlist/ListSplitterSpec.scala b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/splitlist/ListSplitterUnitTest.scala similarity index 97% rename from scala-core-collections/src/test/scala-2/com/baedung/scala/splitlist/ListSplitterSpec.scala rename to scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/splitlist/ListSplitterUnitTest.scala index d7e00c1c4..d1f2843b5 100644 --- a/scala-core-collections/src/test/scala-2/com/baedung/scala/splitlist/ListSplitterSpec.scala +++ b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baedung/scala/splitlist/ListSplitterUnitTest.scala @@ -3,7 +3,7 @@ package com.baedung.scala.splitlist import com.baeldung.scala.splitlist.ListSplitter import org.scalatest.wordspec.AnyWordSpec -class ListSplitterSpec extends AnyWordSpec { +class ListSplitterUnitTest extends AnyWordSpec { "ListSplitter" should { "return a list of the right number of lists" in { diff --git a/scala-core-collections/src/test/scala-2/com/baeldung/scala/removeduplicates/DuplicatesRemoverSpec.scala b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baeldung/scala/removeduplicates/DuplicatesRemoverUnitTest.scala similarity index 99% rename from scala-core-collections/src/test/scala-2/com/baeldung/scala/removeduplicates/DuplicatesRemoverSpec.scala rename to scala-core-collections-modules/scala-core-collections/src/test/scala/com/baeldung/scala/removeduplicates/DuplicatesRemoverUnitTest.scala index 169b2de82..047217154 100644 --- a/scala-core-collections/src/test/scala-2/com/baeldung/scala/removeduplicates/DuplicatesRemoverSpec.scala +++ b/scala-core-collections-modules/scala-core-collections/src/test/scala/com/baeldung/scala/removeduplicates/DuplicatesRemoverUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.removeduplicates import org.scalatest.wordspec.AnyWordSpec -class DuplicatesRemoverSpec extends AnyWordSpec { +class DuplicatesRemoverUnitTest extends AnyWordSpec { "DuplicatesRemover" should { "return a shorter integers list without duplicates" in { diff --git a/scala-core-collections-modules/scala-core-map/README.md b/scala-core-collections-modules/scala-core-map/README.md new file mode 100644 index 000000000..e020f8d72 --- /dev/null +++ b/scala-core-collections-modules/scala-core-map/README.md @@ -0,0 +1,7 @@ +### Relevant Articles: + +- [Iterating Over a Scala Map](https://www.baeldung.com/scala/iterate-map) +- [Map Both Keys and Values of a Scala Map](https://www.baeldung.com/scala/map-keys-values) +- [Merge Two Maps in Scala](https://www.baeldung.com/scala/merge-two-maps) +- [Filter “None” Values From a Map](https://www.baeldung.com/scala/filter-none-from-map) +- [Guide to Scala ListSet](https://www.baeldung.com/scala/listset) \ No newline at end of file diff --git a/scala-core-collections/src/main/scala-2/com/baeldung/scala/collections/IterateMap.scala b/scala-core-collections-modules/scala-core-map/src/main/scala/com/baeldung/scala/collections/IterateMap.scala similarity index 100% rename from scala-core-collections/src/main/scala-2/com/baeldung/scala/collections/IterateMap.scala rename to scala-core-collections-modules/scala-core-map/src/main/scala/com/baeldung/scala/collections/IterateMap.scala diff --git a/scala-core-collections-2/src/main/scala-2/com/baeldung/scala/filtermap/FilterMap.scala b/scala-core-collections-modules/scala-core-map/src/main/scala/com/baeldung/scala/filtermap/FilterMap.scala similarity index 100% rename from scala-core-collections-2/src/main/scala-2/com/baeldung/scala/filtermap/FilterMap.scala rename to scala-core-collections-modules/scala-core-map/src/main/scala/com/baeldung/scala/filtermap/FilterMap.scala diff --git a/scala-core-collections/src/main/scala-2/com/baeldung/scala/mergemaps/CombineIterables.scala b/scala-core-collections-modules/scala-core-map/src/main/scala/com/baeldung/scala/mergemaps/CombineIterables.scala similarity index 100% rename from scala-core-collections/src/main/scala-2/com/baeldung/scala/mergemaps/CombineIterables.scala rename to scala-core-collections-modules/scala-core-map/src/main/scala/com/baeldung/scala/mergemaps/CombineIterables.scala diff --git a/scala-core-collections/src/test/scala-2/com/baedung/scala/collections/IterateMapUnitTest.scala b/scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/collections/IterateMapUnitTest.scala similarity index 81% rename from scala-core-collections/src/test/scala-2/com/baedung/scala/collections/IterateMapUnitTest.scala rename to scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/collections/IterateMapUnitTest.scala index 5d6478d3d..1dceade57 100644 --- a/scala-core-collections/src/test/scala-2/com/baedung/scala/collections/IterateMapUnitTest.scala +++ b/scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/collections/IterateMapUnitTest.scala @@ -1,7 +1,6 @@ -package com.baedung.scala.collections +package com.baeldung.scala.collections import org.scalatest.flatspec.AnyFlatSpec -import com.baeldung.scala.collections.IterateMap class IterateMapUnitTest extends AnyFlatSpec { "Keys" should "return four chars" in { diff --git a/scala-core-collections/src/test/scala-2/com/baedung/scala/collections/MapKeyValue.scala b/scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/collections/MapKeyValueUnitTest.scala similarity index 91% rename from scala-core-collections/src/test/scala-2/com/baedung/scala/collections/MapKeyValue.scala rename to scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/collections/MapKeyValueUnitTest.scala index 9d601e873..66e416798 100644 --- a/scala-core-collections/src/test/scala-2/com/baedung/scala/collections/MapKeyValue.scala +++ b/scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/collections/MapKeyValueUnitTest.scala @@ -1,13 +1,13 @@ -package com.baedung.scala.collections +package com.baeldung.scala.collections import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class MapKeyValue extends AnyWordSpec with Matchers { +class MapKeyValueUnitTest extends AnyWordSpec with Matchers { "Map::map" should { "map both keys and values to a new Iterable" in { val m = Map(1 -> "A", 2 -> "B") - val f = { t: (Int, String) => s"${t._1}${t._2}" } + val f = { (t: (Int, String)) => s"${t._1}${t._2}" } (m map f) shouldBe Iterable("1A", "2B") } diff --git a/scala-core-collections-3/src/test/scala-2/com/baeldung/scala/commoncollections/ListMapOperationsUnitTest.scala b/scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/commoncollections/ListMapOperationsUnitTest.scala similarity index 100% rename from scala-core-collections-3/src/test/scala-2/com/baeldung/scala/commoncollections/ListMapOperationsUnitTest.scala rename to scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/commoncollections/ListMapOperationsUnitTest.scala diff --git a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/filtermap/FilterMapSpec.scala b/scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/filtermap/FilterMapUnitTest.scala similarity index 91% rename from scala-core-collections-2/src/test/scala-2/com/baeldung/scala/filtermap/FilterMapSpec.scala rename to scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/filtermap/FilterMapUnitTest.scala index c78e70041..112523268 100644 --- a/scala-core-collections-2/src/test/scala-2/com/baeldung/scala/filtermap/FilterMapSpec.scala +++ b/scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/filtermap/FilterMapUnitTest.scala @@ -5,7 +5,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.prop.TableDrivenPropertyChecks import FilterMap._ -class FilterMapSpec +class FilterMapUnitTest extends AnyFlatSpec with Matchers with TableDrivenPropertyChecks { @@ -23,7 +23,7 @@ class FilterMapSpec filterKeysSet.toMap, withFilterMap.map(x => x) ) - forAll(filteredMapTable) { map: Map[Rank, Option[String]] => + forAll(filteredMapTable) { (map: Map[Rank, Option[String]]) => map.valuesIterator.exists(_.contains(None)) shouldBe false } } diff --git a/scala-core-collections/src/test/scala-2/com/baedung/scala/mergemaps/MergeMapsUnitTest.scala b/scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/mergemaps/MergeMapsUnitTest.scala similarity index 89% rename from scala-core-collections/src/test/scala-2/com/baedung/scala/mergemaps/MergeMapsUnitTest.scala rename to scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/mergemaps/MergeMapsUnitTest.scala index a6f463501..bca95fa6d 100644 --- a/scala-core-collections/src/test/scala-2/com/baedung/scala/mergemaps/MergeMapsUnitTest.scala +++ b/scala-core-collections-modules/scala-core-map/src/test/scala/com/baeldung/scala/mergemaps/MergeMapsUnitTest.scala @@ -1,6 +1,5 @@ -package com.baedung.scala.mergemaps +package com.baeldung.scala.mergemaps -import com.baeldung.scala.mergemaps.CombineIterables import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers diff --git a/scala-core-modules/scala-core-10/README.md b/scala-core-modules/scala-core-10/README.md new file mode 100644 index 000000000..38c74cb46 --- /dev/null +++ b/scala-core-modules/scala-core-10/README.md @@ -0,0 +1 @@ +### Relevant Articles diff --git a/scala-core-modules/scala-core-10/src/main/scala/com/baeldung/scala/valsintraits/ValInTraitExamples.scala b/scala-core-modules/scala-core-10/src/main/scala/com/baeldung/scala/valsintraits/ValInTraitExamples.scala new file mode 100644 index 000000000..59010dfba --- /dev/null +++ b/scala-core-modules/scala-core-10/src/main/scala/com/baeldung/scala/valsintraits/ValInTraitExamples.scala @@ -0,0 +1,39 @@ +package com.baeldung.scala.valsintraits + +import java.time.LocalDateTime + +object ValInTraitExamples { + trait CurrentTimePrinter { + val currentTime: LocalDateTime + def printCurrentTime(): Unit + } + object TimePrinterImpl extends CurrentTimePrinter { + override val currentTime: LocalDateTime = LocalDateTime.now() + override def printCurrentTime(): Unit = println(currentTime) + } + + trait CurrentTimePrinterWithDef { + def currentTime: LocalDateTime + def printCurrentTime(): Unit + } + object TimePrinterWithDefImpl extends CurrentTimePrinterWithDef { + override def currentTime: LocalDateTime = LocalDateTime.now() + override def printCurrentTime(): Unit = println(currentTime) + } + + trait TraitA { + val multiplier: Int + val result: Int = multiplier * 7 + } + trait TraitB { + val multiplier: Int = 10 + } + trait TraitAWithDef { + val multiplier: Int + def result: Int = multiplier * 7 + } + + object AB extends TraitA with TraitB + object BA extends TraitB with TraitA + object ABWithDef extends TraitAWithDef with TraitB +} diff --git a/scala-core-modules/scala-core-10/src/test/scala/com/baeldung/scala/ValInTraitExamplesUnitTest.scala b/scala-core-modules/scala-core-10/src/test/scala/com/baeldung/scala/ValInTraitExamplesUnitTest.scala new file mode 100644 index 000000000..3ae3a23a1 --- /dev/null +++ b/scala-core-modules/scala-core-10/src/test/scala/com/baeldung/scala/ValInTraitExamplesUnitTest.scala @@ -0,0 +1,40 @@ +package com.baeldung.scala + +import com.baeldung.scala.valsintraits.ValInTraitExamples +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ValInTraitExamplesUnitTest extends AnyFlatSpec with Matchers { + "TimePrinterImpl" should "return same time every call" in { + val printer = ValInTraitExamples.TimePrinterImpl + val first = printer.currentTime + Thread.sleep(10) + val second = printer.currentTime + + first.shouldBe(second) + } + + "TimePrinterWithDefImpl" should "return different time every call" in { + val printer = ValInTraitExamples.TimePrinterWithDefImpl + val first = printer.currentTime + Thread.sleep(10) + val second = printer.currentTime + + first should not be second + } + + "ClassAB" should "return result as 0" in { + val classAb = ValInTraitExamples.AB + classAb.result shouldBe 0 + } + + "ClassBA" should "return result as 70" in { + val classBa = ValInTraitExamples.BA + classBa.result shouldBe 70 + } + + "ClassABWithDef" should "return result as 70" in { + val classAbWithDef = ValInTraitExamples.ABWithDef + classAbWithDef.result shouldBe 70 + } +} diff --git a/scala-core-2/README.md b/scala-core-modules/scala-core-2/README.md similarity index 91% rename from scala-core-2/README.md rename to scala-core-modules/scala-core-2/README.md index b5acf3ade..3cc148175 100644 --- a/scala-core-2/README.md +++ b/scala-core-modules/scala-core-2/README.md @@ -9,7 +9,6 @@ This module contains articles about Scala's core features. - [Futures and Promises in Scala](https://www.baeldung.com/scala/futures-promises) - [Named and Default Arguments in Scala](https://www.baeldung.com/scala/named-default-arguments) - [Self-Type Annotation in Scala](https://www.baeldung.com/scala/self-type-annotation) -- [Usages of Underscore (_) in Scala](https://www.baeldung.com/scala/underscore) - [A DSL for Writing “20 seconds” in Scala](https://www.baeldung.com/scala/dsl-writing-20-seconds) - [Error Handling in Scala](https://www.baeldung.com/scala/error-handling) - [Higher-Kinded Types](https://www.baeldung.com/scala/higher-kinded-types) diff --git a/scala-core-2/src/main/java/com/baedung/scala/forcomprehension/ForComprehension.java b/scala-core-modules/scala-core-2/src/main/java/com/baedung/scala/forcomprehension/ForComprehension.java similarity index 100% rename from scala-core-2/src/main/java/com/baedung/scala/forcomprehension/ForComprehension.java rename to scala-core-modules/scala-core-2/src/main/java/com/baedung/scala/forcomprehension/ForComprehension.java diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/concurrency/FutureAndPromise.scala b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/concurrency/FutureAndPromise.scala similarity index 100% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/concurrency/FutureAndPromise.scala rename to scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/concurrency/FutureAndPromise.scala diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/durationsugar/package.scala b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/durationsugar/package.scala similarity index 100% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/durationsugar/package.scala rename to scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/durationsugar/package.scala diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/exceptionhandling/ExceptionHandling.scala b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/exceptionhandling/ExceptionHandling.scala similarity index 100% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/exceptionhandling/ExceptionHandling.scala rename to scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/exceptionhandling/ExceptionHandling.scala diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/extractorobject/RequestApp.scala b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/extractorobject/RequestApp.scala similarity index 100% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/extractorobject/RequestApp.scala rename to scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/extractorobject/RequestApp.scala diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/extractorobject/User.scala b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/extractorobject/User.scala similarity index 100% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/extractorobject/User.scala rename to scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/extractorobject/User.scala diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/forcomprehension/ForComprehension.scala b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/forcomprehension/ForComprehension.scala similarity index 97% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/forcomprehension/ForComprehension.scala rename to scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/forcomprehension/ForComprehension.scala index 325dfa40e..600d463a5 100644 --- a/scala-core-2/src/main/scala-2/com/baeldung/scala/forcomprehension/ForComprehension.scala +++ b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/forcomprehension/ForComprehension.scala @@ -56,7 +56,7 @@ object ForComprehension { def foreach(f: A => Unit): Unit = f(result) def map[B](f: A => B): Result[B] = Result(f(result)) def flatMap[B](f: A => Result[B]): Result[B] = f(result) - def withFilter(f: A => Boolean): Result[_] = + def withFilter(f: A => Boolean): Result[?] = if (f(result)) this else EmptyResult } diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/higherkindedtypes/HigherKindedTypes.scala b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/higherkindedtypes/HigherKindedTypes.scala similarity index 100% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/higherkindedtypes/HigherKindedTypes.scala rename to scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/higherkindedtypes/HigherKindedTypes.scala diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/implicitclasses/Currency.scala b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/implicitclasses/Currency.scala similarity index 100% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/implicitclasses/Currency.scala rename to scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/implicitclasses/Currency.scala diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/implicitclasses/Money.scala b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/implicitclasses/Money.scala similarity index 100% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/implicitclasses/Money.scala rename to scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/implicitclasses/Money.scala diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/implicitclasses/MoneySyntax.scala b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/implicitclasses/MoneySyntax.scala similarity index 100% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/implicitclasses/MoneySyntax.scala rename to scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/implicitclasses/MoneySyntax.scala diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/namedanddefaultargs/NamedAndDefaultArgs.scala b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/namedanddefaultargs/NamedAndDefaultArgs.scala similarity index 100% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/namedanddefaultargs/NamedAndDefaultArgs.scala rename to scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/namedanddefaultargs/NamedAndDefaultArgs.scala diff --git a/scala-core-2/src/main/scala-2/com/baeldung/scala/selftype/SelfType.scala b/scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/selftype/SelfType.scala similarity index 100% rename from scala-core-2/src/main/scala-2/com/baeldung/scala/selftype/SelfType.scala rename to scala-core-modules/scala-core-2/src/main/scala/com/baeldung/scala/selftype/SelfType.scala diff --git a/scala-core-2/src/test/scala-2/com/baeldung/scala/concurrency/FutureAndPromiseUnitTest.scala b/scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/concurrency/FutureAndPromiseUnitTest.scala similarity index 100% rename from scala-core-2/src/test/scala-2/com/baeldung/scala/concurrency/FutureAndPromiseUnitTest.scala rename to scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/concurrency/FutureAndPromiseUnitTest.scala diff --git a/scala-core-2/src/test/scala-2/com/baeldung/scala/durationsugar/DurationSugarUnitTest.scala b/scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/durationsugar/DurationSugarUnitTest.scala similarity index 100% rename from scala-core-2/src/test/scala-2/com/baeldung/scala/durationsugar/DurationSugarUnitTest.scala rename to scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/durationsugar/DurationSugarUnitTest.scala diff --git a/scala-core-2/src/test/scala-2/com/baeldung/scala/exceptionhandling/ExceptionHandlingUnitTest.scala b/scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/exceptionhandling/ExceptionHandlingUnitTest.scala similarity index 87% rename from scala-core-2/src/test/scala-2/com/baeldung/scala/exceptionhandling/ExceptionHandlingUnitTest.scala rename to scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/exceptionhandling/ExceptionHandlingUnitTest.scala index 0874fd887..a86f19bbd 100644 --- a/scala-core-2/src/test/scala-2/com/baeldung/scala/exceptionhandling/ExceptionHandlingUnitTest.scala +++ b/scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/exceptionhandling/ExceptionHandlingUnitTest.scala @@ -1,11 +1,6 @@ package com.baeldung.scala.exceptionhandling -import com.baeldung.scala.exceptionhandling.ExceptionHandling.{ - DivideByZero, - divideWithEither, - divideWithOption, - divideWithTry -} +import com.baeldung.scala.exceptionhandling.ExceptionHandling._ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers diff --git a/scala-core-2/src/test/scala-2/com/baeldung/scala/higherkindedtypes/HigherKindedTypesUnitTest.scala b/scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/higherkindedtypes/HigherKindedTypesUnitTest.scala similarity index 100% rename from scala-core-2/src/test/scala-2/com/baeldung/scala/higherkindedtypes/HigherKindedTypesUnitTest.scala rename to scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/higherkindedtypes/HigherKindedTypesUnitTest.scala diff --git a/scala-core-2/src/test/scala-2/com/baeldung/scala/implicitclasses/MoneyUnitTest.scala b/scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/implicitclasses/MoneyUnitTest.scala similarity index 100% rename from scala-core-2/src/test/scala-2/com/baeldung/scala/implicitclasses/MoneyUnitTest.scala rename to scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/implicitclasses/MoneyUnitTest.scala diff --git a/scala-core-2/src/test/scala-2/com/baeldung/scala/namedanddefaultargs/NamedAndDefaultArgsUnitTest.scala b/scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/namedanddefaultargs/NamedAndDefaultArgsUnitTest.scala similarity index 100% rename from scala-core-2/src/test/scala-2/com/baeldung/scala/namedanddefaultargs/NamedAndDefaultArgsUnitTest.scala rename to scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/namedanddefaultargs/NamedAndDefaultArgsUnitTest.scala diff --git a/scala-core-2/src/test/scala-2/com/baeldung/scala/selftype/SelfTypeUnitTest.scala b/scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/selftype/SelfTypeUnitTest.scala similarity index 100% rename from scala-core-2/src/test/scala-2/com/baeldung/scala/selftype/SelfTypeUnitTest.scala rename to scala-core-modules/scala-core-2/src/test/scala/com/baeldung/scala/selftype/SelfTypeUnitTest.scala diff --git a/scala-core-3/README.md b/scala-core-modules/scala-core-3/README.md similarity index 87% rename from scala-core-3/README.md rename to scala-core-modules/scala-core-3/README.md index 2f260ad4b..08bc7b0f7 100644 --- a/scala-core-3/README.md +++ b/scala-core-modules/scala-core-3/README.md @@ -3,7 +3,6 @@ - [Access Modifiers in Scala](https://www.baeldung.com/scala/access-modifiers) - [Guide to Data Types in Scala](https://www.baeldung.com/scala/data-types) - [Companion Objects in Scala](https://www.baeldung.com/scala/companion-objects) -- [Preserving Type Information at Runtime in Scala](https://www.baeldung.com/scala/type-information-at-runtime) - [Synchronous Handling of Futures](https://www.baeldung.com/scala/synchronous-handling-of-futures) - [The Option Type in Scala](https://www.baeldung.com/scala/option-type) - [Implicit Parameters in Scala](https://www.baeldung.com/scala/implicit-parameters) diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/accessmodifiers/Figure.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/accessmodifiers/Figure.scala similarity index 94% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/accessmodifiers/Figure.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/accessmodifiers/Figure.scala index 96dec3234..c4d7dcb25 100644 --- a/scala-core-3/src/main/scala-2/com/baeldung/scala/accessmodifiers/Figure.scala +++ b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/accessmodifiers/Figure.scala @@ -3,12 +3,12 @@ package com.baeldung.scala.accessmodifiers import java.util.UUID.randomUUID abstract class Figure { - private[this] val code = + private val code = randomUUID.toString // accessible in the scope of the object-only def printCode: Unit = println(s"$code") // public access protected[accessmodifiers] val color: String // accessible in the scope of the package - protected[this] val lineWidth: Int // accessible for instances of this class and its subclasses instances + protected val lineWidth: Int // accessible for instances of this class and its subclasses instances protected val topPoint: Double // accessible in the scope of the class and subclasses protected val rightMostPoint: Double diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/accessmodifiers/Star.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/accessmodifiers/Star.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/accessmodifiers/Star.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/accessmodifiers/Star.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/await/AwaitFuture.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/await/AwaitFuture.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/await/AwaitFuture.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/await/AwaitFuture.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/companionobject/Task.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/companionobject/Task.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/companionobject/Task.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/companionobject/Task.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/implicitparameter/ImplicitParameter.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/implicitparameter/ImplicitParameter.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/implicitparameter/ImplicitParameter.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/implicitparameter/ImplicitParameter.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/iteratorsvsstreamsvsviews/NonStrictDataStructures.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/iteratorsvsstreamsvsviews/NonStrictDataStructures.scala similarity index 91% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/iteratorsvsstreamsvsviews/NonStrictDataStructures.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/iteratorsvsstreamsvsviews/NonStrictDataStructures.scala index 3c01e6250..acdc563f3 100644 --- a/scala-core-3/src/main/scala-2/com/baeldung/scala/iteratorsvsstreamsvsviews/NonStrictDataStructures.scala +++ b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/iteratorsvsstreamsvsviews/NonStrictDataStructures.scala @@ -8,7 +8,7 @@ object NonStrictDataStructures { val stream = data.toStream // todo: check if this is ok, better to separate into separate module // val view: AnyRef with SeqView[Int, Seq[Int]] = data.view - val view: AnyRef with SeqView[Int] = data.view + val view: AnyRef & SeqView[Int] = data.view } case class Factorial() { diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/options/OptionBuilder.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/options/OptionBuilder.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/options/OptionBuilder.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/options/OptionBuilder.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/options/OptionExample.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/options/OptionExample.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/options/OptionExample.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/options/OptionExample.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/options/Player.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/options/Player.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/options/Player.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/options/Player.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/options/Tournament.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/options/Tournament.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/options/Tournament.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/options/Tournament.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/scaladoc/Carnivore.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/scaladoc/Carnivore.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/scaladoc/Carnivore.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/scaladoc/Carnivore.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/scaladoc/IntervalTimer.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/scaladoc/IntervalTimer.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/scaladoc/IntervalTimer.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/scaladoc/IntervalTimer.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/scaladoc/TasmanianDevil.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/scaladoc/TasmanianDevil.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/scaladoc/TasmanianDevil.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/scaladoc/TasmanianDevil.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/scaladoc/package.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/scaladoc/package.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/scaladoc/package.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/scaladoc/package.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/typecasts/ErrorHandling.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/typecasts/ErrorHandling.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/typecasts/ErrorHandling.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/typecasts/ErrorHandling.scala diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/typecasts/TypeErasure.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/typecasts/TypeErasure.scala similarity index 95% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/typecasts/TypeErasure.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/typecasts/TypeErasure.scala index 1501db2ec..3ed06179f 100644 --- a/scala-core-3/src/main/scala-2/com/baeldung/scala/typecasts/TypeErasure.scala +++ b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/typecasts/TypeErasure.scala @@ -13,6 +13,6 @@ object TypeErasure { // Simply function that converts a variable number of values to a List of that type def convertValuesToList[T](values: T*): List[T] = { - List[T](values: _*) + List[T](values*) } } diff --git a/scala-core-3/src/main/scala-2/com/baeldung/scala/typecasts/ValueTypeCasting.scala b/scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/typecasts/ValueTypeCasting.scala similarity index 100% rename from scala-core-3/src/main/scala-2/com/baeldung/scala/typecasts/ValueTypeCasting.scala rename to scala-core-modules/scala-core-3/src/main/scala/com/baeldung/scala/typecasts/ValueTypeCasting.scala diff --git a/scala-core-3/src/test/scala-2/com/baeldung/scala/accessmodifiers/AccessModifiersUnitTest.scala b/scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/accessmodifiers/AccessModifiersUnitTest.scala similarity index 100% rename from scala-core-3/src/test/scala-2/com/baeldung/scala/accessmodifiers/AccessModifiersUnitTest.scala rename to scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/accessmodifiers/AccessModifiersUnitTest.scala diff --git a/scala-core-3/src/test/scala-2/com/baeldung/scala/await/AwaitFutureUnitTest.scala b/scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/await/AwaitFutureUnitTest.scala similarity index 100% rename from scala-core-3/src/test/scala-2/com/baeldung/scala/await/AwaitFutureUnitTest.scala rename to scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/await/AwaitFutureUnitTest.scala diff --git a/scala-core-3/src/test/scala-2/com/baeldung/scala/companionobject/TaskUnitTest.scala b/scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/companionobject/TaskUnitTest.scala similarity index 87% rename from scala-core-3/src/test/scala-2/com/baeldung/scala/companionobject/TaskUnitTest.scala rename to scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/companionobject/TaskUnitTest.scala index 08eefe780..58036aa97 100644 --- a/scala-core-3/src/test/scala-2/com/baeldung/scala/companionobject/TaskUnitTest.scala +++ b/scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/companionobject/TaskUnitTest.scala @@ -8,19 +8,19 @@ class TaskUnitTest extends AnyFlatSpec with Matchers { "Task" should "be instantiated with default constructor" in { val task = new Task("do something") task.description should be("do something") - task.status should be("pending") + task.status() should be("pending") } it should "be able to construct without new keywork" in { val task = Task("do something") task.description should be("do something") - task.status should be("pending") + task.status() should be("pending") } it should "have a contructor with status parameter" in { val task = Task("do something", "started") task.description should be("do something") - task.status should be("started") + task.status() should be("started") } it should "extract status" in { diff --git a/scala-core-3/src/test/scala-2/com/baeldung/scala/dataTypesAndOps/DatatypesAndOpsUnitTest.scala b/scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/dataTypesAndOps/DatatypesAndOpsUnitTest.scala similarity index 100% rename from scala-core-3/src/test/scala-2/com/baeldung/scala/dataTypesAndOps/DatatypesAndOpsUnitTest.scala rename to scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/dataTypesAndOps/DatatypesAndOpsUnitTest.scala diff --git a/scala-core-3/src/test/scala-2/com/baeldung/scala/implicitparameter/ImplicitParameterUnitTest.scala b/scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/implicitparameter/ImplicitParameterUnitTest.scala similarity index 94% rename from scala-core-3/src/test/scala-2/com/baeldung/scala/implicitparameter/ImplicitParameterUnitTest.scala rename to scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/implicitparameter/ImplicitParameterUnitTest.scala index 2e050f6c3..f1ee58f51 100644 --- a/scala-core-3/src/test/scala-2/com/baeldung/scala/implicitparameter/ImplicitParameterUnitTest.scala +++ b/scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/implicitparameter/ImplicitParameterUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.implicitparameter import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class ImplicitParameterTest extends AnyWordSpec with Matchers { +class ImplicitParameterUnitTest extends AnyWordSpec with Matchers { import ImplicitParameter._ "Implicit Parameter" should { diff --git a/scala-core-3/src/test/scala-2/com/baeldung/scala/iteratorsvsstreamsvsviews/IteratorVsStreamVsViewUnitTest.scala b/scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/iteratorsvsstreamsvsviews/IteratorVsStreamVsViewUnitTest.scala similarity index 100% rename from scala-core-3/src/test/scala-2/com/baeldung/scala/iteratorsvsstreamsvsviews/IteratorVsStreamVsViewUnitTest.scala rename to scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/iteratorsvsstreamsvsviews/IteratorVsStreamVsViewUnitTest.scala diff --git a/scala-core-3/src/test/scala-2/com/baeldung/scala/options/OptionUnitTest.scala b/scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/options/OptionUnitTest.scala similarity index 100% rename from scala-core-3/src/test/scala-2/com/baeldung/scala/options/OptionUnitTest.scala rename to scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/options/OptionUnitTest.scala diff --git a/scala-core-3/src/test/scala-2/com/baeldung/scala/options/OptionWhenUnlessUnitTest.scala b/scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/options/OptionWhenUnlessUnitTest.scala similarity index 100% rename from scala-core-3/src/test/scala-2/com/baeldung/scala/options/OptionWhenUnlessUnitTest.scala rename to scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/options/OptionWhenUnlessUnitTest.scala diff --git a/scala-core-3/src/test/scala-2/com/baeldung/scala/typecasts/TypeErasureUnitTest.scala b/scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/typecasts/TypeErasureUnitTest.scala similarity index 96% rename from scala-core-3/src/test/scala-2/com/baeldung/scala/typecasts/TypeErasureUnitTest.scala rename to scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/typecasts/TypeErasureUnitTest.scala index 75746a447..3017b884f 100644 --- a/scala-core-3/src/test/scala-2/com/baeldung/scala/typecasts/TypeErasureUnitTest.scala +++ b/scala-core-modules/scala-core-3/src/test/scala/com/baeldung/scala/typecasts/TypeErasureUnitTest.scala @@ -39,7 +39,7 @@ class TypeErasureUnitTest extends AnyWordSpec with Matchers { "work with varargs" in { def varargFn(str: String*) = str.length val input = Seq("Hello", "World") - assert(varargFn(input: _*) == 2) + assert(varargFn(input*) == 2) } } } diff --git a/scala-core-4/README.md b/scala-core-modules/scala-core-4/README.md similarity index 89% rename from scala-core-4/README.md rename to scala-core-modules/scala-core-4/README.md index 00e9716d6..5cebdc075 100644 --- a/scala-core-4/README.md +++ b/scala-core-modules/scala-core-4/README.md @@ -5,7 +5,6 @@ - [Type Declaration in Scala](https://www.baeldung.com/scala/type-declaration) - [Implicitly in Scala](https://www.baeldung.com/scala/implicitly) - [Path-Dependent Types in Scala](https://www.baeldung.com/scala/path-dependent-types) -- [Type Disjunction (Union Types) in Scala](https://www.baeldung.com/scala/type-disjunction) - [Private and Protected Constructors in Scala](https://www.baeldung.com/scala/private-protected-constructors) - [Difference Between assert and require in Scala](https://www.baeldung.com/scala/assert-vs-require) - [How To Use Structural Types in Scala](https://www.baeldung.com/scala/structural-types) diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/assertvsrequire/AssertUsage.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/assertvsrequire/AssertUsage.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/assertvsrequire/AssertUsage.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/assertvsrequire/AssertUsage.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/assertvsrequire/RequireUsage.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/assertvsrequire/RequireUsage.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/assertvsrequire/RequireUsage.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/assertvsrequire/RequireUsage.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/constructors/Constructors.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/constructors/Constructors.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/constructors/Constructors.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/constructors/Constructors.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/future/FutureRecovery.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/future/FutureRecovery.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/future/FutureRecovery.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/future/FutureRecovery.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/implicitly/ImplicitlyUsage.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/implicitly/ImplicitlyUsage.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/implicitly/ImplicitlyUsage.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/implicitly/ImplicitlyUsage.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/pathdependenttypes/AwardPunishmentDisipline.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/pathdependenttypes/AwardPunishmentDisipline.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/pathdependenttypes/AwardPunishmentDisipline.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/pathdependenttypes/AwardPunishmentDisipline.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/pathdependenttypes/KeyValueStore.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/pathdependenttypes/KeyValueStore.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/pathdependenttypes/KeyValueStore.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/pathdependenttypes/KeyValueStore.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/pathdependenttypes/PathDependentTypeInnerClasses.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/pathdependenttypes/PathDependentTypeInnerClasses.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/pathdependenttypes/PathDependentTypeInnerClasses.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/pathdependenttypes/PathDependentTypeInnerClasses.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/structuraltypes/Quack.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/structuraltypes/Quack.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/structuraltypes/Quack.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/structuraltypes/Quack.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/structuraltypes/ResourceClosing.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/structuraltypes/ResourceClosing.scala similarity index 64% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/structuraltypes/ResourceClosing.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/structuraltypes/ResourceClosing.scala index c9bc0fe75..315397e01 100644 --- a/scala-core-4/src/main/scala-2/com/baeldung/scala/structuraltypes/ResourceClosing.scala +++ b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/structuraltypes/ResourceClosing.scala @@ -1,17 +1,18 @@ package com.baeldung.scala.structuraltypes import scala.io.Source +import scala.language.reflectiveCalls trait ResourceClosing { type Closable = { def close(): Unit } - def using(resource: Closable)(fn: () => Unit) { + def using(resource: Closable)(fn: () => Unit) = { try { fn() } finally { resource.close() } } - def using(file: Source)(fn: () => Unit) { + def using(file: Source)(fn: () => Unit) = { try { fn() } finally { file.close() } diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/switchannotation/LookupSwitchExample.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/switchannotation/LookupSwitchExample.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/switchannotation/LookupSwitchExample.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/switchannotation/LookupSwitchExample.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/switchannotation/NotOptimizedExample.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/switchannotation/NotOptimizedExample.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/switchannotation/NotOptimizedExample.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/switchannotation/NotOptimizedExample.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/switchannotation/TableSwitchExample.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/switchannotation/TableSwitchExample.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/switchannotation/TableSwitchExample.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/switchannotation/TableSwitchExample.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/typemembersalias/ListIntFunctions.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/typemembersalias/ListIntFunctions.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/typemembersalias/ListIntFunctions.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/typemembersalias/ListIntFunctions.scala diff --git a/scala-core-4/src/main/scala-2/com/baeldung/scala/typemembersalias/Repeat.scala b/scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/typemembersalias/Repeat.scala similarity index 100% rename from scala-core-4/src/main/scala-2/com/baeldung/scala/typemembersalias/Repeat.scala rename to scala-core-modules/scala-core-4/src/main/scala/com/baeldung/scala/typemembersalias/Repeat.scala diff --git a/scala-core-4/src/test/resources/animals b/scala-core-modules/scala-core-4/src/test/resources/animals similarity index 100% rename from scala-core-4/src/test/resources/animals rename to scala-core-modules/scala-core-4/src/test/resources/animals diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/assertvsrequire/AssertUsageUnitTest.scala b/scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/assertvsrequire/AssertUsageUnitTest.scala similarity index 100% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/assertvsrequire/AssertUsageUnitTest.scala rename to scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/assertvsrequire/AssertUsageUnitTest.scala diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/assertvsrequire/RequireUsageUnitTest.scala b/scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/assertvsrequire/RequireUsageUnitTest.scala similarity index 100% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/assertvsrequire/RequireUsageUnitTest.scala rename to scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/assertvsrequire/RequireUsageUnitTest.scala diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/future/FutureRecoveryUnitTest.scala b/scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/future/FutureRecoveryUnitTest.scala similarity index 100% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/future/FutureRecoveryUnitTest.scala rename to scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/future/FutureRecoveryUnitTest.scala diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/implicitimports/ImplicitImportsUnitTest.scala b/scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/implicitimports/ImplicitImportsUnitTest.scala similarity index 100% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/implicitimports/ImplicitImportsUnitTest.scala rename to scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/implicitimports/ImplicitImportsUnitTest.scala diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/implicitly/ImplicitlyUsageUnitTest.scala b/scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/implicitly/ImplicitlyUsageUnitTest.scala similarity index 100% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/implicitly/ImplicitlyUsageUnitTest.scala rename to scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/implicitly/ImplicitlyUsageUnitTest.scala diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/pathdependenttypes/KeyValueStoreUnitTest.scala b/scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/pathdependenttypes/KeyValueStoreUnitTest.scala similarity index 100% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/pathdependenttypes/KeyValueStoreUnitTest.scala rename to scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/pathdependenttypes/KeyValueStoreUnitTest.scala diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/structuraltypes/DuckUnitTest.scala b/scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/structuraltypes/DuckUnitTest.scala similarity index 95% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/structuraltypes/DuckUnitTest.scala rename to scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/structuraltypes/DuckUnitTest.scala index 72cc38959..2e6a54b41 100644 --- a/scala-core-4/src/test/scala-2/com/baeldung/scala/structuraltypes/DuckUnitTest.scala +++ b/scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/structuraltypes/DuckUnitTest.scala @@ -3,6 +3,8 @@ package com.baeldung.scala.structuraltypes import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers +import scala.language.reflectiveCalls + class DuckUnitTest extends AnyFlatSpec with Matchers { type Flyer = { def fly(): Unit } def callFly(thing: Flyer): Unit = thing.fly() diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/structuraltypes/ResourceClosingUnitTest.scala b/scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/structuraltypes/ResourceClosingUnitTest.scala similarity index 100% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/structuraltypes/ResourceClosingUnitTest.scala rename to scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/structuraltypes/ResourceClosingUnitTest.scala diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/typemembersalias/ListIntFunctionsUnitTest.scala b/scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/typemembersalias/ListIntFunctionsUnitTest.scala similarity index 100% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/typemembersalias/ListIntFunctionsUnitTest.scala rename to scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/typemembersalias/ListIntFunctionsUnitTest.scala diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/typemembersalias/RepeatUnitTest.scala b/scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/typemembersalias/RepeatUnitTest.scala similarity index 100% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/typemembersalias/RepeatUnitTest.scala rename to scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/typemembersalias/RepeatUnitTest.scala diff --git a/scala-core-4/src/test/scala-2/com/baeldung/scala/withfilter/WithFilterVsFilterUnitTest.scala b/scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/withfilter/WithFilterVsFilterUnitTest.scala similarity index 100% rename from scala-core-4/src/test/scala-2/com/baeldung/scala/withfilter/WithFilterVsFilterUnitTest.scala rename to scala-core-modules/scala-core-4/src/test/scala/com/baeldung/scala/withfilter/WithFilterVsFilterUnitTest.scala diff --git a/scala-core-5/README.md b/scala-core-modules/scala-core-5/README.md similarity index 90% rename from scala-core-5/README.md rename to scala-core-modules/scala-core-5/README.md index 8fa68480b..d55dc020b 100644 --- a/scala-core-5/README.md +++ b/scala-core-modules/scala-core-5/README.md @@ -7,7 +7,6 @@ This module contains articles about Scala's core features. - [@switch Annotation in Scala](https://www.baeldung.com/scala/switch-annotation) - [Benefits of Using Vector in Scala](https://www.baeldung.com/scala/vector-benefits) - [Rich Wrappers in Scala](https://www.baeldung.com/scala/rich-wrappers) -- [Working With Dates and Times in Scala](https://www.baeldung.com/scala/date-time) - [@inline and @noinline in Scala](https://www.baeldung.com/scala/inline-noinline-annotations) - [Lambda Expressions in Scala](https://www.baeldung.com/scala/lambda-expressions) - [Algebraic Data Types in Scala](https://www.baeldung.com/scala/algebraic-data-types) diff --git a/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/adt/Examples.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/adt/Examples.scala new file mode 100644 index 000000000..bb266bce3 --- /dev/null +++ b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/adt/Examples.scala @@ -0,0 +1,35 @@ +package com.baeldung.scala.adt + +object Examples { + sealed trait Color + case object White extends Color + case object Black extends Color + + sealed trait Name + case object Pawn extends Name + case object Rook extends Name + case object Knight extends Name + case object Bishop extends Name + case object Queen extends Name + case object King extends Name + + case class ChessPiece(color: Color, name: Name) + + def isTheMostImportantPiece(c: ChessPiece): Boolean = c match { + case ChessPiece(_, King) => true + case _ => false + } + + trait Semaphore { + val color: SemaphoreColor + } + + sealed trait SemaphoreColor + case object Green extends SemaphoreColor + case object Amber extends SemaphoreColor + case object Red extends SemaphoreColor + + trait Feline + trait Animal + trait Cat extends Animal with Feline +} diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/inlining/Inlining.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/inlining/Inlining.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/inlining/Inlining.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/inlining/Inlining.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/inlining/InliningWarning.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/inlining/InliningWarning.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/inlining/InliningWarning.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/inlining/InliningWarning.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/inlining/MegamorphicCallsite.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/inlining/MegamorphicCallsite.scala similarity index 68% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/inlining/MegamorphicCallsite.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/inlining/MegamorphicCallsite.scala index 96111a595..39a22646c 100644 --- a/scala-core-5/src/main/scala-2/com/baeldung/scala/inlining/MegamorphicCallsite.scala +++ b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/inlining/MegamorphicCallsite.scala @@ -6,9 +6,9 @@ object MegamorphicCallsite extends App { fun(n) // callsite } - val f1 = { n: Int => n * n } - val f2 = { n: Int => n + n } - val f3 = { n: Int => n } + val f1 = { (n: Int) => n * n } + val f2 = { (n: Int) => n + n } + val f3 = { (n: Int) => n } execute(f1)(5) execute(f2)(5) diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/lambdas/IntTransformer.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/lambdas/IntTransformer.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/lambdas/IntTransformer.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/lambdas/IntTransformer.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/listcreation/ListCreationMethods.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/listcreation/ListCreationMethods.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/listcreation/ListCreationMethods.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/listcreation/ListCreationMethods.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/richwrappers/RichIntImplicits.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/richwrappers/RichIntImplicits.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/richwrappers/RichIntImplicits.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/richwrappers/RichIntImplicits.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/richwrappers/SimpleRichInt.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/richwrappers/SimpleRichInt.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/richwrappers/SimpleRichInt.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/richwrappers/SimpleRichInt.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/switchannotation/LookupSwitchExample.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/switchannotation/LookupSwitchExample.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/switchannotation/LookupSwitchExample.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/switchannotation/LookupSwitchExample.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/switchannotation/NotOptimizedExample.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/switchannotation/NotOptimizedExample.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/switchannotation/NotOptimizedExample.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/switchannotation/NotOptimizedExample.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/switchannotation/TableSwitchExample.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/switchannotation/TableSwitchExample.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/switchannotation/TableSwitchExample.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/switchannotation/TableSwitchExample.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/vectorbenefits/VectorAppendPrepend.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/vectorbenefits/VectorAppendPrepend.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/vectorbenefits/VectorAppendPrepend.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/vectorbenefits/VectorAppendPrepend.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/vectorbenefits/VectorBasics.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/vectorbenefits/VectorBasics.scala similarity index 77% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/vectorbenefits/VectorBasics.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/vectorbenefits/VectorBasics.scala index 87fe999fe..6291e239b 100644 --- a/scala-core-5/src/main/scala-2/com/baeldung/scala/vectorbenefits/VectorBasics.scala +++ b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/vectorbenefits/VectorBasics.scala @@ -7,5 +7,7 @@ object VectorBasics extends App { val vecAppended: Vector[Int] = vec :+ 4 val vecPrepended: Vector[Int] = 0 +: vec - println(vec, vecPrepended, vecAppended) + println(vec) + println(vecPrepended) + println(vecAppended) } diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/vectorbenefits/VectorHeadTailAccess.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/vectorbenefits/VectorHeadTailAccess.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/vectorbenefits/VectorHeadTailAccess.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/vectorbenefits/VectorHeadTailAccess.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/vectorbenefits/VectorIteration.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/vectorbenefits/VectorIteration.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/vectorbenefits/VectorIteration.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/vectorbenefits/VectorIteration.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/vectorbenefits/VectorRandomAccess.scala b/scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/vectorbenefits/VectorRandomAccess.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/vectorbenefits/VectorRandomAccess.scala rename to scala-core-modules/scala-core-5/src/main/scala/com/baeldung/scala/vectorbenefits/VectorRandomAccess.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/adt/ExamplesUnitTest.scala b/scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/adt/ExamplesUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/adt/ExamplesUnitTest.scala rename to scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/adt/ExamplesUnitTest.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/lambdas/LambdasUnitTest.scala b/scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/lambdas/LambdasUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/lambdas/LambdasUnitTest.scala rename to scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/lambdas/LambdasUnitTest.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/listcreation/ListCreationMethodsUnitTest.scala b/scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/listcreation/ListCreationMethodsUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/listcreation/ListCreationMethodsUnitTest.scala rename to scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/listcreation/ListCreationMethodsUnitTest.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/regex/RegexUnitTest.scala b/scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/regex/RegexUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/regex/RegexUnitTest.scala rename to scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/regex/RegexUnitTest.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/richwrappers/RichIntUnitTest.scala b/scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/richwrappers/RichIntUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/richwrappers/RichIntUnitTest.scala rename to scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/richwrappers/RichIntUnitTest.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/richwrappers/RichWrappersUnitTest.scala b/scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/richwrappers/RichWrappersUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/richwrappers/RichWrappersUnitTest.scala rename to scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/richwrappers/RichWrappersUnitTest.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/richwrappers/SimpleRichUnitTest.scala b/scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/richwrappers/SimpleRichUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/richwrappers/SimpleRichUnitTest.scala rename to scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/richwrappers/SimpleRichUnitTest.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/sorting/SortingUnitTest.scala b/scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/sorting/SortingUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/sorting/SortingUnitTest.scala rename to scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/sorting/SortingUnitTest.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/vectorbenefits/VectorAppendPrependUnitTest.scala b/scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/vectorbenefits/VectorAppendPrependUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/vectorbenefits/VectorAppendPrependUnitTest.scala rename to scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/vectorbenefits/VectorAppendPrependUnitTest.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/vectorbenefits/VectorHeadTailAccessUnitTest.scala b/scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/vectorbenefits/VectorHeadTailAccessUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/vectorbenefits/VectorHeadTailAccessUnitTest.scala rename to scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/vectorbenefits/VectorHeadTailAccessUnitTest.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/vectorbenefits/VectorIterationUnitTest.scala b/scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/vectorbenefits/VectorIterationUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/vectorbenefits/VectorIterationUnitTest.scala rename to scala-core-modules/scala-core-5/src/test/scala/com/baeldung/scala/vectorbenefits/VectorIterationUnitTest.scala diff --git a/scala-core-6/README.md b/scala-core-modules/scala-core-6/README.md similarity index 90% rename from scala-core-6/README.md rename to scala-core-modules/scala-core-6/README.md index e0f672246..77ce5770d 100644 --- a/scala-core-6/README.md +++ b/scala-core-modules/scala-core-6/README.md @@ -10,7 +10,6 @@ This module contains articles about Scala's core features. - [Functions in Scala](https://www.baeldung.com/scala/functions) - [Loops in Functional Scala](https://www.baeldung.com/scala/loops-functional-scala) - [Pimp My Library Pattern in Scala](https://www.baeldung.com/scala/pimp-my-library-pattern) -- [Implicit Conversions](https://www.baeldung.com/scala/implicit-conversions) - [A Guide to Scala Maps](https://www.baeldung.com/scala/maps-guide) - [Break Statement in Scala](https://www.baeldung.com/scala/break-statement) - [Range in Scala](https://www.baeldung.com/scala/range) diff --git a/scala-core-6/src/main/scala-2/com/baeldung/scala/break/BreakStatement.scala b/scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/break/BreakStatement.scala similarity index 100% rename from scala-core-6/src/main/scala-2/com/baeldung/scala/break/BreakStatement.scala rename to scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/break/BreakStatement.scala diff --git a/scala-core-6/src/main/scala-2/com/baeldung/scala/fileio/FileIO.scala b/scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/fileio/FileIO.scala similarity index 100% rename from scala-core-6/src/main/scala-2/com/baeldung/scala/fileio/FileIO.scala rename to scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/fileio/FileIO.scala diff --git a/scala-core-6/src/main/scala-2/com/baeldung/scala/functionalloops/FunctionalLoops.scala b/scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/functionalloops/FunctionalLoops.scala similarity index 100% rename from scala-core-6/src/main/scala-2/com/baeldung/scala/functionalloops/FunctionalLoops.scala rename to scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/functionalloops/FunctionalLoops.scala diff --git a/scala-core-6/src/main/scala-2/com/baeldung/scala/functions/Functions.scala b/scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/functions/Functions.scala similarity index 98% rename from scala-core-6/src/main/scala-2/com/baeldung/scala/functions/Functions.scala rename to scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/functions/Functions.scala index 7824334b6..6294ac7a0 100644 --- a/scala-core-6/src/main/scala-2/com/baeldung/scala/functions/Functions.scala +++ b/scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/functions/Functions.scala @@ -35,8 +35,8 @@ object Functions { // getNameLengthDef.andThen(multiplyByTwoDef) //doesn't compile /** Method to Function value */ - val getNameLengthDefFnValue = getNameLengthDef _ - val multiplyByTwoDefFnValue = multiplyByTwoDef _ + val getNameLengthDefFnValue = getNameLengthDef + val multiplyByTwoDefFnValue = multiplyByTwoDef getNameLengthDefFnValue.andThen(multiplyByTwoDefFnValue) // compiles diff --git a/scala-core-6/src/main/scala-2/com/baeldung/scala/futures/Futures.scala b/scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/futures/Futures.scala similarity index 100% rename from scala-core-6/src/main/scala-2/com/baeldung/scala/futures/Futures.scala rename to scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/futures/Futures.scala diff --git a/scala-core-6/src/main/scala-2/com/baeldung/scala/pimpmylib/PimpLibExample.scala b/scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/pimpmylib/PimpLibExample.scala similarity index 100% rename from scala-core-6/src/main/scala-2/com/baeldung/scala/pimpmylib/PimpLibExample.scala rename to scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/pimpmylib/PimpLibExample.scala diff --git a/scala-core-6/src/main/scala-2/com/baeldung/scala/tailrec/StringLength.scala b/scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/tailrec/StringLength.scala similarity index 100% rename from scala-core-6/src/main/scala-2/com/baeldung/scala/tailrec/StringLength.scala rename to scala-core-modules/scala-core-6/src/main/scala/com/baeldung/scala/tailrec/StringLength.scala diff --git a/scala-core-6/src/test/scala-2/com/baeldung/scala/maps/MapsUnitTest.scala b/scala-core-modules/scala-core-6/src/test/scala/com/baeldung/scala/maps/MapsUnitTest.scala similarity index 100% rename from scala-core-6/src/test/scala-2/com/baeldung/scala/maps/MapsUnitTest.scala rename to scala-core-modules/scala-core-6/src/test/scala/com/baeldung/scala/maps/MapsUnitTest.scala diff --git a/scala-core-6/src/test/scala-2/com/baeldung/scala/pimpmylib/PimpLibExampleUnitTest.scala b/scala-core-modules/scala-core-6/src/test/scala/com/baeldung/scala/pimpmylib/PimpLibExampleUnitTest.scala similarity index 100% rename from scala-core-6/src/test/scala-2/com/baeldung/scala/pimpmylib/PimpLibExampleUnitTest.scala rename to scala-core-modules/scala-core-6/src/test/scala/com/baeldung/scala/pimpmylib/PimpLibExampleUnitTest.scala diff --git a/scala-core-6/src/test/scala-2/com/baeldung/scala/ranges/RangeUnitTest.scala b/scala-core-modules/scala-core-6/src/test/scala/com/baeldung/scala/ranges/RangeUnitTest.scala similarity index 100% rename from scala-core-6/src/test/scala-2/com/baeldung/scala/ranges/RangeUnitTest.scala rename to scala-core-modules/scala-core-6/src/test/scala/com/baeldung/scala/ranges/RangeUnitTest.scala diff --git a/scala-core-7/README.md b/scala-core-modules/scala-core-7/README.md similarity index 88% rename from scala-core-7/README.md rename to scala-core-modules/scala-core-7/README.md index e103bcb15..8cc53e9c7 100644 --- a/scala-core-7/README.md +++ b/scala-core-modules/scala-core-7/README.md @@ -2,7 +2,6 @@ - [Type Inference in Scala](https://www.baeldung.com/scala/type-inference) - [The return Keyword in Scala](https://www.baeldung.com/scala/return-keyword) -- [Reading Command-Line Arguments in Scala](https://www.baeldung.com/scala/read-command-line-arguments) - [Copy an Array to Another in Scala](https://www.baeldung.com/scala/array-copy) - [Initializing an Array in Scala](https://www.baeldung.com/scala/array-initialize) - [Count All Occurrences of a char Within a String in Scala](https://www.baeldung.com/scala/string-char-count) diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/applyfunction/ApplyFunction.scala b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/applyfunction/ApplyFunction.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/applyfunction/ApplyFunction.scala rename to scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/applyfunction/ApplyFunction.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/arrayvswrappedarray/ArrayVsWrappedArray.scala b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/arrayvswrappedarray/ArrayVsWrappedArray.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/arrayvswrappedarray/ArrayVsWrappedArray.scala rename to scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/arrayvswrappedarray/ArrayVsWrappedArray.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/countchar/CountCharsInString.scala b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/countchar/CountCharsInString.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/countchar/CountCharsInString.scala rename to scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/countchar/CountCharsInString.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/productserializable/ColorInference.scala b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/productserializable/ColorInference.scala similarity index 93% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/productserializable/ColorInference.scala rename to scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/productserializable/ColorInference.scala index 2a5f13dc6..69fc8044e 100644 --- a/scala-core-7/src/main/scala-2/com/baeldung/scala/productserializable/ColorInference.scala +++ b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/productserializable/ColorInference.scala @@ -14,7 +14,7 @@ object ColorV2 { object Inference { def isError: Boolean = true - val consoleColor: Product with Serializable with Color = + val consoleColor: Product & Serializable & Color = if (isError) Color.Red else Color.Green val consoleColorV2: Color = if (isError) Color.Red else Color.Green diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/return/ReturnExamples.scala b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/return/ReturnExamples.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/return/ReturnExamples.scala rename to scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/return/ReturnExamples.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/specialized/AllTypesSpecialized.scala b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/specialized/AllTypesSpecialized.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/specialized/AllTypesSpecialized.scala rename to scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/specialized/AllTypesSpecialized.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/specialized/NotSpecialized.scala b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/specialized/NotSpecialized.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/specialized/NotSpecialized.scala rename to scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/specialized/NotSpecialized.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/specialized/OnlyTwoTypesSpecialized.scala b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/specialized/OnlyTwoTypesSpecialized.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/specialized/OnlyTwoTypesSpecialized.scala rename to scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/specialized/OnlyTwoTypesSpecialized.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/typeinference/TypeInferenceLimitations.scala b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/typeinference/TypeInferenceLimitations.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/typeinference/TypeInferenceLimitations.scala rename to scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/typeinference/TypeInferenceLimitations.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/typeinference/TypeInferredFunctions.scala b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/typeinference/TypeInferredFunctions.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/typeinference/TypeInferredFunctions.scala rename to scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/typeinference/TypeInferredFunctions.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/typeinference/TypeInferredParameters.scala b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/typeinference/TypeInferredParameters.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/typeinference/TypeInferredParameters.scala rename to scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/typeinference/TypeInferredParameters.scala diff --git a/scala-core-7/src/main/scala-2/com/baeldung/scala/typeinference/TypeInferredVariables.scala b/scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/typeinference/TypeInferredVariables.scala similarity index 100% rename from scala-core-7/src/main/scala-2/com/baeldung/scala/typeinference/TypeInferredVariables.scala rename to scala-core-modules/scala-core-7/src/main/scala/com/baeldung/scala/typeinference/TypeInferredVariables.scala diff --git a/scala-core-7/src/test/scala-2/com/baeldung/scala/arrays/CopyAnArrayToAnotherUnitTest.scala b/scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/arrays/CopyAnArrayToAnotherUnitTest.scala similarity index 98% rename from scala-core-7/src/test/scala-2/com/baeldung/scala/arrays/CopyAnArrayToAnotherUnitTest.scala rename to scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/arrays/CopyAnArrayToAnotherUnitTest.scala index 5a14a240d..1625443fc 100644 --- a/scala-core-7/src/test/scala-2/com/baeldung/scala/arrays/CopyAnArrayToAnotherUnitTest.scala +++ b/scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/arrays/CopyAnArrayToAnotherUnitTest.scala @@ -7,7 +7,7 @@ class CopyAnArrayToAnotherUnitTest extends AnyFlatSpec with Matchers { val array1 = Array(1, 2, 3, 4) "splat operator" should "copy an entire array to another" in { - var array2 = Array(array1: _*) + var array2 = Array(array1*) array1(1) should be(array2(1)) array1 should not be (array2) diff --git a/scala-core-7/src/test/scala-2/com/baeldung/scala/arrays/InitializeAnArrayUnitTest.scala b/scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/arrays/InitializeAnArrayUnitTest.scala similarity index 98% rename from scala-core-7/src/test/scala-2/com/baeldung/scala/arrays/InitializeAnArrayUnitTest.scala rename to scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/arrays/InitializeAnArrayUnitTest.scala index 347041189..ed2b8b963 100644 --- a/scala-core-7/src/test/scala-2/com/baeldung/scala/arrays/InitializeAnArrayUnitTest.scala +++ b/scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/arrays/InitializeAnArrayUnitTest.scala @@ -30,7 +30,7 @@ class InitializeAnArrayUnitTest extends AnyFlatSpec with Matchers { "splat operator" should "copy an entire list to an array" in { var list = List(1, 2, 3, 4) - var array = Array[Int](list: _*) + var array = Array[Int](list*) array(1) should be(list(1)) array.length should be(4) diff --git a/scala-core-7/src/test/scala-2/com/baeldung/scala/countchar/CountCharsInStringUnitTest.scala b/scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/countchar/CountCharsInStringUnitTest.scala similarity index 100% rename from scala-core-7/src/test/scala-2/com/baeldung/scala/countchar/CountCharsInStringUnitTest.scala rename to scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/countchar/CountCharsInStringUnitTest.scala diff --git a/scala-core-7/src/test/scala-2/com/baeldung/scala/typeinference/TypeInferenceLimitationsTest.scala b/scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/typeinference/TypeInferenceLimitationsUnitTest.scala similarity index 86% rename from scala-core-7/src/test/scala-2/com/baeldung/scala/typeinference/TypeInferenceLimitationsTest.scala rename to scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/typeinference/TypeInferenceLimitationsUnitTest.scala index 052b099f2..70deb1e87 100644 --- a/scala-core-7/src/test/scala-2/com/baeldung/scala/typeinference/TypeInferenceLimitationsTest.scala +++ b/scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/typeinference/TypeInferenceLimitationsUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.typeinference import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class TypeInferenceLimitationsTest extends AnyWordSpec with Matchers { +class TypeInferenceLimitationsUnitTest extends AnyWordSpec with Matchers { "type inference limitations" should { import com.baeldung.scala.typeinference.TypeInferenceLimitations._ "An list of integers given input to recursiveSum function should calculate sum of its elements" in { diff --git a/scala-core-7/src/test/scala-2/com/baeldung/scala/typeinference/TypeInferredFunctionsTest.scala b/scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/typeinference/TypeInferredFunctionsUnitTest.scala similarity index 87% rename from scala-core-7/src/test/scala-2/com/baeldung/scala/typeinference/TypeInferredFunctionsTest.scala rename to scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/typeinference/TypeInferredFunctionsUnitTest.scala index 568de7c07..45642f479 100644 --- a/scala-core-7/src/test/scala-2/com/baeldung/scala/typeinference/TypeInferredFunctionsTest.scala +++ b/scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/typeinference/TypeInferredFunctionsUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.typeinference import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class TypeInferredFunctionsTest extends AnyWordSpec with Matchers { +class TypeInferredFunctionsUnitTest extends AnyWordSpec with Matchers { "type inference for functions" should { import com.baeldung.scala.typeinference.TypeInferredFunctions._ "An integer number given as input to the function squareInt should calculate its square value, and its return type is inferred as Integer type" in { diff --git a/scala-core-7/src/test/scala-2/com/baeldung/scala/typeinference/TypeInferredVariablesTest.scala b/scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/typeinference/TypeInferredVariablesUnitTest.scala similarity index 91% rename from scala-core-7/src/test/scala-2/com/baeldung/scala/typeinference/TypeInferredVariablesTest.scala rename to scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/typeinference/TypeInferredVariablesUnitTest.scala index e37541c4b..f16388ee4 100644 --- a/scala-core-7/src/test/scala-2/com/baeldung/scala/typeinference/TypeInferredVariablesTest.scala +++ b/scala-core-modules/scala-core-7/src/test/scala/com/baeldung/scala/typeinference/TypeInferredVariablesUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.typeinference import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class TypeInferredVariablesTest extends AnyWordSpec with Matchers { +class TypeInferredVariablesUnitTest extends AnyWordSpec with Matchers { "type inference for variables " should { import com.baeldung.scala.typeinference.TypeInferredVariables._ "An integer number given as initial value should infer a Integer type" in { diff --git a/scala-core-8/README.md b/scala-core-modules/scala-core-8/README.md similarity index 55% rename from scala-core-8/README.md rename to scala-core-modules/scala-core-8/README.md index 979172813..0b1df72a2 100644 --- a/scala-core-8/README.md +++ b/scala-core-modules/scala-core-8/README.md @@ -1,10 +1,9 @@ ### Relevant Articles: -- [Introduction to Scala Macros](https://www.baeldung.com/scala/scala2-macros) -- [Introduction to Macros in Scala 2](https://www.baeldung.com/scala/scala2-macros) - [The Builder Pattern in Scala](https://www.baeldung.com/scala/builder-pattern) -- [Guide to Scala Duration and FiniteDuration](https://www.baeldung.com/scala/duration-finiteduration) -- [Get Difference Between Two Dates](https://www.baeldung.com/scala/difference-between-two-dates) - [The Either Type in Scala](https://www.baeldung.com/scala/either-type) - [Understanding the Differences: reduceLeft, reduceRight, foldLeft, foldRight, scanLeft, and scanRight in Scala](https://www.baeldung.com/scala/reduce-fold-scan-left-right) - [Different Ways to Reverse a Sequence in Scala](https://www.baeldung.com/scala/reverse-sequence) +- [Transforming a List of Futures Ignoring Failures](https://www.baeldung.com/scala/transform-list-futures-ignore-failures) +- [Difference Between Braces and Parentheses in Scala](https://www.baeldung.com/scala/braces-vs-parentheses) +- [Stackable Trait Pattern in Scala](https://www.baeldung.com/scala/stackable-trait-pattern) diff --git a/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/DoubleEdgedSword.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/DoubleEdgedSword.scala new file mode 100644 index 000000000..01ada1519 --- /dev/null +++ b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/DoubleEdgedSword.scala @@ -0,0 +1,12 @@ +package com.baeldung.scala.braces + +object DoubleEdgedSword extends App { + def process(value: Int): Int = value * 2 + + val result = process { + 1 + 2 + 5 // This is the actual integer passed to process + } + + println(result) +} diff --git a/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/NoParenthesis.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/NoParenthesis.scala new file mode 100644 index 000000000..98fad9980 --- /dev/null +++ b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/NoParenthesis.scala @@ -0,0 +1,14 @@ +package com.baeldung.scala.braces + +object NoParenthesis extends App { + def greet(name: String): Unit = { + println(s"Hello, $name!") + } + + // This won't work + // greet "Alice" + + // But this works and it equivalent to greet("Alice") + this `greet` "Alice" + +} diff --git a/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/ScalaBlockExamples.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/ScalaBlockExamples.scala new file mode 100644 index 000000000..4e39ce971 --- /dev/null +++ b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/ScalaBlockExamples.scala @@ -0,0 +1,52 @@ +package com.baeldung.scala.braces + +object ScalaBlockExamples extends App { + // A simple block returning a value + val a = { + val x = 5 + val y = 10 + // The last expression is the value of the block + x + y + } + println(s"Value of a: $a") // Output: Value of a: 15 + + // Block in an if-else construct + val number = 7 + val result = if (number > 5) { + // This block is executed because the condition is true + "Greater than 5" + } else { + "Not greater than 5" + } + println(s"Result: $result") // Output: Result: Greater than 5 + + // Block in a function definition + def square(x: Int) = { + // A block defining the body of the function + x * x + } + println(s"Square of 4: ${square(4)}") // Output: Square of 4: 16 + + // Block as an argument to a higher-order function + val numbers = List(1, 2, 3, 4, 5) + val doubledNumbers = numbers.map { n => + // A block that is passed as a lambda to the map function + n * 2 + } + println( + s"Doubled numbers: $doubledNumbers" + ) // Output: Doubled numbers: List(2, 4, 6, 8, 10) + + // Block in a for-comprehension + val squares = for { + n <- numbers + if n % 2 == 0 // Filtering even numbers + } yield { + // A block that computes the square of each number + n * n + } + + println(s"Squares of even numbers: $squares") + // Output: Squares of even numbers: List(4, 16) + +} diff --git a/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/TimingExample.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/TimingExample.scala new file mode 100644 index 000000000..04c103f5e --- /dev/null +++ b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/TimingExample.scala @@ -0,0 +1,20 @@ +package com.baeldung.scala.braces + +object TimingExample extends App { + def measureTime[T](block: => T): (T, Long) = { + val startTime = System.nanoTime() + val result = block + val endTime = System.nanoTime() + (result, endTime - startTime) + } + + // Using the measureTime method with braces + val (result, time) = measureTime { + // Code block whose execution time is to be measured + val numbers = (1 to 1000000).toList + numbers.filter(_ % 2 == 0).sum + } + + println(s"Result: $result") + println(s"Execution Time: $time nanoseconds") +} diff --git a/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/project/build.properties b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/project/build.properties new file mode 100644 index 000000000..e8a1e246e --- /dev/null +++ b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/braces/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.9.7 diff --git a/scala-core-8/src/main/scala-2/com/baeldung/scala/builderpattern/Guitar.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/builderpattern/Guitar.scala similarity index 100% rename from scala-core-8/src/main/scala-2/com/baeldung/scala/builderpattern/Guitar.scala rename to scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/builderpattern/Guitar.scala diff --git a/scala-core-8/src/main/scala-2/com/baeldung/scala/builderpattern/GuitarBuilder.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/builderpattern/GuitarBuilder.scala similarity index 92% rename from scala-core-8/src/main/scala-2/com/baeldung/scala/builderpattern/GuitarBuilder.scala rename to scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/builderpattern/GuitarBuilder.scala index 78a033752..78e0f3480 100644 --- a/scala-core-8/src/main/scala-2/com/baeldung/scala/builderpattern/GuitarBuilder.scala +++ b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/builderpattern/GuitarBuilder.scala @@ -31,3 +31,6 @@ case class GuitarBuilder private ( delay = delay ) } +object GuitarBuilder { + def apply(): GuitarBuilder = new GuitarBuilder() +} diff --git a/scala-core-8/src/main/scala-2/com/baeldung/scala/builderpattern/SafeGuitarBuilder.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/builderpattern/SafeGuitarBuilder.scala similarity index 100% rename from scala-core-8/src/main/scala-2/com/baeldung/scala/builderpattern/SafeGuitarBuilder.scala rename to scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/builderpattern/SafeGuitarBuilder.scala diff --git a/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/futurelist/FutureFailuresList.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/futurelist/FutureFailuresList.scala new file mode 100644 index 000000000..cc52ed794 --- /dev/null +++ b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/futurelist/FutureFailuresList.scala @@ -0,0 +1,42 @@ +package com.baeldung.futurelist + +import scala.concurrent.{ExecutionContext, Future} +import scala.util.{Success, Failure, Try} +import scala.concurrent.ExecutionContext.Implicits.global + +object FutureFailuresList { + + def getSuccessful( + futures: List[Future[String]] + )( + fn: List[Future[String]] => Future[List[Try[String]]] + ): Future[List[String]] = { + val futuresSeq = fn(futures) + futuresSeq.map(f => f.collect { case Success(str) => str }) + } + + def getFailures( + futures: List[Future[String]] + )( + fn: List[Future[String]] => Future[List[Try[String]]] + ): Future[List[Throwable]] = { + val futuresSeq = fn(futures) + futuresSeq.map(f => f.collect { case Failure(ex) => ex }) + } + + def usingTransform( + futures: List[Future[String]] + ): Future[List[Try[String]]] = { + Future.sequence(futures.map(f => f.transform(Success(_)))) + } + + def usingRecover( + futures: List[Future[String]] + ): Future[List[Try[String]]] = { + Future.sequence( + futures.map(f => { + f.map(Success(_)).recover { case x => Failure(x) } + }) + ) + } +} diff --git a/scala-core-8/src/main/scala-2/com/baeldung/scala/reverselists/ListReverser.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/reverselists/ListReverser.scala similarity index 100% rename from scala-core-8/src/main/scala-2/com/baeldung/scala/reverselists/ListReverser.scala rename to scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/reverselists/ListReverser.scala diff --git a/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/stackabletrait/DecoratorExample.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/stackabletrait/DecoratorExample.scala new file mode 100644 index 000000000..7a91806b2 --- /dev/null +++ b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/stackabletrait/DecoratorExample.scala @@ -0,0 +1,43 @@ +package com.baeldung.scala.stackabletrait + +object DecoratorExample { + trait IntTransformation { + def transform(value: Int): Int = value + } + + class TransformationDecorator(wrappee: IntTransformation) + extends IntTransformation { + override def transform(value: Int): Int = wrappee.transform(value) + } + + class DoubleDecorator(wrappee: IntTransformation) + extends TransformationDecorator(wrappee) { + override def transform(value: Int): Int = + super.transform(value * 2) + } + + class LogInt(wrappee: IntTransformation) + extends TransformationDecorator(wrappee) { + override def transform(value: Int): Int = { + println(s"Transforming value: $value") + super.transform(value) + } + } + + class CustomDecorator(f: Int => Int, wrappee: IntTransformation) + extends TransformationDecorator(wrappee) { + override def transform(value: Int): Int = + super.transform(f(value)) + } + + @main + def mainDec(): Unit = { + val identity = new IntTransformation {} + + val withLogging = new LogInt(identity) + val withDouble = new DoubleDecorator(withLogging) + val withCustom = new CustomDecorator(_ + 1, withDouble) + + println(s"With increment, double, and logging: ${withCustom.transform(5)}") + } +} diff --git a/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/stackabletrait/MixinExample.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/stackabletrait/MixinExample.scala new file mode 100644 index 000000000..79e225bd6 --- /dev/null +++ b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/stackabletrait/MixinExample.scala @@ -0,0 +1,24 @@ +package com.baeldung.scala.stackabletrait + +object MixinExample { + trait Person { + val name: String + val country: String + } + + case class ItalianPerson(name: String) extends Person { + val country = "Italy" + } + + trait WithPrettyPrinting extends Person { + def prettyPrint: String = + s"""Name: $name + |Country: $country""".stripMargin + } + + @main + def main(): Unit = + val italian = new ItalianPerson("Mario") with WithPrettyPrinting + println(italian) + println(italian.prettyPrint) +} diff --git a/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/stackabletrait/StackableTraitExample.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/stackabletrait/StackableTraitExample.scala new file mode 100644 index 000000000..bb8dbbd62 --- /dev/null +++ b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/stackabletrait/StackableTraitExample.scala @@ -0,0 +1,41 @@ +package com.baeldung.scala.stackabletrait + +object StackableTraitExample { + trait IntTransformation { + def transform(value: Int): Int = value + } + + trait DoubleTransformation extends IntTransformation { + override def transform(value: Int): Int = + super.transform(value * 2) + } + + trait LogInt extends IntTransformation { + override def transform(value: Int): Int = { + println(s"Transforming value: $value") + super.transform(value) + } + } + + trait CustomTransformation(f: Int => Int) extends IntTransformation { + override def transform(value: Int): Int = + super.transform(f(value)) + } + + @main + def mainST(): Unit = { + val logAndDouble = new IntTransformation + with DoubleTransformation + with LogInt {} + val doubleAndLog = new IntTransformation + with LogInt + with DoubleTransformation {} + val logAndCustom = new IntTransformation + with CustomTransformation(_ + 1) + with LogInt {} + + println(s"Log and double: ${logAndDouble.transform(5)}") + println(s"Double and log: ${doubleAndLog.transform(5)}") + println(s"Log and increment: ${logAndCustom.transform(5)}") + } +} diff --git a/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/stackabletrait/StackableTraitWithExplicitBaseAndCoreExample.scala b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/stackabletrait/StackableTraitWithExplicitBaseAndCoreExample.scala new file mode 100644 index 000000000..83bb6332d --- /dev/null +++ b/scala-core-modules/scala-core-8/src/main/scala/com/baeldung/scala/stackabletrait/StackableTraitWithExplicitBaseAndCoreExample.scala @@ -0,0 +1,45 @@ +package com.baeldung.scala.stackabletrait + +object StackableTraitWithExplicitBaseAndCoreExample { + trait BaseIntTransformation { + def transform(value: Int): Int + } + + trait CoreIntTransformation extends BaseIntTransformation { + def transform(value: Int): Int = value + } + + trait DoubleTransformation extends CoreIntTransformation { + override def transform(value: Int): Int = + super.transform(value * 2) + } + + trait LogInt extends CoreIntTransformation { + override def transform(value: Int): Int = { + println(s"Transforming value: $value") + super.transform(value) + } + } + + trait CustomTransformation(f: Int => Int) extends CoreIntTransformation { + override def transform(value: Int): Int = + super.transform(f(value)) + } + + @main + def mainSTE(): Unit = { + val logAndDouble = new CoreIntTransformation + with DoubleTransformation + with LogInt {} + val doubleAndLog = new CoreIntTransformation + with LogInt + with DoubleTransformation {} + val logAndCustom = new CoreIntTransformation + with CustomTransformation(_ + 1) + with LogInt {} + + println(s"Log and double: ${logAndDouble.transform(5)}") + println(s"Double and log: ${doubleAndLog.transform(5)}") + println(s"Log and increment: ${logAndCustom.transform(5)}") + } +} diff --git a/scala-core-8/src/test/scala-2/com/baeldung/scala/builderpattern/GuitarSpec.scala b/scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/builderpattern/GuitarUnitTest.scala similarity index 96% rename from scala-core-8/src/test/scala-2/com/baeldung/scala/builderpattern/GuitarSpec.scala rename to scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/builderpattern/GuitarUnitTest.scala index 9f7a80734..46fa01d6f 100644 --- a/scala-core-8/src/test/scala-2/com/baeldung/scala/builderpattern/GuitarSpec.scala +++ b/scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/builderpattern/GuitarUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.builderpattern import org.scalatest.wordspec.AnyWordSpec -class GuitarSpec extends AnyWordSpec { +class GuitarUnitTest extends AnyWordSpec { "an unsafe guitar builder" should { "resort to defaults when not initialised" in { diff --git a/scala-core-8/src/test/scala-2/com/baeldung/scala/either/EitherTest.scala b/scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/either/EitherUnitTest.scala similarity index 97% rename from scala-core-8/src/test/scala-2/com/baeldung/scala/either/EitherTest.scala rename to scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/either/EitherUnitTest.scala index f3d1de3bc..d1ada69cd 100644 --- a/scala-core-8/src/test/scala-2/com/baeldung/scala/either/EitherTest.scala +++ b/scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/either/EitherUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.either import org.scalatest.funsuite.AnyFunSuite -class EitherTest extends AnyFunSuite { +class EitherUnitTest extends AnyFunSuite { test("Pattern matching can be used to extract the value of an Either") { val e: Either[String, Int] = Right(5) e match { diff --git a/scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/futurelist/FutureFailuresListUnitTest.scala b/scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/futurelist/FutureFailuresListUnitTest.scala new file mode 100644 index 000000000..077b4e406 --- /dev/null +++ b/scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/futurelist/FutureFailuresListUnitTest.scala @@ -0,0 +1,70 @@ +package com.baeldung.futurelist + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration._ + +import scala.concurrent.{Future, Await} + +class FutureFailuresListUnitTest extends AnyFlatSpec with Matchers { + val testList = List( + Future.successful("success 1"), + Future.failed(Exception("failure 1")), + Future.successful("success 2"), + Future.failed(Exception("failure 2")), + Future.successful("success 3") + ) + + "getSuccessful usingTransform" should "return all successful futures" in { + val result = Await.result( + FutureFailuresList.getSuccessful(testList)( + FutureFailuresList.usingTransform + ), + 2.seconds + ) + result shouldBe List( + "success 1", + "success 2", + "success 3" + ) + } + + "getFailures usingTransform" should "return all failed futures" in { + val result = Await.result( + FutureFailuresList.getFailures(testList)( + FutureFailuresList.usingTransform + ), + 2.seconds + ) + result.map(_.getMessage) shouldBe List( + "failure 1", + "failure 2" + ) + } + + "getSuccessful usingRecover" should "return all successful futures" in { + val result = Await.result( + FutureFailuresList.getSuccessful(testList)( + FutureFailuresList.usingRecover + ), + 2.seconds + ) + result shouldBe List( + "success 1", + "success 2", + "success 3" + ) + } + + "getFailures usingRecover" should "return all failed futures" in { + val result = Await.result( + FutureFailuresList.getFailures(testList)(FutureFailuresList.usingRecover), + 2.seconds + ) + result.map(_.getMessage) shouldBe List( + "failure 1", + "failure 2" + ) + } +} diff --git a/scala-core-8/src/test/scala/com/baeldung/scala/highorderfunctions/HighOrderFunctionsUnitTest.scala b/scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/highorderfunctions/HighOrderFunctionsUnitTest.scala similarity index 100% rename from scala-core-8/src/test/scala/com/baeldung/scala/highorderfunctions/HighOrderFunctionsUnitTest.scala rename to scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/highorderfunctions/HighOrderFunctionsUnitTest.scala diff --git a/scala-core-8/src/test/scala-2/com/baeldung/scala/reverselists/ListReverserSpec.scala b/scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/reverselists/ListReverserUnitTest.scala similarity index 78% rename from scala-core-8/src/test/scala-2/com/baeldung/scala/reverselists/ListReverserSpec.scala rename to scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/reverselists/ListReverserUnitTest.scala index e6cf21635..a92485526 100644 --- a/scala-core-8/src/test/scala-2/com/baeldung/scala/reverselists/ListReverserSpec.scala +++ b/scala-core-modules/scala-core-8/src/test/scala/com/baeldung/scala/reverselists/ListReverserUnitTest.scala @@ -5,18 +5,18 @@ import org.scalatest.{Assertion, BeforeAndAfterEach} import scala.util.Random -class ListReverserSpec extends AnyWordSpec with BeforeAndAfterEach { +class ListReverserUnitTest extends AnyWordSpec with BeforeAndAfterEach { import ListReverser._ - def testReverseSmallList(f: Seq[_] => Seq[_]): Assertion = { + def testReverseSmallList(f: Seq[?] => Seq[?]): Assertion = { val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val expectedReversedList = List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1) val actualReversedList = f(list) assertResult(expectedReversedList)(actualReversedList) } - def testReverseBigList(f: Seq[_] => Seq[_]): Assertion = { + def testReverseBigList(f: Seq[?] => Seq[?]): Assertion = { val n = 100_000 val vector: Vector[String] = (0 to n).foldLeft(Vector.empty[String])((v, _) => @@ -29,7 +29,7 @@ class ListReverserSpec extends AnyWordSpec with BeforeAndAfterEach { } "The naive list reverser" should { - val reversingFunction: Seq[_] => Seq[_] = naiveRecursiveReverse + val reversingFunction: Seq[?] => Seq[?] = naiveRecursiveReverse "reverse small lists" in { testReverseSmallList(reversingFunction) @@ -41,7 +41,7 @@ class ListReverserSpec extends AnyWordSpec with BeforeAndAfterEach { } "The tail-recursive list reverser" should { - val reversingFunction: Seq[_] => Seq[_] = tailRecursiveReverse + val reversingFunction: Seq[?] => Seq[?] = tailRecursiveReverse "reverse small lists" in { testReverseSmallList(reversingFunction) @@ -53,7 +53,7 @@ class ListReverserSpec extends AnyWordSpec with BeforeAndAfterEach { } "The folding list reverser" should { - val reversingFunction: Seq[_] => Seq[_] = foldBasedReverse + val reversingFunction: Seq[?] => Seq[?] = foldBasedReverse "reverse small lists" in { testReverseSmallList(reversingFunction) diff --git a/scala-core-modules/scala-core-9/README.md b/scala-core-modules/scala-core-9/README.md new file mode 100644 index 000000000..81dfecdee --- /dev/null +++ b/scala-core-modules/scala-core-9/README.md @@ -0,0 +1,10 @@ +### Relevant Articles +- [Check if a Collection Is Sorted in Scala](https://www.baeldung.com/scala/check-collection-sorted) +- [Check if a String Is a Palindrome in Scala](https://www.baeldung.com/scala/string-palindrome-test) +- [Scala’s Destructuring with the @ Operator](https://www.baeldung.com/scala/destructuring-operator) +- [Check if Two Strings Are Isomorphic in Scala](https://www.baeldung.com/scala/string-isomorphism) +- [Find Two’s Complement of a Number in Scala](https://www.baeldung.com/scala/compute-twos-complement) +- [Convert Option to Either in Scala](https://www.baeldung.com/scala/option-either-conversion) +- [Meaning of _root_ In Scala Import Clause](https://www.baeldung.com/scala/root-import-clause) +- [Check if All Characters in a Scala String Are Either Upper or Lower Case](https://www.baeldung.com/scala/string-check-all-characters-upper-lower-case) +- [TASTy Files in Scala 3](https://www.baeldung.com/scala/scala-3-tasty) diff --git a/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/classT/Person.scala b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/classT/Person.scala new file mode 100644 index 000000000..1b9871511 --- /dev/null +++ b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/classT/Person.scala @@ -0,0 +1,5 @@ +package com.baeldung.scala.classT + +class Person(val name: String) { + override def toString: String = s"Person with name: $name" +} diff --git a/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/destructuring/Notifications.scala b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/destructuring/Notifications.scala new file mode 100644 index 000000000..5e057a7a6 --- /dev/null +++ b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/destructuring/Notifications.scala @@ -0,0 +1,16 @@ +package com.baeldung.scala.destructuring + +trait Notification +case class Email(subject: String, body: String, recipient: String) + extends Notification +case class SMS(number: String, message: String) extends Notification + +case class Processed[N <: Notification](notification: N, msg: String) + +def processEmail(email: Email): Processed[Email] = Processed( + email, + s"Sent email to ${email.recipient} with subject: ${email.subject}" +) + +def processSMS(sms: SMS): Processed[SMS] = + Processed(sms, s"Sending SMS to ${sms.number}: ${sms.message}") diff --git a/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/isomorphic/IsomorphicStringsChecker.scala b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/isomorphic/IsomorphicStringsChecker.scala new file mode 100644 index 000000000..9cbe61ca7 --- /dev/null +++ b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/isomorphic/IsomorphicStringsChecker.scala @@ -0,0 +1,26 @@ +package com.baeldung.scala.isomorphic + +object IsomorphicStringsChecker: + def checkIsomorphicBothWays(str1: String, str2: String): Boolean = + (str1.length == str2.length) && checkIsomorphic( + str1, + str2 + ) && checkIsomorphic(str2, str1) + + def checkIsomorphic2BothWays(str1: String, str2: String): Boolean = + (str1.length == str2.length) && checkIsomorphic2( + str1, + str2 + ) && checkIsomorphic2(str2, str1) + + private def checkIsomorphic(str1: String, str2: String): Boolean = + val z = str1.zip(str2) + val distinctCounts = z.map(tup => { + z.distinct.count(tupZ => tupZ._1 == tup._1) + }) + distinctCounts.count(_ > 1) == 0 + + private def checkIsomorphic2(str1: String, str2: String): Boolean = + val z = str1.zip(str2) + val m = z.groupMap(_._1)(_._2) + m.forall(_._2.distinct.length == 1) diff --git a/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/palindrome/Palindrome.scala b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/palindrome/Palindrome.scala new file mode 100644 index 000000000..7fac3faea --- /dev/null +++ b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/palindrome/Palindrome.scala @@ -0,0 +1,36 @@ +package com.baeldung.scala.palindrome + +import scala.annotation.tailrec + +object Palindrome { + + extension (str: String) { + def sanitize: String = str.replaceAll("[^A-Za-z0-9]", "").toLowerCase + } + + def isPalindromeByReverse(str: String): Boolean = { + val sanitizedStr = str.sanitize + sanitizedStr == sanitizedStr.reverse + } + + def isPalindromeByRecursion(str: String): Boolean = { + val sanitizedStr = str.sanitize + @tailrec + def isPalindromeRec(str: String): Boolean = { + str match { + case _ if str.length <= 1 => true + case _ if str.head == str.last => isPalindromeRec(str.tail.init) + case _ => false + } + } + isPalindromeRec(sanitizedStr) + } + + def isPalindromeByForAll(str: String): Boolean = { + val sanitizedStr = str.sanitize + sanitizedStr.zipWithIndex.forall { case (ch, i) => + ch == sanitizedStr.charAt(sanitizedStr.length - i - 1) + } + } + +} diff --git a/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/sort/IsSortedCollection.scala b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/sort/IsSortedCollection.scala new file mode 100644 index 000000000..24fb2399b --- /dev/null +++ b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/sort/IsSortedCollection.scala @@ -0,0 +1,62 @@ +package com.baeldung.scala.sort + +import com.baeldung.scala.sort.IsSortedCollection.Direction.ASC + +import scala.annotation.tailrec + +object IsSortedCollection { + + enum Direction: + case ASC, DESC + + def isSortedBySorting[A](list: List[A], direction: Direction)(using + ord: Ordering[A] + ): Boolean = { + direction match { + case Direction.ASC => list.sorted == list + case Direction.DESC => list.sorted.reverse == list + } + } + + def isSortedBySliding[A](list: List[A], direction: Direction)(using + ord: Ordering[A] + ): Boolean = { + val comparator = if (direction == ASC) ord.lteq else ord.gteq + list match { + case Nil | _ :: Nil => true + case _ => list.sliding(2).forall { case List(a, b) => comparator(a, b) } + } + } + + def isSortedByZip[A](list: List[A], direction: Direction)(using + ord: Ordering[A] + ): Boolean = { + val comparator = if (direction == ASC) ord.lteq else ord.gteq + if (list.size < 2) + true + else + list.zip(list.tail).forall { case (a, b) => comparator(a, b) } + } + + def isSortedByLazyZip[A](list: List[A], direction: Direction)(using + ord: Ordering[A] + ): Boolean = { + val comparator = if (direction == ASC) ord.lteq else ord.gteq + if (list.size < 2) + true + else + list.lazyZip(list.tail).forall { case (a, b) => comparator(a, b) } + } + + @tailrec + def isSortedRecursive[A](list: List[A], direction: Direction)(using + ord: Ordering[A] + ): Boolean = { + val comparator = if (direction == ASC) ord.lteq else ord.gteq + list match { + case Nil | _ :: Nil => true + case a :: b :: tail => + comparator(a, b) && isSortedRecursive(b :: tail, direction) + } + } +} diff --git a/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/tastyfiles/PowerMacro.scala b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/tastyfiles/PowerMacro.scala new file mode 100644 index 000000000..be3da7db8 --- /dev/null +++ b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/tastyfiles/PowerMacro.scala @@ -0,0 +1,30 @@ +package com.baeldung.scala.tastyfiles + +import scala.quoted.* + +object PowerMacro: + // The macro that unrolls the computation of powers and then generates the expression + inline def showAsPowerTerm(inline x: Double, n: Int): String = ${ + showAsTermImpl('x, 'n) + } + + // The actual implementation of the macro + private def showAsTermImpl(x: Expr[Double], n: Expr[Int])(using + Quotes + ): Expr[String] = + import quotes.reflect.* + + n.value match + case Some(num) => + val powerExpr = unrolledPowerCode(x, num) + Expr( + powerExpr.asTerm.toString + ) // Ensures that the asTerm method call is evaluated at compile-time + case None => + '{ "Error: 'n' must be a known constant at compile time." } + + // Helper method to unroll the power computation + def unrolledPowerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] = + if n == 0 then '{ 1.0 } + else if n == 1 then x + else '{ $x * ${ unrolledPowerCode(x, n - 1) } } diff --git a/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/twoscomplement/TwosComplement.scala b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/twoscomplement/TwosComplement.scala new file mode 100644 index 000000000..e655c6c45 --- /dev/null +++ b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/twoscomplement/TwosComplement.scala @@ -0,0 +1,53 @@ +package com.baeldung.scala.twoscomplement + +import scala.annotation.tailrec + +object TwosComplement { + def convertWithFold(bin: String): String = { + addOneWithFold(onesComplement(bin)) + } + + def convertUsingRecursion(bin: String): String = { + addOneWithRec(onesComplement(bin)) + } + + private def onesComplement(bin: String): String = { + bin.map { + case '1' => '0' + case '0' => '1' + } + } + + private def addOneWithFold(bin: String): String = { + bin.reverse + .foldLeft((true, ""))((added, bit) => { + val (needsAdding, acc) = added + if (needsAdding) { + if (bit == '0') (false, acc + '1') else (true, acc + '0') + } else { + (false, acc + bit) + } + }) + ._2 + .reverse + } + + private def addOneWithRec(bin: String): String = { + @tailrec + def addOne(needsAdding: Boolean, acc: String)(binString: String): String = { + if (needsAdding) { + binString match + case "" => acc + case _ => { + val (bit, tail) = binString.splitAt(1) + if (bit == "0") addOne(false, acc + '1')(tail.mkString) + else addOne(true, acc + '0')(tail.mkString) + } + } else { + acc + binString + } + } + + addOne(true, "")(bin.reverse).reverse + } +} diff --git a/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/uniformcase/UniformCaseChecker.scala b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/uniformcase/UniformCaseChecker.scala new file mode 100644 index 000000000..bdc788962 --- /dev/null +++ b/scala-core-modules/scala-core-9/src/main/scala/com/baeldung/scala/uniformcase/UniformCaseChecker.scala @@ -0,0 +1,24 @@ +package com.baeldung.scala.uniformcase + +object UniformCaseChecker { + + def convertAndCheck(str: String): Boolean = { + str.toUpperCase == str || str.toLowerCase == str + } + + def isUpperLowerAndForAll(str: String): Boolean = { + val filteredStr = str.filter(_.isLetter) + filteredStr.forall(_.isUpper) || filteredStr.forall(_.isLower) + } + + def regexCheck(str: String): Boolean = { + val filteredStr = str.filter(_.isLetter) + filteredStr.matches("^[A-Z]*$") || filteredStr.matches("^[a-z]*$") + } + + def countAndCheck(str: String): Boolean = { + val filteredStr = str.filter(_.isLetter) + filteredStr.count(_.isUpper) == 0 || filteredStr.count(_.isLower) == 0 + } + +} diff --git a/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/classT/ClassTUnitTest.scala b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/classT/ClassTUnitTest.scala new file mode 100644 index 000000000..8778658d7 --- /dev/null +++ b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/classT/ClassTUnitTest.scala @@ -0,0 +1,56 @@ +package com.baeldung.scala.classT + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ClassTUnitTest extends AnyFlatSpec with Matchers { + def createInstance[T](clazz: Class[T], args: Array[AnyRef]): Option[T] = { + try { + // Find the appropriate constructor based on the args types + val constructor = clazz.getConstructors + .find { c => + c.getParameterTypes.length == args.length && + (c.getParameterTypes zip args.map(_.getClass)).forall { + case (paramType, argType) => paramType.isAssignableFrom(argType) + } + } + .getOrElse( + throw new NoSuchMethodException("Suitable constructor not found") + ) + + // Instantiate the class with arguments + Some(constructor.newInstance(args*).asInstanceOf[T]) + } catch { + case e: Exception => + println(s"Error creating instance of ${clazz.getName}: ${e.getMessage}") + None + } + } + + "createInstance with classOf[T]" should "successfully create an instance of Person" in { + val personClass: Class[Person] = classOf[Person] + val personInstance = createInstance(personClass, Array("John Doe": AnyRef)) + + personInstance should not be empty + personInstance.get.toString should include("Person with name: John Doe") + } + + "createInstance with .getClass" should "successfully create another instance of Person" in { + val dummyPerson = new Person("Dummy") + val personClass: Class[? <: Person] = dummyPerson.getClass + val personInstance = createInstance(personClass, Array("Jane Doe": AnyRef)) + + personInstance should not be empty + personInstance.get.toString should include("Person with name: Jane Doe") + } + + "createInstance with .getClass" should "use the type of the variable to bound the type" in { + val dummyPerson: Object = new Person("Dummy") + val personClass: Class[? <: AnyRef] = dummyPerson.getClass + val personInstance = createInstance(personClass, Array("Jane Doe": AnyRef)) + + personInstance should not be empty + personInstance.get.toString should include("Person with name: Jane Doe") + } + +} diff --git a/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/destructuring/DestructuringUnitTest.scala b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/destructuring/DestructuringUnitTest.scala new file mode 100644 index 000000000..63b9203ef --- /dev/null +++ b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/destructuring/DestructuringUnitTest.scala @@ -0,0 +1,95 @@ +package com.baeldung.scala.destructuring + +import scala.util.Random + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class DestructuringUnitTest extends AnyFlatSpec with Matchers { + val random = new Random + + def getRandomElement[A](seq: Seq[A], random: Random): A = seq( + random.nextInt(seq.length) + ) + + // Sample notifications + val emailNotification1 = + Email("Greeting", "Hello, Scala user!", "user@example.com") + val emailNotification2 = + Email("Follow up", "Hello, are you loving scala?", "user@example.com") + val emails = List(emailNotification1, emailNotification2) + val smsNotification = SMS("1234567890", "Your verification code is 12345") + val notifications = List(emailNotification1, smsNotification) + + "We" should "be able to pattern match without @" in { + getRandomElement(notifications, random) match { + case Email(subject, body, recipient) => + println(s"Logging Email to $recipient with subject $subject") + // Reassemble and call the specific handler + processEmail( + Email(subject, body, recipient) + ).msg `startsWith` ("Sent email to ") + + case SMS(number, message) => + println(s"Logging SMS to $number: $message") + // Reassemble and call the specific handler + processSMS(SMS(number, message)).msg `startsWith` ("Sending SMS to") + } + } + + "We" should "be able to pattern match on the types without @" in { + getRandomElement(notifications, random) match { + case email: Email => + println( + s"Logging Email to ${email.recipient} with subject ${email.subject}" + ) + // Reassemble and call the specific handler + processEmail( + email + ).msg `startsWith` ("Sent email to ") + + case sms: SMS => + println(s"Logging SMS to ${sms.number}: ${sms.message}") + // Reassemble and call the specific handler + processSMS(sms).msg `startsWith` ("Sending SMS to") + } + } + + "We" should "be able to pattern match with @" in { + getRandomElement(notifications, random) match { + case email @ Email(subject, _, recipient) => + println(s"Logging Email to $recipient with subject $subject") + processEmail(email).msg `startsWith` ("Sent email to ") + + case sms @ SMS(number, message) => + println(s"Logging SMS to $number: $message") + // Reassemble and call the specific handler + processSMS(sms).msg `startsWith` ("Sending SMS to") + } + } + + "We" should "be able to use @ while declaring a variable" in { + val email @ Email(subject, _, recipient) = getRandomElement(emails, random) + println(s"Logging Email to $recipient with subject $subject") + processEmail(email).msg `startsWith` ("Sent email to ") + } + + "We" should "be able to use @ in for comprehensions" in { + for (email @ Email(subject, _, recipient) <- emails) { + println(s"Logging Email to $recipient with subject $subject") + processEmail(email).msg `startsWith` ("Sent email to ") + } + } + + "We" should "be able to use @ in a subpattern" in { + emails match { + case _ :: (email2 @ Email(subject, _, recipient)) :: _ => + println(s"Logging 2nd Email to $recipient with subject $subject") + processEmail(email2).msg `startsWith` ("Sent email to ") + + case _ => + println(s"Only one message") + } + } + +} diff --git a/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/isomorphic/IsomorphicStringsUnitTest.scala b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/isomorphic/IsomorphicStringsUnitTest.scala new file mode 100644 index 000000000..de78a75cd --- /dev/null +++ b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/isomorphic/IsomorphicStringsUnitTest.scala @@ -0,0 +1,37 @@ +package com.baeldung.scala.isomorphic + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should +import com.baeldung.scala.isomorphic.IsomorphicStringsChecker.* +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +class IsomorphicStringsUnitTest + extends AnyFlatSpec + with TableDrivenPropertyChecks + with Matchers { + private val isomorphicStringChecker = Seq( + ("isomorphicChecker", checkIsomorphicBothWays), + ("isomorphicChecker2", checkIsomorphic2BothWays) + ) + + private val table = Table( + ("str1", "str2", "Isomorphic"), + ("aab", "xxy", true), + ("aab", "xxyz", false), + ("aaba", "xxyz", false), + ("ab", "xxyz", false), + ("aabac", "xxyz", false), + ("aab", "xxb", true), + (" ", "x", true), + ("", "x", false), + ("aab", "xyy", false), + ("xxm", "aab", true) + ) + isomorphicStringChecker.foreach { (name, fn) => + it should s"Check Isomorphic two String using $name" in { + forAll(table) { (str1, str2, expectedResult) => + fn(str1, str2) shouldBe expectedResult + } + } + } +} diff --git a/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/optiontoeither/OptionToEitherUnitTest.scala b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/optiontoeither/OptionToEitherUnitTest.scala new file mode 100644 index 000000000..2d67db31a --- /dev/null +++ b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/optiontoeither/OptionToEitherUnitTest.scala @@ -0,0 +1,82 @@ +package com.baeldung.scala.optiontoeither + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +sealed trait Error +case object EmptyOptionValue extends Error + +class OptionToEitherUnitTest extends AnyWordSpec with Matchers { + + def usingIfElse(option: Option[String]): Either[Error, String] = { + if (option.isDefined) Right(option.get) else Left(EmptyOptionValue) + } + + def usingPatternMatch(option: Option[String]): Either[Error, String] = { + option match { + case Some(value) => Right(value) + case None => Left(EmptyOptionValue) + } + } + + def usingToRight(option: Option[String]): Either[Error, String] = { + option.toRight(EmptyOptionValue) + } + + def usingCond(option: Option[String]): Either[Error, String] = { + Either.cond(option.nonEmpty, option.get, EmptyOptionValue) + } + + def usingFold(option: Option[String]): Either[Error, String] = { + option.fold(Left(EmptyOptionValue))(Right(_)) + } + + def usingMap(option: Option[String]): Either[Error, String] = { + option.map(Right(_)).getOrElse(Left(EmptyOptionValue)) + } + + "Option" should { + "be converted to Either using if else" in { + val either = usingIfElse(Option("Baeldung")) + either shouldBe Right("Baeldung") + val left = usingIfElse(None) + left shouldBe Left(EmptyOptionValue) + } + + "be converted to Either using pattern matching" in { + val either = usingPatternMatch(Option("Baeldung")) + either shouldBe Right("Baeldung") + val left = usingPatternMatch(None) + left shouldBe Left(EmptyOptionValue) + } + + "be converted to Either using usingToRight" in { + val either = usingToRight(Option("Baeldung")) + either shouldBe Right("Baeldung") + val left = usingToRight(None) + left shouldBe Left(EmptyOptionValue) + } + + "be converted to Either using usingCond" in { + val either = usingCond(Option("Baeldung")) + either shouldBe Right("Baeldung") + val left = usingCond(None) + left shouldBe Left(EmptyOptionValue) + } + + "be converted to Either using fold" in { + val either = usingFold(Option("Baeldung")) + either shouldBe Right("Baeldung") + val left = usingFold(None) + left shouldBe Left(EmptyOptionValue) + } + + "be converted to Either using map" in { + val either = usingMap(Option("Baeldung")) + either shouldBe Right("Baeldung") + val left = usingMap(None) + left shouldBe Left(EmptyOptionValue) + } + } + +} diff --git a/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/palindrom/PalindromeUnitTest.scala b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/palindrom/PalindromeUnitTest.scala new file mode 100644 index 000000000..7627af4eb --- /dev/null +++ b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/palindrom/PalindromeUnitTest.scala @@ -0,0 +1,43 @@ +package com.baeldung.scala.palindrom + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +import com.baeldung.scala.palindrome.Palindrome.* + +class PalindromeUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + private val functions = Seq( + ("isPalindromeByReverse", isPalindromeByReverse), + ("isPalindromeByRecursion", isPalindromeByRecursion), + ("isPalindromeByForAll", isPalindromeByForAll) + ) + + private val wordsTable = Table( + ("Word", "Is Palindrome"), + ("racecar", true), + ("Madam", true), + ("AaAA", true), + ("AAbaa", true), + ("mala yalam", true), + ("racecar!!", true), + ("madam1", false), + (" ", true), + ("", true), + ("@#$%", true), + ("hello", false), + ("10101", true) + ) + + functions.foreach { (name, fn) => + it should s"check if a string is palindrome using the function ${name}" in { + forAll(wordsTable) { (str, isPalindrome) => + fn(str) shouldBe isPalindrome + } + } + } + +} diff --git a/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/rootimport/RootImportUnitTest.scala b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/rootimport/RootImportUnitTest.scala new file mode 100644 index 000000000..72596d678 --- /dev/null +++ b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/rootimport/RootImportUnitTest.scala @@ -0,0 +1,19 @@ +package com.baeldung.scala.rootimport + +import org.scalatest.flatspec.AnyFlatSpec + +class RootImportUnitTest extends AnyFlatSpec { + + it should "use the custom List when using relative import" in { + import scala.* + val myList: scala.List = List(100) + assert(myList.toString == "MyList[100]") + } + + it should "use the scala collection List when using root import" in { + import _root_.scala.* + val scalaList: List[Int] = List(100) + assert(scalaList.toString == "List(100)") + } + +} diff --git a/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/rootimport/scala/List.scala b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/rootimport/scala/List.scala new file mode 100644 index 000000000..5d668bf3b --- /dev/null +++ b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/rootimport/scala/List.scala @@ -0,0 +1,9 @@ +package com.baeldung.scala.rootimport.scala + +class List(val num: Int) { + override def toString: String = s"MyList[${num}]" +} + +object List { + def apply(num: Int) = new List(num) +} diff --git a/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/sort/IsSortedCollectionUnitTest.scala b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/sort/IsSortedCollectionUnitTest.scala new file mode 100644 index 000000000..2e5daef15 --- /dev/null +++ b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/sort/IsSortedCollectionUnitTest.scala @@ -0,0 +1,61 @@ +package com.baeldung.scala.sort + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +import IsSortedCollection.* + +class IsSortedCollectionUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + private val fnsToTest = List( + (("isSortedBySorting", isSortedBySorting[Int])), + (("isSortedBySliding", isSortedBySliding[Int])), + (("isSortedByZip", isSortedByZip[Int])), + (("isSortedByLazyZip", isSortedByLazyZip[Int])), + (("isSortedRecursive", isSortedRecursive[Int])) + ) + + // format: off + private val intTable = Table( + ("Functions", "Input", "Direction", "IsSorted"), + (fnsToTest, List(1,2,3,4), Direction.ASC, true), + (fnsToTest, List(1,2,3,4), Direction.DESC, false), + (fnsToTest, List(4,3,2,1), Direction.DESC, true), + (fnsToTest, List(1), Direction.DESC, true), + (fnsToTest, List(1), Direction.ASC, true), + (fnsToTest, Nil, Direction.ASC, true), + (fnsToTest, Nil, Direction.DESC, true), + ) + + private val stringTable = Table( + ("Function Name", "Function", "Input", "Direction", "IsSorted"), + ("isSortedBySorting", isSortedBySorting[String], List("a", "b", "c"), Direction.ASC, true), + ("isSortedBySorting", isSortedBySorting[String], List("a", "b", "c"), Direction.DESC, false), + ("isSortedBySorting", isSortedBySorting[String], List("a", "aa", "aab", "bcd"), Direction.ASC, true), + ("isSortedBySorting", isSortedBySorting[String], List("ccc", "bbb", "a"), Direction.DESC, true), + ) + // format: on + + it should "check if a list is ordered for numbers" in { + forAll(intTable) { (fnsToTest, input, dir, res) => + fnsToTest.foreach { (name, fn) => + withClue(s"For function with name: ${name}") { + fn(input, dir) shouldBe res + } + } + + } + } + + it should "check if a list of strings are ordered" in { + forAll(stringTable) { (name, fn, input, dir, res) => + withClue(s"For function with name: ${name}") { + fn(input, dir) shouldBe res + } + } + } + +} diff --git a/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/tastyfiles/PowerMacroTest.scala b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/tastyfiles/PowerMacroTest.scala new file mode 100644 index 000000000..fa5093073 --- /dev/null +++ b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/tastyfiles/PowerMacroTest.scala @@ -0,0 +1,15 @@ +package com.baeldung.scala.tastyfiles + +import com.baeldung.scala.tastyfiles.* + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class PowerMacroUnitTest extends AnyFlatSpec with Matchers: + + "PowerMacro.showAsPowerTerm" should "generate compile-time term structure" in: + val expr: String = PowerMacro.showAsPowerTerm(2.0, 3) + + expr == """ + Inlined(Ident(PowerMacro$),List(),Apply(Select(Inlined(EmptyTree,List(),Inlined(EmptyTree,List(),Literal(Constant(2.0)))),*),List(Inlined(EmptyTree,List(),Inlined(Ident(PowerMacro$),List(),Apply(Select(Inlined(EmptyTree,List(),Inlined(EmptyTree,List(),Literal(Constant(2.0)))),*),List(Inlined(EmptyTree,List(),Inlined(EmptyTree,List(),Literal(Constant(2.0))))))))))) + """.trim diff --git a/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/twoscomplement/TwosComplementUnitTest.scala b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/twoscomplement/TwosComplementUnitTest.scala new file mode 100644 index 000000000..f7467e0e2 --- /dev/null +++ b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/twoscomplement/TwosComplementUnitTest.scala @@ -0,0 +1,31 @@ +package com.baeldung.scala.twoscomplement + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class TwosComplementUnitTest extends AnyFlatSpec with Matchers { + + val testExamples = List( + ("00110100", "11001100"), + ("00110101", "11001011"), + ("01111101", "10000011"), + ("00000000", "00000000"), + ("01111111", "10000001") + ) + + "convertWithFold" should "convert binary to twos complement" in { + val comparer = compare(TwosComplement.convertWithFold) + comparer(testExamples) + } + + "convertUsingRecursion" should "convert binary to twos complement" in { + val comparer = compare(TwosComplement.convertUsingRecursion) + comparer(testExamples) + } + + private def compare( + fn: String => String + )(tests: List[(String, String)]): Unit = { + tests.foreach((input, expected) => fn(input) shouldBe expected) + } +} diff --git a/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/uniformcase/UniformCaseCheckerUnitTest.scala b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/uniformcase/UniformCaseCheckerUnitTest.scala new file mode 100644 index 000000000..815399146 --- /dev/null +++ b/scala-core-modules/scala-core-9/src/test/scala/com/baeldung/scala/uniformcase/UniformCaseCheckerUnitTest.scala @@ -0,0 +1,34 @@ +package com.baeldung.scala.uniformcase + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +import UniformCaseChecker.* + +class UniformCaseCheckerUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + private val fns = + Seq(convertAndCheck, isUpperLowerAndForAll, regexCheck, countAndCheck) + + private val table = Table( + ("Input", "Expected Result"), + ("BAELDUNG @ 2024", true), + ("baeldung @ 2024", true), + ("Baeldung @ 2024", false), + ("2024 @@@ ", true), + (" ", true) + ) + + it should "check if all characters are upper or lower" in { + fns foreach { fn => + forAll(table) { (input, expected) => + withClue("for string: " + input) { + fn(input) shouldBe expected + } + } + } + } +} diff --git a/scala-core-modules/scala-core-dates/README.md b/scala-core-modules/scala-core-dates/README.md new file mode 100644 index 000000000..bc3359747 --- /dev/null +++ b/scala-core-modules/scala-core-dates/README.md @@ -0,0 +1,11 @@ +## Core Scala + +This module contains articles about Scala's date features. + +### Relevant Articles: + +- [Working With Dates and Times in Scala](https://www.baeldung.com/scala/date-time) +- [Guide to Scala Duration and FiniteDuration](https://www.baeldung.com/scala/duration-finiteduration) +- [Get Difference Between Two Dates](https://www.baeldung.com/scala/difference-between-two-dates) +- [Convert a String to Date in Scala](https://www.baeldung.com/scala/string-to-date) +- [Generate List of Dates Between a Range in Scala](https://www.baeldung.com/scala/date-range-list) diff --git a/scala-core-8/src/main/resources/application.conf b/scala-core-modules/scala-core-dates/src/main/resources/application.conf similarity index 100% rename from scala-core-8/src/main/resources/application.conf rename to scala-core-modules/scala-core-dates/src/main/resources/application.conf diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/date/DateParser.scala b/scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/date/DateParser.scala similarity index 96% rename from scala-libraries-2/src/main/scala-2/com/baeldung/date/DateParser.scala rename to scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/date/DateParser.scala index 9a4e66feb..1e52621fe 100644 --- a/scala-libraries-2/src/main/scala-2/com/baeldung/date/DateParser.scala +++ b/scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/date/DateParser.scala @@ -1,4 +1,4 @@ -package com.baeldung.date +package com.baeldung.scala.date import java.util.GregorianCalendar import scala.util.Try diff --git a/scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/dates/DateListGenerator.scala b/scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/dates/DateListGenerator.scala new file mode 100644 index 000000000..cb7805e88 --- /dev/null +++ b/scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/dates/DateListGenerator.scala @@ -0,0 +1,69 @@ +package com.baeldung.scala.dates + +import java.time.LocalDate +import java.time.temporal.ChronoUnit +import scala.annotation.tailrec + +object DateListGenerator { + + def recursiveDateList( + startDate: LocalDate, + endDate: LocalDate + ): List[LocalDate] = { + @tailrec + def findNextDate( + currentDate: LocalDate, + accDates: List[LocalDate] + ): List[LocalDate] = { + if (currentDate.isAfter(endDate)) { + accDates + } else { + findNextDate(currentDate.plusDays(1), accDates :+ currentDate) + } + } + findNextDate(startDate, Nil) + } + + def foldLeftDateList( + startDate: LocalDate, + endDate: LocalDate + ): List[LocalDate] = { + val noOfDays = ChronoUnit.DAYS.between(startDate, endDate) + 1 + (0 until noOfDays.toInt).foldLeft(List.empty[LocalDate]) { (acc, incr) => + acc :+ startDate.plusDays(incr) + } + } + + def iteratorDateList( + startDate: LocalDate, + endDate: LocalDate + ): List[LocalDate] = { + Iterator + .iterate(startDate)(_.plusDays(1)) + .takeWhile(!_.isAfter(endDate)) + .toList + } + + def tabulateDateList( + startDate: LocalDate, + endDate: LocalDate + ): List[LocalDate] = { + val noOfDays = ChronoUnit.DAYS.between(startDate, endDate) + 1 + List.tabulate(noOfDays.toInt)(startDate.plusDays(_)) + } + + def dateListEpochDays( + startDate: LocalDate, + endDate: LocalDate + ): List[LocalDate] = { + startDate.toEpochDay.to(endDate.toEpochDay).map(LocalDate.ofEpochDay).toList + } + + def dateListDaysBetween( + startDate: LocalDate, + endDate: LocalDate + ): List[LocalDate] = { + val noOfDays = ChronoUnit.DAYS.between(startDate, endDate) + 1 + (0 until noOfDays.toInt).map(startDate.plusDays(_)).toList + } +} diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/datesandtimes/JavaTime.scala b/scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/datesandtimes/JavaTime.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/datesandtimes/JavaTime.scala rename to scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/datesandtimes/JavaTime.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/datesandtimes/JavaUtilDate.scala b/scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/datesandtimes/JavaUtilDate.scala similarity index 76% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/datesandtimes/JavaUtilDate.scala rename to scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/datesandtimes/JavaUtilDate.scala index 7aaacd543..09e4b9cdc 100644 --- a/scala-core-5/src/main/scala-2/com/baeldung/scala/datesandtimes/JavaUtilDate.scala +++ b/scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/datesandtimes/JavaUtilDate.scala @@ -1,11 +1,12 @@ package com.baeldung.scala.datesandtimes import java.text.SimpleDateFormat import scala.concurrent.duration.Duration -import scala.concurrent.{Await, Future} +import scala.concurrent.{Await, ExecutionContext, Future} object JavaUtilDate extends App { - implicit val ec = scala.concurrent.ExecutionContext.Implicits.global + implicit val ec: ExecutionContext = + scala.concurrent.ExecutionContext.Implicits.global val format: ThreadLocal[SimpleDateFormat] = new ThreadLocal[SimpleDateFormat] { @@ -31,5 +32,5 @@ object JavaUtilDate extends App { val date1 = Await.result(r1, Duration.Inf) val date2 = Await.result(r2, Duration.Inf) - println(date1 + ", " + date2) // Prints "Wed Jul 01 2020, Fri Aug 21 2020" + println(s"$date1, $date2") // Prints "Wed Jul 01 2020, Fri Aug 21 2020" } diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/datesandtimes/JodaTime.scala b/scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/datesandtimes/JodaTime.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/datesandtimes/JodaTime.scala rename to scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/datesandtimes/JodaTime.scala diff --git a/scala-core-5/src/main/scala-2/com/baeldung/scala/datesandtimes/NScalaTime.scala b/scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/datesandtimes/NScalaTime.scala similarity index 100% rename from scala-core-5/src/main/scala-2/com/baeldung/scala/datesandtimes/NScalaTime.scala rename to scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/datesandtimes/NScalaTime.scala diff --git a/scala-core-8/src/main/scala-2/com/baeldung/scala/differencedates/DifferenceBetweenDates.scala b/scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/differencedates/DifferenceBetweenDates.scala similarity index 100% rename from scala-core-8/src/main/scala-2/com/baeldung/scala/differencedates/DifferenceBetweenDates.scala rename to scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/differencedates/DifferenceBetweenDates.scala diff --git a/scala-core-8/src/main/scala-2/com/baeldung/scala/duration/JavaToScalaDuration.scala b/scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/duration/JavaToScalaDuration.scala similarity index 100% rename from scala-core-8/src/main/scala-2/com/baeldung/scala/duration/JavaToScalaDuration.scala rename to scala-core-modules/scala-core-dates/src/main/scala/com/baeldung/scala/duration/JavaToScalaDuration.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/date/DateParserSpec.scala b/scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/date/DateParserUnitTest.scala similarity index 72% rename from scala-libraries-2/src/test/scala-2/com/baeldung/date/DateParserSpec.scala rename to scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/date/DateParserUnitTest.scala index c3ca11b0c..e0a73aa7b 100644 --- a/scala-libraries-2/src/test/scala-2/com/baeldung/date/DateParserSpec.scala +++ b/scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/date/DateParserUnitTest.scala @@ -1,14 +1,15 @@ -package com.baeldung.date +package com.baeldung.scala.date import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import java.time.format.DateTimeFormatter import java.time.temporal.ChronoField -import java.time.{ZoneId, ZonedDateTime} +import java.time.{LocalDate, LocalDateTime, ZoneId, ZonedDateTime} import java.util.Calendar import scala.util.Try -class DateParserSpec extends AnyWordSpec with Matchers { +class DateParserUnitTest extends AnyWordSpec with Matchers { val parser = new DateParser "a simple parser" should { @@ -55,11 +56,13 @@ class DateParserSpec extends AnyWordSpec with Matchers { val attemptedParse = Try(ZonedDateTime.parse("2022-02-14T20:30:00.00Z[Europe/Paris]")) assert(attemptedParse.isSuccess) + // This parsing is different in JDK 8 and 11 due to a bug in JDK 8 + // https://bugs.openjdk.org/browse/JDK-8066982 val zdt = attemptedParse.get assert(zdt.get(ChronoField.YEAR) == 2022) assert(zdt.get(ChronoField.MONTH_OF_YEAR) == 2) assert(zdt.get(ChronoField.DAY_OF_MONTH) == 14) - assert(zdt.get(ChronoField.HOUR_OF_DAY) == 20) + assert(zdt.get(ChronoField.HOUR_OF_DAY) == 21) // 21 due to timezone DST assert(zdt.get(ChronoField.MINUTE_OF_HOUR) == 30) assert(zdt.getZone == ZoneId.of("Europe/Paris")) } @@ -71,5 +74,21 @@ class DateParserSpec extends AnyWordSpec with Matchers { attemptedParse.failed.get.getMessage.contains("could not be parsed") ) } + + "parse ISO date using java.time" in { + val dateStr = "2024-09-19" + LocalDate.parse(dateStr) shouldBe LocalDate.of(2024, 9, 19) + } + "parse non ISO date string" in { + val dateStr = "19.09.2024" + val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") + LocalDate.parse(dateStr, formatter) shouldBe LocalDate.of(2024, 9, 19) + } + "parse datetime using java.time" in { + val dateStr = "19.09.2024 10:20:30" + val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss") + LocalDateTime.parse(dateStr, formatter) shouldBe LocalDateTime.of(2024, 9, + 19, 10, 20, 30) + } } } diff --git a/scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/dates/DateListGeneratorUnitTest.scala b/scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/dates/DateListGeneratorUnitTest.scala new file mode 100644 index 000000000..2cae444b6 --- /dev/null +++ b/scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/dates/DateListGeneratorUnitTest.scala @@ -0,0 +1,71 @@ +package com.baeldung.scala.dates + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks + +import java.time.LocalDate + +class DateListGeneratorUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + private val start = LocalDate.parse("2023-10-28") + private val end = LocalDate.parse("2023-11-03") + + private val expectedResult = List( + "2023-10-28", + "2023-10-29", + "2023-10-30", + "2023-10-31", + "2023-11-01", + "2023-11-02", + "2023-11-03" + ).map(LocalDate.parse) + + private val inOutTable = Table( + ("startDate", "endDate", "expectedOutput", "message"), + (start, end, expectedResult, "1 week"), + (start, start, List(start), "start and end as start"), + (end, end, List(end), "start and end as end"), + (start, start.minusDays(1), List.empty, "end before start") + ) + + forAll(inOutTable) { (startDate, endDate, expectedDates, prefix) => + + it should s"[${prefix}] generate a list of dates between a range using recursion" in { + val recDateList = DateListGenerator.recursiveDateList(startDate, endDate) + assert(recDateList == expectedDates) + } + + it should s"[${prefix}] generate a list of dates between a range using foldLeft" in { + val foldedDateList = + DateListGenerator.foldLeftDateList(startDate, endDate) + assert(foldedDateList == expectedDates) + } + + it should s"[${prefix}] generate a list of dates between a range using iterator" in { + val iteratorDates = DateListGenerator.iteratorDateList(startDate, endDate) + assert(iteratorDates == expectedDates) + } + + it should s"[${prefix}] generate a list of dates between a range using tabulator" in { + val tabulateDateList = + DateListGenerator.tabulateDateList(startDate, endDate) + assert(tabulateDateList == expectedDates) + } + + it should s"[${prefix}] generate a list of dates between a range using epoch days" in { + val datesEpoch = DateListGenerator.dateListEpochDays(startDate, endDate) + assert(datesEpoch == expectedDates) + } + + it should s"[${prefix}] generate a list of dates between a range using daysBetween" in { + val dates = DateListGenerator.dateListDaysBetween(startDate, endDate) + assert(dates == expectedDates) + } + + } + +} diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/datesandtimes/JavaTimeUnitTest.scala b/scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/datesandtimes/JavaTimeUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/datesandtimes/JavaTimeUnitTest.scala rename to scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/datesandtimes/JavaTimeUnitTest.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/datesandtimes/JodaTimeUnitTest.scala b/scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/datesandtimes/JodaTimeUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/datesandtimes/JodaTimeUnitTest.scala rename to scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/datesandtimes/JodaTimeUnitTest.scala diff --git a/scala-core-5/src/test/scala-2/com/baeldung/scala/datesandtimes/NScalaTimeUnitTest.scala b/scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/datesandtimes/NScalaTimeUnitTest.scala similarity index 100% rename from scala-core-5/src/test/scala-2/com/baeldung/scala/datesandtimes/NScalaTimeUnitTest.scala rename to scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/datesandtimes/NScalaTimeUnitTest.scala diff --git a/scala-core-8/src/test/scala-2/com/baeldung/scala/differencedates/DifferenceBetweenDatesTest.scala b/scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/differencedates/DifferenceBetweenDatesUnitTest.scala similarity index 94% rename from scala-core-8/src/test/scala-2/com/baeldung/scala/differencedates/DifferenceBetweenDatesTest.scala rename to scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/differencedates/DifferenceBetweenDatesUnitTest.scala index a95f823f8..df7e726c7 100644 --- a/scala-core-8/src/test/scala-2/com/baeldung/scala/differencedates/DifferenceBetweenDatesTest.scala +++ b/scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/differencedates/DifferenceBetweenDatesUnitTest.scala @@ -1,13 +1,13 @@ package com.baeldung.scala.differencedates import org.scalatest.wordspec.AnyWordSpec -import com.baeldung.scala.differencedates.DifferenceBetweenDates._ import org.scalatest.matchers.should.Matchers import java.time.temporal.ChronoUnit import java.time.{LocalDate, Period} +import DifferenceBetweenDates._ -class DifferenceBetweenDatesTest extends AnyWordSpec with Matchers { +class DifferenceBetweenDatesUnitTest extends AnyWordSpec with Matchers { val testDate1 = LocalDate.parse("2020-01-01") val testDate2 = LocalDate.parse("2020-01-04") diff --git a/scala-core-8/src/test/scala-2/com/baeldung/scala/duration/DurationTest.scala b/scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/duration/DurationUnitTest.scala similarity index 96% rename from scala-core-8/src/test/scala-2/com/baeldung/scala/duration/DurationTest.scala rename to scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/duration/DurationUnitTest.scala index 39731b144..61182c523 100644 --- a/scala-core-8/src/test/scala-2/com/baeldung/scala/duration/DurationTest.scala +++ b/scala-core-modules/scala-core-dates/src/test/scala/com/baeldung/scala/duration/DurationUnitTest.scala @@ -1,19 +1,18 @@ package com.baeldung.scala.duration +import com.baeldung.scala.duration.JavaToScalaDuration.{ + asFiniteDuration, + asFiniteDurationFromConf +} import org.scalatest.funsuite.AnyFunSuite import java.time.Duration import java.time.temporal.ChronoUnit import java.util.concurrent.TimeUnit import scala.concurrent.duration.{FiniteDuration, HOURS} -import com.baeldung.scala.duration.JavaToScalaDuration.{ - asFiniteDuration, - asFiniteDurationFromConf -} - import scala.jdk.DurationConverters.JavaDurationOps -class DurationTest extends AnyFunSuite { +class DurationUnitTest extends AnyFunSuite { test("conversion from javaDuration using constructor") { val javaDuration = Duration.of(1, ChronoUnit.HOURS) diff --git a/scala-core-fp/README.md b/scala-core-modules/scala-core-fp/README.md similarity index 93% rename from scala-core-fp/README.md rename to scala-core-modules/scala-core-fp/README.md index 1d226835a..28bb09a32 100644 --- a/scala-core-fp/README.md +++ b/scala-core-modules/scala-core-fp/README.md @@ -14,3 +14,4 @@ This module contains articles about Scala's Functional Programming features - [Functors in Functional Programming](https://www.baeldung.com/scala/functors-functional-programming) - [Case Objects vs Enumerations in Scala](https://www.baeldung.com/scala/case-objects-vs-enumerations) - [Function Composition in Scala](https://www.baeldung.com/scala/function-composition) +- [Free Monads in Scala] https://www.baeldung.com/scala/free-monads-in-scala \ No newline at end of file diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/caseobjectsvsenums/CurrencyADT.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/caseobjectsvsenums/CurrencyADT.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/caseobjectsvsenums/CurrencyADT.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/caseobjectsvsenums/CurrencyADT.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/caseobjectsvsenums/CurrencyEnum.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/caseobjectsvsenums/CurrencyEnum.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/caseobjectsvsenums/CurrencyEnum.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/caseobjectsvsenums/CurrencyEnum.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/firstclassfunctions/AnonymousFunctionInHOF.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/firstclassfunctions/AnonymousFunctionInHOF.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/firstclassfunctions/AnonymousFunctionInHOF.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/firstclassfunctions/AnonymousFunctionInHOF.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/firstclassfunctions/Closure.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/firstclassfunctions/Closure.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/firstclassfunctions/Closure.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/firstclassfunctions/Closure.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/firstclassfunctions/Currying.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/firstclassfunctions/Currying.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/firstclassfunctions/Currying.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/firstclassfunctions/Currying.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/firstclassfunctions/FunctionAsArgumentInHOF.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/firstclassfunctions/FunctionAsArgumentInHOF.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/firstclassfunctions/FunctionAsArgumentInHOF.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/firstclassfunctions/FunctionAsArgumentInHOF.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/firstclassfunctions/FunctionAsResultInHOF.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/firstclassfunctions/FunctionAsResultInHOF.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/firstclassfunctions/FunctionAsResultInHOF.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/firstclassfunctions/FunctionAsResultInHOF.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/firstclassfunctions/PartiallyAppliedFunction.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/firstclassfunctions/PartiallyAppliedFunction.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/firstclassfunctions/PartiallyAppliedFunction.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/firstclassfunctions/PartiallyAppliedFunction.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/foldvsreduce/FoldLeftVsReduceLeft.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/foldvsreduce/FoldLeftVsReduceLeft.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/foldvsreduce/FoldLeftVsReduceLeft.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/foldvsreduce/FoldLeftVsReduceLeft.scala diff --git a/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/freemonad/FreeMonads.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/freemonad/FreeMonads.scala new file mode 100644 index 000000000..6b8be4575 --- /dev/null +++ b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/freemonad/FreeMonads.scala @@ -0,0 +1,203 @@ +package com.baeldung.scala.freemonad + +import scala.concurrent.{Future, ExecutionContext} +import scala.util.{Try, Success, Failure} +import scala.io.StdIn.readChar +import scala.reflect.* + +given ExecutionContext = ExecutionContext.Implicits.global + +trait Monad[F[_]]: + def flatMap[A, B](fa: F[A])(f: (A) => F[B]): F[B] + + def pure[A](a: A): F[A] + + def map[A, B](fa: F[A])(f: A => B): F[B] = + flatMap(fa)(a => pure(f(a))) + +// List composition example: + +lazy val listComposition = + for + number <- 0 to 9 + letter <- 'A' to 'Z' + yield s"$number$letter" + +// Which the compiler transforms to: + +lazy val desugaredListComposition = + (0 to 9).flatMap: number => + ('A' to 'Z').map: letter => + s"$number$letter" + +// A functor is simpler and less powerful than a monad: + +trait Functor[F[_]]: + def map[A, B](fa: F[A])(f: A => B): F[B] + +// A transformation between two higher-kinded types with the same type parameter: + +trait ~>[F[_], G[_]]: + def apply[A: Typeable](f: F[A]): G[A] + +// Free allows us to lift a functor with monadic composition as a data structure: + +sealed trait Free[F[_], A: Typeable]: + def map[B: Typeable](f: A => B): Free[F, B] = + FlatMap(this, (a: A) => Pure(f(a))) + def flatMap[B: Typeable](f: A => Free[F, B]): Free[F, B] = FlatMap(this, f) + + def foldMapAs[G[_]: Monad](using F ~> G): G[A] = this match + case Pure(value) => summon[Monad[G]].pure(value) + case FlatMap(sub, f) => + summon[Monad[G]] + .flatMap(sub.foldMapAs[G]): in => + f(in).foldMapAs[G] + case Suspend(s) => summon[F ~> G](s) + +final case class Pure[F[_], A: Typeable](value: A) extends Free[F, A] +final case class FlatMap[F[_], A: Typeable, B: Typeable]( + sub: Free[F, A], + f: A => Free[F, B] +) extends Free[F, B] +final case class Suspend[F[_], A: Typeable](s: F[A]) extends Free[F, A] + +// We define a non-monadic type: + +trait LazyCatchable[+A]: + def run(): Either[Catch, A] + +final class Lazy[A](value: => A) extends LazyCatchable[A]: + def run(): Either[Catch, A] = Try(value) match + case Success(value) => Right(value) + case Failure(e) => Left(Catch(e)) + +final case class Catch(e: Throwable) extends LazyCatchable[Nothing]: + def run(): Either[Catch, Nothing] = Left(this) + +// We can write monadic programs with it: + +lazy val sumProgram: Free[LazyCatchable, Int] = + for + a <- Suspend(Lazy(1)) + b <- Suspend(Lazy(2)) + result <- Pure(a + b) + yield result + +// Which is translated by the compiler to this: + +lazy val desugaredSumProgram = + FlatMap( + Suspend(Lazy(1)), + (num1: Int) => + FlatMap( + Suspend(Lazy(2)), + (num2: Int) => Pure(num1 + num2) + ) + ) + +// We provide a ~> to a Future: + +given LazyCatchable2Future: (LazyCatchable ~> Future) with + def apply[A: Typeable](f: LazyCatchable[A]): Future[A] = f match + case Catch(e) => Future.failed(e) + case lazyValue: Lazy[_] => + Future: + lazyValue.run() match + case Left(Catch(e)) => throw e + case Right(value: A @unchecked) => value + +// We define a Monad instance for Future: + +given FutureMonad: Monad[Future] with + def flatMap[A, B](fa: Future[A])(f: (A) => Future[B]): Future[B] = + fa.flatMap(f) + + def pure[A](a: A): Future[A] = Future(a) + + override def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f) + +// We can then convert our sumProgram to a Future: + +lazy val sumProgramFuture: Future[Int] = sumProgram.foldMapAs[Future](using + FutureMonad, + LazyCatchable2Future +) // Future computes to 3 + +// Let's consider a more advanced workflow DSL: + +enum WorkflowCommand: + case FeelInspiredToLearn + case LikeFriendlyEnvironments + case WantToHelpPeopleBuildConfidenceCoding + case JoinBaeldungAsAWriter + +// We can then define our logic: + +def command[C <: WorkflowCommand](c: => C): Free[LazyCatchable, C] = Suspend( + Lazy(c) +) + +lazy val joinBaeldungWorkflow: Free[LazyCatchable, WorkflowCommand] = + for + _ <- command(WorkflowCommand.FeelInspiredToLearn) + _ <- command(WorkflowCommand.LikeFriendlyEnvironments) + _ <- command(WorkflowCommand.WantToHelpPeopleBuildConfidenceCoding) + `reachOutToday!` <- Pure(WorkflowCommand.JoinBaeldungAsAWriter) + yield `reachOutToday!` + +// Then we define a translation to Future: + +given BaeldungWorkflowInterpreter: (LazyCatchable ~> Future) with + private def askQuestion(question: String, repeat: Boolean = false): Boolean = + if repeat then print(s"\nInvalid response: try again (y or n) ") + else print(s"\n$question (y or n) ") + + readChar() match + case 'y' | 'Y' => true + case 'n' | 'N' => false + case _ => askQuestion(question, true) + + private def step[C <: WorkflowCommand]( + question: String, + command: C, + error: String + ): Future[C] = Future: + if askQuestion(question) then command + else throw new Exception(error) + + def apply[A: Typeable](f: LazyCatchable[A]): Future[A] = f match + case Catch(e) => Future.failed(e) + case lazyCmd: Lazy[_] => + lazyCmd.run() match + case Left(Catch(e)) => Future.failed(e) + case Right(command: WorkflowCommand) => + command match + case WorkflowCommand.FeelInspiredToLearn => + step( + question = "Do you feel inspired to learn Scala?", + command = command, + error = + "Baeldung has tutorials for other technologies too, like Java." + ) + case WorkflowCommand.LikeFriendlyEnvironments => + step( + question = "Do you like friendly environments?", + command = command, + error = "Bye." + ) + case WorkflowCommand.WantToHelpPeopleBuildConfidenceCoding => + step( + question = + "Do you want to help people build confidence coding?", + command = command, + error = "Baeldung tutorials are reliable and informative." + ) + case WorkflowCommand.JoinBaeldungAsAWriter => + Future.successful(command) + case Right(misc) => Future.successful(misc) + +// The translation is then very simple and intuitive: + +lazy val joinBaeldung: Future[WorkflowCommand] = joinBaeldungWorkflow + .foldMapAs[Future](using FutureMonad, BaeldungWorkflowInterpreter) diff --git a/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/freemonad/InteractiveInterpretation.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/freemonad/InteractiveInterpretation.scala new file mode 100644 index 000000000..e92e6128f --- /dev/null +++ b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/freemonad/InteractiveInterpretation.scala @@ -0,0 +1,8 @@ +package com.baeldung.scala.freemonad + +// import scala.concurrent.Await +// import scala.concurrent.duration.* + +// @main def runWorkflow(): Unit = +// val result = Await.result(joinBaeldung, 100.seconds) +// println(result) diff --git a/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/freemonad/README.md b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/freemonad/README.md new file mode 100644 index 000000000..8084bd11b --- /dev/null +++ b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/freemonad/README.md @@ -0,0 +1,2 @@ +### Relevant Articles +- [Free Monads in Scala](https://www.baeldung.com/scala/free-monads) diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/monad/LazyMonad.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/monad/LazyMonad.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/monad/LazyMonad.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/monad/LazyMonad.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/monoid/ListMonoidInstance.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/monoid/ListMonoidInstance.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/monoid/ListMonoidInstance.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/monoid/ListMonoidInstance.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/monoid/MapMonoidInstance.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/monoid/MapMonoidInstance.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/monoid/MapMonoidInstance.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/monoid/MapMonoidInstance.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/monoid/Monoid.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/monoid/Monoid.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/monoid/Monoid.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/monoid/Monoid.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/tagless/TaglessFinal.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/tagless/TaglessFinal.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/tagless/TaglessFinal.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/tagless/TaglessFinal.scala diff --git a/scala-core-fp/src/main/scala-2/com/baeldung/scala/typeclasses/TypeClassExample.scala b/scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/typeclasses/TypeClassExample.scala similarity index 100% rename from scala-core-fp/src/main/scala-2/com/baeldung/scala/typeclasses/TypeClassExample.scala rename to scala-core-modules/scala-core-fp/src/main/scala/com/baeldung/scala/typeclasses/TypeClassExample.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/caseobjectsvsenums/CurrencyAdtUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/caseobjectsvsenums/CurrencyAdtUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/caseobjectsvsenums/CurrencyAdtUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/caseobjectsvsenums/CurrencyAdtUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/caseobjectsvsenums/CurrencyEnumUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/caseobjectsvsenums/CurrencyEnumUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/caseobjectsvsenums/CurrencyEnumUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/caseobjectsvsenums/CurrencyEnumUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/currying/CurryingUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/currying/CurryingUnitTest.scala similarity index 86% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/currying/CurryingUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/currying/CurryingUnitTest.scala index 52affb620..5272ada5f 100644 --- a/scala-core-fp/src/test/scala-2/com/baeldung/scala/currying/CurryingUnitTest.scala +++ b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/currying/CurryingUnitTest.scala @@ -6,7 +6,8 @@ import org.scalatest.matchers.should.Matchers class CurryingUnitTest extends Matchers { @Test - def givenMultipleArgumentsFunction_whenCurried_thenReturnCurriedFunction() { + def givenMultipleArgumentsFunction_whenCurried_thenReturnCurriedFunction() + : Unit = { val sum: (Int, Int) => Int = (x, y) => x + y val curriedSum: Int => Int => Int = sum.curried @@ -15,17 +16,19 @@ class CurryingUnitTest extends Matchers { } @Test - def givenMultipleArgumentsMethod_whenCurried_thenReturnCurriedFunction() { + def givenMultipleArgumentsMethod_whenCurried_thenReturnCurriedFunction() + : Unit = { def sum(x: Int, y: Int): Int = x + y - val curriedSum: Int => Int => Int = (sum _).curried + val curriedSum: Int => Int => Int = (sum).curried sum(1, 2) shouldBe 3 curriedSum(1)(2) shouldBe 3 } @Test - def givenMultipleArgumentListsMethod_whenCurried_thenReturnCurriedFunction() { + def givenMultipleArgumentListsMethod_whenCurried_thenReturnCurriedFunction() + : Unit = { def sum(x: Int)(y: Int): Int = x + y val curriedSum: Int => Int => Int = sum @@ -35,7 +38,8 @@ class CurryingUnitTest extends Matchers { } @Test - def givenCurriedFunction_whenPartialApplied_thenReturnLowerArityFunction() { + def givenCurriedFunction_whenPartialApplied_thenReturnLowerArityFunction() + : Unit = { val sum: Int => Int => Int = x => y => x + y val increment: Int => Int = sum(1) @@ -43,7 +47,8 @@ class CurryingUnitTest extends Matchers { } @Test - def givenMultipleArgumentListsMethod_whenPartialApplied_thenReturnLowerArityMethod() { + def givenMultipleArgumentListsMethod_whenPartialApplied_thenReturnLowerArityMethod() + : Unit = { def sum(x: Int)(y: Int): Int = x + y val increment: Int => Int = sum(1) @@ -52,7 +57,8 @@ class CurryingUnitTest extends Matchers { } @Test - def givenMultipleArgumentsFindMethod_whenCalled_thenPredicateFunctionNeedsExplicitType() { + def givenMultipleArgumentsFindMethod_whenCalled_thenPredicateFunctionNeedsExplicitType() + : Unit = { def find[A](xs: List[A], predicate: A => Boolean): Option[A] = { xs match { case Nil => None @@ -65,7 +71,8 @@ class CurryingUnitTest extends Matchers { } @Test - def givenMultipleArgumentListFindMethod_whenCalled_thenPredicateFunctionDoesNotNeedExplicitType() { + def givenMultipleArgumentListFindMethod_whenCalled_thenPredicateFunctionDoesNotNeedExplicitType() + : Unit = { def find[A](xs: List[A])(predicate: A => Boolean): Option[A] = { xs match { case Nil => None @@ -78,7 +85,8 @@ class CurryingUnitTest extends Matchers { } @Test - def givenGenericMultipleArgumentListSumMethod_whenPartialApplied_thenReturnSimpleMethods() { + def givenGenericMultipleArgumentListSumMethod_whenPartialApplied_thenReturnSimpleMethods() + : Unit = { def sumF(f: Int => Int)(x: Int, y: Int): Int = f(x) + f(y) val sum: (Int, Int) => Int = sumF(identity) val sumSquare: (Int, Int) => Int = sumF(x => x * x) @@ -92,7 +100,8 @@ class CurryingUnitTest extends Matchers { } @Test - def givenMultipleArgumentsFunction_whenUseItAsOneArgumentFunction_thenNeedsExplicitArgumentPassing() { + def givenMultipleArgumentsFunction_whenUseItAsOneArgumentFunction_thenNeedsExplicitArgumentPassing() + : Unit = { val sum: (Int, Int) => Int = (x, y) => x + y val numbers: List[Int] = List(1, 2, 3) @@ -100,7 +109,8 @@ class CurryingUnitTest extends Matchers { } @Test - def givenCurriedFunction_whenUseItAsOneArgumentFunction_thenDoesNotNeedExplicitArgumentPassing() { + def givenCurriedFunction_whenUseItAsOneArgumentFunction_thenDoesNotNeedExplicitArgumentPassing() + : Unit = { val curriedSum: Int => Int => Int = x => y => x + y val numbers: List[Int] = List(1, 2, 3) diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/firstclassfunctions/AnonymousFunctionInHOFUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/firstclassfunctions/AnonymousFunctionInHOFUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/firstclassfunctions/AnonymousFunctionInHOFUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/firstclassfunctions/AnonymousFunctionInHOFUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/firstclassfunctions/ClosureUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/firstclassfunctions/ClosureUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/firstclassfunctions/ClosureUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/firstclassfunctions/ClosureUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/firstclassfunctions/CurryingUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/firstclassfunctions/CurryingUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/firstclassfunctions/CurryingUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/firstclassfunctions/CurryingUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/firstclassfunctions/FunctionAsArgumentInHOFUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/firstclassfunctions/FunctionAsArgumentInHOFUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/firstclassfunctions/FunctionAsArgumentInHOFUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/firstclassfunctions/FunctionAsArgumentInHOFUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/firstclassfunctions/FunctionAsResultInHOFUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/firstclassfunctions/FunctionAsResultInHOFUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/firstclassfunctions/FunctionAsResultInHOFUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/firstclassfunctions/FunctionAsResultInHOFUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/firstclassfunctions/PartiallyAppliedFunctionUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/firstclassfunctions/PartiallyAppliedFunctionUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/firstclassfunctions/PartiallyAppliedFunctionUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/firstclassfunctions/PartiallyAppliedFunctionUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/foldvsreduce/FoldLeftVsReduceLeftUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/foldvsreduce/FoldLeftVsReduceLeftUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/foldvsreduce/FoldLeftVsReduceLeftUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/foldvsreduce/FoldLeftVsReduceLeftUnitTest.scala diff --git a/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/freemonad/FreeMonadUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/freemonad/FreeMonadUnitTest.scala new file mode 100644 index 000000000..82aed52b1 --- /dev/null +++ b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/freemonad/FreeMonadUnitTest.scala @@ -0,0 +1,32 @@ +package com.baeldung.scala.freemonad + +import com.baeldung.scala.freemonad.{*, given} + +import scala.concurrent.Future + +import org.scalatest.concurrent.ScalaFutures +import org.scalatest.freespec.AsyncFreeSpecLike +import org.scalatest.matchers.should.Matchers + +class FreeMonadUnitTest + extends AsyncFreeSpecLike + with Matchers + with ScalaFutures: + + "sumProgram should be transformed as a free structure into it's value using a proper monad" in: + sumProgram + .foldMapAs[Future](using FutureMonad, LazyCatchable2Future) + .map(_ shouldBe 3) + + "BaeldungWorkflowInterpreter should preserve logic on non-workflow types" in: + sumProgram + .foldMapAs[Future](using FutureMonad, BaeldungWorkflowInterpreter) + .map(_ shouldBe 3) + + "should demonstrate for-comprehension and flatMap/map equivalence" in: + listComposition shouldBe desugaredListComposition + + "joinBaeldungWorkflows spec should succeed" in: + joinBaeldungWorkflow + .foldMapAs[Future](using FutureMonad, LazyCatchable2Future) + .map(_ shouldBe WorkflowCommand.JoinBaeldungAsAWriter) diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/functioncomposition/FunctionCompositionTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/functioncomposition/FunctionCompositionUnitTest.scala similarity index 96% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/functioncomposition/FunctionCompositionTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/functioncomposition/FunctionCompositionUnitTest.scala index b27fb4001..8435dff96 100644 --- a/scala-core-fp/src/test/scala-2/com/baeldung/scala/functioncomposition/FunctionCompositionTest.scala +++ b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/functioncomposition/FunctionCompositionUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.functioncomposition import org.scalatest.wordspec.AnyWordSpec -class FunctionCompositionTest extends AnyWordSpec { +class FunctionCompositionUnitTest extends AnyWordSpec { "f andThen g" should { "apply first f and then g" in { diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/functor/FunctorUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/functor/FunctorUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/functor/FunctorUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/functor/FunctorUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/monad/LazyMonadUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/monad/LazyMonadUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/monad/LazyMonadUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/monad/LazyMonadUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/monoid/MapMonoidUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/monoid/MapMonoidUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/monoid/MapMonoidUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/monoid/MapMonoidUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/monoid/WordFrequencyCounterUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/monoid/WordFrequencyCounterUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/monoid/WordFrequencyCounterUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/monoid/WordFrequencyCounterUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/tagless/TaglessFinalUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/tagless/TaglessFinalUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/tagless/TaglessFinalUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/tagless/TaglessFinalUnitTest.scala diff --git a/scala-core-fp/src/test/scala-2/com/baeldung/scala/typeclasses/TypeClassExampleUnitTest.scala b/scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/typeclasses/TypeClassExampleUnitTest.scala similarity index 100% rename from scala-core-fp/src/test/scala-2/com/baeldung/scala/typeclasses/TypeClassExampleUnitTest.scala rename to scala-core-modules/scala-core-fp/src/test/scala/com/baeldung/scala/typeclasses/TypeClassExampleUnitTest.scala diff --git a/scala-core-io/README.md b/scala-core-modules/scala-core-io/README.md similarity index 100% rename from scala-core-io/README.md rename to scala-core-modules/scala-core-io/README.md diff --git a/scala-core-io/src/main/scala-2/com/baeldung/scala/FileDeletion.scala b/scala-core-modules/scala-core-io/src/main/scala/com/baeldung/scala/FileDeletion.scala similarity index 86% rename from scala-core-io/src/main/scala-2/com/baeldung/scala/FileDeletion.scala rename to scala-core-modules/scala-core-io/src/main/scala/com/baeldung/scala/FileDeletion.scala index 6c2a9ec91..e6312da3e 100644 --- a/scala-core-io/src/main/scala-2/com/baeldung/scala/FileDeletion.scala +++ b/scala-core-modules/scala-core-io/src/main/scala/com/baeldung/scala/FileDeletion.scala @@ -3,15 +3,8 @@ package com.baeldung.scala import java.io.{File, IOException} import java.nio.file.attribute.BasicFileAttributes import java.nio.file.{FileVisitResult, Files, Path, SimpleFileVisitor} -import scala.reflect.io.Directory object FileDeletion { - - def deletePureScalaDeletion(path: String): Unit = { - new Directory(new File(path)) - .deleteRecursively() - } - def deleteRecursively(file: File): Unit = { if (file.isDirectory) { file.listFiles.foreach(deleteRecursively) diff --git a/scala-core-io/src/test/resources/com.baeldung.scala.io/four_lines_string.txt b/scala-core-modules/scala-core-io/src/test/resources/com.baeldung.scala.io/four_lines_string.txt similarity index 100% rename from scala-core-io/src/test/resources/com.baeldung.scala.io/four_lines_string.txt rename to scala-core-modules/scala-core-io/src/test/resources/com.baeldung.scala.io/four_lines_string.txt diff --git a/scala-core-io/src/test/resources/com.baeldung.scala.io/one_line_string.txt b/scala-core-modules/scala-core-io/src/test/resources/com.baeldung.scala.io/one_line_string.txt similarity index 100% rename from scala-core-io/src/test/resources/com.baeldung.scala.io/one_line_string.txt rename to scala-core-modules/scala-core-io/src/test/resources/com.baeldung.scala.io/one_line_string.txt diff --git a/scala-core-io/src/test/scala-2/com/baeldung/scala/io/FileDeletionTest.scala b/scala-core-modules/scala-core-io/src/test/scala/com/baeldung/scala/io/FileDeletionUnitTest.scala similarity index 86% rename from scala-core-io/src/test/scala-2/com/baeldung/scala/io/FileDeletionTest.scala rename to scala-core-modules/scala-core-io/src/test/scala/com/baeldung/scala/io/FileDeletionUnitTest.scala index 82356b131..0b4a4c341 100644 --- a/scala-core-io/src/test/scala-2/com/baeldung/scala/io/FileDeletionTest.scala +++ b/scala-core-modules/scala-core-io/src/test/scala/com/baeldung/scala/io/FileDeletionUnitTest.scala @@ -8,7 +8,7 @@ import org.scalatest.BeforeAndAfterAll import java.io.{File, FileWriter} import java.nio.file.{Files, Paths} -class FileDeletionTest +class FileDeletionUnitTest extends AnyFlatSpec with Matchers with BeforeAndAfterAll { @@ -35,11 +35,6 @@ class FileDeletionTest assert(!new File(path).exists()) } - "delete with pure scala" should "delete all directory" in { - FileDeletion.deletePureScalaDeletion(path) - assert(!new File(path).exists()) - } - "delete recursively" should "delete all directory" in { FileDeletion.deleteRecursively(new File(path)) assert(!new File(path).exists()) diff --git a/scala-core-io/src/test/scala-2/com/baeldung/scala/io/SourceUnitTest.scala b/scala-core-modules/scala-core-io/src/test/scala/com/baeldung/scala/io/SourceUnitTest.scala similarity index 100% rename from scala-core-io/src/test/scala-2/com/baeldung/scala/io/SourceUnitTest.scala rename to scala-core-modules/scala-core-io/src/test/scala/com/baeldung/scala/io/SourceUnitTest.scala diff --git a/scala-core-modules/scala-core-numbers-2/README.md b/scala-core-modules/scala-core-numbers-2/README.md new file mode 100644 index 000000000..c6b4331e7 --- /dev/null +++ b/scala-core-modules/scala-core-numbers-2/README.md @@ -0,0 +1 @@ +[Different Ways to Calculate the Sum of a List in Scala](https://www.baeldung.com/scala/list-sum-items) diff --git a/scala-core-modules/scala-core-numbers-2/src/main/scala/com/baeldung/scala/listsum/SumList.scala b/scala-core-modules/scala-core-numbers-2/src/main/scala/com/baeldung/scala/listsum/SumList.scala new file mode 100644 index 000000000..c87074752 --- /dev/null +++ b/scala-core-modules/scala-core-numbers-2/src/main/scala/com/baeldung/scala/listsum/SumList.scala @@ -0,0 +1,40 @@ +package com.baeldung.scala.listsum + +import scala.annotation.tailrec + +object SumList { + + def sum(list: List[Int]): Int = list.sum + + def sumByReduce(list: List[Int]): Int = list.reduceOption(_ + _).getOrElse(0) + + def sumByFold(list: List[Int]): Int = list.foldLeft(0)(_ + _) + + def sumByIteration(list: List[Int]): Int = { + var sum = 0 + list.foreach(num => sum += num) + sum + } + + def sumByFor(list: List[Int]): Int = { + var sum = 0 + for (num <- list) { sum += num } + sum + } + + def sumByTailRecursion(list: List[Int]): Int = { + @tailrec + def rec(list: List[Int], sum: Int): Int = { + list match { + case Nil => sum + case a :: tail => rec(tail, sum + a) + } + } + rec(list, 0) + } + + def sumNumeric[T](list: List[T])(using numeric: Numeric[T]): T = { + list.sum + } + +} diff --git a/scala-core-modules/scala-core-numbers-2/src/main/scala/com/baeldung/scala/roundingdecimals/RoundingDecimals.scala b/scala-core-modules/scala-core-numbers-2/src/main/scala/com/baeldung/scala/roundingdecimals/RoundingDecimals.scala new file mode 100644 index 000000000..68d090c50 --- /dev/null +++ b/scala-core-modules/scala-core-numbers-2/src/main/scala/com/baeldung/scala/roundingdecimals/RoundingDecimals.scala @@ -0,0 +1,12 @@ +package com.baeldung.scala.roundingdecimals + +object RoundingDecimals { + def roundUp(decimal: Double): Int = + math.ceil(decimal).toInt + + def roundDown(decimal: Double): Int = + math.floor(decimal).toInt + + def roundToNearestWhole(decimal: Double): Int = + math.round(decimal).toInt +} diff --git a/scala-core-modules/scala-core-numbers-2/src/test/scala/com/baeldung/scala/listsum/SumListUnitTest.scala b/scala-core-modules/scala-core-numbers-2/src/test/scala/com/baeldung/scala/listsum/SumListUnitTest.scala new file mode 100644 index 000000000..32c5c00ae --- /dev/null +++ b/scala-core-modules/scala-core-numbers-2/src/test/scala/com/baeldung/scala/listsum/SumListUnitTest.scala @@ -0,0 +1,57 @@ +package com.baeldung.scala.listsum + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks + +class SumListUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + private val table = Table( + ("list", "expected sum"), + (List(1, 2, 3, 4), 10), + (List(0), 0), + (List.empty[Int], 0) + ) + + private val fns = List( + ("sum()", SumList.sum), + ("sumByFold()", SumList.sumByFold), + ("sumByReduce()", SumList.sumByReduce), + ("sumByIteration()", SumList.sumByIteration), + ("sumByFor()", SumList.sumByFor), + ("sumByTailRecursion()", SumList.sumByTailRecursion) + ) + + it should "calculate sum for a list" in { + forAll(table) { (input, expected) => + fns map { (name, fn) => + withClue(s"sum using the function `${name}` ") { + fn(input) shouldBe expected + } + } + } + } + + it should "calculate sum for any numeric types" in { + val ints = List(1, 2, 3, 4) + SumList.sumNumeric(ints) shouldBe 10 + SumList.sumNumeric(List.empty[Int]) shouldBe 0 + + val doubles = List(1d, 2d, 3d, 4d) + SumList.sumNumeric(doubles) shouldBe 10d + + val bigints = List(BigInt(1), BigInt(2), BigInt(3), BigInt(4)) + SumList.sumNumeric(bigints) shouldBe BigInt(10) + + } + + it should "calculate the sum of inside fields" in { + case class Item(price: Int) + val items = List(Item(1), Item(2), Item(3), Item(4)) + items.foldLeft(0)((acc, item) => acc + item.price) shouldBe 10 + } + +} diff --git a/scala-core-modules/scala-core-numbers-2/src/test/scala/com/baeldung/scala/roundingdecimals/RoundingDecimalsUnitTest.scala b/scala-core-modules/scala-core-numbers-2/src/test/scala/com/baeldung/scala/roundingdecimals/RoundingDecimalsUnitTest.scala new file mode 100644 index 000000000..daecb8e71 --- /dev/null +++ b/scala-core-modules/scala-core-numbers-2/src/test/scala/com/baeldung/scala/roundingdecimals/RoundingDecimalsUnitTest.scala @@ -0,0 +1,29 @@ +package com.baeldung.scala.roundingdecimals + +import com.baeldung.scala.roundingdecimals.RoundingDecimals +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +class RoundingDecimalsUnitTest extends AnyWordSpec with Matchers { + + "RoundingDecimals" should { + "Always round literals down" in { + 3 / 4 shouldBe 0 + } + "Always round up in roundUp" in { + RoundingDecimals.roundUp(1.1) shouldBe 2 + RoundingDecimals.roundUp(1.5) shouldBe 2 + RoundingDecimals.roundUp(1.9) shouldBe 2 + } + "Always round down in roundDown" in { + RoundingDecimals.roundDown(1.1) shouldBe 1 + RoundingDecimals.roundDown(1.5) shouldBe 1 + RoundingDecimals.roundDown(1.9) shouldBe 1 + } + "Always round to nearest in roundDown" in { + RoundingDecimals.roundToNearestWhole(1.1) shouldBe 1 + RoundingDecimals.roundToNearestWhole(1.5) shouldBe 2 + RoundingDecimals.roundToNearestWhole(1.9) shouldBe 2 + } + } +} diff --git a/scala-core-modules/scala-core-numbers/README.md b/scala-core-modules/scala-core-numbers/README.md new file mode 100644 index 000000000..78d8d0e88 --- /dev/null +++ b/scala-core-modules/scala-core-numbers/README.md @@ -0,0 +1,9 @@ +### Relevant Articles: + +- [Check if a Number Is Magic Number in Scala](https://www.baeldung.com/scala/check-magic-number) +- [Check if a Number Is Prime in Scala](https://www.baeldung.com/scala/check-number-prime) +- [Generating the Fibonacci Sequence in Scala](https://www.baeldung.com/scala/generating-fibonacci-series) +- [Find Factorial of a Number in Scala](https://www.baeldung.com/scala/find-factorial) +- [Check if a Number Is a Power of Two in Scala](https://www.baeldung.com/scala/check-power-of-two) +- [Get the Individual Digits of a Number in Scala](https://www.baeldung.com/scala/number-extract-digits) +- [Convert a Number to Roman Numeral in Scala](https://www.baeldung.com/scala/convert-number-roman) diff --git a/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/digits/IndividualDigits.scala b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/digits/IndividualDigits.scala new file mode 100644 index 000000000..fe933c35d --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/digits/IndividualDigits.scala @@ -0,0 +1,31 @@ +package com.baeldung.scala.digits + +import scala.annotation.tailrec + +object IndividualDigits { + + def getDigitsByTailRecursion(num: Long): List[Int] = { + @tailrec + def rec(num: Long, digits: List[Int]): List[Int] = { + if (num < 10) { + num.toInt :: digits + } else { + rec((num / 10), (num % 10).toInt :: digits) + } + } + rec(num, Nil) + } + + def getDigitsByRecursion(num: Long): List[Int] = { + if (num < 10) { + List(num.toInt) + } else { + getDigitsByRecursion(num / 10) ++ List((num % 10).toInt) + } + } + + def getDigitsByToString(num: Long): List[Int] = { + num.toString.map(_.asDigit).toList + } + +} diff --git a/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/digits/RemoveDigits.scala b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/digits/RemoveDigits.scala new file mode 100644 index 000000000..41ea7bf3f --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/digits/RemoveDigits.scala @@ -0,0 +1,45 @@ +package com.baeldung.scala.digits + +object RemoveDigits { + + def removeFirstNDigits(num: Int, n: Int): Int = { + num.toString.drop(n).toInt + } + + def removeFirstNDigitsLimit(num: Int, n: Int): Int = { + val numString = num.toString + if (n >= numString.length) + 0 + else + numString.drop(n).toInt + } + + def removeFirstNDigitsPatternMatching(num: Int, n: Int): Int = { + val numString = num.toString + numString match { + case str if n >= str.length => + 0 + case str => + str.drop(n).toInt + } + } + + def removeFirstNDigitsNegative(num: Int, n: Int): Int = { + val numString = num.toString + if (num < 0) { + // For negative numbers, remove the first n digits but keep the negative sign + val absNumString = numString.drop(1) // Drop the negative sign + if (n >= absNumString.length) + 0 + else + ("-" + absNumString.drop(n)).toInt + } else { + // For positive numbers, apply the same logic as before + if (n >= numString.length) + 0 + else + numString.drop(n).toInt + } + } + +} diff --git a/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/factorial/FactorialCalculation.scala b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/factorial/FactorialCalculation.scala new file mode 100644 index 000000000..c05755587 --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/factorial/FactorialCalculation.scala @@ -0,0 +1,40 @@ +package com.baeldung.scala.factorial + +import scala.annotation.tailrec + +object FactorialCalculation { + + def factorialUsingRecursion(num: Int): BigInt = { + require(num >= 0, "Factorial is not defined for negative numbers") + if (num == 0) 1 + else num * factorialUsingRecursion(num - 1) + } + + def factorialUsingTailRecursion(num: Int): BigInt = { + require(num >= 0, "Factorial is not defined for negative numbers") + @tailrec + def fac(n: Int, acc: BigInt): BigInt = { + n match { + case 0 => acc + case num => fac(num - 1, acc * num) + } + } + fac(num, BigInt(1)) + } + + def factorialUsingProduct(num: Int): BigInt = { + require(num >= 0, "Factorial is not defined for negative numbers") + num match { + case 0 => BigInt(1) + case _ => (BigInt(1) to BigInt(num)).product + } + } + + def factorialUsingReduce(num: Int): BigInt = { + require(num >= 0, "Factorial is not defined for negative numbers") + num match { + case 0 => BigInt(1) + case _ => (BigInt(1) to BigInt(num)).reduce(_ * _) + } + } +} diff --git a/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/fibonacci/FibonacciSequenceGenerator.scala b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/fibonacci/FibonacciSequenceGenerator.scala new file mode 100644 index 000000000..1287e9dee --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/fibonacci/FibonacciSequenceGenerator.scala @@ -0,0 +1,39 @@ +package com.baeldung.scala.fibonacci + +import scala.annotation.tailrec + +object FibonacciSequenceGenerator { + + def fibSequenceRecursion(sequenceSize: Int): Seq[Long] = { + def getNextNum(num: Long): Long = { + if (num <= 1) { + num + } else { + getNextNum(num - 1) + getNextNum(num - 2) + } + } + (0L until sequenceSize).map(getNextNum) + } + + def fibTailRec(sequenceSize: Int): Seq[Long] = { + @tailrec + def fib(n: Int, a: Long, b: Long, acc: List[Long]): List[Long] = { + if (n <= 0) acc + else fib(n - 1, b, a + b, acc :+ a) + } + fib(sequenceSize, 0L, 1, Nil) + } + + def fibLazyList(sequenceSize: Int): Seq[Long] = { + lazy val fib: LazyList[Long] = 0L #:: 1L #:: fib.zip(fib.tail).map(_ + _) + fib.take(sequenceSize).toList + } + + def fibIterator(sequenceSize: Int): List[Long] = { + Iterator + .iterate((0L, 1L)) { case (a, b) => (b, a + b) } + .map(_._1) + .take(sequenceSize) + .toList + } +} diff --git a/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/magicnumber/MagicNumber.scala b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/magicnumber/MagicNumber.scala new file mode 100644 index 000000000..6eba10a14 --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/magicnumber/MagicNumber.scala @@ -0,0 +1,57 @@ +package com.baeldung.scala.magicnumber + +import scala.annotation.tailrec + +object MagicNumber { + + def isMagicNumber(sumFn: Long => Long)(num: Long): Boolean = { + // Only positive numbers are used, so we take abs of any number. + // Instead of this, we could also add require condition + // require(num >= 0, "Only positive numbers are allowed!") + val sum = sumOfDigitsUsingRecursion(Math.abs(num)) + sum == 1 + } + + // Impl 1 + def sumOfDigitsUsingRecursion(num: Long): Long = { + + @tailrec + def recursiveSum(num: Long, sum: Long): Long = { + num match { + case 0 if sum < 10 => sum + case 0 if sum >= 10 => recursiveSum(sum / 10, sum % 10) + case n => recursiveSum(n / 10, sum + (num % 10)) + } + } + recursiveSum(num, 0) + } + + // Impl 2 + @tailrec + def sumOfDigitsUsingAsDigit(n: Long): Long = { + if (n < 10) { + // If the number is a single digit, return it + n + } else { + // Calculate the sum of digits recursively + val digitSum = n.toString.map(_.asDigit).sum + sumOfDigitsUsingAsDigit(digitSum) + } + } + + // Impl 3 + @tailrec + def sumOfDigitsUsingFold(num: Long): Long = { + def sum = num.toString.foldLeft(0)((acc, dig) => acc + dig.asDigit) + if (sum < 10) sum else sumOfDigitsUsingFold(sum) + } + + // Impl 4 + def sumOfDigitsUsingIterator(num: Long): Long = { + Iterator + .iterate(num)(currentNum => currentNum.toString.map(_.asDigit).sum) + .dropWhile(_ >= 10) + .next() + } + +} diff --git a/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/powerof2/PowerOfTwo.scala b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/powerof2/PowerOfTwo.scala new file mode 100644 index 000000000..0980bca65 --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/powerof2/PowerOfTwo.scala @@ -0,0 +1,29 @@ +package com.baeldung.scala.powerof2 + +import scala.annotation.tailrec + +object PowerOfTwo { + + def isPowerOfTwoByDivision(num: Long): Boolean = { + @tailrec + def isPowerRec(n: Long): Boolean = { + if (n == 1) true + else if (n % 2 == 0) isPowerRec(n / 2) + else false + } + num > 0 && isPowerRec(num) + } + + def isPowerOfTwoByCountingOnes(num: Long): Boolean = { + num > 0 && num.toBinaryString.count(_ == '1') == 1 + } + + def isPowerOfTwoByBitwiseAnd(num: Long): Boolean = { + num > 0 && (num & (num - 1)) == 0 + } + + def isPowerOfTwoByLazyList(num: Long): Boolean = { + num > 0 && LazyList.iterate(num)(_ / 2).takeWhile(_ > 1).forall(_ % 2 == 0) + } + +} diff --git a/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/primecheck/PrimeNumber.scala b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/primecheck/PrimeNumber.scala new file mode 100644 index 000000000..a366bf7d8 --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/primecheck/PrimeNumber.scala @@ -0,0 +1,26 @@ +package com.baeldung.scala.primecheck + +object PrimeNumber { + def isPrime(num: Long): Boolean = { + if (num <= 1) false + else if (num == 2 || num == 3) true + else + (2L to Math.sqrt(num).toLong).forall(num % _ != 0) + } + + def isPrimeOptimized(num: Long): Boolean = { + if (num < 2) false + else if (num == 2 || num == 3) true + else if (num % 2 == 0 || num % 3 == 0) false + else + (5 to Math.sqrt(num).toInt by 6).forall(n => + num % n != 0 && num % (n + 2) != 0 + ) + } + + def isPrimeUsingBigInt(num: Long): Boolean = { + if (num < 0) false + else + BigInt(num).isProbablePrime(certainty = 100) + } +} diff --git a/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/romannumerals/NumberToRomanNumeral.scala b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/romannumerals/NumberToRomanNumeral.scala new file mode 100644 index 000000000..186fe2ba4 --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/main/scala/com/baeldung/scala/romannumerals/NumberToRomanNumeral.scala @@ -0,0 +1,58 @@ +package com.baeldung.scala.romannumerals + +import scala.annotation.tailrec +import scala.math.BigDecimal.int2bigDecimal + +object NumberToRomanNumeral { + + private val numbersToRomans = List( + (1000, "M"), + (900, "CM"), + (500, "D"), + (400, "CD"), + (100, "C"), + (90, "XC"), + (50, "L"), + (40, "XL"), + (10, "X"), + (9, "IX"), + (5, "V"), + (4, "IV"), + (1, "I") + ) + + def usingRecursion(num: Int): String = { + numbersToRomans.find((n, _) => { + num - n >= 0 + }) match + case Some((n, r)) => r + usingRecursion(num - n) + case None => "" + } + + def usingTailRecursion(num: Int): String = { + @tailrec + def recursiveFn(remaining: Int, acc: String): String = { + numbersToRomans.find((n, _) => { + remaining - n >= 0 + }) match + case Some((n, r)) => recursiveFn(remaining - n, acc + r) + case None => acc + } + recursiveFn(num, "") + } + + def usingFold(num: Int): String = { + numbersToRomans + .foldLeft((num, ""))((remainingAndAcc, numAndRoman) => { + val (remaining, acc) = remainingAndAcc + if (remaining == 0) remainingAndAcc + else { + val (n, r) = numAndRoman + + (remaining % n, acc + r * (remaining / n)) + } + }) + ._2 + } + +} diff --git a/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/digits/IndividualDigitsUnitTest.scala b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/digits/IndividualDigitsUnitTest.scala new file mode 100644 index 000000000..1680fa1ae --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/digits/IndividualDigitsUnitTest.scala @@ -0,0 +1,40 @@ +package com.baeldung.scala.digits + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks + +class IndividualDigitsUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + private val table = Table( + ("Number", "Digits"), + (987654321L, List(9, 8, 7, 6, 5, 4, 3, 2, 1)), + (123456L, List(1, 2, 3, 4, 5, 6)), + (1L, List(1)), + (0L, List(0)) + ) + + it should "get individual digits of a number using getDigitsByTailRecursion" in { + forAll(table) { (number, digits) => + val result = IndividualDigits.getDigitsByTailRecursion(number) + result shouldBe digits + } + } + + it should "get individual digits of a number using getDigitsByToString" in { + forAll(table) { (number, digits) => + val result = IndividualDigits.getDigitsByToString(number) + result shouldBe digits + } + } + + it should "get individual digits of a number using getDigitsByRecursion" in { + forAll(table) { (number, digits) => + val result = IndividualDigits.getDigitsByRecursion(number) + result shouldBe digits + } + } +} diff --git a/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/digits/RemoveDigitsUnitTest.scala b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/digits/RemoveDigitsUnitTest.scala new file mode 100644 index 000000000..ee019b4a5 --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/digits/RemoveDigitsUnitTest.scala @@ -0,0 +1,35 @@ +package com.baeldung.scala.digits + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class RemoveDigitsUnitTest extends AnyFlatSpec with Matchers { + + val number = 123456 + val negativeNumber = -number + + "naive" should "return remove first N digits" in { + RemoveDigits.removeFirstNDigits(number, 2) shouldBe 3456 + RemoveDigits.removeFirstNDigits(number, 5) shouldBe 6 + } + + "limit" should "return remove first N digits" in { + RemoveDigits.removeFirstNDigitsLimit(number, 2) shouldBe 3456 + RemoveDigits.removeFirstNDigitsLimit(number, 5) shouldBe 6 + RemoveDigits.removeFirstNDigitsLimit(number, 6) shouldBe 0 + } + + "patternMatching" should "return remove first N digits" in { + RemoveDigits.removeFirstNDigitsPatternMatching(number, 2) shouldBe 3456 + RemoveDigits.removeFirstNDigitsPatternMatching(number, 5) shouldBe 6 + RemoveDigits.removeFirstNDigitsPatternMatching(number, 6) shouldBe 0 + } + + "negative" should "return remove first N digits" in { + RemoveDigits.removeFirstNDigitsNegative(number, 2) shouldBe 3456 + RemoveDigits.removeFirstNDigitsNegative(number, 5) shouldBe 6 + RemoveDigits.removeFirstNDigitsNegative(number, 6) shouldBe 0 + RemoveDigits.removeFirstNDigitsNegative(negativeNumber, 2) shouldBe -3456 + } + +} diff --git a/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/factorial/FactorialCalculationUnitTest.scala b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/factorial/FactorialCalculationUnitTest.scala new file mode 100644 index 000000000..4c81f912a --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/factorial/FactorialCalculationUnitTest.scala @@ -0,0 +1,37 @@ +package com.baeldung.scala.factorial + +import com.baeldung.scala.factorial.FactorialCalculation.* +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks + +class FactorialCalculationUnitTest + extends AnyFlatSpec + with TableDrivenPropertyChecks + with Matchers { + + private val fns = Seq( + ("recursion", factorialUsingRecursion), + ("tail recursion", factorialUsingTailRecursion), + ("product", factorialUsingProduct), + ("reduce", factorialUsingReduce) + ) + + private val table = Table( + ("Number", "Factorial"), + (0, BigInt(1)), + (1, BigInt(1)), + (2, BigInt(2)), + (8, BigInt(40320)), + (30, BigInt("265252859812191058636308480000000")) + ) + + fns.foreach { (name, fn) => + it should s"Calculate the factorial of a number using $name" in { + forAll(table) { (num, expectedFactorial) => + fn(num) shouldBe expectedFactorial + } + } + } + +} diff --git a/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/fibonacci/FibonacciSequenceGeneratorUnitTest.scala b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/fibonacci/FibonacciSequenceGeneratorUnitTest.scala new file mode 100644 index 000000000..21be19fcf --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/fibonacci/FibonacciSequenceGeneratorUnitTest.scala @@ -0,0 +1,39 @@ +package com.baeldung.scala.fibonacci + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +import FibonacciSequenceGenerator._ + +class FibonacciSequenceGeneratorUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + private val fns = List( + ("Recursion", fibSequenceRecursion), + ("Tail Recursion", fibTailRec), + ("LazyList", fibLazyList), + ("Iterator", fibIterator) + ) + + private val fibTables = Table( + ("fib limit", "sequence"), + (3, List(0L, 1L, 1L)), + (-5, List()), + (0, List()), + (1, List(0L)), + (2, List(0L, 1L)), + (6, List(0L, 1L, 1L, 2L, 3L, 5L)) + ) + + fns.foreach { (name, fn) => + it should s"[$name] generate fibonacci sequence correctly for each implementations" in { + forAll(fibTables) { (num, exp) => + withClue(s"Executing function ${name} for input $num") { + fn(num) shouldBe exp + } + } + } + } +} diff --git a/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/magicnumber/MagicNumberUnitTest.scala b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/magicnumber/MagicNumberUnitTest.scala new file mode 100644 index 000000000..e28aa266a --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/magicnumber/MagicNumberUnitTest.scala @@ -0,0 +1,46 @@ +package com.baeldung.scala.magicnumber + +import com.baeldung.scala.magicnumber.MagicNumber.* +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +import org.scalatest.wordspec.AnyWordSpec + +class MagicNumberUnitTest + extends AnyWordSpec + with Matchers + with TableDrivenPropertyChecks { + + private val sumFunctions = List( + sumOfDigitsUsingRecursion, + sumOfDigitsUsingAsDigit, + sumOfDigitsUsingFold, + sumOfDigitsUsingIterator + ) + private val table = Table( + ("number", "sum", "functions", "isMagicNumber"), + (100, 1, sumFunctions, true), + (99, 9, sumFunctions, false), + (0, 0, sumFunctions, false), + (1, 1, sumFunctions, true), + (1234, 1, sumFunctions, true), + (98765, 8, sumFunctions, false), + (Integer.MAX_VALUE, 1, sumFunctions, true) // 2147483647 + ) + + forAll(table) { (number, sum, sumFns, isMagic) => + "Sum of digit calculation method" should { + s"[No: $number] calculate the sum of digits till single digit correctly" in { + sumFns.map { fn => + fn(number) shouldBe sum + } + } + } + "Magic number checker" should { + s"[No: $number] return true/false if the input is a magic number" in { + sumFns.map { fn => + isMagicNumber(fn)(number) shouldBe isMagic + } + } + } + } +} diff --git a/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/powerof2/PowerOfTwoUnitTest.scala b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/powerof2/PowerOfTwoUnitTest.scala new file mode 100644 index 000000000..1043b27e7 --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/powerof2/PowerOfTwoUnitTest.scala @@ -0,0 +1,48 @@ +package com.baeldung.scala.powerof2 + +import org.scalacheck.Gen +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + +class PowerOfTwoUnitTest + extends AnyFlatSpec + with Matchers + with ScalaCheckPropertyChecks { + + private val powerOfTwoFunctions = Seq( + ("Division", PowerOfTwo.isPowerOfTwoByDivision), + ("Counting Ones", PowerOfTwo.isPowerOfTwoByCountingOnes), + ("Bitwise AND", PowerOfTwo.isPowerOfTwoByBitwiseAnd), + ("LazyList", PowerOfTwo.isPowerOfTwoByLazyList) + ) + + powerOfTwoFunctions.foreach { (desc, fn) => + it should s"[$desc] return true for a number that is power of 2" in { + val powersOfTwo: Gen[Long] = + Gen.choose(0, 62).map(n => Math.pow(2, n).toLong) + forAll(powersOfTwo) { num => + fn(num) shouldBe true + } + } + + it should s"[$desc] return false for a number that is NOT a power of 2" in { + val powersOfTwo: Gen[Long] = + Gen.choose(0, 62).map(n => Math.pow(2, n).toLong) + val notPowerOf2 = Gen + .choose(0L, Long.MaxValue) + .suchThat(n => !powersOfTwo.sample.contains(n)) + forAll(notPowerOf2) { num => + fn(num) shouldBe false + } + } + + it should s"[$desc] return false for any negative numbers" in { + val negativeNumbers = Gen.choose(Long.MinValue, 0L) + forAll(negativeNumbers) { num => + fn(num) shouldBe false + } + } + } + +} diff --git a/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/primecheck/PrimeCheckUnitTest.scala b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/primecheck/PrimeCheckUnitTest.scala new file mode 100644 index 000000000..6d2493868 --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/primecheck/PrimeCheckUnitTest.scala @@ -0,0 +1,49 @@ +package com.baeldung.scala.primecheck + +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +import org.scalatest.wordspec.AnyWordSpec +import com.baeldung.scala.primecheck.PrimeNumber.* + +class PrimeCheckUnitTest + extends AnyWordSpec + with Matchers + with TableDrivenPropertyChecks { + + private val primeTestTable = Table( + ("number", "status"), + (0L, false), + (1L, false), + (2L, true), + (3L, true), + (-5L, false), + (2L, true), + (13L, true), + (10L, false), + (4871L, true), + (4873L, false), + (Int.MaxValue.toLong, true), + (10_000_099_999L, true) + ) + + "Prime number checker" should { + "check if the provided bigint is prime" in { + forAll(primeTestTable) { (num, status) => + isPrimeUsingBigInt(num) shouldBe status + } + } + + "check if a number is prime using custom method" in { + forAll(primeTestTable) { (num, status) => + isPrime(num) shouldBe status + } + } + + "check if a number is prime using custom optimized method" in { + forAll(primeTestTable) { (num, status) => + isPrimeOptimized(num) shouldBe status + } + } + } + +} diff --git a/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/romannumeral/NumberToRomanNumeralUnitTest.scala b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/romannumeral/NumberToRomanNumeralUnitTest.scala new file mode 100644 index 000000000..10f0bcd4a --- /dev/null +++ b/scala-core-modules/scala-core-numbers/src/test/scala/com/baeldung/scala/romannumeral/NumberToRomanNumeralUnitTest.scala @@ -0,0 +1,61 @@ +package com.baeldung.scala.romannumeral + +import com.baeldung.scala.romannumerals.NumberToRomanNumeral +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +import org.scalatest.wordspec.AnyWordSpec + +class NumberToRomanNumeralUnitTest + extends AnyWordSpec + with Matchers + with TableDrivenPropertyChecks { + val testValuesWithResults = Table( + ("Number", "Roman"), + (0, ""), + (1, "I"), + (2, "II"), + (4, "IV"), + (5, "V"), + (6, "VI"), + (9, "IX"), + (10, "X"), + (11, "XI"), + (14, "XIV"), + (15, "XV"), + (17, "XVII"), + (19, "XIX"), + (20, "XX"), + (40, "XL"), + (44, "XLIV"), + (49, "XLIX"), + (50, "L"), + (90, "XC"), + (100, "C"), + (400, "CD"), + (500, "D"), + (900, "CM"), + (1000, "M"), + (1949, "MCMXLIX") + ) + + "Correction roman numeral should be returned for number" should { + "using usingRecursion" in { + forAll(testValuesWithResults)((num, roman) => { + NumberToRomanNumeral.usingRecursion(num) shouldBe roman + }) + } + + "using usingTailRecursion" in { + forAll(testValuesWithResults)((num, roman) => { + NumberToRomanNumeral.usingTailRecursion(num) shouldBe roman + }) + } + + "using usingFold" in { + forAll(testValuesWithResults)((num, roman) => { + NumberToRomanNumeral.usingFold(num) shouldBe roman + }) + } + } + +} diff --git a/scala-core-oop/README.md b/scala-core-modules/scala-core-oop/README.md similarity index 90% rename from scala-core-oop/README.md rename to scala-core-modules/scala-core-oop/README.md index 44d03093e..b80cf2db7 100644 --- a/scala-core-oop/README.md +++ b/scala-core-modules/scala-core-oop/README.md @@ -11,6 +11,5 @@ This module contains articles about Scala's Object Oriented Programming features - [Classes and Objects in Scala](https://www.baeldung.com/scala/classes-objects) - [Difference Between Case Object and Object](https://www.baeldung.com/scala/case-object-vs-object) - [Object Oriented Programming in Scala](https://www.baeldung.com/scala/oop-intro) -- [Demystifying View and Context Bounds](https://www.baeldung.com/scala/view-context-bounds) - [Polymorphism in Scala](https://www.baeldung.com/scala/polymorphism) - [Lifting in Scala](https://www.baeldung.com/scala/lifting) diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/caseobject/ObjectExample.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/caseobject/ObjectExample.scala similarity index 97% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/caseobject/ObjectExample.scala rename to scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/caseobject/ObjectExample.scala index d76267733..b4cbadc71 100644 --- a/scala-core-oop/src/main/scala-2/com/baeldung/scala/caseobject/ObjectExample.scala +++ b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/caseobject/ObjectExample.scala @@ -20,7 +20,7 @@ object ObjectExample extends App { } // Throws exception in case we call FlyingObject other than airplane and bird - def nonExhaustive(objects: FlyingObject.Value) { + def nonExhaustive(objects: FlyingObject.Value) = { objects match { case FlyingObject.airplane => println("I am an airplane") case FlyingObject.bird => println("I am a bird") diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/classcompositionwithtraits/CarTraits.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/classcompositionwithtraits/CarTraits.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/classcompositionwithtraits/CarTraits.scala rename to scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/classcompositionwithtraits/CarTraits.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/classvsobject/ClassExamples.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/classvsobject/ClassExamples.scala similarity index 96% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/classvsobject/ClassExamples.scala rename to scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/classvsobject/ClassExamples.scala index 759065295..32bf5ac98 100644 --- a/scala-core-oop/src/main/scala-2/com/baeldung/scala/classvsobject/ClassExamples.scala +++ b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/classvsobject/ClassExamples.scala @@ -5,15 +5,15 @@ object ClassExamples { val constA = "A" val constB = 4 class Abc(var a: String, var b: Int) { - def this(a: String) { + def this(a: String) = { this(a, constB) this.a = a } - def this(b: Int) { + def this(b: Int) = { this(constA, b) this.b = b } - def this() { + def this() = { this(constA, constB) } } diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/classvsobject/ObjectExamples.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/classvsobject/ObjectExamples.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/classvsobject/ObjectExamples.scala rename to scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/classvsobject/ObjectExamples.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/enumerations/Fingers.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/enumerations/Fingers.scala similarity index 77% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/enumerations/Fingers.scala rename to scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/enumerations/Fingers.scala index c75ae1c99..55b6242de 100644 --- a/scala-core-oop/src/main/scala-2/com/baeldung/scala/enumerations/Fingers.scala +++ b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/enumerations/Fingers.scala @@ -1,5 +1,7 @@ package com.baeldung.scala.enumerations +// This is Scala 2 based enumeration. It still works in Scala 3 for compatibility. +// However, this might become unsupported in future release. In scala 3, use the keyword `enum` object Fingers extends Enumeration { type Finger = Value diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/enumerations/FingersOperation.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/enumerations/FingersOperation.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/enumerations/FingersOperation.scala rename to scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/enumerations/FingersOperation.scala diff --git a/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/enumerations/scala3/Fingers.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/enumerations/scala3/Fingers.scala new file mode 100644 index 000000000..0547be82e --- /dev/null +++ b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/enumerations/scala3/Fingers.scala @@ -0,0 +1,10 @@ +package com.baeldung.scala.enumerations.scala3 + +enum Fingers(val height: Double) { + case Thumb extends Fingers(1) + case Index extends Fingers(4) + case Middle extends Fingers(4.1) + case Ring extends Fingers(3.2) + case Little extends Fingers(0.5) + def heightInCms(): Double = height * 2.54 +} diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/generics/GenericsIntro.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/generics/GenericsIntro.scala similarity index 97% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/generics/GenericsIntro.scala rename to scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/generics/GenericsIntro.scala index fcdb4481f..6429ad8dc 100644 --- a/scala-core-oop/src/main/scala-2/com/baeldung/scala/generics/GenericsIntro.scala +++ b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/generics/GenericsIntro.scala @@ -69,7 +69,7 @@ object GenericsIntro { } object NonGenericMethods { - def totalSize(list1: List[_], list2: List[_]): Int = + def totalSize(list1: List[?], list2: List[?]): Int = list1.length + list2.length def run() = { val rabbits = List[Rabbit](Rabbit(2), Rabbit(3), Rabbit(7)) diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/generics/Queue.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/generics/Queue.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/generics/Queue.scala rename to scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/generics/Queue.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/lifting/Examples.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/lifting/Examples.scala similarity index 96% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/lifting/Examples.scala rename to scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/lifting/Examples.scala index 3fae5ef3a..bcec58cc0 100644 --- a/scala-core-oop/src/main/scala-2/com/baeldung/scala/lifting/Examples.scala +++ b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/lifting/Examples.scala @@ -31,8 +31,8 @@ object Examples { def add5(x: Int) = x + 5 def isEven(x: Int) = x % 2 == 0 - val funcAdd5 = add5 _ - val funcIsEven = isEven _ + val funcAdd5 = add5 + val funcIsEven = isEven val sayHello: Future[Option[String]] = Future.successful(Some("Say hello to")) val firstname: Future[String] = Future.successful("Fabio") diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/oopinscala/OopConcepts.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/oopinscala/OopConcepts.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/oopinscala/OopConcepts.scala rename to scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/oopinscala/OopConcepts.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/oopinscala/polymorphism/PolymorphismExamples.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/oopinscala/polymorphism/PolymorphismExamples.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/oopinscala/polymorphism/PolymorphismExamples.scala rename to scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/oopinscala/polymorphism/PolymorphismExamples.scala diff --git a/scala-core-oop/src/main/scala-2/com/baeldung/scala/variance/Variance.scala b/scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/variance/Variance.scala similarity index 100% rename from scala-core-oop/src/main/scala-2/com/baeldung/scala/variance/Variance.scala rename to scala-core-modules/scala-core-oop/src/main/scala/com/baeldung/scala/variance/Variance.scala diff --git a/scala-core-oop/src/test/scala-2/com/baeldung/scala/caseobject/ObjectExampleUnitTest.scala b/scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/caseobject/ObjectExampleUnitTest.scala similarity index 100% rename from scala-core-oop/src/test/scala-2/com/baeldung/scala/caseobject/ObjectExampleUnitTest.scala rename to scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/caseobject/ObjectExampleUnitTest.scala diff --git a/scala-core-oop/src/test/scala-2/com/baeldung/scala/classcompositionwithtraits/CarTraitsUnitTest.scala b/scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/classcompositionwithtraits/CarTraitsUnitTest.scala similarity index 100% rename from scala-core-oop/src/test/scala-2/com/baeldung/scala/classcompositionwithtraits/CarTraitsUnitTest.scala rename to scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/classcompositionwithtraits/CarTraitsUnitTest.scala diff --git a/scala-core-oop/src/test/scala-2/com/baeldung/scala/classvsobject/ClassExamplesUnitTest.scala b/scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/classvsobject/ClassExamplesUnitTest.scala similarity index 100% rename from scala-core-oop/src/test/scala-2/com/baeldung/scala/classvsobject/ClassExamplesUnitTest.scala rename to scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/classvsobject/ClassExamplesUnitTest.scala diff --git a/scala-core-oop/src/test/scala-2/com/baeldung/scala/classvsobject/ObjectExamplesUnitTest.scala b/scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/classvsobject/ObjectExamplesUnitTest.scala similarity index 100% rename from scala-core-oop/src/test/scala-2/com/baeldung/scala/classvsobject/ObjectExamplesUnitTest.scala rename to scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/classvsobject/ObjectExamplesUnitTest.scala diff --git a/scala-core-oop/src/test/scala-2/com/baeldung/scala/enumerations/FingersOperationUnitTest.scala b/scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/enumerations/FingersOperationUnitTest.scala similarity index 100% rename from scala-core-oop/src/test/scala-2/com/baeldung/scala/enumerations/FingersOperationUnitTest.scala rename to scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/enumerations/FingersOperationUnitTest.scala diff --git a/scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/enumerations/scala3/FingersUnitTest.scala b/scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/enumerations/scala3/FingersUnitTest.scala new file mode 100644 index 000000000..34f772887 --- /dev/null +++ b/scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/enumerations/scala3/FingersUnitTest.scala @@ -0,0 +1,20 @@ +package com.baeldung.scala.enumerations.scala3 + +import org.junit.Test +import org.junit.Assert.assertTrue + +class FingersUnitTest { + + @Test + def givenAFinger_getTheHeight(): Unit = { + val Thumb = Fingers.Thumb + assertTrue(Thumb.height == 1d) + } + + @Test + def givenAFinger_getTheHeightInCm(): Unit = { + val Thumb = Fingers.Thumb + assertTrue(Thumb.heightInCms() == 2.54d) + } + +} diff --git a/scala-core-oop/src/test/scala-2/com/baeldung/scala/lifting/ExamplesUnitTest.scala b/scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/lifting/ExamplesUnitTest.scala similarity index 100% rename from scala-core-oop/src/test/scala-2/com/baeldung/scala/lifting/ExamplesUnitTest.scala rename to scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/lifting/ExamplesUnitTest.scala diff --git a/scala-core-oop/src/test/scala-2/com/baeldung/scala/oopinscala/OopConceptsUnitTest.scala b/scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/oopinscala/OopConceptsUnitTest.scala similarity index 100% rename from scala-core-oop/src/test/scala-2/com/baeldung/scala/oopinscala/OopConceptsUnitTest.scala rename to scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/oopinscala/OopConceptsUnitTest.scala diff --git a/scala-core-oop/src/test/scala-2/com/baeldung/scala/oopinscala/polymorphism/PolymorphismExamplesUnitTest.scala b/scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/oopinscala/polymorphism/PolymorphismExamplesUnitTest.scala similarity index 100% rename from scala-core-oop/src/test/scala-2/com/baeldung/scala/oopinscala/polymorphism/PolymorphismExamplesUnitTest.scala rename to scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/oopinscala/polymorphism/PolymorphismExamplesUnitTest.scala diff --git a/scala-core-oop/src/test/scala-2/com/baeldung/scala/variance/VarianceUnitTest.scala b/scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/variance/VarianceUnitTest.scala similarity index 100% rename from scala-core-oop/src/test/scala-2/com/baeldung/scala/variance/VarianceUnitTest.scala rename to scala-core-modules/scala-core-oop/src/test/scala/com/baeldung/scala/variance/VarianceUnitTest.scala diff --git a/scala-core/README.md b/scala-core-modules/scala-core/README.md similarity index 77% rename from scala-core/README.md rename to scala-core-modules/scala-core/README.md index 1cc7cca91..448b80317 100644 --- a/scala-core/README.md +++ b/scala-core-modules/scala-core/README.md @@ -4,13 +4,13 @@ This module contains articles about Scala's core features ### Relevant Articles: -- [Introduction to Scala](https://www.baeldung.com/scala/scala-intro) - [Higher-Order Functions in Scala](https://www.baeldung.com/scala/higher-order-functions) - [A Guide to Scala Tuples](https://www.baeldung.com/scala/tuples) - [Guide to lazy val in Scala](https://www.baeldung.com/scala/lazy-val) - [Partial Functions in Scala](https://www.baeldung.com/scala/partial-functions) - [For Loops in Scala](https://www.baeldung.com/scala/for-loops) - [Introduction to Traits in Scala](https://www.baeldung.com/scala/traits) -- [Exception Handling in Scala](https://www.baeldung.com/scala/exception-handling) - [Sealed Keyword in Scala](https://www.baeldung.com/scala/sealed-keyword) - [Pattern Matching in Scala](https://www.baeldung.com/scala/pattern-matching) +- [Using java.lang.class Objects in Scala](https://www.baeldung.com/scala/java-lang-class) +- [Handling Multiple Patterns with Scala Pattern Matching](https://www.baeldung.com/scala/multiple-pattern-matching) diff --git a/scala-core/build.sbt b/scala-core-modules/scala-core/build.sbt similarity index 100% rename from scala-core/build.sbt rename to scala-core-modules/scala-core/build.sbt diff --git a/scala-core-modules/scala-core/src/main/scala-3/com/baeldung/scala/patternmatching/MultipleMatches.scala b/scala-core-modules/scala-core/src/main/scala-3/com/baeldung/scala/patternmatching/MultipleMatches.scala new file mode 100644 index 000000000..f8f157ba8 --- /dev/null +++ b/scala-core-modules/scala-core/src/main/scala-3/com/baeldung/scala/patternmatching/MultipleMatches.scala @@ -0,0 +1,34 @@ +package com.baeldung.scala.patternmatching + +sealed trait Command +case object Start extends Command +case object Stop extends Command +case object Report extends Command +case class CustomCommand(cmd: String) extends Command + +def executeCommand(command: Command): String = command match { + case Start | CustomCommand("begin") => + "System Starting." + case Stop | CustomCommand("halt") => + "System Stopping." + case Report | CustomCommand("status") => + "Generating Report." + case _ => + "Unknown Command." +} + +def httpResponse(response: Int): String = response match { + case 200 | 201 | 202 => "Success" + case 400 | 404 | 500 => "Error" + case _ => "Unknown status" +} + +def multipleTypePatterns(obj: Any): String = obj match { + case _: String | _: Int => "It's either a String or an Int" + case _ => "It's something else" +} + +def unionTypePattern(obj: Any): String = obj match { + case _: (String | Int) => "It's either a String or an Int" + case _ => "It's something else" +} diff --git a/scala-core/src/main/scala-3/com/baeldung/scala/traits/Author.scala b/scala-core-modules/scala-core/src/main/scala-3/com/baeldung/scala/traits/Author.scala similarity index 100% rename from scala-core/src/main/scala-3/com/baeldung/scala/traits/Author.scala rename to scala-core-modules/scala-core/src/main/scala-3/com/baeldung/scala/traits/Author.scala diff --git a/scala-core/src/main/scala-3/com/baeldung/scala/traits/MultipleInheritance.scala b/scala-core-modules/scala-core/src/main/scala-3/com/baeldung/scala/traits/MultipleInheritance.scala similarity index 100% rename from scala-core/src/main/scala-3/com/baeldung/scala/traits/MultipleInheritance.scala rename to scala-core-modules/scala-core/src/main/scala-3/com/baeldung/scala/traits/MultipleInheritance.scala diff --git a/scala-core/src/main/scala-3/com/baeldung/scala/traits/Poet.scala b/scala-core-modules/scala-core/src/main/scala-3/com/baeldung/scala/traits/Poet.scala similarity index 100% rename from scala-core/src/main/scala-3/com/baeldung/scala/traits/Poet.scala rename to scala-core-modules/scala-core/src/main/scala-3/com/baeldung/scala/traits/Poet.scala diff --git a/scala-core/src/main/scala-3/com/baeldung/scala/traits/Writer.scala b/scala-core-modules/scala-core/src/main/scala-3/com/baeldung/scala/traits/Writer.scala similarity index 100% rename from scala-core/src/main/scala-3/com/baeldung/scala/traits/Writer.scala rename to scala-core-modules/scala-core/src/main/scala-3/com/baeldung/scala/traits/Writer.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/forloop/ForLoop.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/forloop/ForLoop.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/forloop/ForLoop.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/forloop/ForLoop.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/higherorder/HigherOrderFunctions.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/higherorder/HigherOrderFunctions.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/higherorder/HigherOrderFunctions.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/higherorder/HigherOrderFunctions.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/lazyval/LazyVal.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/lazyval/LazyVal.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/lazyval/LazyVal.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/lazyval/LazyVal.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/partialfunctions/SquareRoot.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/partialfunctions/SquareRoot.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/partialfunctions/SquareRoot.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/partialfunctions/SquareRoot.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/partialfunctions/SwapIntegerSign.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/partialfunctions/SwapIntegerSign.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/partialfunctions/SwapIntegerSign.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/partialfunctions/SwapIntegerSign.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/patternmatching/PatternMatching.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/patternmatching/PatternMatching.scala similarity index 91% rename from scala-core/src/main/scala-2/com/baeldung/scala/patternmatching/PatternMatching.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/patternmatching/PatternMatching.scala index 2769b27a1..68f8e20d7 100644 --- a/scala-core/src/main/scala-2/com/baeldung/scala/patternmatching/PatternMatching.scala +++ b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/patternmatching/PatternMatching.scala @@ -91,6 +91,17 @@ class PatternMatching { } } + def stringInterpolationMatching(toMatch: String): String = { + toMatch match { + case s"$firstName.$lastName@$domain.$extension" => + s"Hey ${firstName.capitalize} ${lastName.capitalize}, $domain.$extension is your email domain" + case s"$day-$month-${year}T$time" => s"$month $day, $year" + case s"$something($parenthesis)${_}" => + s"String between parenthesis: $parenthesis" + case _ => "unknown pattern" + } + } + def optionsPatternMatching(option: Option[String]): String = { option match { case Some(value) => s"I'm not an empty option. Value $value" diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/sealedkeyword/AlgebraicDataType.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/sealedkeyword/AlgebraicDataType.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/sealedkeyword/AlgebraicDataType.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/sealedkeyword/AlgebraicDataType.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/sealedkeyword/SealedCaseObjectsAsEnum.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/sealedkeyword/SealedCaseObjectsAsEnum.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/sealedkeyword/SealedCaseObjectsAsEnum.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/sealedkeyword/SealedCaseObjectsAsEnum.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/sealedkeyword/SealedClassExample.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/sealedkeyword/SealedClassExample.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/sealedkeyword/SealedClassExample.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/sealedkeyword/SealedClassExample.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/sealedkeyword/SelaedExtendedDifferentFile.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/sealedkeyword/SelaedExtendedDifferentFile.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/sealedkeyword/SelaedExtendedDifferentFile.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/sealedkeyword/SelaedExtendedDifferentFile.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/traits/Composition.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/Composition.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/traits/Composition.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/Composition.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/traits/Mixing.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/Mixing.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/traits/Mixing.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/Mixing.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/traits/MixingAlgorithm.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/MixingAlgorithm.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/traits/MixingAlgorithm.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/MixingAlgorithm.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/traits/Orchestration.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/Orchestration.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/traits/Orchestration.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/Orchestration.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/traits/RecordLabel.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/RecordLabel.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/traits/RecordLabel.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/RecordLabel.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/traits/Score.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/Score.scala similarity index 93% rename from scala-core/src/main/scala-2/com/baeldung/scala/traits/Score.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/Score.scala index f6580baa1..e400c9fbc 100644 --- a/scala-core/src/main/scala-2/com/baeldung/scala/traits/Score.scala +++ b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/Score.scala @@ -18,7 +18,7 @@ class Score( override def produce(): String = s"The score is produced by $engineer" - override def algorithm(): MixingAlgorithm = { + override def algorithm: MixingAlgorithm = { if (qualityRatio < 3) LowInstrumentalQuality else super.algorithm } diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/traits/SoundProduction.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/SoundProduction.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/traits/SoundProduction.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/SoundProduction.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/traits/Vocals.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/Vocals.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/traits/Vocals.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/traits/Vocals.scala diff --git a/scala-core/src/main/scala-2/com/baeldung/scala/tuples/Tuples.scala b/scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/tuples/Tuples.scala similarity index 100% rename from scala-core/src/main/scala-2/com/baeldung/scala/tuples/Tuples.scala rename to scala-core-modules/scala-core/src/main/scala/com/baeldung/scala/tuples/Tuples.scala diff --git a/scala-core-modules/scala-core/src/test/scala-3/com/baeldung/scala/patternmatching/MultipleMatchesUnitTest.scala b/scala-core-modules/scala-core/src/test/scala-3/com/baeldung/scala/patternmatching/MultipleMatchesUnitTest.scala new file mode 100644 index 000000000..1264fd75e --- /dev/null +++ b/scala-core-modules/scala-core/src/test/scala-3/com/baeldung/scala/patternmatching/MultipleMatchesUnitTest.scala @@ -0,0 +1,33 @@ +package com.baeldung.scala.patternmatching + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class MultipleMatchesUnitTest extends AnyFlatSpec with Matchers { + + "executeCommand" should "start the system when given the command" in { + val result = executeCommand(Start) + result shouldEqual "System Starting." + } + + it should "stop the system when given the command" in { + val result = executeCommand(CustomCommand("halt")) + result shouldEqual "System Stopping." + } + + "httpResponse" should "Error" in { + val result = httpResponse(404) + result shouldEqual "Error" + } + + "multipleTypePatterns" should "Error" in { + val result = multipleTypePatterns(4.4) + result shouldEqual "It's something else" + } + + "unionTypePattern" should "Error" in { + val result = unionTypePattern(42) + result shouldEqual "It's either a String or an Int" + } + +} diff --git a/scala-core/src/test/scala-3/com/baeldung/scala/traits/MultipleInheritanceUnitTest.scala b/scala-core-modules/scala-core/src/test/scala-3/com/baeldung/scala/traits/MultipleInheritanceUnitTest.scala similarity index 100% rename from scala-core/src/test/scala-3/com/baeldung/scala/traits/MultipleInheritanceUnitTest.scala rename to scala-core-modules/scala-core/src/test/scala-3/com/baeldung/scala/traits/MultipleInheritanceUnitTest.scala diff --git a/scala-core/src/test/scala-3/com/baeldung/scala/traits/TraitParametersUnitTest.scala b/scala-core-modules/scala-core/src/test/scala-3/com/baeldung/scala/traits/TraitParametersUnitTest.scala similarity index 100% rename from scala-core/src/test/scala-3/com/baeldung/scala/traits/TraitParametersUnitTest.scala rename to scala-core-modules/scala-core/src/test/scala-3/com/baeldung/scala/traits/TraitParametersUnitTest.scala diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/forloop/ForLoopUnitTest.scala b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/forloop/ForLoopUnitTest.scala similarity index 100% rename from scala-core/src/test/scala-2/com/baeldung/scala/forloop/ForLoopUnitTest.scala rename to scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/forloop/ForLoopUnitTest.scala diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/higherorder/HigherOrderFunctionsExamplesUnitTest.scala b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/higherorder/HigherOrderFunctionsExamplesUnitTest.scala similarity index 100% rename from scala-core/src/test/scala-2/com/baeldung/scala/higherorder/HigherOrderFunctionsExamplesUnitTest.scala rename to scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/higherorder/HigherOrderFunctionsExamplesUnitTest.scala diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/higherorder/HigherOrderFunctionsUnitTest.scala b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/higherorder/HigherOrderFunctionsUnitTest.scala similarity index 97% rename from scala-core/src/test/scala-2/com/baeldung/scala/higherorder/HigherOrderFunctionsUnitTest.scala rename to scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/higherorder/HigherOrderFunctionsUnitTest.scala index 11a966271..bf98ab67a 100644 --- a/scala-core/src/test/scala-2/com/baeldung/scala/higherorder/HigherOrderFunctionsUnitTest.scala +++ b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/higherorder/HigherOrderFunctionsUnitTest.scala @@ -40,7 +40,7 @@ class HigherOrderFunctionsUnitTest { // partial application of curried function // trailing underscore is required to make function type explicit - val sumMod5 = sum(mod(5)) _ + val sumMod5 = sum(mod(5)) assertEquals(10, sumMod5(6, 10)) } diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/lazyval/LazyValUnitTest.scala b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/lazyval/LazyValUnitTest.scala similarity index 100% rename from scala-core/src/test/scala-2/com/baeldung/scala/lazyval/LazyValUnitTest.scala rename to scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/lazyval/LazyValUnitTest.scala diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/partialfunctions/SquareRootUnitTest.scala b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/partialfunctions/SquareRootUnitTest.scala similarity index 100% rename from scala-core/src/test/scala-2/com/baeldung/scala/partialfunctions/SquareRootUnitTest.scala rename to scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/partialfunctions/SquareRootUnitTest.scala diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/partialfunctions/SwapIntegerSignUnitTest.scala b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/partialfunctions/SwapIntegerSignUnitTest.scala similarity index 100% rename from scala-core/src/test/scala-2/com/baeldung/scala/partialfunctions/SwapIntegerSignUnitTest.scala rename to scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/partialfunctions/SwapIntegerSignUnitTest.scala diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/patternmatching/PatternMatchingUnitTest.scala b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/patternmatching/PatternMatchingUnitTest.scala similarity index 88% rename from scala-core/src/test/scala-2/com/baeldung/scala/patternmatching/PatternMatchingUnitTest.scala rename to scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/patternmatching/PatternMatchingUnitTest.scala index 8a4791ed8..0dfae9c2b 100644 --- a/scala-core/src/test/scala-2/com/baeldung/scala/patternmatching/PatternMatchingUnitTest.scala +++ b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/patternmatching/PatternMatchingUnitTest.scala @@ -155,6 +155,46 @@ class PatternMatchingUnitTest { ) } + @Test + def whenEmailIsPassedWithComDomain_itShouldExtractParts(): Unit = { + val result = new PatternMatching().stringInterpolationMatching( + "james.kirk@starfleet.com" + ) + assertEquals("Hey James Kirk, starfleet.com is your email domain", result) + } + + @Test + def whenEmailIsPassedWithCoInDomain_itShouldExtractParts(): Unit = { + val result = new PatternMatching().stringInterpolationMatching( + "james.kirk@starfleet.co.in" + ) + assertEquals("Hey James Kirk, starfleet.co.in is your email domain", result) + } + + @Test + def whenDateTimeIsPassed_itShouldExtractDate(): Unit = { + val result = new PatternMatching().stringInterpolationMatching( + "01-April-2024T10:20:30" + ) + assertEquals("April 01, 2024", result) + } + + @Test + def whenStringIsPassed_itShouldExtractDataBetweenParenthesis(): Unit = { + val result = new PatternMatching().stringInterpolationMatching( + "Here is a (special) string" + ) + assertEquals("String between parenthesis: special", result) + } + + @Test + def whenUnknownDataIsPassed_itShouldReturnDefaultString(): Unit = { + val result = new PatternMatching().stringInterpolationMatching( + "something-unknown.unmatched" + ) + assertEquals("unknown pattern", result) + } + @Test def whenAFilledOptionIsPassed_ThenItShouldMatchTheSomeClause(): Unit = { val result = diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/traits/ScoreUnitTest.scala b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/traits/ScoreUnitTest.scala similarity index 94% rename from scala-core/src/test/scala-2/com/baeldung/scala/traits/ScoreUnitTest.scala rename to scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/traits/ScoreUnitTest.scala index 5ddea3678..2ab1a62ce 100644 --- a/scala-core/src/test/scala-2/com/baeldung/scala/traits/ScoreUnitTest.scala +++ b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/traits/ScoreUnitTest.scala @@ -46,7 +46,7 @@ class ScoreUnitTest { val studio = "Abbey Studios" val score = new Score(composer, engineer, orchestra, mixer, 1, studio) - assertEquals(score.algorithm().toString, "Low instrumental quality") + assertEquals(score.algorithm.toString, "Low instrumental quality") } @Test @@ -59,7 +59,7 @@ class ScoreUnitTest { val studio = "Abbey Studios" val score = new Score(composer, engineer, orchestra, mixer, 10, studio) - assertEquals(score.algorithm().toString, "High instrumental quality") + assertEquals(score.algorithm.toString, "High instrumental quality") } @Test diff --git a/scala-core/src/test/scala-2/com/baeldung/scala/tuples/TuplesUnitTest.scala b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/tuples/TuplesUnitTest.scala similarity index 77% rename from scala-core/src/test/scala-2/com/baeldung/scala/tuples/TuplesUnitTest.scala rename to scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/tuples/TuplesUnitTest.scala index 0558b7b51..22a66a3a0 100644 --- a/scala-core/src/test/scala-2/com/baeldung/scala/tuples/TuplesUnitTest.scala +++ b/scala-core-modules/scala-core/src/test/scala/com/baeldung/scala/tuples/TuplesUnitTest.scala @@ -16,8 +16,9 @@ class TuplesUnitTest extends AnyWordSpec with Matchers { name shouldBe "Joe" age shouldBe 34 } - "not contain more then 22 elements" in { - "(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)" shouldNot compile + // From Scala3 onwards, we can have more than 22 fields in tuple. + "allow more then 22 elements in Scala 3" in { + "(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)" should compile } } } diff --git a/scala-core-modules/scala-strings-2/README.md b/scala-core-modules/scala-strings-2/README.md new file mode 100644 index 000000000..38a047a7e --- /dev/null +++ b/scala-core-modules/scala-strings-2/README.md @@ -0,0 +1,8 @@ +## Core Scala Strings + +This module contains articles about Scala's Strings. + +### Relevant Articles: +- [How to Check if a String Is Null or Empty in Scala](https://www.baeldung.com/scala/string-test-null-empty) +- [Convert From Camel Case to Snake Case in Scala](https://www.baeldung.com/scala/camel-snake-case-automatic-conversion) +- [Convert Multiple Spaces Into a Single Space in Scala](https://www.baeldung.com/scala/multiple-spaces-to-single-space) diff --git a/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/cameltosnake/CamelToSnakeConversion.scala b/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/cameltosnake/CamelToSnakeConversion.scala new file mode 100644 index 000000000..33cd3d069 --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/cameltosnake/CamelToSnakeConversion.scala @@ -0,0 +1,65 @@ +package com.baeldung.scala.strings.cameltosnake + +import scala.annotation.tailrec + +object CamelToSnakeConversion { + + def usingRegex(camelCaseString: String): String = { + val regex = "([A-Z])".r + regex.replaceAllIn( + camelCaseString, + m => s"_${m.group(1).toLowerCase}" + ) + } + + def usingFoldLeft(camelCaseString: String): String = { + camelCaseString.foldLeft("") { (acc, char) => + if (char.isUpper) { + if (acc.isEmpty) char.toLower.toString + else acc + "_" + char.toLower + } else { + acc + char + } + } + } + + def handleAcronymsWithRegex(camelCaseString: String): String = { + camelCaseString + .replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2") + .replaceAll("([a-z\\d])([A-Z])", "$1_$2") + .toLowerCase + } + + def usingFlatMap(camelCase: String): String = { + camelCase + .flatMap { c => + if (c.isUpper) List('_', c) else List(c) + } + .mkString + .toLowerCase + } + + def usingPatternMatching(camelCase: String): String = { + @tailrec + def rec(chars: List[Char], acc: List[Char]): List[Char] = { + chars match { + case Nil => acc + case a :: tail if a.isUpper => rec(tail, acc ++ Seq('_', a)) + case a :: tail => rec(tail, acc ++ Seq(a)) + } + } + rec(camelCase.toList, Nil).mkString.toLowerCase + } + + def usingCollect(camelCase: String): String = { + camelCase + .collect { + case c if c.isUpper => Seq('_', c) + case c => Seq(c) + } + .flatten + .mkString + .toLowerCase + } + +} diff --git a/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/checkempty/EmptyPatternMatchingExtensions.scala b/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/checkempty/EmptyPatternMatchingExtensions.scala new file mode 100644 index 000000000..fcae999cc --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/checkempty/EmptyPatternMatchingExtensions.scala @@ -0,0 +1,25 @@ +package com.baeldung.scala.strings.checkempty + +object EmptyPatternMatchingExtensions { + extension (seq: Seq[?]) + def isNullOrEmpty: Boolean = seq match { + case null => true + case Seq() => true + case s => false + } + + extension (seq: Seq[Char]) + def isNullOrEmptyOrWhitespace: Boolean = seq match { + case null => true + case Seq() => true + case s => s.forall(_.isWhitespace) + } + + extension (str: String) + def isNullOrEmptyOrWhitespace: Boolean = str match { + case null => true + case "" => true + case s => s.trim.isEmpty + } + +} diff --git a/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/checkempty/EmptySeqExtensions.scala b/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/checkempty/EmptySeqExtensions.scala new file mode 100644 index 000000000..94a7cf1c8 --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/checkempty/EmptySeqExtensions.scala @@ -0,0 +1,10 @@ +package com.baeldung.scala.strings.checkempty + +object EmptySeqExtensions { + extension (objs: Seq[?]) + def isNullOrEmpty: Boolean = objs == null || objs.isEmpty + + extension (objs: Seq[Char]) + def isNullOrEmptyOrWhitespace: Boolean = + objs.isNullOrEmpty || objs.forall(_.isWhitespace) +} diff --git a/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/checkempty/EmptyStringExtensions.scala b/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/checkempty/EmptyStringExtensions.scala new file mode 100644 index 000000000..ac9f4cb9d --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/checkempty/EmptyStringExtensions.scala @@ -0,0 +1,8 @@ +package com.baeldung.scala.strings.checkempty + +object EmptyStringExtensions { + extension (str: String) + def isEmptyOrWhitespace: Boolean = str.trim.isEmpty + def isNullOrEmptyOrWhitespace: Boolean = + str == null || str.isEmptyOrWhitespace +} diff --git a/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/checkempty/VerifiedStringExtensions.scala b/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/checkempty/VerifiedStringExtensions.scala new file mode 100644 index 000000000..10a4bc205 --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/checkempty/VerifiedStringExtensions.scala @@ -0,0 +1,22 @@ +package com.baeldung.scala.strings.checkempty + +type Empty +type NonEmpty + +opaque type VerifiedString[T] = String + +object VerifiedString: + def apply[T](value: String): VerifiedString[T] = value + + given Conversion[VerifiedString[NonEmpty], String] = _.value + + extension (str: String) + def asVerifiedString = + if str.isNullOrEmptyOrWhitespace then VerifiedString[Empty](str) + else VerifiedString[NonEmpty](str) + + def isEmptyOrWhitespace: Boolean = str.trim.isEmpty + def isNullOrEmptyOrWhitespace: Boolean = + str == null || str.isEmptyOrWhitespace + + extension (vstr: VerifiedString[NonEmpty]) def value: String = vstr diff --git a/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/removemultispace/RemoveMultipleSpaces.scala b/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/removemultispace/RemoveMultipleSpaces.scala new file mode 100644 index 000000000..91aea6a0d --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/main/scala/com/baeldung/scala/strings/removemultispace/RemoveMultipleSpaces.scala @@ -0,0 +1,34 @@ +package com.baeldung.scala.strings.removemultispace + +object RemoveMultipleSpaces { + def usingReplaceAll(str: String): String = { + str.trim.replaceAll("\\s+", " ") + } + + def usingSplit(str: String): String = { + str.trim.split("\\s+").mkString(" ") + } + + def usingZip(str: String): String = { + if (str.trim.isEmpty) { + str.trim + } else { + val zipped = str.trim.zip(str.trim.tail) + str.trim.head + zipped.collect { + case (a, b) if !(a == ' ' && b == ' ') => b + }.mkString + } + } + + def usingStringBuilder(str: String): String = { + val sb = new StringBuilder + var lastCharWasSpace = false + + for (c <- str.trim) { + if (c != ' ' || !lastCharWasSpace) sb.append(c) + lastCharWasSpace = c == ' ' + } + sb.toString + } + +} diff --git a/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/cameltosnake/CamelToSnakeConversionUnitTest.scala b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/cameltosnake/CamelToSnakeConversionUnitTest.scala new file mode 100644 index 000000000..1d19b5521 --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/cameltosnake/CamelToSnakeConversionUnitTest.scala @@ -0,0 +1,59 @@ +package com.baeldung.scala.strings.cameltosnake + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks + +class CamelToSnakeConversionUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + private val table = Table( + ("input", "expected"), + ("thisIsCamelCase", "this_is_camel_case"), + ("isThisCamel?", "is_this_camel?"), + ("alllower", "alllower"), + ("thisIsUSA", "this_is_u_s_a"), + ("xmlHttpRequest", "xml_http_request"), + ("convertXMLToJSON", "convert_x_m_l_to_j_s_o_n"), + ("parseHTML", "parse_h_t_m_l"), + ("classOfT", "class_of_t") + ) + private val fns = Seq( + ("usingRegex", CamelToSnakeConversion.usingRegex), + ("usingFoldLeft", CamelToSnakeConversion.usingFoldLeft), + ("usingFlatMap", CamelToSnakeConversion.usingFlatMap), + ("usingPatterMatching", CamelToSnakeConversion.usingPatternMatching), + ("usingCollect", CamelToSnakeConversion.usingCollect) + ) + + it should "convert camel to snake case" in { + forAll(table) { (camel, expectedSnake) => + fns.map { (name, fn) => + withClue("function name: " + name) { + fn(camel) shouldBe expectedSnake + } + } + } + } + + it should "handle camel case starting with upper case character" in { + val output = CamelToSnakeConversion.usingRegex("HelloWorld") + output.stripPrefix("_") shouldBe "hello_world" + } + + it should "handle special case" in { + Seq( + ("HelloWorld", "hello_world"), + ("thisIsUSA", "this_is_usa"), + ("convertXMLToJSON", "convert_xml_to_json"), + ("xmlToHTTPRequest", "xml_to_http_request") + ).map { (in, out) => + val output = CamelToSnakeConversion.handleAcronymsWithRegex(in) + output shouldBe out + } + + } + +} diff --git a/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/checkempty/EmptyPatternMatchingUnitTest.scala b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/checkempty/EmptyPatternMatchingUnitTest.scala new file mode 100644 index 000000000..d97b8ff47 --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/checkempty/EmptyPatternMatchingUnitTest.scala @@ -0,0 +1,39 @@ +package com.baeldung.scala.strings.checkempty + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +import EmptyPatternMatchingExtensions._ + +class EmptyPatternMatchingUnitTest extends AnyFlatSpec with Matchers { + "isNullOrEmpty" should "return true for null reference" in { + val seq: Seq[?] = null + seq.isNullOrEmpty.shouldBe(true) + } + + it should "return true for empty sequences" in { + val seq: Seq[Any] = Seq.empty + seq.isNullOrEmpty.shouldBe(true) + } + + "isNullOrEmptyOrWhitespace" should "return true for null strings" in { + val str: String = null + str.isNullOrEmptyOrWhitespace.shouldBe(true) + } + + it should "return true for empty strings" in { + val str: String = "" + str.isNullOrEmptyOrWhitespace.shouldBe(true) + } + + it should "return true for strings with only whitespace" in { + val str: String = " " + str.isNullOrEmptyOrWhitespace.shouldBe(true) + } + + it should "return false for non-empty strings" in { + val str: String = "Hello, Scala" + str.isNullOrEmptyOrWhitespace.shouldBe(false) + } + +} diff --git a/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/checkempty/EmptySeqUnitTest.scala b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/checkempty/EmptySeqUnitTest.scala new file mode 100644 index 000000000..03ce1ab19 --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/checkempty/EmptySeqUnitTest.scala @@ -0,0 +1,39 @@ +package com.baeldung.scala.strings.checkempty + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +import EmptySeqExtensions._ + +class EmptySeqUnitTest extends AnyFlatSpec with Matchers { + "isNullOrEmpty" should "return true for null reference" in { + val seq: Seq[?] = null + seq.isNullOrEmpty.shouldBe(true) + } + + it should "return true for empty sequences" in { + val seq: Seq[Any] = Seq.empty + seq.isNullOrEmpty.shouldBe(true) + } + + "isNullOrEmptyOrWhitespace" should "return true for null strings" in { + val str: String = null + str.isNullOrEmptyOrWhitespace.shouldBe(true) + } + + it should "return true for empty strings" in { + val str: String = "" + str.isNullOrEmptyOrWhitespace.shouldBe(true) + } + + it should "return true for strings with only whitespace" in { + val str: String = " " + str.isNullOrEmptyOrWhitespace.shouldBe(true) + } + + it should "return false for non-empty strings" in { + val str: String = "Hello, Scala" + str.isNullOrEmptyOrWhitespace.shouldBe(false) + } + +} diff --git a/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/checkempty/EmptyStringUnitTest.scala b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/checkempty/EmptyStringUnitTest.scala new file mode 100644 index 000000000..08206cf6d --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/checkempty/EmptyStringUnitTest.scala @@ -0,0 +1,28 @@ +package com.baeldung.scala.strings.checkempty + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import EmptyStringExtensions._ + +class EmptyStringUnitTest extends AnyFlatSpec with Matchers { + + "isNullOrEmptyOrWhitespace" should "return true for null strings" in { + val str: String = null + str.isNullOrEmptyOrWhitespace.shouldBe(true) + } + + it should "return true for empty strings" in { + val str: String = "" + str.isNullOrEmptyOrWhitespace.shouldBe(true) + } + + it should "return true for strings with only whitespace" in { + val str: String = " " + str.isNullOrEmptyOrWhitespace.shouldBe(true) + } + + it should "return false for non-empty strings" in { + val str: String = "Hello, Scala" + str.isNullOrEmptyOrWhitespace.shouldBe(false) + } +} diff --git a/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/checkempty/VerifiedStringUnitTest.scala b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/checkempty/VerifiedStringUnitTest.scala new file mode 100644 index 000000000..7234dca42 --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/checkempty/VerifiedStringUnitTest.scala @@ -0,0 +1,30 @@ +package com.baeldung.scala.strings.checkempty + +import com.baeldung.scala.strings.checkempty.VerifiedString.* +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class VerifiedStringUnitTest extends AnyFlatSpec with Matchers { + + def lenght(str: VerifiedString[NonEmpty]) = str.length + + "a verified string" should "never be empty" in { + val str: String = null + str.isNullOrEmptyOrWhitespace.shouldBe(true) + } + + it should "return true for empty strings" in { + val str: String = "" + str.isNullOrEmptyOrWhitespace.shouldBe(true) + } + + it should "return true for strings with only whitespace" in { + val str: String = " " + str.isNullOrEmptyOrWhitespace.shouldBe(true) + } + + it should "return false for non-empty strings" in { + val str: String = "Hello, Scala" + str.isNullOrEmptyOrWhitespace.shouldBe(false) + } +} diff --git a/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/concatstrings/ConcatStringUnitTest.scala b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/concatstrings/ConcatStringUnitTest.scala new file mode 100644 index 000000000..79e71a894 --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/concatstrings/ConcatStringUnitTest.scala @@ -0,0 +1,78 @@ +package com.baeldung.scala.strings.concatstrings + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ConcatStringUnitTest extends AnyFlatSpec with Matchers { + + it should "concat strings using + operator" in { + val str1 = "Hello" + val str2 = "Baeldung" + val combined = str1 + str2 + combined shouldBe "HelloBaeldung" + } + + it should "concat string with another type using + operator" in { + val str1 = "Hello" + val combined = str1 + 10 + combined shouldBe "Hello10" + } + + it should "concat strings using ++ operator" in { + val str1 = "Hello" + val str2 = "Baeldung" + val combined = str1 ++ str2 + combined shouldBe "HelloBaeldung" + } + + it should "concat strings using concat" in { + val str1 = "Hello" + val str2 = "Baeldung" + val combined = str1.concat(str2) + combined shouldBe "HelloBaeldung" + } + + it should "concat strings using string interpolation" in { + val str1 = "Hello" + val str2 = "Baeldung" + val combined = s"$str1$str2" + combined shouldBe "HelloBaeldung" + } + + it should "concat strings using multi line string interpolation" in { + val str1 = "Hello" + val str2 = "Baeldung" + val combined = s"""$str1$str2""" + combined shouldBe "HelloBaeldung" + } + + it should "concat strings using StringBuilder" in { + val str1 = "Hello" + val str2 = "Baeldung" + val combined = new StringBuilder(str1).append(str2).toString() + combined shouldBe "HelloBaeldung" + } + + it should "concat strings using mkString" in { + val strings = Seq("Hello", "Baeldung") + val combined = strings.mkString + combined shouldBe "HelloBaeldung" + strings.mkString(",") shouldBe "Hello,Baeldung" + } + + it should "concat strings using reduce" in { + val strings = Seq("Hello", "Baeldung") + val combined = strings.reduce(_ + _) + combined shouldBe "HelloBaeldung" + val combinedWithComma = strings.reduce((a, b) => (a + "," + b)) + combinedWithComma shouldBe "Hello,Baeldung" + } + + it should "concat strings using foldLeft" in { + val strings = Seq("Hello", "Baeldung") + val combined = strings.foldLeft("")(_ + _) + combined shouldBe "HelloBaeldung" + List.empty[String].foldLeft("")(_ + _) shouldBe "" + } + +} diff --git a/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/removemultispace/RemoveMultipleSpacesUnitTest.scala b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/removemultispace/RemoveMultipleSpacesUnitTest.scala new file mode 100644 index 000000000..08d08dd69 --- /dev/null +++ b/scala-core-modules/scala-strings-2/src/test/scala/com/baeldung/scala/strings/removemultispace/RemoveMultipleSpacesUnitTest.scala @@ -0,0 +1,39 @@ +package com.baeldung.scala.strings.removemultispace + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks + +class RemoveMultipleSpacesUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + private val table = Table( + ("input string", "expected"), + (" too many spaces ", "too many spaces"), + (" ", ""), + ("a", "a"), + ("a ", "a"), + ("", "") + ) + + private val functions = Seq( + ("usingReplaceAll", RemoveMultipleSpaces.usingReplaceAll), + ("usingSplit", RemoveMultipleSpaces.usingSplit), + ("usingZip", RemoveMultipleSpaces.usingZip), + ("usingStringBuilder", RemoveMultipleSpaces.usingStringBuilder) + ) + it should "remove multiple spaces with a single space in the string" in { + forAll(table) { (input, expected) => + functions.map { (name, fn) => + withClue( + s"Failed for the input string ${input} in the function ${name}" + ) { + fn(input) shouldBe expected + } + } + } + } + +} diff --git a/scala-design-patterns/src/main/scala-2/com/baeldung/scala/cakepattern/CakePattern.scala b/scala-design-patterns/src/main/scala/com/baeldung/scala/cakepattern/CakePattern.scala similarity index 96% rename from scala-design-patterns/src/main/scala-2/com/baeldung/scala/cakepattern/CakePattern.scala rename to scala-design-patterns/src/main/scala/com/baeldung/scala/cakepattern/CakePattern.scala index 56bafb6f9..e8e2176c1 100644 --- a/scala-design-patterns/src/main/scala-2/com/baeldung/scala/cakepattern/CakePattern.scala +++ b/scala-design-patterns/src/main/scala/com/baeldung/scala/cakepattern/CakePattern.scala @@ -36,7 +36,7 @@ object CakePattern { } trait TestExecutorComponentWithLogging { - this: TestEnvironmentComponent with LoggingComponent => + this: TestEnvironmentComponent & LoggingComponent => val testExecutor: TestExecutor class TestExecutor { def execute(tests: List[Test]): Boolean = { diff --git a/scala-design-patterns/src/main/scala-2/com/baeldung/scala/magnetpattern/MagnetPattern.scala b/scala-design-patterns/src/main/scala/com/baeldung/scala/magnetpattern/MagnetPattern.scala similarity index 77% rename from scala-design-patterns/src/main/scala-2/com/baeldung/scala/magnetpattern/MagnetPattern.scala rename to scala-design-patterns/src/main/scala/com/baeldung/scala/magnetpattern/MagnetPattern.scala index d326488a1..0a62074f5 100644 --- a/scala-design-patterns/src/main/scala-2/com/baeldung/scala/magnetpattern/MagnetPattern.scala +++ b/scala-design-patterns/src/main/scala/com/baeldung/scala/magnetpattern/MagnetPattern.scala @@ -1,5 +1,7 @@ package com.baeldung.scala.magnetpattern +import scala.language.implicitConversions + object MagnetPattern extends App { /* @@ -20,11 +22,15 @@ object MagnetPattern extends App { def combineElements(magnet: CombineMagnet): magnet.Result = magnet() - implicit def intCombineMagnet(intList: List[Int]) = new CombineMagnet { + implicit def intCombineMagnet( + intList: List[Int] + ): CombineMagnet { type Result = Int } = new CombineMagnet { override type Result = Int override def apply(): Result = intList.reduce((i, c) => i + c) } - implicit def strCombineMagnet(stringList: List[String]) = new CombineMagnet { + implicit def strCombineMagnet( + stringList: List[String] + ): CombineMagnet { type Result = String } = new CombineMagnet { override type Result = String override def apply(): Result = stringList.reduce((s, c) => s.concat(c)) } diff --git a/scala-design-patterns/src/test/scala-2/com/baeldung/scala/cakepattern/CakePatternUnitTest.scala b/scala-design-patterns/src/test/scala/com/baeldung/scala/cakepattern/CakePatternUnitTest.scala similarity index 80% rename from scala-design-patterns/src/test/scala-2/com/baeldung/scala/cakepattern/CakePatternUnitTest.scala rename to scala-design-patterns/src/test/scala/com/baeldung/scala/cakepattern/CakePatternUnitTest.scala index 28bc7884b..89c4dae99 100644 --- a/scala-design-patterns/src/test/scala-2/com/baeldung/scala/cakepattern/CakePatternUnitTest.scala +++ b/scala-design-patterns/src/test/scala/com/baeldung/scala/cakepattern/CakePatternUnitTest.scala @@ -1,13 +1,14 @@ package com.baeldung.scala.cakepattern import com.baeldung.scala.cakepattern.CakePattern.Test -import org.scalamock.scalatest.MockFactory +import org.mockito.Mockito.when import org.scalatest.flatspec.AnyFlatSpec +import org.scalatestplus.mockito.MockitoSugar trait TestRegistry extends CakePattern.TestExecutorComponent with CakePattern.TestEnvironmentComponent - with MockFactory { + with MockitoSugar { override val env: TestEnvironment = mock[TestEnvironment] override val testExecutor: TestExecutor = new TestExecutor } @@ -15,7 +16,7 @@ trait TestRegistry class CakePatternUnitTest extends AnyFlatSpec with TestRegistry { "A TestExecutor" should "execute tests using a given environment" in { - (env.readEnvironmentProperties _).expects().returning(Map("ENV" -> "true")) + when(env.readEnvironmentProperties).thenReturn(Map("ENV" -> "true")) val test = Test( "test-1", { environment => diff --git a/scala-design-patterns/src/test/scala-2/com/baeldung/scala/magnetpattern/MagnetPatternTest.scala b/scala-design-patterns/src/test/scala/com/baeldung/scala/magnetpattern/MagnetPatternUnitTest.scala similarity index 86% rename from scala-design-patterns/src/test/scala-2/com/baeldung/scala/magnetpattern/MagnetPatternTest.scala rename to scala-design-patterns/src/test/scala/com/baeldung/scala/magnetpattern/MagnetPatternUnitTest.scala index 84b688960..583cec60d 100644 --- a/scala-design-patterns/src/test/scala-2/com/baeldung/scala/magnetpattern/MagnetPatternTest.scala +++ b/scala-design-patterns/src/test/scala/com/baeldung/scala/magnetpattern/MagnetPatternUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.magnetpattern import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class MagnetPatternTest extends AnyFlatSpec with Matchers { +class MagnetPatternUnitTest extends AnyFlatSpec with Matchers { "combineElements" should "be able to combine the elements in a collection" in { val intList = List(1, 2, 3, 4) val strList = List("a", "b", "c") diff --git a/scala-gatling/.gitignore b/scala-gatling/.gitignore new file mode 100644 index 000000000..fc40e2d95 --- /dev/null +++ b/scala-gatling/.gitignore @@ -0,0 +1,5 @@ +target +.idea +.settings +.classpath +.project diff --git a/scala-gatling/README.md b/scala-gatling/README.md new file mode 100644 index 000000000..2fa9e1f13 --- /dev/null +++ b/scala-gatling/README.md @@ -0,0 +1,10 @@ +### Relevant Articles: +- [Testing With Gatling Using Scala]() + +### Gatling Executions +From this module's folder, we first compile tests: `sbt test:compile` + +Then run the simulation: `sbt 'Gatling/testOnly com.baeldung.gatling.PeakLoadSimulation'` + +**Notes**: in order to spin-up the right API this example uses, we also need to start the server: +`sbt resApi/run` from root folder diff --git a/scala-gatling/build.sbt b/scala-gatling/build.sbt new file mode 100644 index 000000000..1dda5af89 --- /dev/null +++ b/scala-gatling/build.sbt @@ -0,0 +1,11 @@ +enablePlugins(GatlingPlugin) + +scalaVersion := "2.13.15" + +scalacOptions := Seq( + "-encoding", "UTF-8", "-release:8", "-deprecation", + "-feature", "-unchecked", "-language:implicitConversions", "-language:postfixOps") + +val gatlingVersion = "3.13.1" +libraryDependencies += "io.gatling.highcharts" % "gatling-charts-highcharts" % gatlingVersion % "test,it" +libraryDependencies += "io.gatling" % "gatling-test-framework" % gatlingVersion % "test,it" diff --git a/scala-gatling/project/build.properties b/scala-gatling/project/build.properties new file mode 100644 index 000000000..01a16ed14 --- /dev/null +++ b/scala-gatling/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.11.7 diff --git a/scala-gatling/project/plugins.sbt b/scala-gatling/project/plugins.sbt new file mode 100644 index 000000000..e5a0f0f81 --- /dev/null +++ b/scala-gatling/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("io.gatling" % "gatling-sbt" % "4.11.1") diff --git a/scala-gatling/src/test/scala/com/baeldung/gatling/ChainRequestsProvider.scala b/scala-gatling/src/test/scala/com/baeldung/gatling/ChainRequestsProvider.scala new file mode 100644 index 000000000..38c6195a7 --- /dev/null +++ b/scala-gatling/src/test/scala/com/baeldung/gatling/ChainRequestsProvider.scala @@ -0,0 +1,31 @@ +package com.baeldung.gatling + +import io.gatling.core.Predef._ +import io.gatling.core.structure.ChainBuilder +import io.gatling.http.Predef._ +import io.gatling.http.request.builder.HttpRequestBuilder + +object ChainRequestsProvider { + + def simpleRequest( + requestName: String, + requestPath: String, + expectedResponseStatus: Int + ): ChainBuilder = { + val request: HttpRequestBuilder = http(requestName) + .get(requestPath) + .check(status.is(expectedResponseStatus)) + .check(bodyString.optional.saveAs("sBodyString")) + + exec(session => session.markAsSucceeded) + .exec(request) + .doIf(_.isFailed) { + exec { session => + println("***Failure on [" + requestPath + "] endpoint:") + print("Gatling Session Data: ") + println(session.attributes.get("sBodyString")) + session + } + } + } +} diff --git a/scala-gatling/src/test/scala/com/baeldung/gatling/PeakLoadSimulation.scala b/scala-gatling/src/test/scala/com/baeldung/gatling/PeakLoadSimulation.scala new file mode 100644 index 000000000..506ba463e --- /dev/null +++ b/scala-gatling/src/test/scala/com/baeldung/gatling/PeakLoadSimulation.scala @@ -0,0 +1,31 @@ +package com.baeldung.gatling + +import io.gatling.core.Predef.{details, _} +import com.baeldung.gatling.ChainRequestsProvider.simpleRequest +import com.baeldung.gatling.ScenariosProvider.getScenario + +class PeakLoadSimulation extends Simulation { + + setUp( + getScenario( + "getExistingEndpoint", + simpleRequest("request_todo_endpoint", "/todo", 200), + 50, + 10, + 60 + ), + getScenario( + "nonExistingEndpoint", + simpleRequest("request_wrong_endpoint", "/not-todo", 200), + 5, + 10, + 60 + ) + ).assertions( + details("request_todo_endpoint").successfulRequests.percent.gt(99.99), + details("request_todo_endpoint").responseTime.percentile4.lt(20), + details("request_todo_endpoint").requestsPerSec.gt(40), + details("request_wrong_endpoint").successfulRequests.percent.lt(1), + details("request_wrong_endpoint").responseTime.percentile4.lt(20) + ) +} diff --git a/scala-gatling/src/test/scala/com/baeldung/gatling/ScenariosProvider.scala b/scala-gatling/src/test/scala/com/baeldung/gatling/ScenariosProvider.scala new file mode 100644 index 000000000..1423f178d --- /dev/null +++ b/scala-gatling/src/test/scala/com/baeldung/gatling/ScenariosProvider.scala @@ -0,0 +1,31 @@ +package com.baeldung.gatling + +import io.gatling.core.Predef._ +import io.gatling.core.structure.{ChainBuilder, PopulationBuilder} +import io.gatling.http.Predef.http + +import scala.language.postfixOps + +object ScenariosProvider { + + private val httpProtocol = + http.baseUrl("http://localhost:9000").disableCaching.disableFollowRedirect + + def getScenario( + scenarioName: String, + request: ChainBuilder, + tps: Double, + rampUpSeconds: Int, + durationSeconds: Int + ): PopulationBuilder = { + scenario(scenarioName) + .exec(request) + .inject( + rampUsersPerSec(0).to(tps).during(rampUpSeconds), + constantUsersPerSec(tps) + .during(durationSeconds - rampUpSeconds - rampUpSeconds), + rampUsersPerSec(tps).to(0).during(rampUpSeconds) + ) + .protocols(httpProtocol) + } +} diff --git a/scala-graalvm/scala-graalvm-http-app/build.sbt b/scala-graalvm/scala-graalvm-http-app/build.sbt index 78d78f30d..a644d9128 100644 --- a/scala-graalvm/scala-graalvm-http-app/build.sbt +++ b/scala-graalvm/scala-graalvm-http-app/build.sbt @@ -3,7 +3,7 @@ organization := "com.baeldung" version := "1.0-SNAPSHOT" -scalaVersion := "2.13.12" +scalaVersion := "3.4.1" libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-actor" % "2.8.0", "com.typesafe.akka" %% "akka-stream" % "2.8.0", diff --git a/scala-graalvm/scala-graalvm-http-app/project/build.properties b/scala-graalvm/scala-graalvm-http-app/project/build.properties index 563a014da..01a16ed14 100644 --- a/scala-graalvm/scala-graalvm-http-app/project/build.properties +++ b/scala-graalvm/scala-graalvm-http-app/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.2 +sbt.version=1.11.7 diff --git a/scala-js/build.sbt b/scala-js/build.sbt index a2087ec4c..9a6eeef9d 100644 --- a/scala-js/build.sbt +++ b/scala-js/build.sbt @@ -5,11 +5,9 @@ version := "1.0-SNAPSHOT" enablePlugins(ScalaJSPlugin) -scalaVersion := "2.13.12" - scalaJSUseMainModuleInitializer := true -libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.2.0" -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.1.2" % Test +libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.8.0" +libraryDependencies += "org.scalatest" %%% "scalatest" % "3.2.19" % Test //enable the below setting after installing npm package jsdom. jsEnv := new org.scalajs.jsenv.jsdomnodejs.JSDOMNodeJSEnv() diff --git a/scala-js/src/main/scala-2/com/baeldung/scala/ScalaJsApp.scala b/scala-js/src/main/scala/com/baeldung/scala/ScalaJsApp.scala similarity index 100% rename from scala-js/src/main/scala-2/com/baeldung/scala/ScalaJsApp.scala rename to scala-js/src/main/scala/com/baeldung/scala/ScalaJsApp.scala diff --git a/scala-js/src/test/scala-2/com/baeldung/scala/ScalaJsUnitTest.scala b/scala-js/src/test/scala/com/baeldung/scala/ScalaJsUnitTest.scala similarity index 100% rename from scala-js/src/test/scala-2/com/baeldung/scala/ScalaJsUnitTest.scala rename to scala-js/src/test/scala/com/baeldung/scala/ScalaJsUnitTest.scala diff --git a/scala-lagom/build.sbt b/scala-lagom/build.sbt index a00058979..cef4f1ee3 100644 --- a/scala-lagom/build.sbt +++ b/scala-lagom/build.sbt @@ -2,7 +2,7 @@ organization in ThisBuild := "com.baeldung" version in ThisBuild := "1.0-SNAPSHOT" // the Scala version that will be used for cross-compiled libraries -scalaVersion in ThisBuild := "2.13.12" +scalaVersion in ThisBuild := "2.13.18" val macwire = "com.softwaremill.macwire" %% "macros" % "2.3.3" % "provided" val scalaTest = "org.scalatest" %% "scalatest" % "3.1.1" % Test diff --git a/scala-lang-2/README.md b/scala-lang-modules/scala-lang-2/README.md similarity index 55% rename from scala-lang-2/README.md rename to scala-lang-modules/scala-lang-2/README.md index ce9c60d99..610a8ae08 100644 --- a/scala-lang-2/README.md +++ b/scala-lang-modules/scala-lang-2/README.md @@ -5,3 +5,7 @@ - [Nested Functions in Scala](https://www.baeldung.com/scala/nested-functions) - [Guide to Inheritance in Scala](https://www.baeldung.com/scala/inheritance) - [Using the “with” Keyword When Creating Instances](https://www.baeldung.com/scala/create-instances-with) +- [Different Ways to Filter Elements From a Scala Collection](https://www.baeldung.com/scala/filter-collections) +- [Type Lambdas in Scala 3](https://www.baeldung.com/scala/type-lambdas-scala-3) +- [Immutable Arrays in Scala 3 Using IArray](https://www.baeldung.com/scala/iarray-immutable-arrays) +- [Difference Between NonFatal and Exception in Scala](https://www.baeldung.com/scala/nonfatal-vs-exception) diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/inheritance/Hierarchical.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/inheritance/Hierarchical.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/inheritance/Hierarchical.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/inheritance/Hierarchical.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/inheritance/Hybrid.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/inheritance/Hybrid.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/inheritance/Hybrid.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/inheritance/Hybrid.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/inheritance/Multilevel.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/inheritance/Multilevel.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/inheritance/Multilevel.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/inheritance/Multilevel.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/inheritance/Multiple.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/inheritance/Multiple.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/inheritance/Multiple.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/inheritance/Multiple.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/inheritance/Single.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/inheritance/Single.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/inheritance/Single.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/inheritance/Single.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/nested/Factorial.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/nested/Factorial.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/nested/Factorial.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/nested/Factorial.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/singletons/Counter.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/singletons/Counter.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/singletons/Counter.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/singletons/Counter.scala diff --git a/scala3-lang-3/src/main/scala/com/baeldung/scala/typelambdas/Main.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/typelambdas/Main.scala similarity index 88% rename from scala3-lang-3/src/main/scala/com/baeldung/scala/typelambdas/Main.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/typelambdas/Main.scala index b780653d0..e811c5ae8 100644 --- a/scala3-lang-3/src/main/scala/com/baeldung/scala/typelambdas/Main.scala +++ b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/typelambdas/Main.scala @@ -1,3 +1,5 @@ +package com.baeldung.scala.typelambdas + type MyTry = [X] =>> Either[Throwable, X] type MyTuple = [X] =>> [Y] =>> (X, Y) diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/voidtypes/NilEmptyList.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/voidtypes/NilEmptyList.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/voidtypes/NilEmptyList.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/voidtypes/NilEmptyList.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/voidtypes/NoneEmptyOption.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/voidtypes/NoneEmptyOption.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/voidtypes/NoneEmptyOption.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/voidtypes/NoneEmptyOption.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/voidtypes/NothingTrait.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/voidtypes/NothingTrait.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/voidtypes/NothingTrait.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/voidtypes/NothingTrait.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/voidtypes/NullTypeAndnullValue.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/voidtypes/NullTypeAndnullValue.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/voidtypes/NullTypeAndnullValue.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/voidtypes/NullTypeAndnullValue.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/voidtypes/UnitReturnType.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/voidtypes/UnitReturnType.scala similarity index 72% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/voidtypes/UnitReturnType.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/voidtypes/UnitReturnType.scala index 5b06a383e..389459a74 100644 --- a/scala-lang-2/src/main/scala-2/com/baeldung/scala/voidtypes/UnitReturnType.scala +++ b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/voidtypes/UnitReturnType.scala @@ -2,21 +2,21 @@ package com.baeldung.scala.voidtypes object UnitReturnType extends App { - def functionReturnUnit: Unit = { + def functionReturnUnit(): Unit = { """ do something, don't return anything """ } - println("result of function returning Unit: %s".format(functionReturnUnit)) + println("result of function returning Unit: %s".format(functionReturnUnit())) - def functionReturnImplicitUnit { + def functionReturnImplicitUnit(): Unit = { s""" do something, don't return anything """ } println( "result of function returning implicit Unit: %s".format( - functionReturnImplicitUnit + functionReturnImplicitUnit() ) ) } diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/withtrait/Animal.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/withtrait/Animal.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/withtrait/Animal.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/withtrait/Animal.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/withtrait/Musician.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/withtrait/Musician.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/withtrait/Musician.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/withtrait/Musician.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/withtrait/Person.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/withtrait/Person.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/withtrait/Person.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/withtrait/Person.scala diff --git a/scala-lang-2/src/main/scala-2/com/baeldung/scala/withtrait/Politician.scala b/scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/withtrait/Politician.scala similarity index 100% rename from scala-lang-2/src/main/scala-2/com/baeldung/scala/withtrait/Politician.scala rename to scala-lang-modules/scala-lang-2/src/main/scala/com/baeldung/scala/withtrait/Politician.scala diff --git a/scala3-lang-3/src/test/scala/com/baeldung/scala/filters/FindFilterCollector.scala b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/filters/FindFilterCollectorUnitTest.scala similarity index 97% rename from scala3-lang-3/src/test/scala/com/baeldung/scala/filters/FindFilterCollector.scala rename to scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/filters/FindFilterCollectorUnitTest.scala index 424b4ddc1..d4028b91a 100644 --- a/scala3-lang-3/src/test/scala/com/baeldung/scala/filters/FindFilterCollector.scala +++ b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/filters/FindFilterCollectorUnitTest.scala @@ -5,7 +5,7 @@ import org.scalatest.wordspec.AnyWordSpec import scala.collection.SortedSet -class FindFilterCollector extends AnyWordSpec with Matchers { +class FindFilterCollectorUnitTest extends AnyWordSpec with Matchers { "find method" should { "select one element from a collection using find method" in { val numbers = List(1, 2, 3, 4, 5, 6) diff --git a/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/immutablearrays/ImmutableArrayUnitTest.scala b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/immutablearrays/ImmutableArrayUnitTest.scala new file mode 100644 index 000000000..3c3d995f5 --- /dev/null +++ b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/immutablearrays/ImmutableArrayUnitTest.scala @@ -0,0 +1,42 @@ +package com.baeldung.scala.immutablearrays + +import org.scalatest.wordspec.AnyWordSpec + +class ImmutableArrayUnitTest extends AnyWordSpec { + trait Pet(val name: String, val age: Int) + case class Dog(override val name: String, override val age: Int) + extends Pet(name = name, age = age) + case class Cat(override val name: String, override val age: Int) + extends Pet(name = name, age = age) + + val dogs = IArray(Dog("champ", 2), Dog("barky", 3)) + val cats = IArray(Cat("overlord", 3), Cat("silky", 5)) + + "dogs and cats" should { + "get along together" in { + val myPets = cats ++ dogs // myPets is an IArray[Pet] + // is the size of the array correct? + assertResult(4)(myPets.length) + + // check contents by full comparison + assert(myPets.indexOf(Dog("barky", 3)) == 3) + assert(myPets.indexOf(Cat("silky", 5)) == 1) + assert(myPets.indexOf(Dog("champ", 2)) == 2) + assert(myPets.indexOf(Cat("overlord", 3)) == 0) + + // check contents by predicate + assert(myPets.exists(_.name == "barky")) + assert(myPets.filter(_.age == 3).size == 2) + + // check type is respected by closure + assert(myPets(1).isInstanceOf[Cat]) + assert(myPets(2).isInstanceOf[Dog]) + } + } + + "a dogs array" should { + "be immutable" in { + assertDoesNotCompile("dogs(0) = Dog(\"unwanted\", 8)") + } + } +} diff --git a/scala3-lang-2/src/test/scala/com/baeldung/scala3/nonfatal/NonFatalExceptionsTest.scala b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/nonfatal/NonFatalExceptionsUnitTest.scala similarity index 98% rename from scala3-lang-2/src/test/scala/com/baeldung/scala3/nonfatal/NonFatalExceptionsTest.scala rename to scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/nonfatal/NonFatalExceptionsUnitTest.scala index 491739381..373d835f2 100644 --- a/scala3-lang-2/src/test/scala/com/baeldung/scala3/nonfatal/NonFatalExceptionsTest.scala +++ b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/nonfatal/NonFatalExceptionsUnitTest.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala3.nonfatal +package com.baeldung.scala.nonfatal import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec @@ -6,7 +6,7 @@ import org.scalatest.wordspec.AnyWordSpec import scala.util.* import scala.util.control.{ControlThrowable, NonFatal} -class NonFatalExceptionsTest extends AnyWordSpec with Matchers { +class NonFatalExceptionsUnitTest extends AnyWordSpec with Matchers { "NonFatalExceptionsTest" should { diff --git a/scala-lang-2/src/test/scala-2/com/baeldung/scala/singletons/CounterUnitTest.scala b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/singletons/CounterUnitTest.scala similarity index 100% rename from scala-lang-2/src/test/scala-2/com/baeldung/scala/singletons/CounterUnitTest.scala rename to scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/singletons/CounterUnitTest.scala diff --git a/scala-lang-2/src/test/scala-2/com/baeldung/scala/voidtypes/NilEmptyListUnitTest.scala b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/voidtypes/NilEmptyListUnitTest.scala similarity index 100% rename from scala-lang-2/src/test/scala-2/com/baeldung/scala/voidtypes/NilEmptyListUnitTest.scala rename to scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/voidtypes/NilEmptyListUnitTest.scala diff --git a/scala-lang-2/src/test/scala-2/com/baeldung/scala/voidtypes/NoneEmptyOptionUnitTest.scala b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/voidtypes/NoneEmptyOptionUnitTest.scala similarity index 100% rename from scala-lang-2/src/test/scala-2/com/baeldung/scala/voidtypes/NoneEmptyOptionUnitTest.scala rename to scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/voidtypes/NoneEmptyOptionUnitTest.scala diff --git a/scala-lang-2/src/test/scala-2/com/baeldung/scala/voidtypes/NothingTraitUnitTest.scala b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/voidtypes/NothingTraitUnitTest.scala similarity index 100% rename from scala-lang-2/src/test/scala-2/com/baeldung/scala/voidtypes/NothingTraitUnitTest.scala rename to scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/voidtypes/NothingTraitUnitTest.scala diff --git a/scala-lang-2/src/test/scala-2/com/baeldung/scala/voidtypes/NullTypeAndNullValueUnitTest.scala b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/voidtypes/NullTypeAndNullValueUnitTest.scala similarity index 86% rename from scala-lang-2/src/test/scala-2/com/baeldung/scala/voidtypes/NullTypeAndNullValueUnitTest.scala rename to scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/voidtypes/NullTypeAndNullValueUnitTest.scala index 1c8dd2d36..753ebaf98 100644 --- a/scala-lang-2/src/test/scala-2/com/baeldung/scala/voidtypes/NullTypeAndNullValueUnitTest.scala +++ b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/voidtypes/NullTypeAndNullValueUnitTest.scala @@ -13,7 +13,7 @@ class NullTypeAndNullValueUnitTest extends AnyFunSuite { test("null equality check using equals ") { val exceptionThrown = intercept[NullPointerException] { - NullTypeAndnullValue.nullValue equals NullTypeAndnullValue.nullRefCar + NullTypeAndnullValue.nullValue `equals` NullTypeAndnullValue.nullRefCar } } diff --git a/scala-lang-2/src/test/scala-2/com/baeldung/scala/voidtypes/UnitReturnTypeUnitTest.scala b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/voidtypes/UnitReturnTypeUnitTest.scala similarity index 68% rename from scala-lang-2/src/test/scala-2/com/baeldung/scala/voidtypes/UnitReturnTypeUnitTest.scala rename to scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/voidtypes/UnitReturnTypeUnitTest.scala index 37b1d8ebd..4b6849e46 100644 --- a/scala-lang-2/src/test/scala-2/com/baeldung/scala/voidtypes/UnitReturnTypeUnitTest.scala +++ b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/voidtypes/UnitReturnTypeUnitTest.scala @@ -5,11 +5,11 @@ import org.scalatest.funsuite.AnyFunSuite class UnitReturnTypeUnitTest extends AnyFunSuite { test("test return value of unit function") { - assert(UnitReturnType.functionReturnUnit == ()) + assert(UnitReturnType.functionReturnUnit() == ()) } test("test return value of implicit unit function") { - assert(UnitReturnType.functionReturnImplicitUnit == ()) + assert(UnitReturnType.functionReturnImplicitUnit() == ()) } } diff --git a/scala-lang-2/src/test/scala-2/com/baeldung/scala/withtrait/WithTraitSpec.scala b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/withtrait/WithTraitUnitTest.scala similarity index 88% rename from scala-lang-2/src/test/scala-2/com/baeldung/scala/withtrait/WithTraitSpec.scala rename to scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/withtrait/WithTraitUnitTest.scala index 24cb2f15a..1929b7915 100644 --- a/scala-lang-2/src/test/scala-2/com/baeldung/scala/withtrait/WithTraitSpec.scala +++ b/scala-lang-modules/scala-lang-2/src/test/scala/com/baeldung/scala/withtrait/WithTraitUnitTest.scala @@ -4,7 +4,7 @@ import org.scalatest.wordspec.AnyWordSpec import java.time.LocalDate -class WithTraitSpec extends AnyWordSpec { +class WithTraitUnitTest extends AnyWordSpec { import WithTraitSpec._ @@ -51,28 +51,28 @@ object WithTraitSpec { val pat: Person = new Person("Pat", "123 Main St.", LocalDate.of(1933, 10, 11)) - val mary: Person with Musician = + val mary: Person & Musician = new Person("Mary", "456 Second St.", LocalDate.of(1982, 9, 9)) with Musician { override val instrument: String = "guitar" } - val prudence: Person with Politician = + val prudence: Person & Politician = new Person("Prudence", "789 Third St.", LocalDate.of(1972, 6, 3)) with Politician - val giorgio: Person with Politician with Musician = + val giorgio: Person & Politician & Musician = new Person("Giorgio", "121 Fourth St.", LocalDate.of(1980, 2, 19)) with Politician with Musician { override val instrument: String = "flute" } - val ellie: Animal with Musician = + val ellie: Animal & Musician = new Animal("Ellie", "elephant") with Musician { override val instrument: String = "trombone" } - val vasily: Animal with Politician = + val vasily: Animal & Politician = new Animal("Vasily", "monkey") with Politician } diff --git a/scala3-lang/README.md b/scala-lang-modules/scala-lang-3/README.md similarity index 100% rename from scala3-lang/README.md rename to scala-lang-modules/scala-lang-3/README.md diff --git a/play-scala/dependency-injection/project/build.properties b/scala-lang-modules/scala-lang-3/project/build.properties similarity index 100% rename from play-scala/dependency-injection/project/build.properties rename to scala-lang-modules/scala-lang-3/project/build.properties diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/enum/ADTExample.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/enum/ADTExample.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/enum/ADTExample.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/enum/ADTExample.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/enum/ColorExamples.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/enum/ColorExamples.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/enum/ColorExamples.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/enum/ColorExamples.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/ExtensionMethod.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/ExtensionMethod.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/ExtensionMethod.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/ExtensionMethod.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/ImplicitConversion.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/ImplicitConversion.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/ImplicitConversion.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/ImplicitConversion.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/ProvidingContextualEnvironment.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/ProvidingContextualEnvironment.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/ProvidingContextualEnvironment.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/ProvidingContextualEnvironment.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/WritingTypeclassInstances.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/WritingTypeclassInstances.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/WritingTypeclassInstances.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/WritingTypeclassInstances.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/Extension.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/Extension.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/Extension.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/Extension.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitConversion.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitConversion.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitConversion.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitConversion.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitParameter.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitParameter.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitParameter.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitParameter.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitlyMagic.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitlyMagic.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitlyMagic.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitlyMagic.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/Extension.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/Extension.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/Extension.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/Extension.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitConversion.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitConversion.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitConversion.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitConversion.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitParameter.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitParameter.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitParameter.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitParameter.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitlyMagic.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitlyMagic.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitlyMagic.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitlyMagic.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/implicits/extensions/StringExtensions.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/extensions/StringExtensions.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/implicits/extensions/StringExtensions.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/implicits/extensions/StringExtensions.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/intersectiontypes/BasicIntersectionType.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/intersectiontypes/BasicIntersectionType.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/intersectiontypes/BasicIntersectionType.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/intersectiontypes/BasicIntersectionType.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/intersectiontypes/DuckTyping.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/intersectiontypes/DuckTyping.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/intersectiontypes/DuckTyping.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/intersectiontypes/DuckTyping.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/intersectiontypes/Inheritance.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/intersectiontypes/Inheritance.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/intersectiontypes/Inheritance.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/intersectiontypes/Inheritance.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/intersectiontypes/Overloading.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/intersectiontypes/Overloading.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/intersectiontypes/Overloading.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/intersectiontypes/Overloading.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineCompilerValidation.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineCompilerValidation.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineCompilerValidation.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineCompilerValidation.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineDef.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineDef.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineDef.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineDef.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineIf.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineIf.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineIf.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineIf.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineMatch.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineMatch.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineMatch.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineMatch.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineParams.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineParams.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineParams.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineParams.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineVal.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineVal.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineVal.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineVal.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineValDef.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineValDef.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/InlineValDef.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/InlineValDef.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/TransparentInline.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/TransparentInline.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/macros/inline/TransparentInline.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/macros/inline/TransparentInline.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/mainmethods/HelloWorld.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/mainmethods/HelloWorld.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/mainmethods/HelloWorld.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/mainmethods/HelloWorld.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/mainmethods/HelloWorldObject.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/mainmethods/HelloWorldObject.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/mainmethods/HelloWorldObject.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/mainmethods/HelloWorldObject.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/mainmethods/WithParameters.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/mainmethods/WithParameters.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/mainmethods/WithParameters.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/mainmethods/WithParameters.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/multiversalequality/CanEqualDeriveClause.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/multiversalequality/CanEqualDeriveClause.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/multiversalequality/CanEqualDeriveClause.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/multiversalequality/CanEqualDeriveClause.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/multiversalequality/CanEqualGivenInstance.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/multiversalequality/CanEqualGivenInstance.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/multiversalequality/CanEqualGivenInstance.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/multiversalequality/CanEqualGivenInstance.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/multiversalequality/MultiversalEquality.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/multiversalequality/MultiversalEquality.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/multiversalequality/MultiversalEquality.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/multiversalequality/MultiversalEquality.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/multiversalequality/UniversalEquality.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/multiversalequality/UniversalEquality.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/multiversalequality/UniversalEquality.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/multiversalequality/UniversalEquality.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/openclasses/Album.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/openclasses/Album.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/openclasses/Album.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/openclasses/Album.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/openclasses/DeluxeEdition.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/openclasses/DeluxeEdition.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/openclasses/DeluxeEdition.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/openclasses/DeluxeEdition.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/quitesyntax/Example.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/quitesyntax/Example.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/quitesyntax/Example.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/quitesyntax/Example.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/traits/ParameterizedTrait.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/traits/ParameterizedTrait.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/traits/ParameterizedTrait.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/traits/ParameterizedTrait.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/typesystem/CompoundTypes.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/typesystem/CompoundTypes.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/typesystem/CompoundTypes.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/typesystem/CompoundTypes.scala diff --git a/scala3-lang/src/main/scala/com/baeldung/scala3/typesystem/OpaqueTypeAlias.scala b/scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/typesystem/OpaqueTypeAlias.scala similarity index 100% rename from scala3-lang/src/main/scala/com/baeldung/scala3/typesystem/OpaqueTypeAlias.scala rename to scala-lang-modules/scala-lang-3/src/main/scala/com/baeldung/scala3/typesystem/OpaqueTypeAlias.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/implicits/ExtensionMethodUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/ExtensionMethodUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/implicits/ExtensionMethodUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/ExtensionMethodUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/implicits/ImplicitConversionUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/ImplicitConversionUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/implicits/ImplicitConversionUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/ImplicitConversionUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/implicits/ProvidingContextualEnvironmentUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/ProvidingContextualEnvironmentUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/implicits/ProvidingContextualEnvironmentUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/ProvidingContextualEnvironmentUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/implicits/comparison/scala2/ExtensionUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/comparison/scala2/ExtensionUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/implicits/comparison/scala2/ExtensionUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/comparison/scala2/ExtensionUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitConversionUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitConversionUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitConversionUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitConversionUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitParameterUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitParameterUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitParameterUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/comparison/scala2/ImplicitParameterUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/implicits/comparison/scala3/ExtensionUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/comparison/scala3/ExtensionUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/implicits/comparison/scala3/ExtensionUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/comparison/scala3/ExtensionUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitConversionUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitConversionUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitConversionUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitConversionUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitParameterUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitParameterUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitParameterUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/comparison/scala3/ImplicitParameterUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/implicits/extensions/ExtensionsUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/extensions/ExtensionsUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/implicits/extensions/ExtensionsUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/implicits/extensions/ExtensionsUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/intersectiontypes/BasicIntersectionTypeUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/intersectiontypes/BasicIntersectionTypeUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/intersectiontypes/BasicIntersectionTypeUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/intersectiontypes/BasicIntersectionTypeUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/intersectiontypes/DuckTypingUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/intersectiontypes/DuckTypingUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/intersectiontypes/DuckTypingUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/intersectiontypes/DuckTypingUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/intersectiontypes/InheritanceUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/intersectiontypes/InheritanceUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/intersectiontypes/InheritanceUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/intersectiontypes/InheritanceUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/intersectiontypes/OverloadingUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/intersectiontypes/OverloadingUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/intersectiontypes/OverloadingUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/intersectiontypes/OverloadingUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/multiversalequality/CanEqualDeriveClauseUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/multiversalequality/CanEqualDeriveClauseUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/multiversalequality/CanEqualDeriveClauseUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/multiversalequality/CanEqualDeriveClauseUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/multiversalequality/CanEqualGivenInstanceUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/multiversalequality/CanEqualGivenInstanceUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/multiversalequality/CanEqualGivenInstanceUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/multiversalequality/CanEqualGivenInstanceUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/multiversalequality/MultiversalEqualityUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/multiversalequality/MultiversalEqualityUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/multiversalequality/MultiversalEqualityUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/multiversalequality/MultiversalEqualityUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/multiversalequality/UniversalEqualityUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/multiversalequality/UniversalEqualityUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/multiversalequality/UniversalEqualityUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/multiversalequality/UniversalEqualityUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/traits/ParameterizedTraitUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/traits/ParameterizedTraitUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/traits/ParameterizedTraitUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/traits/ParameterizedTraitUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/typesystem/CompoundTypesUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/typesystem/CompoundTypesUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/typesystem/CompoundTypesUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/typesystem/CompoundTypesUnitTest.scala diff --git a/scala3-lang/src/test/scala/com/baeldung/scala3/typesystem/OpaqueTypeAliasUnitTest.scala b/scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/typesystem/OpaqueTypeAliasUnitTest.scala similarity index 100% rename from scala3-lang/src/test/scala/com/baeldung/scala3/typesystem/OpaqueTypeAliasUnitTest.scala rename to scala-lang-modules/scala-lang-3/src/test/scala/com/baeldung/scala3/typesystem/OpaqueTypeAliasUnitTest.scala diff --git a/scala3-lang-2/README.md b/scala-lang-modules/scala-lang-4/README.md similarity index 88% rename from scala3-lang-2/README.md rename to scala-lang-modules/scala-lang-4/README.md index 9e51ae5cc..f8b84e55a 100644 --- a/scala3-lang-2/README.md +++ b/scala-lang-modules/scala-lang-4/README.md @@ -7,6 +7,6 @@ - [@targetName Annotation in Scala 3](https://www.baeldung.com/scala/targetname-annotation) - [Export Clause in Scala 3](https://www.baeldung.com/scala/scala-3-export) - [Introduction to Macros in Scala 3](https://www.baeldung.com/scala/macros-scala-3) -- [Difference Between NonFatal and Exception in Scala](https://www.baeldung.com/scala/nonfatal-vs-exception) - [Introduction to the @threadUnsafe Annotation in Scala 3](https://www.baeldung.com/scala/scala-3-threadunsafe) - [locally Block In Scala](https://www.baeldung.com/scala/locally-block) +- [Match Expression Improvements in Scala 3](https://www.baeldung.com/scala/scala-3-match-expression) diff --git a/scala3-lang-2/build.sbt b/scala-lang-modules/scala-lang-4/build.sbt similarity index 78% rename from scala3-lang-2/build.sbt rename to scala-lang-modules/scala-lang-4/build.sbt index b955a8259..57f3ce3af 100644 --- a/scala3-lang-2/build.sbt +++ b/scala-lang-modules/scala-lang-4/build.sbt @@ -1,7 +1,3 @@ -val scala3Version = "3.2.2" - -scalaVersion := scala3Version - /* extra runtime checks to find ill-formed trees or types as soon as they are created * and check compiler invariants for tree well-formedness */ diff --git a/scala-libraries-3/src/tapir/project/build.properties b/scala-lang-modules/scala-lang-4/project/build.properties similarity index 100% rename from scala-libraries-3/src/tapir/project/build.properties rename to scala-lang-modules/scala-lang-4/project/build.properties diff --git a/scala3-lang-2/src/main/java/com/baeldung/JavaOperator.java b/scala-lang-modules/scala-lang-4/src/main/java/com/baeldung/JavaOperator.java similarity index 79% rename from scala3-lang-2/src/main/java/com/baeldung/JavaOperator.java rename to scala-lang-modules/scala-lang-4/src/main/java/com/baeldung/JavaOperator.java index d01118180..5ee1676ce 100644 --- a/scala3-lang-2/src/main/java/com/baeldung/JavaOperator.java +++ b/scala-lang-modules/scala-lang-4/src/main/java/com/baeldung/JavaOperator.java @@ -1,5 +1,5 @@ package com.baeldung; -import com.baeldung.scala3.targetname.*; +import com.baeldung.scala.targetname.Operator; public class JavaOperator { public static void main(String[] args) { diff --git a/scala3-lang-2/src/main/scala/com/baeldung/scala3/TuplesInScala.scala b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/TuplesInScala.scala similarity index 95% rename from scala3-lang-2/src/main/scala/com/baeldung/scala3/TuplesInScala.scala rename to scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/TuplesInScala.scala index 056f1aeb8..168e3892e 100644 --- a/scala3-lang-2/src/main/scala/com/baeldung/scala3/TuplesInScala.scala +++ b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/TuplesInScala.scala @@ -1,4 +1,5 @@ -package com.baeldung.tuples +package com.baeldung.scala + object TupleTest { @main def tupleMain = { diff --git a/scala3-lang-2/src/main/scala/com/baeldung/scala3/contextualabstractions/Givens.scala b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/contextualabstractions/Givens.scala similarity index 85% rename from scala3-lang-2/src/main/scala/com/baeldung/scala3/contextualabstractions/Givens.scala rename to scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/contextualabstractions/Givens.scala index b0ee7f7b6..d70cf00f6 100644 --- a/scala3-lang-2/src/main/scala/com/baeldung/scala3/contextualabstractions/Givens.scala +++ b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/contextualabstractions/Givens.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala3.contextualabstractions +package com.baeldung.scala.contextualabstractions object Givens extends App { diff --git a/scala3-lang-2/src/main/scala/com/baeldung/scala3/contextualabstractions/UsingClause.scala b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/contextualabstractions/UsingClause.scala similarity index 80% rename from scala3-lang-2/src/main/scala/com/baeldung/scala3/contextualabstractions/UsingClause.scala rename to scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/contextualabstractions/UsingClause.scala index 3d8c7d92f..7395dea62 100644 --- a/scala3-lang-2/src/main/scala/com/baeldung/scala3/contextualabstractions/UsingClause.scala +++ b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/contextualabstractions/UsingClause.scala @@ -1,6 +1,6 @@ -package com.baeldung.scala3.contextualabstractions +package com.baeldung.scala.contextualabstractions -import com.baeldung.scala3.contextualabstractions.Givens.Item +import Givens.Item import Givens.{Item, priceOrdering, pageLimit} object UsingClause extends App { diff --git a/scala3-lang-2/src/main/scala/com/baeldung/scala3/locally/LocallyBlockSample.scala b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/locally/LocallyBlockSample.scala similarity index 96% rename from scala3-lang-2/src/main/scala/com/baeldung/scala3/locally/LocallyBlockSample.scala rename to scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/locally/LocallyBlockSample.scala index b8388642a..db36c70eb 100644 --- a/scala3-lang-2/src/main/scala/com/baeldung/scala3/locally/LocallyBlockSample.scala +++ b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/locally/LocallyBlockSample.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala3.locally +package com.baeldung.scala.locally object LocallyBlockSampleDangling extends App { class MyClass diff --git a/scala3-lang-2/src/main/scala/com/baeldung/scala3/macros/GenericMacros.scala b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/macros/GenericMacros.scala similarity index 89% rename from scala3-lang-2/src/main/scala/com/baeldung/scala3/macros/GenericMacros.scala rename to scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/macros/GenericMacros.scala index 6b0bb9fc3..8e362f980 100644 --- a/scala3-lang-2/src/main/scala/com/baeldung/scala3/macros/GenericMacros.scala +++ b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/macros/GenericMacros.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala3.macros +package com.baeldung.scala.macros import scala.quoted.* import scala.reflect.ClassTag diff --git a/scala3-lang-2/src/main/scala/com/baeldung/scala3/macros/OddEvenMacros.scala b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/macros/OddEvenMacros.scala similarity index 96% rename from scala3-lang-2/src/main/scala/com/baeldung/scala3/macros/OddEvenMacros.scala rename to scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/macros/OddEvenMacros.scala index 30e05782d..4a5fad4a5 100644 --- a/scala3-lang-2/src/main/scala/com/baeldung/scala3/macros/OddEvenMacros.scala +++ b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/macros/OddEvenMacros.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala3.macros +package com.baeldung.scala.macros import scala.quoted._ diff --git a/scala3-lang-2/src/main/scala/com/baeldung/scala3/mainfeatures/App.scala b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/mainfeatures/App.scala similarity index 97% rename from scala3-lang-2/src/main/scala/com/baeldung/scala3/mainfeatures/App.scala rename to scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/mainfeatures/App.scala index b6e4e595a..e85550a1e 100644 --- a/scala3-lang-2/src/main/scala/com/baeldung/scala3/mainfeatures/App.scala +++ b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/mainfeatures/App.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala3.mainfeatures +package com.baeldung.scala.mainfeatures import java.util import java.util.regex.Pattern diff --git a/scala3-lang-2/src/main/scala/com/baeldung/scala3/matchexpressions/MatchExpressionImprovements.scala b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/matchexpressions/MatchExpressionImprovements.scala similarity index 95% rename from scala3-lang-2/src/main/scala/com/baeldung/scala3/matchexpressions/MatchExpressionImprovements.scala rename to scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/matchexpressions/MatchExpressionImprovements.scala index 97d9fd6fc..2b20da594 100644 --- a/scala3-lang-2/src/main/scala/com/baeldung/scala3/matchexpressions/MatchExpressionImprovements.scala +++ b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/matchexpressions/MatchExpressionImprovements.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala3.matchexpressions +package com.baeldung.scala.matchexpressions object MatchExpressionImprovements { def wordFromOptionNoBraces(strOpt: Option[String]) = { diff --git a/scala3-lang-2/src/main/scala/com/baeldung/scala3/matchtypes/FirstComponentOfScala2.scala b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/matchtypes/FirstComponentOfScala2.scala similarity index 97% rename from scala3-lang-2/src/main/scala/com/baeldung/scala3/matchtypes/FirstComponentOfScala2.scala rename to scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/matchtypes/FirstComponentOfScala2.scala index dbec00f9f..2f10152d6 100644 --- a/scala3-lang-2/src/main/scala/com/baeldung/scala3/matchtypes/FirstComponentOfScala2.scala +++ b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/matchtypes/FirstComponentOfScala2.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala3.matchtypes +package com.baeldung.scala.matchtypes sealed trait FirstComponentOfScala2[-T] { type Result diff --git a/scala3-lang-2/src/main/scala/com/baeldung/scala3/matchtypes/MatchTypes.scala b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/matchtypes/MatchTypes.scala similarity index 94% rename from scala3-lang-2/src/main/scala/com/baeldung/scala3/matchtypes/MatchTypes.scala rename to scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/matchtypes/MatchTypes.scala index 667143ac6..d6b820f66 100644 --- a/scala3-lang-2/src/main/scala/com/baeldung/scala3/matchtypes/MatchTypes.scala +++ b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/matchtypes/MatchTypes.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala3.matchtypes +package com.baeldung.scala.matchtypes type FirstComponentOf[T] = T match case String => Option[Char] diff --git a/scala3-lang-2/src/main/scala/com/baeldung/scala3/targetname/TargetNameSample.scala b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/targetname/TargetNameSample.scala similarity index 96% rename from scala3-lang-2/src/main/scala/com/baeldung/scala3/targetname/TargetNameSample.scala rename to scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/targetname/TargetNameSample.scala index 3fa0cf8fa..7050535c2 100644 --- a/scala3-lang-2/src/main/scala/com/baeldung/scala3/targetname/TargetNameSample.scala +++ b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/targetname/TargetNameSample.scala @@ -1,4 +1,5 @@ -package com.baeldung.scala3.targetname +package com.baeldung.scala.targetname + import scala.annotation.targetName class Operator { diff --git a/scala3-lang-2/src/main/scala/com/baeldung/scala3/transparent/TrasparentTraitDemo.scala b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/transparent/TrasparentTraitDemo.scala similarity index 94% rename from scala3-lang-2/src/main/scala/com/baeldung/scala3/transparent/TrasparentTraitDemo.scala rename to scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/transparent/TrasparentTraitDemo.scala index 317eeebcd..62965b42c 100644 --- a/scala3-lang-2/src/main/scala/com/baeldung/scala3/transparent/TrasparentTraitDemo.scala +++ b/scala-lang-modules/scala-lang-4/src/main/scala/com/baeldung/scala/transparent/TrasparentTraitDemo.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala3.transparent +package com.baeldung.scala.transparent object NonTransparent { trait Marker diff --git a/scala3-lang-2/src/test/scala/com/baeldung/scala3/annotations/ThreadUnsafeAnnotationTest.scala b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/annotations/ThreadUnsafeAnnotationUnitTest.scala similarity index 94% rename from scala3-lang-2/src/test/scala/com/baeldung/scala3/annotations/ThreadUnsafeAnnotationTest.scala rename to scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/annotations/ThreadUnsafeAnnotationUnitTest.scala index 26f0c5d6f..9aabcd0b6 100644 --- a/scala3-lang-2/src/test/scala/com/baeldung/scala3/annotations/ThreadUnsafeAnnotationTest.scala +++ b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/annotations/ThreadUnsafeAnnotationUnitTest.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala3.annotations +package com.baeldung.scala.annotations import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers @@ -6,7 +6,7 @@ import org.scalatest.matchers.should.Matchers import java.util.concurrent.atomic.AtomicInteger import scala.annotation.threadUnsafe -class ThreadUnsafeAnnotationTest extends AnyFlatSpec with Matchers { +class ThreadUnsafeAnnotationUnitTest extends AnyFlatSpec with Matchers { it should "lock the entire class when lazy val is initialized" in { val lazyValClass = new LazyValClass() diff --git a/scala3-lang-2/src/test/scala/com/baeldung/scala3/contextualabstractions/GivensUsingClauseUnitTest.scala b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/contextualabstractions/GivensUsingClauseUnitTest.scala similarity index 83% rename from scala3-lang-2/src/test/scala/com/baeldung/scala3/contextualabstractions/GivensUsingClauseUnitTest.scala rename to scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/contextualabstractions/GivensUsingClauseUnitTest.scala index b6bf9defd..b420d4733 100644 --- a/scala3-lang-2/src/test/scala/com/baeldung/scala3/contextualabstractions/GivensUsingClauseUnitTest.scala +++ b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/contextualabstractions/GivensUsingClauseUnitTest.scala @@ -1,5 +1,6 @@ -package com.baeldung.scala3.contextualabstractions -import com.baeldung.scala3.contextualabstractions.Givens.Item +package com.baeldung.scala.contextualabstractions + +import Givens.Item import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec diff --git a/scala3-lang-2/src/test/scala/com/baeldung/scala3/exportclause/ExportClauseUnitTest.scala b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/exportclause/ExportClauseUnitTest.scala similarity index 98% rename from scala3-lang-2/src/test/scala/com/baeldung/scala3/exportclause/ExportClauseUnitTest.scala rename to scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/exportclause/ExportClauseUnitTest.scala index 2f48f5bad..bac1eae2f 100644 --- a/scala3-lang-2/src/test/scala/com/baeldung/scala3/exportclause/ExportClauseUnitTest.scala +++ b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/exportclause/ExportClauseUnitTest.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala3.exportclause +package com.baeldung.scala.exportclause import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers diff --git a/scala3-lang-2/src/test/scala/com/baeldung/scala3/locally/LocallyBlockTest.scala b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/locally/LocallyBlockUnitTest.scala similarity index 81% rename from scala3-lang-2/src/test/scala/com/baeldung/scala3/locally/LocallyBlockTest.scala rename to scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/locally/LocallyBlockUnitTest.scala index 183cf0dd5..0fcb31f9a 100644 --- a/scala3-lang-2/src/test/scala/com/baeldung/scala3/locally/LocallyBlockTest.scala +++ b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/locally/LocallyBlockUnitTest.scala @@ -1,9 +1,9 @@ -package com.baeldung.scala3.locally +package com.baeldung.scala.locally import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class LocallyBlockTest extends AnyFlatSpec with Matchers { +class LocallyBlockUnitTest extends AnyFlatSpec with Matchers { it should "create dangling code block without locally" in { // intentionally place a new line to create dangling code block diff --git a/scala3-lang-2/src/test/scala/com/baeldung/scala3/macros/GenericMacrosTest.scala b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/macros/GenericMacrosUnitTest.scala similarity index 84% rename from scala3-lang-2/src/test/scala/com/baeldung/scala3/macros/GenericMacrosTest.scala rename to scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/macros/GenericMacrosUnitTest.scala index cb20654fc..4d913964a 100644 --- a/scala3-lang-2/src/test/scala/com/baeldung/scala3/macros/GenericMacrosTest.scala +++ b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/macros/GenericMacrosUnitTest.scala @@ -1,9 +1,9 @@ -package com.baeldung.scala3.macros +package com.baeldung.scala.macros import org.scalatest.matchers.must.Matchers import org.scalatest.wordspec.AnyWordSpec -class GenericMacrosTest extends AnyWordSpec with Matchers { +class GenericMacrosUnitTest extends AnyWordSpec with Matchers { "generic macro" should { "return String for string argument" in { GenericMacros.getTypeMacro("this is a string") mustBe "String" diff --git a/scala3-lang-2/src/test/scala/com/baeldung/scala3/macros/OddEvenMacrosTest.scala b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/macros/OddEvenMacrosUnitTest.scala similarity index 93% rename from scala3-lang-2/src/test/scala/com/baeldung/scala3/macros/OddEvenMacrosTest.scala rename to scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/macros/OddEvenMacrosUnitTest.scala index cfd93ec40..9f9b24473 100644 --- a/scala3-lang-2/src/test/scala/com/baeldung/scala3/macros/OddEvenMacrosTest.scala +++ b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/macros/OddEvenMacrosUnitTest.scala @@ -1,9 +1,9 @@ -package com.baeldung.scala3.macros +package com.baeldung.scala.macros import org.scalatest.matchers.must.Matchers import org.scalatest.wordspec.AnyWordSpec -class OddEvenMacrosTest extends AnyWordSpec with Matchers { +class OddEvenMacrosUnitTest extends AnyWordSpec with Matchers { "inline macros" should { "return literal odd for odd number" in { val res: String = OddEvenMacros.oddEvenMacroInline(3) diff --git a/scala3-lang-2/src/test/scala/com/baeldung/scala3/matchexpressions/MatchExpressionImprovementstTest.scala b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/matchexpressions/MatchExpressionImprovementstUnitTest.scala similarity index 89% rename from scala3-lang-2/src/test/scala/com/baeldung/scala3/matchexpressions/MatchExpressionImprovementstTest.scala rename to scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/matchexpressions/MatchExpressionImprovementstUnitTest.scala index f21718d11..bec98590d 100644 --- a/scala3-lang-2/src/test/scala/com/baeldung/scala3/matchexpressions/MatchExpressionImprovementstTest.scala +++ b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/matchexpressions/MatchExpressionImprovementstUnitTest.scala @@ -1,10 +1,9 @@ -package com.baeldung.scala3.matchexpressions +package com.baeldung.scala.matchexpressions -import com.baeldung.scala3.matchexpressions.MatchExpressionImprovements import org.scalatest.matchers.must.Matchers import org.scalatest.wordspec.AnyWordSpec -class MatchExpressionImprovementsTest extends AnyWordSpec with Matchers { +class MatchExpressionImprovementstUnitTest extends AnyWordSpec with Matchers { val testNone = None val testSome = Some("Hello World!") diff --git a/scala3-lang-2/src/test/scala/com/baeldung/scala3/matchtypes/FirstComponentOfScala2Spec.scala b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/matchtypes/FirstComponentOfScala2UnitTest.scala similarity index 88% rename from scala3-lang-2/src/test/scala/com/baeldung/scala3/matchtypes/FirstComponentOfScala2Spec.scala rename to scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/matchtypes/FirstComponentOfScala2UnitTest.scala index 352cbe305..ec09b0ce9 100644 --- a/scala3-lang-2/src/test/scala/com/baeldung/scala3/matchtypes/FirstComponentOfScala2Spec.scala +++ b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/matchtypes/FirstComponentOfScala2UnitTest.scala @@ -1,9 +1,9 @@ -package com.baeldung.scala3.matchtypes +package com.baeldung.scala.matchtypes import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class FirstComponentOfScala2Spec extends AnyWordSpec with Matchers { +class FirstComponentOfScala2UnitTest extends AnyWordSpec with Matchers { "firstComponentOf" should { "return the first digit of an Int" in { FirstComponentOfScala2.firstComponentOf(-153) shouldEqual 1 diff --git a/scala3-lang-2/src/test/scala/com/baeldung/scala3/matchtypes/MatchTypesSpec.scala b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/matchtypes/MatchTypesUnitTest.scala similarity index 88% rename from scala3-lang-2/src/test/scala/com/baeldung/scala3/matchtypes/MatchTypesSpec.scala rename to scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/matchtypes/MatchTypesUnitTest.scala index c69b18070..b171f3135 100644 --- a/scala3-lang-2/src/test/scala/com/baeldung/scala3/matchtypes/MatchTypesSpec.scala +++ b/scala-lang-modules/scala-lang-4/src/test/scala/com/baeldung/scala/matchtypes/MatchTypesUnitTest.scala @@ -1,9 +1,9 @@ -package com.baeldung.scala3.matchtypes +package com.baeldung.scala.matchtypes import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class MatchTypesSpec extends AnyWordSpec with Matchers: +class MatchTypesUnitTest extends AnyWordSpec with Matchers: "firstComponentOf" should { "return the first digit of an Int" in { firstComponentOf(-153) shouldEqual 1 diff --git a/scala-lang-modules/scala-lang-5/src/main/scala/com/baeldung/scala/contextfunctions/Examples.scala b/scala-lang-modules/scala-lang-5/src/main/scala/com/baeldung/scala/contextfunctions/Examples.scala new file mode 100644 index 000000000..dcec0eff8 --- /dev/null +++ b/scala-lang-modules/scala-lang-5/src/main/scala/com/baeldung/scala/contextfunctions/Examples.scala @@ -0,0 +1,36 @@ +package com.baeldung.scala.contextfunctions + +val increment: Int ?=> Int = summon[Int] + 1 + +val repeatString: String ?=> Int ?=> String = summon[String].repeat(summon[Int]) + +opaque type WrappedAlso[T] = T + +def it[T](using a: WrappedAlso[T]): T = a + +extension [T](x: T) + def also(f: WrappedAlso[T] ?=> Unit): T = + f(using x) + x + +@main +def main(): Unit = { + // Implicit parameter provided via the scope + given Int = 1 + println(s"Result scope: $increment") + + // Implicit parameter provided explicitly to the call + println(s"Result explicit: ${increment(using 5)}") + + println(s"Result repeatString: ${repeatString(using "Baeldung")(using 3)}") + + val numbers = List(1, 2, 3, 4, 5) + numbers + .also(println(s"The list before adding 6: $it")) + .appended(6) + .also(println(s"The list after adding 6: $it")) + + numbers + .also(it appended 7) + .also(println(s"The list after adding 7: $it")) +} diff --git a/scala-lang/README.md b/scala-lang-modules/scala-lang/README.md similarity index 100% rename from scala-lang/README.md rename to scala-lang-modules/scala-lang/README.md diff --git a/play-scala/async-tasks/project/build.properties b/scala-lang-modules/scala-lang/project/build.properties similarity index 100% rename from play-scala/async-tasks/project/build.properties rename to scala-lang-modules/scala-lang/project/build.properties diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/OuterStackedSample.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/OuterStackedSample.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/OuterStackedSample.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/OuterStackedSample.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/callbynameandvalue/CallByNameCallByValue.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/callbynameandvalue/CallByNameCallByValue.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/callbynameandvalue/CallByNameCallByValue.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/callbynameandvalue/CallByNameCallByValue.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/conditional/ScalaConditionalExpressions.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/conditional/ScalaConditionalExpressions.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/conditional/ScalaConditionalExpressions.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/conditional/ScalaConditionalExpressions.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/equality/package.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/equality/package.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/equality/package.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/equality/package.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/functionsandmethods/FunctionsAndMethods.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/functionsandmethods/FunctionsAndMethods.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/functionsandmethods/FunctionsAndMethods.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/functionsandmethods/FunctionsAndMethods.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/mutability/ImmutabilityCar.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/mutability/ImmutabilityCar.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/mutability/ImmutabilityCar.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/mutability/ImmutabilityCar.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/operators/ScalaOperators.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/operators/ScalaOperators.scala similarity index 95% rename from scala-lang/src/main/scala-2/com/baeldung/scala/operators/ScalaOperators.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/operators/ScalaOperators.scala index f3dfe43a5..bce84d829 100644 --- a/scala-lang/src/main/scala-2/com/baeldung/scala/operators/ScalaOperators.scala +++ b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/operators/ScalaOperators.scala @@ -9,11 +9,11 @@ object ScalaOperators { assert(1.+(2) == 3) assert("Baeldung".charAt(0) == 'B') - val char: Char = "Baeldung" charAt 0 + val char: Char = "Baeldung" `charAt` 0 assert(char == 'B') assert("Baeldung".replace('g', 'G') == "BaeldunG") - val str: String = "Baeldung" replace ('g', 'G') + val str: String = "Baeldung" `replace` ('g', 'G') assert(str == "BaeldunG") // Unary operator notation diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/Importing.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/Importing.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/Importing.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/Importing.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/InnerStackSample.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/InnerStackSample.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/InnerStackSample.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/InnerStackSample.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/package.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/package.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/package.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/package.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/stacking/StackedAccess.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/stacking/StackedAccess.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/stacking/StackedAccess.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/stacking/StackedAccess.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/stacking/StackedPackage.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/stacking/StackedPackage.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/stacking/StackedPackage.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/stacking/StackedPackage.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/vehicle/Bicycle.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/vehicle/Bicycle.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/vehicle/Bicycle.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/vehicle/Bicycle.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/vehicle/Car.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/vehicle/Car.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/vehicle/Car.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/vehicle/Car.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/vehicle/Vehicle.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/vehicle/Vehicle.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/packageimport/vehicle/Vehicle.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/packageimport/vehicle/Vehicle.scala diff --git a/scala-lang/src/main/scala-2/com/baeldung/scala/typehierarchy/TypeHierarchy.scala b/scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/typehierarchy/TypeHierarchy.scala similarity index 100% rename from scala-lang/src/main/scala-2/com/baeldung/scala/typehierarchy/TypeHierarchy.scala rename to scala-lang-modules/scala-lang/src/main/scala/com/baeldung/scala/typehierarchy/TypeHierarchy.scala diff --git a/scala-lang/src/main/scala-2/defvarval/LazyValues.scala b/scala-lang-modules/scala-lang/src/main/scala/defvarval/LazyValues.scala similarity index 100% rename from scala-lang/src/main/scala-2/defvarval/LazyValues.scala rename to scala-lang-modules/scala-lang/src/main/scala/defvarval/LazyValues.scala diff --git a/scala-lang/src/main/scala-2/defvarval/Methods.scala b/scala-lang-modules/scala-lang/src/main/scala/defvarval/Methods.scala similarity index 100% rename from scala-lang/src/main/scala-2/defvarval/Methods.scala rename to scala-lang-modules/scala-lang/src/main/scala/defvarval/Methods.scala diff --git a/scala-lang/src/main/scala-2/defvarval/Values.scala b/scala-lang-modules/scala-lang/src/main/scala/defvarval/Values.scala similarity index 100% rename from scala-lang/src/main/scala-2/defvarval/Values.scala rename to scala-lang-modules/scala-lang/src/main/scala/defvarval/Values.scala diff --git a/scala-lang/src/main/scala-2/defvarval/Variables.scala b/scala-lang-modules/scala-lang/src/main/scala/defvarval/Variables.scala similarity index 100% rename from scala-lang/src/main/scala-2/defvarval/Variables.scala rename to scala-lang-modules/scala-lang/src/main/scala/defvarval/Variables.scala diff --git a/scala-lang/src/test/scala-2/com/baeldung/scala/callbynameandvalue/CallByNameCallByValueUnitTest.scala b/scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/callbynameandvalue/CallByNameCallByValueUnitTest.scala similarity index 100% rename from scala-lang/src/test/scala-2/com/baeldung/scala/callbynameandvalue/CallByNameCallByValueUnitTest.scala rename to scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/callbynameandvalue/CallByNameCallByValueUnitTest.scala diff --git a/scala-lang/src/test/scala-2/com/baeldung/scala/caseclasses/CaseClassesUnitTest.scala b/scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/caseclasses/CaseClassesUnitTest.scala similarity index 95% rename from scala-lang/src/test/scala-2/com/baeldung/scala/caseclasses/CaseClassesUnitTest.scala rename to scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/caseclasses/CaseClassesUnitTest.scala index 510357df1..37820b50c 100644 --- a/scala-lang/src/test/scala-2/com/baeldung/scala/caseclasses/CaseClassesUnitTest.scala +++ b/scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/caseclasses/CaseClassesUnitTest.scala @@ -43,7 +43,7 @@ class CaseClassesUnitTest { @Test def givenTuple_whenCallingApply_thenCreatesNewInstance() = { val tuple = ("PL", 776, 15366) - val covidPL = (CovidCountryStats.apply _).tupled(tuple) + val covidPL = (CovidCountryStats.apply).tupled(tuple) assertEquals(CovidCountryStats("PL", 776, 15366), covidPL) } diff --git a/scala-lang/src/test/scala-2/com/baeldung/scala/conditional/ScalaConditionalExpressionsUnitTest.scala b/scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/conditional/ScalaConditionalExpressionsUnitTest.scala similarity index 100% rename from scala-lang/src/test/scala-2/com/baeldung/scala/conditional/ScalaConditionalExpressionsUnitTest.scala rename to scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/conditional/ScalaConditionalExpressionsUnitTest.scala diff --git a/scala-lang/src/test/scala-2/com/baeldung/scala/equality/EqualityUnitTest.scala b/scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/equality/EqualityUnitTest.scala similarity index 100% rename from scala-lang/src/test/scala-2/com/baeldung/scala/equality/EqualityUnitTest.scala rename to scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/equality/EqualityUnitTest.scala diff --git a/scala-lang/src/test/scala-2/com/baeldung/scala/functionsandmethods/FunctionsAndMethodsUnitTest.scala b/scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/functionsandmethods/FunctionsAndMethodsUnitTest.scala similarity index 100% rename from scala-lang/src/test/scala-2/com/baeldung/scala/functionsandmethods/FunctionsAndMethodsUnitTest.scala rename to scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/functionsandmethods/FunctionsAndMethodsUnitTest.scala diff --git a/scala-lang/src/test/scala-2/com/baeldung/scala/mutability/ImmutabilityCarUnitTest.scala b/scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/mutability/ImmutabilityCarUnitTest.scala similarity index 100% rename from scala-lang/src/test/scala-2/com/baeldung/scala/mutability/ImmutabilityCarUnitTest.scala rename to scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/mutability/ImmutabilityCarUnitTest.scala diff --git a/scala-lang/src/test/scala-2/com/baeldung/scala/mutability/ImmutableCollectionsUnitTest.scala b/scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/mutability/ImmutableCollectionsUnitTest.scala similarity index 100% rename from scala-lang/src/test/scala-2/com/baeldung/scala/mutability/ImmutableCollectionsUnitTest.scala rename to scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/mutability/ImmutableCollectionsUnitTest.scala diff --git a/scala-lang/src/test/scala-2/com/baeldung/scala/mutability/MutableCollectionsUnitTest.scala b/scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/mutability/MutableCollectionsUnitTest.scala similarity index 100% rename from scala-lang/src/test/scala-2/com/baeldung/scala/mutability/MutableCollectionsUnitTest.scala rename to scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/mutability/MutableCollectionsUnitTest.scala diff --git a/scala-lang/src/test/scala-2/com/baeldung/scala/typehierarchy/TypeHierarchyUnitTest.scala b/scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/typehierarchy/TypeHierarchyUnitTest.scala similarity index 100% rename from scala-lang/src/test/scala-2/com/baeldung/scala/typehierarchy/TypeHierarchyUnitTest.scala rename to scala-lang-modules/scala-lang/src/test/scala/com/baeldung/scala/typehierarchy/TypeHierarchyUnitTest.scala diff --git a/scala-lang/project/build.properties b/scala-lang/project/build.properties deleted file mode 100644 index 797e7ccfd..000000000 --- a/scala-lang/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.3.10 diff --git a/scala-libraries-2/README.md b/scala-libraries-2/README.md index 5032084e1..97c76dc43 100644 --- a/scala-libraries-2/README.md +++ b/scala-libraries-2/README.md @@ -1,12 +1,2 @@ ## Relevant Articles: -- [Parsing JSON with Circe](https://www.baeldung.com/scala/circe-json) -- [Asynchronous and Reactive Programming With Monix in Scala](https://www.baeldung.com/scala/monix) -- [Database Access with Play](https://www.baeldung.com/scala/play-database-access) -- [ScalaCache: A Caching Library To Rule Them All](https://www.baeldung.com/scala/scalacache) -- [Testing Akka Actors](https://www.baeldung.com/scala/testing-akka-actors) -- [Introduction to ScalaCheck](https://www.baeldung.com/scala/scalacheck) -- [Simple REST Requests Using Requests-Scala](https://www.baeldung.com/scala/rest-with-requests-scala) -- [Better Enumerations in Scala Using Enumeratum](https://www.baeldung.com/scala/enumeratum) -- [Guide to elastic4s – Elasticsearch Scala Client](https://www.baeldung.com/scala/elastic4s-elasticsearch-client) -- [Convert a String to Date in Scala](https://www.baeldung.com/scala/string-to-date) diff --git a/scala-libraries-2/src/main/resources/application.conf b/scala-libraries-2/src/main/resources/application.conf deleted file mode 100644 index 25ff922a8..000000000 --- a/scala-libraries-2/src/main/resources/application.conf +++ /dev/null @@ -1,11 +0,0 @@ -slick.dbs.default { - profile = "slick.jdbc.PostgresProfile$" - db { - driver = "org.postgresql.Driver" - url="jdbc:postgres://localhost:5432/postgres" - user=postgres - password=postgres - numThreads=20 - maxConnections=20 - } -} \ No newline at end of file diff --git a/scala-libraries-2/src/main/scala/com/baeldung/chimney/Codec.scala b/scala-libraries-2/src/main/scala/com/baeldung/chimney/Codec.scala new file mode 100644 index 000000000..18a225758 --- /dev/null +++ b/scala-libraries-2/src/main/scala/com/baeldung/chimney/Codec.scala @@ -0,0 +1,10 @@ +package com.baeldung.chimney + +import io.scalaland.chimney.*, dsl.*, partial.* + +object ChimneyCodec extends App: + + case class Domain(a: Int, b: String) + case class Dto(b: Option[String], a: Option[Int]) + + given Codec[Domain, Dto] = Codec.derive diff --git a/scala-libraries-2/src/main/scala/com/baeldung/chimney/Iso.scala b/scala-libraries-2/src/main/scala/com/baeldung/chimney/Iso.scala new file mode 100644 index 000000000..8f5f2d45a --- /dev/null +++ b/scala-libraries-2/src/main/scala/com/baeldung/chimney/Iso.scala @@ -0,0 +1,10 @@ +package com.baeldung.chimney + +import io.scalaland.chimney.*, dsl.*, partial.* + +object ChimneyIso extends App: + + case class StructuredItem(uuid: java.util.UUID) + case class DomainItem(uuid: java.util.UUID) + + given Iso[StructuredItem, DomainItem] = Iso.derive diff --git a/scala-libraries-2/src/main/scala/com/baeldung/chimney/PartialTransformer.scala b/scala-libraries-2/src/main/scala/com/baeldung/chimney/PartialTransformer.scala new file mode 100644 index 000000000..e61534e5a --- /dev/null +++ b/scala-libraries-2/src/main/scala/com/baeldung/chimney/PartialTransformer.scala @@ -0,0 +1,19 @@ +package com.baeldung.chimney + +import io.scalaland.chimney.*, dsl.*, partial.* + +object ChimneyPartialTransformer extends App: + + val fn: Int => Boolean = + case 0 => false + case 1 => true + case i => throw Exception(s"Provided integer invalid: $i") + + given PartialTransformer[Int, Boolean] = + PartialTransformer.fromFunction(fn) + + val result: Result[Boolean] = 0.transformIntoPartial[Boolean] + + result match + case Result.Value(bool) => println(bool) + case Result.Errors(errs) => println(errs) diff --git a/scala-libraries-2/src/main/scala/com/baeldung/chimney/Patcher.scala b/scala-libraries-2/src/main/scala/com/baeldung/chimney/Patcher.scala new file mode 100644 index 000000000..5c0d969a7 --- /dev/null +++ b/scala-libraries-2/src/main/scala/com/baeldung/chimney/Patcher.scala @@ -0,0 +1,39 @@ +package com.baeldung.chimney + +import io.scalaland.chimney.dsl.* + +object ChimneyPatcher extends App: + + case class Book( + name: Title, + authors: List[Author], + isbn: ISBN + ) + + case class Title(name: String) extends AnyVal + case class Author(name: String, surname: String) + + type ISBN = Option[String] + + case class UpdateISBN(isbn: ISBN) + + val book = Book( + name = Title("Synergetics"), + authors = List(Author("Buckminster", "Fuller")), + isbn = None + ) + + val isbnUpdateForm = UpdateISBN( + isbn = Some("978-0206532048") + ) + + val hardcover: Book = book.patchUsing(isbnUpdateForm) + + // Standard Library Alternative + + val softcover: Book = + book.copy( + authors = + List(Author("Buckminster", "Fuller"), Author("Edmund", "Applewhite")), + isbn = Some("978-0020653202") + ) diff --git a/scala-libraries-2/src/main/scala/com/baeldung/chimney/Transformer.scala b/scala-libraries-2/src/main/scala/com/baeldung/chimney/Transformer.scala new file mode 100644 index 000000000..3fe8577e7 --- /dev/null +++ b/scala-libraries-2/src/main/scala/com/baeldung/chimney/Transformer.scala @@ -0,0 +1,95 @@ +package com.baeldung.chimney + +import io.scalaland.chimney.*, dsl.*, partial.* + +object ChimneyTransformers extends App: + + class MyType(val a: Int) + + class MyOtherType(val b: String): + override def toString: String = s"MyOtherType($b)" + + val transformer: Transformer[MyType, MyOtherType] = (src: MyType) => + new MyOtherType(src.a.toString) + + transformer.transform(new MyType(10)) // new MyOtherType("10") + + implicit val transformerAsImplicit: Transformer[MyType, MyOtherType] = + transformer + + (new MyType(10)).transformInto[MyOtherType] + + // Transitive Given Instances + + trait Serial[T]: + def serial(v: T): String + + given Serial[MyOtherType] with + def serial(v: MyOtherType): String = v.toString + + given [F, T](using Serial[T], Transformer[F, T]): Serial[F] with + def serial(v: F): String = + summon[Serial[T]].serial: + summon[Transformer[F, T]].transform(v) + + // Automatic Case Class Transformation + + // BookDTO + + case class BookDTO( + name: String, // 1. primitive + authors: Seq[AuthorDTO], // 2. Seq collection + isbn: Option[String] // 3. Option type + ) + + case class AuthorDTO(name: String, surname: String) + + // Book Domain Model + + case class Book( + name: Title, + authors: List[Author], + isbn: ISBN + ) + + case class Title(name: String) extends AnyVal + case class Author(name: String, surname: String) + + type ISBN = Option[String] + + // we can do a transformation: + + val book = Book( + name = Title("The Universal One"), + authors = List(Author("Walter", "Russell")), + isbn = None + ) + + val bookDTO: BookDTO = book.transformInto[BookDTO] + + // Standard Library alternatives + + // Selectable + + class Record(elems: (String, Any)*) extends Selectable: + private val fields = elems.toMap + def selectDynamic(name: String): Any = fields(name) + + type BookRecord = Record { + val name: Title + val authors: List[Author] + val isbn: ISBN + } + + val naturesOpenSecret: BookRecord = Record( + "name" -> Title("Nature's Open Secret"), + "authors" -> List(Author("Rudolph", "Steiner")), + "isbn" -> Some("978-0880103930") + ).asInstanceOf[BookRecord] + + // Tuple Generics + + val bookTuple: (Title, List[Author], ISBN) = Tuple.fromProductTyped(book) + + val bookAgain: Book = + summon[deriving.Mirror.Of[Book]].fromProduct(bookTuple) diff --git a/scala-libraries-3/README.md b/scala-libraries-3/README.md deleted file mode 100644 index eca05eeab..000000000 --- a/scala-libraries-3/README.md +++ /dev/null @@ -1,9 +0,0 @@ -### Relevant Articles: - -- [Load Configuration Files In Scala Using PureConfig](https://www.baeldung.com/scala/pureconfig-load-config-files) -- [Introduction to http4s](https://www.baeldung.com/scala/http4s-intro) -- [Introduction to ScalaMock](https://www.baeldung.com/scala/scalamock) -- [A Guide to the Scala Retry Library](https://www.baeldung.com/scala/retry-library) -- [Introduction to Lightbend Config](https://www.baeldung.com/scala/lightbend-config) -- [Introduction to Apache Log4j in Scala](https://www.baeldung.com/scala/apache-log4j) -- [Introduction to Tapir](https://www.baeldung.com/scala/tapir) \ No newline at end of file diff --git a/scala-libraries-4/README.md b/scala-libraries-4/README.md deleted file mode 100644 index caaf185dc..000000000 --- a/scala-libraries-4/README.md +++ /dev/null @@ -1,11 +0,0 @@ -## Relevant Articles - -- [Introduction to uTest](https://www.baeldung.com/scala/utest-intro) -- [Introduction to scala-async](https://www.baeldung.com/scala/scala-async) -- [Introduction to Skunk – Scala Driver for PostgreSQL](https://www.baeldung.com/scala/skunk-postgresql-driver) -- [Apache Pulsar Scala Client – pulsar4s](https://www.baeldung.com/scala/pulsar4s) -- [Logging in Scala Applications Using Scala-Logging](https://www.baeldung.com/scala/scala-logging) -- [Making Integration Testing Easier With TestContainers-scala](https://www.baeldung.com/scala/testcontainers-scala) -- [Writer Monad in Cats](https://www.baeldung.com/scala/writer-monad-in-cats) -- [AWScala – AWS SDK for Scala](https://www.baeldung.com/scala/awscala-aws-sdk-for-scala) -- [Working with Redis in Scala](https://www.baeldung.com/scala/redis) diff --git a/scala-libraries-4/src/redis-intro/build.sbt b/scala-libraries-4/src/redis-intro/build.sbt deleted file mode 100644 index b2d2e646a..000000000 --- a/scala-libraries-4/src/redis-intro/build.sbt +++ /dev/null @@ -1,14 +0,0 @@ -import Dependencies._ - -ThisBuild / scalaVersion := "2.13.8" -ThisBuild / version := "1.0.0" -ThisBuild / organization := "com.baeldung" - -lazy val root = (project in file(".")) - .settings( - name := "Redis Intro", - libraryDependencies += redisClients, - libraryDependencies += jacksonScala, - libraryDependencies += scalaTest % Test, - libraryDependencies += mockito % Test - ) diff --git a/scala-libraries-4/src/redis-intro/project/Dependencies.scala b/scala-libraries-4/src/redis-intro/project/Dependencies.scala deleted file mode 100644 index 07ebd10b4..000000000 --- a/scala-libraries-4/src/redis-intro/project/Dependencies.scala +++ /dev/null @@ -1,9 +0,0 @@ -import sbt._ - -object Dependencies { - lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.2.11" - lazy val redisClients = "redis.clients" % "jedis" % "4.3.1" - lazy val jacksonScala = - "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.14.1" - lazy val mockito = "org.scalatestplus" %% "mockito-3-4" % "3.2.10.0" -} diff --git a/scala-libraries-4/src/redis-intro/project/build.properties b/scala-libraries-4/src/redis-intro/project/build.properties deleted file mode 100644 index c8fcab543..000000000 --- a/scala-libraries-4/src/redis-intro/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/scala-libraries-5/README.md b/scala-libraries-5/README.md deleted file mode 100644 index 4e3fa6fb2..000000000 --- a/scala-libraries-5/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## Relevant Articles -- [Introduction to Spire](https://www.baeldung.com/scala/spire-intro) -- [Introduction to Kafka With Scala](https://www.baeldung.com/scala/kafka) diff --git a/scala-libraries-config/README.md b/scala-libraries-config/README.md new file mode 100644 index 000000000..59809077a --- /dev/null +++ b/scala-libraries-config/README.md @@ -0,0 +1,5 @@ +### Relevant Articles: + +- [Introduction to Lightbend Config](https://www.baeldung.com/scala/lightbend-config) +- [Scala App Configurations With Clarity Using ClearConfig](https://www.baeldung.com/scala/clearconfig) +- [Handling YAML in Scala 3](https://www.baeldung.com/scala/yaml-scala-3) \ No newline at end of file diff --git a/scala3-libraries/src/main/resources/kafka.conf b/scala-libraries-config/src/main/resources/kafka.conf similarity index 100% rename from scala3-libraries/src/main/resources/kafka.conf rename to scala-libraries-config/src/main/resources/kafka.conf diff --git a/scala3-libraries/src/main/resources/notification-prod.conf b/scala-libraries-config/src/main/resources/notification-prod.conf similarity index 100% rename from scala3-libraries/src/main/resources/notification-prod.conf rename to scala-libraries-config/src/main/resources/notification-prod.conf diff --git a/scala3-libraries/src/main/resources/notification.conf b/scala-libraries-config/src/main/resources/notification.conf similarity index 100% rename from scala3-libraries/src/main/resources/notification.conf rename to scala-libraries-config/src/main/resources/notification.conf diff --git a/scala-libraries-config/src/main/resources/postgresConfig.json b/scala-libraries-config/src/main/resources/postgresConfig.json new file mode 100644 index 000000000..19b67de22 --- /dev/null +++ b/scala-libraries-config/src/main/resources/postgresConfig.json @@ -0,0 +1,4 @@ +{ + "username": "username", + "password": "password" +} \ No newline at end of file diff --git a/scala-libraries-config/src/main/resources/postgresConfig.yaml b/scala-libraries-config/src/main/resources/postgresConfig.yaml new file mode 100644 index 000000000..103cb57bd --- /dev/null +++ b/scala-libraries-config/src/main/resources/postgresConfig.yaml @@ -0,0 +1,2 @@ +username: username +password: password \ No newline at end of file diff --git a/scala-libraries-config/src/main/scala/com/baeldung/circeyaml/YamlExample.scala b/scala-libraries-config/src/main/scala/com/baeldung/circeyaml/YamlExample.scala new file mode 100644 index 000000000..9b8db5cce --- /dev/null +++ b/scala-libraries-config/src/main/scala/com/baeldung/circeyaml/YamlExample.scala @@ -0,0 +1,156 @@ +package com.baeldung.circeyaml + +import io.circe.yaml +import io.circe.* +import cats.syntax.either.* +import io.circe.generic.auto.* +import java.io.FileReader +import java.io.File +import java.nio.file.Paths +import scala.util.Try +import io.circe.parser.* +import java.io.FileWriter +import io.circe.yaml.syntax.* +import io.circe.syntax.* + +object YamlExample: + case class Server(host: String, port: Int) + case class OrdersConfig( + name: String, + server: Server, + serverType: List[String] + ) + + val ordersYamlConfig: String = + """ + name: Orders String + server: + host: localhost + port: 8080 + serverType: + - Http + - Grpc + """ + val ordersStringConfig: Either[ParsingFailure, Json] = + yaml.parser.parse(ordersYamlConfig) + + def processJson( + json: Either[ParsingFailure, Json] + ): Either[Error, OrdersConfig] = + json + .leftMap(err => err: Error) + .flatMap(_.as[OrdersConfig]) + + def printValue(value: Either[Error | Throwable, OrdersConfig]) = + value match + case Right(v) => println(v) + case Left(err) => println(err.getMessage) + + // Reading a yaml file + val yamlFileReader: Either[Throwable, FileReader] = + Try { + new FileReader( + "src/main/scala/resources/orders.yaml" + ) + }.toEither + + val ordersFileConfig: Either[Throwable, OrdersConfig] = + yamlFileReader + .map(fileReader => processJson(yaml.parser.parse(fileReader))) + .flatten + + // Reading multiple yaml documents in a single file. + val yamlFileReader2: Either[Throwable, FileReader] = + Try { + new FileReader( + "src/main/scala/resources/service.yaml" + ) + }.toEither + + val ordersFileConfig2: Either[Throwable, List[Either[Error, OrdersConfig]]] = + yamlFileReader2 + .map(fileReader => yaml.parser.parseDocuments(fileReader).toList) + .map(_.map(processJson)) + + def fileWriter(path: String): Either[Throwable, FileWriter] = + Try { + new FileWriter(new File(path)) + }.toEither + + def writeYaml(jsnValue: Json, fw: FileWriter, path: String): String = + Try { + fw.write(jsnValue.asYaml.spaces2) + fw.close() + }.fold( + e => e.getMessage(), + _ => s"${Paths.get(path).getFileName().toString()} has been written" + ) + + val jsonString = + """ + { + "name": "Orders Json", + "server": + { + "host": "localhost", + "port": 8080 + }, + "serverType": ["Http", "Grpc"] + } + """ + + def writeJsonStr(path: String, jsonStr: String): Either[Throwable, String] = + for + jsnValue <- parse(jsonString) + fw <- fileWriter(path) + yield writeYaml(jsnValue, fw, path) + + val myCaseClass = + OrdersConfig("Orders", Server("localhost", 8080), List("Http", "Grpc")) + + def writeOrdersConfig(path: String, oc: OrdersConfig): String = + fileWriter(path) match + case Right(fw) => writeYaml(oc.asJson, fw, path) + case Left(err) => err.getMessage + +end YamlExample + +@main +def program = + import YamlExample.* + // Reading a Yaml String + printValue(processJson(ordersStringConfig)) + + /** OrdersConfig(Orders String,Server(localhost,8080),List(Http, Grpc)) + */ + + // Reading from a yaml File + printValue(ordersFileConfig) + + /** OrdersConfig(Orders File,Server(localhost,8080),List(Http, Grpc)) + */ + + ordersFileConfig2 match + case Right(lst) => + lst.foreach(printValue) + case Left(err) => println(err.getMessage) + + /** OrdersConfig(Orders,Server(localhost,8080),List(Http, Grpc)) + * OrdersConfig(Test,Server(localhost,9999),List(Http, Grpc)) + */ + + // write json String to yaml file + writeJsonStr("src/main/resources/sample.yaml", jsonString) match + case Right(v) => println(v) + case Left(err) => println(err.getMessage) + + /** sample.yaml has been written + */ + + // write case class to yaml file + println( + writeOrdersConfig("src/main/resources/sample2.yaml", myCaseClass) + ) + + /** sample2.yaml has been written + */ diff --git a/scala-libraries-config/src/main/scala/com/baeldung/cirisconfig/Configuration.scala b/scala-libraries-config/src/main/scala/com/baeldung/cirisconfig/Configuration.scala new file mode 100644 index 000000000..bb3194248 --- /dev/null +++ b/scala-libraries-config/src/main/scala/com/baeldung/cirisconfig/Configuration.scala @@ -0,0 +1,138 @@ +package cirisconfig + +import ciris.* +import cats.syntax.all.* +import cats.effect.* +import java.nio.file.Path +import ciris.circe.circeConfigDecoder +import io.circe.Decoder +import cats.effect.Async +import ciris.circe.yaml.circeYamlConfigDecoder + +object Configuration: + + final case class PostgresConfig(username: String, password: String) + + def postgresConfig: ConfigValue[Effect, PostgresConfig] = + ( + env("POSTGRES_USERNAME").as[String], + env("POSTGRES_PASSWORD").as[String] + ).parMapN(PostgresConfig.apply) + + final case class PostgresConfig2( + username: Option[String], + password: Option[String] + ) + + def postgresConfig2: ConfigValue[Effect, PostgresConfig2] = + ( + env("POSTGRES_USERNAME").as[String].option, + env("POSTGRES_PASSWORD").as[String].option + ).parMapN(PostgresConfig2.apply) + + case class Username(name: String) + object Username: + given ConfigDecoder[String, Username] = + ConfigDecoder[String, String].map(Username.apply) + + case class Password(value: String) + object Password: + given ConfigDecoder[String, Password] = + ConfigDecoder[String, String].map(Password.apply) + + final case class PostgresConfig3( + username: Option[Username], + password: Option[Password] + ) + + def postgresConfig3: ConfigValue[Effect, PostgresConfig3] = + ( + env("POSTGRES_USERNAME").as[Username].option, + env("POSTGRES_PASSWORD").as[Password].option + ).parMapN(PostgresConfig3.apply) + + final case class PostgresConfig4(username: Username, password: Password) + + object PostgresConfig4: + // given ConfigDecoder[String, PostgresConfig4] = + // circeConfigDecoder("PostgresConfig4") + + given ConfigDecoder[String, PostgresConfig4] = + circeYamlConfigDecoder("PostgresConfig4") + + given Decoder[PostgresConfig4] = Decoder.instance { h => + for + username <- h.get[String]("username") + password <- h.get[String]("password") + yield PostgresConfig4(Username(username), Password(password)) + } + + val postgresConfig4: ConfigValue[Effect, PostgresConfig4] = + file( + Path.of("src/main/resources/postgresConfig.json") + ).as[PostgresConfig4] + + def postgresConfig5[F[_]: Async]: F[Either[ConfigError, PostgresConfig4]] = + file( + Path.of("src/main/resources/postgresConfig.json") + ).as[PostgresConfig4].attempt[F] + + def postgresConfig6[F[_]: Async]: F[Either[ConfigError, PostgresConfig4]] = + file( + Path.of("src/main/resources/postgresConfig.yaml") + ).as[PostgresConfig4].attempt[F] + + // handling secrets + case class Password2(value: Secret[String]) + object Password2: + def apply(value: String) = + new Password2(Secret(value)) + + def postgresConfig7[F[_]: Async]: F[Either[ConfigError, PostgresConfig4]] = + file( + Path.of("src/main/resources/missing.yaml") + ).as[PostgresConfig4] + .default { + PostgresConfig4(Username("username"), Password("password")) + } + .attempt[F] + +object program extends IOApp.Simple: + import Configuration.* + + override def run: IO[Unit] = + // blows up application + // postgresConfig.load[IO].map(println).void + + // handled errors + // postgresConfig2.load[IO].map(println).void + + // added typeclasses + // postgresConfig3.load[IO].map(println).void + + // config loading from json file + // postgresConfig4.load[IO].map(println).void + + // managing errors + // postgresConfig5[IO].map{config => + // config match + // case Right(value) => println(value) + // case Left(err) => err.messages.map(println) + // } + + // config loading from yaml file + // postgresConfig6[IO].map{config => + // config match + // case Right(value) => println(value) + // case Left(err) => err.messages.map(println) + // } + + // config loading with secret + // IO(println(Password2(Secret("password")))) + + // config loading with fallback values + postgresConfig7[IO].map { config => + config match + case Right(value) => println(value) + case Left(err) => err.messages.map(println) + } diff --git a/scala3-libraries/src/main/scala/com/baledung/clearconfig/ClearConfigLoader.scala b/scala-libraries-config/src/main/scala/com/baeldung/clearconfig/ClearConfigLoader.scala similarity index 100% rename from scala3-libraries/src/main/scala/com/baledung/clearconfig/ClearConfigLoader.scala rename to scala-libraries-config/src/main/scala/com/baeldung/clearconfig/ClearConfigLoader.scala diff --git a/scala-libraries-config/src/test/resources/application-default.conf b/scala-libraries-config/src/test/resources/application-default.conf new file mode 100644 index 000000000..c129546e1 --- /dev/null +++ b/scala-libraries-config/src/test/resources/application-default.conf @@ -0,0 +1,3 @@ +fallback { + version = "9.9" +} \ No newline at end of file diff --git a/scala-libraries-3/src/test/resources/application.conf b/scala-libraries-config/src/test/resources/application.conf similarity index 100% rename from scala-libraries-3/src/test/resources/application.conf rename to scala-libraries-config/src/test/resources/application.conf diff --git a/scala-libraries-config/src/test/resources/database.conf b/scala-libraries-config/src/test/resources/database.conf new file mode 100644 index 000000000..41d6b8fdd --- /dev/null +++ b/scala-libraries-config/src/test/resources/database.conf @@ -0,0 +1,6 @@ +db { + dbUrl = "postgresql://localhost:5555" + dbName = "baeldung" + username = "postgres" + password = "admin" +} \ No newline at end of file diff --git a/scala-libraries-config/src/test/resources/http.conf b/scala-libraries-config/src/test/resources/http.conf new file mode 100644 index 000000000..f893cca8f --- /dev/null +++ b/scala-libraries-config/src/test/resources/http.conf @@ -0,0 +1,5 @@ +http { + port ="8090" + host = "localhost" + protocol = "https" +} \ No newline at end of file diff --git a/scala-libraries-config/src/test/resources/main.conf b/scala-libraries-config/src/test/resources/main.conf new file mode 100644 index 000000000..88fba87af --- /dev/null +++ b/scala-libraries-config/src/test/resources/main.conf @@ -0,0 +1,3 @@ +include "http.conf" +include "database.conf" +appName = "Baeldung" \ No newline at end of file diff --git a/scala-libraries-config/src/test/resources/notification.conf b/scala-libraries-config/src/test/resources/notification.conf new file mode 100644 index 000000000..e655391da --- /dev/null +++ b/scala-libraries-config/src/test/resources/notification.conf @@ -0,0 +1,2 @@ +notification-url = "http://mynotificationservice.com/push" +params = "status=completed" \ No newline at end of file diff --git a/scala-libraries-config/src/test/scala/com/baeldung/circeyaml/YamlExampleUnitTest.scala b/scala-libraries-config/src/test/scala/com/baeldung/circeyaml/YamlExampleUnitTest.scala new file mode 100644 index 000000000..a4f19e2b8 --- /dev/null +++ b/scala-libraries-config/src/test/scala/com/baeldung/circeyaml/YamlExampleUnitTest.scala @@ -0,0 +1,82 @@ +package com.baeldung.circeyaml + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import YamlExample.* +import io.circe.* +import java.io.* +import org.scalatest.prop.TableDrivenPropertyChecks + +class YamlExampleUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks: + val myServer = Server("localhost", 8080) + val myOC = OrdersConfig("test", myServer, List("Http", "Grpc")) + + "Server" should "contain a host String and port Int" in { + myServer.host shouldBe an[String] + myServer.port shouldBe an[Int] + } + + "OrdersConfig" should "contain a name String, Server, and serverType, List[String]" in { + myOC.name shouldBe an[String] + myOC.server shouldBe an[Server] + myOC.serverType shouldBe an[List[String]] + } + + "ordersStringConfig" should "be of type Either[ParsingFailure, Json]" in { + ordersStringConfig shouldBe an[Either[ParsingFailure, Json]] + } + + "processJson()" should "return an Either[Error, OrdersConfig]" in { + processJson(ordersStringConfig) shouldBe an[Either[Error, OrdersConfig]] + } + + "printValue" should "return Unit" in { + printValue(processJson(ordersStringConfig)) shouldBe an[Unit] + } + + "yamlFileReader and yamlFileReader2" should "be of type Either[Throwable, FileReader]" in { + forAll(Table("Either File Reader", yamlFileReader, yamlFileReader2)) { v => + v shouldBe an[Either[Throwable, FileReader]] + } + } + + "ordersFileConfig" should "be of type Either[Throwable, OrdersConfig] " in { + ordersFileConfig2 shouldBe an[ + Either[Throwable, OrdersConfig] + ] + } + + "ordersFileConfig2" should "be of type Either[Throwable, List[Either[Error, OrdersConfig]]]" in { + ordersFileConfig2 shouldBe an[ + Either[Throwable, List[Either[Error, OrdersConfig]]] + ] + } + + "fileWriter" should "be of type Either[Throwable, FileWriter]" in { + fileWriter("src/test/resources/sample.yaml") shouldBe an[ + Either[Throwable, FileWriter] + ] + } + + "jsonString" should "be of type String" in { + jsonString shouldBe an[String] + } + + "writeJsonStr" should "be of type Either[Throwable, String]" in { + writeJsonStr( + "src/test/resources/sample.yaml", + jsonString + ) shouldBe an[Either[Throwable, String]] + } + + "writeOrdersConfig" should "be of type String" in { + writeOrdersConfig( + "src/test/resources/sample2.yaml", + myCaseClass + ) shouldBe an[String] + } + +end YamlExampleUnitTest diff --git a/scala-libraries-config/src/test/scala/com/baeldung/cirisconfig/ConfigurationUnitTest.scala b/scala-libraries-config/src/test/scala/com/baeldung/cirisconfig/ConfigurationUnitTest.scala new file mode 100644 index 000000000..500f9e527 --- /dev/null +++ b/scala-libraries-config/src/test/scala/com/baeldung/cirisconfig/ConfigurationUnitTest.scala @@ -0,0 +1,66 @@ +package cirisconfig + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +import Configuration.* +import ciris.* +import cats.effect.IO + +class ConfigurationUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks: + + "postgresConfig" should "be of type ConfigValue[Effect, PostgresConfig]" in { + postgresConfig shouldBe an[ConfigValue[Effect, PostgresConfig]] + } + + "postgresConfig2" should "be of type ConfigValue[Effect, PostgresConfig2]" in { + postgresConfig2 shouldBe an[ConfigValue[Effect, PostgresConfig2]] + } + + "postgresConfig3" should "be of type ConfigValue[Effect, PostgresConfig3]" in { + postgresConfig3 shouldBe an[ConfigValue[Effect, PostgresConfig3]] + } + + "postgresConfig4, " should "be of type ConfigValue[Effect, PostgresConfig4]" in { + postgresConfig4 shouldBe an[ConfigValue[Effect, PostgresConfig4]] + } + + "postgresConfig5, postgresConfig6, postgresConfig7" should "be of type IO[Either[ConfigError, PostgresConfig4]]" in { + forAll( + Table( + "postgresConfig567", + postgresConfig5[IO], + postgresConfig6[IO], + postgresConfig7[IO] + ) + ) { v => + v shouldBe an[IO[Either[ConfigError, PostgresConfig4]]] + } + } + + "PostgresConfig" should "be of contain type String" in { + val pconfig = PostgresConfig("u", "p") + pconfig.username shouldBe an[String] + pconfig.password shouldBe an[String] + } + + "PostgresConfig2" should "be of contain type Option[String]" in { + val pconfig2 = PostgresConfig2(Some("u"), Some("p")) + pconfig2.username shouldBe an[Option[String]] + pconfig2.password shouldBe an[Option[String]] + } + + "PostgresConfig3" should "be of contain type Option[Username] and Option[Password]" in { + val pconfig3 = PostgresConfig3(Some(Username("u")), Some(Password("p"))) + pconfig3.username shouldBe an[Option[Username]] + pconfig3.password shouldBe an[Option[Password]] + } + + "PostgresConfig4" should "be of contain type Username and Password" in { + val pconfig4 = PostgresConfig4(Username("u"), Password("p")) + pconfig4.username shouldBe an[Username] + pconfig4.password shouldBe an[Password] + } diff --git a/scala-libraries-3/src/test/scala-2/com/baeldung/scala/config/ConfigTest.scala b/scala-libraries-config/src/test/scala/com/baeldung/scala/config/ConfigUnitTest.scala similarity index 98% rename from scala-libraries-3/src/test/scala-2/com/baeldung/scala/config/ConfigTest.scala rename to scala-libraries-config/src/test/scala/com/baeldung/scala/config/ConfigUnitTest.scala index 512eb0c4f..ee4e15b9e 100644 --- a/scala-libraries-3/src/test/scala-2/com/baeldung/scala/config/ConfigTest.scala +++ b/scala-libraries-config/src/test/scala/com/baeldung/scala/config/ConfigUnitTest.scala @@ -6,7 +6,7 @@ import java.time import com.typesafe.config.ConfigMemorySize import com.typesafe.config.ConfigException -class ConfigTest extends munit.FunSuite { +class ConfigUnitTest extends munit.FunSuite { test("Read configs with primitive types") { val config: Config = ConfigFactory.load() diff --git a/scala-libraries-fp/README.md b/scala-libraries-fp/README.md new file mode 100644 index 000000000..d68642919 --- /dev/null +++ b/scala-libraries-fp/README.md @@ -0,0 +1,10 @@ +### Relevant Articles: + +- [Scala – Introduction to Cats](https://www.baeldung.com/scala/cats-intro) +- [Introduction to FS2: Functional Streams for Scala](https://www.baeldung.com/scala/fs2-functional-streams) +- [Introduction to http4s](https://www.baeldung.com/scala/http4s-intro) +- [Writer Monad in Cats](https://www.baeldung.com/scala/writer-monad-in-cats) +- [Data Validation With Cats](https://www.baeldung.com/scala/cats-data-validation) +- [Introduction to Scalaz](https://www.baeldung.com/scala/scalaz-intro) +- [The Principles Behind Scalaz](https://www.baeldung.com/scala/scalaz-principles) +- [Introduction to Spire](https://www.baeldung.com/scala/spire-intro) diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/cats/Area.scala b/scala-libraries-fp/src/main/scala/cats/Area.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/cats/Area.scala rename to scala-libraries-fp/src/main/scala/cats/Area.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/cats/AreaInstances.scala b/scala-libraries-fp/src/main/scala/cats/AreaInstances.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/cats/AreaInstances.scala rename to scala-libraries-fp/src/main/scala/cats/AreaInstances.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/cats/ShapeArea.scala b/scala-libraries-fp/src/main/scala/cats/ShapeArea.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/cats/ShapeArea.scala rename to scala-libraries-fp/src/main/scala/cats/ShapeArea.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/cats/ShapeAreaSyntax.scala b/scala-libraries-fp/src/main/scala/cats/ShapeAreaSyntax.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/cats/ShapeAreaSyntax.scala rename to scala-libraries-fp/src/main/scala/cats/ShapeAreaSyntax.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/cats/monoids/CollectionMonoid.scala b/scala-libraries-fp/src/main/scala/cats/monoids/CollectionMonoid.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/cats/monoids/CollectionMonoid.scala rename to scala-libraries-fp/src/main/scala/cats/monoids/CollectionMonoid.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/cats/semigroups/CollectionSemigroup.scala b/scala-libraries-fp/src/main/scala/cats/semigroups/CollectionSemigroup.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/cats/semigroups/CollectionSemigroup.scala rename to scala-libraries-fp/src/main/scala/cats/semigroups/CollectionSemigroup.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/cats/semigroups/CustomIntSemigroup.scala b/scala-libraries-fp/src/main/scala/cats/semigroups/CustomIntSemigroup.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/cats/semigroups/CustomIntSemigroup.scala rename to scala-libraries-fp/src/main/scala/cats/semigroups/CustomIntSemigroup.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/cats/semigroups/IntSemigroup.scala b/scala-libraries-fp/src/main/scala/cats/semigroups/IntSemigroup.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/cats/semigroups/IntSemigroup.scala rename to scala-libraries-fp/src/main/scala/cats/semigroups/IntSemigroup.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/cats/show/CustomInstance.scala b/scala-libraries-fp/src/main/scala/cats/show/CustomInstance.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/cats/show/CustomInstance.scala rename to scala-libraries-fp/src/main/scala/cats/show/CustomInstance.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/cats/show/InterfaceSyntax.scala b/scala-libraries-fp/src/main/scala/cats/show/InterfaceSyntax.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/cats/show/InterfaceSyntax.scala rename to scala-libraries-fp/src/main/scala/cats/show/InterfaceSyntax.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/cats/show/ShowImpl.scala b/scala-libraries-fp/src/main/scala/cats/show/ShowImpl.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/cats/show/ShowImpl.scala rename to scala-libraries-fp/src/main/scala/cats/show/ShowImpl.scala diff --git a/scala-libraries-fp/src/main/scala/com/baeldung/datavalidation/DataValidation.scala b/scala-libraries-fp/src/main/scala/com/baeldung/datavalidation/DataValidation.scala new file mode 100644 index 000000000..f1b12f4d4 --- /dev/null +++ b/scala-libraries-fp/src/main/scala/com/baeldung/datavalidation/DataValidation.scala @@ -0,0 +1,129 @@ +package com.baeldung.datavalidation + +import cats.effect.{IOApp, IO, ExitCode} +import cats.data.{EitherNec, ValidatedNec, Validated} +import cats.data.Validated.{Valid, Invalid} +import cats.syntax.all.* + +object Utilities: + case class Country(value: String) + object Country: + private val countries = List("uganda", "kenya", "tanzania") + def apply(value: String): Option[Country] = + Some(value) + .filter(v => countries.contains(v.toLowerCase)) + .map(c => new Country(c.toLowerCase)) + + case class Age(value: Int) + object Age: + def apply(value: Int): Option[Age] = + Some(value).filter(_ >= 25).map(new Age(_)) + + case class Cgpa(value: Double) + object Cgpa: + def apply(value: Double): Option[Cgpa] = + Some(value).filter(_ >= 3.0).map(new Cgpa(_)) + +object Version1: + import Utilities.* + + case class Scholarship(country: Country, age: Age, cgpa: Cgpa) + object Scholarship: + def apply( + value1: String, + value2: Int, + value3: Double + ): EitherNec[String, Scholarship] = + ( + Country(value1).toRightNec("Invalid Coutry"), + Age(value2).toRightNec("Invalid Age"), + Cgpa(value3).toRightNec("Invalid Cgpa") + ).parMapN( + Scholarship.apply + ) + +object Version2: + import Utilities.* + + case class Scholarship(country: Country, age: Age, cgpa: Cgpa) + object Scholarship: + def apply( + value1: String, + value2: Int, + value3: Double + ): ValidatedNec[String, Scholarship] = + ( + Country(value1).toValidNec("Invalid Coutry"), + Age(value2).toValidNec("Invalid Age"), + Cgpa(value3).toValidNec("Invalid Cgpa") + ).mapN( + Scholarship.apply + ) + +object Utilities2: + sealed trait ScholarshipValidationError: + val errMsg: String + object ScholarshipValidationError: + case object CountryValidationError extends ScholarshipValidationError: + override val errMsg: String = "Must come from Uganda, Kenya or Tanzania." + case object AgeValidationError extends ScholarshipValidationError: + override val errMsg: String = "Must be 25 years or more." + case object CgpaValdiationError extends ScholarshipValidationError: + override val errMsg: String = "CGPA must be 3.0 or more" + + import ScholarshipValidationError.* + + case class Country(value: String) + object Country: + private val countries = List("uganda", "kenya", "tanzania") + def apply( + value: String + ): ValidatedNec[ScholarshipValidationError, Country] = + Validated.condNec( + countries.contains(value.toLowerCase), + new Country(value.toLowerCase), + CountryValidationError + ) + + case class Age(value: Int) + object Age: + def apply(value: Int): ValidatedNec[ScholarshipValidationError, Age] = + Validated.condNec( + value >= 25, + new Age(value), + AgeValidationError + ) + + case class Cgpa(value: Double) + object Cgpa: + def apply(value: Double): ValidatedNec[ScholarshipValidationError, Cgpa] = + Validated.condNec( + value >= 3.0, + new Cgpa(value), + CgpaValdiationError + ) + +object Version3: + import Utilities2.* + case class Scholarship(country: Country, age: Age, cgpa: Cgpa) + object Scholarship: + def apply( + value1: String, + value2: Int, + value3: Double + ): ValidatedNec[ScholarshipValidationError, Scholarship] = + ( + Country(value1), + Age(value2), + Cgpa(value3) + ).mapN( + Scholarship.apply + ) + +object DataValidation extends IOApp.Simple: + import Version1.* + def run: IO[Unit] = + Scholarship("Uganda", 23, 2.5) match + case Right(x) => IO.println(x) + case Left(y) => IO.println(y.toChain) +// Chain(Invalid Age, Invalid Cgpa) diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/fs2/Fs2Examples.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/fs2/Fs2Examples.scala similarity index 95% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/fs2/Fs2Examples.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/fs2/Fs2Examples.scala index 1432dbdb0..3ca908a47 100644 --- a/scala-libraries/src/main/scala-2/com/baeldung/scala/fs2/Fs2Examples.scala +++ b/scala-libraries-fp/src/main/scala/com/baeldung/scala/fs2/Fs2Examples.scala @@ -59,7 +59,7 @@ object Fs2Examples { src .through(text.utf8.decode) .through(text.lines) - .flatMap(line => Stream.apply(line.split("\\W+"): _*)) + .flatMap(line => Stream.apply(line.split("\\W+")*)) .fold(Map.empty[String, Int]) { (count, word) => count + (word -> (count.getOrElse(word, 0) + 1)) } @@ -80,7 +80,7 @@ object Fs2Examples { } // Batching in Fs2 - Stream((1 to 100): _*) + Stream((1 to 100)*) .chunkN(10) // group 10 elements together .map(println) .compile @@ -97,7 +97,7 @@ object Fs2Examples { } - Stream((1 to 100).map(_.toString): _*) + Stream((1 to 100).map(_.toString)*) .chunkN(10) .covary[IO] .parEvalMapUnordered(10)(writeToSocket[IO]) diff --git a/scala-libraries-3/src/main/scala-2/com/baeldung/scala/http4s/SimpleClient.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/http4s/SimpleClient.scala similarity index 92% rename from scala-libraries-3/src/main/scala-2/com/baeldung/scala/http4s/SimpleClient.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/http4s/SimpleClient.scala index 41804dff9..90df799b3 100644 --- a/scala-libraries-3/src/main/scala-2/com/baeldung/scala/http4s/SimpleClient.scala +++ b/scala-libraries-fp/src/main/scala/com/baeldung/scala/http4s/SimpleClient.scala @@ -4,7 +4,7 @@ import cats.effect.unsafe.implicits.global import cats.effect.{ExitCode, IO, IOApp} import org.http4s.blaze.client.BlazeClientBuilder import org.http4s.client.Client -import org.http4s.implicits.http4sLiteralsSyntax +import org.http4s.implicits.uri object SimpleClient extends IOApp { def callEffect(client: Client[IO], str: String): IO[String] = diff --git a/scala-libraries-3/src/main/scala-2/com/baeldung/scala/http4s/SimpleServer.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/http4s/SimpleServer.scala similarity index 100% rename from scala-libraries-3/src/main/scala-2/com/baeldung/scala/http4s/SimpleServer.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/http4s/SimpleServer.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/scalaz/ScalazExamples.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/scalaz/ScalazExamples.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/scalaz/ScalazExamples.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/scalaz/ScalazExamples.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/scalaz/principles/ScalazPrinciplesExamples.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/scalaz/principles/ScalazPrinciplesExamples.scala similarity index 97% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/scalaz/principles/ScalazPrinciplesExamples.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/scalaz/principles/ScalazPrinciplesExamples.scala index c6a6a35e4..f75deb2f9 100644 --- a/scala-libraries/src/main/scala-2/com/baeldung/scala/scalaz/principles/ScalazPrinciplesExamples.scala +++ b/scala-libraries-fp/src/main/scala/com/baeldung/scala/scalaz/principles/ScalazPrinciplesExamples.scala @@ -10,7 +10,7 @@ object ScalazPrinciplesExamples { } implicit object stringListDoubler extends Doubler[List, String] { - def makeDouble(xs: List[String]): List[String] = xs.map(s => s concat s) + def makeDouble(xs: List[String]): List[String] = xs.map(s => s `concat` s) } implicit object intOptionDoubler extends Doubler[Option, Int] { diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/Fractional.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/Fractional.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/Fractional.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/Fractional.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/Integral.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/Integral.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/Integral.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/Integral.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/Numeric.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/Numeric.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/Numeric.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/Numeric.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/groups/Semigroups.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/groups/Semigroups.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/groups/Semigroups.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/groups/Semigroups.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/groups/TranscationGroup.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/groups/TranscationGroup.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/groups/TranscationGroup.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/groups/TranscationGroup.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/macros/CFor.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/macros/CFor.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/macros/CFor.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/macros/CFor.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/macros/LiteralNumberSyntax.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/macros/LiteralNumberSyntax.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/macros/LiteralNumberSyntax.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/macros/LiteralNumberSyntax.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/monoids/AdditiveMonoid.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/monoids/AdditiveMonoid.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/monoids/AdditiveMonoid.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/monoids/AdditiveMonoid.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/numbers/Complexes.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/numbers/Complexes.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/numbers/Complexes.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/numbers/Complexes.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/numbers/Intervals.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/numbers/Intervals.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/numbers/Intervals.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/numbers/Intervals.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/numbers/Naturals.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/numbers/Naturals.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/numbers/Naturals.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/numbers/Naturals.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/numbers/Rationals.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/numbers/Rationals.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/numbers/Rationals.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/numbers/Rationals.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/numbers/Reals.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/numbers/Reals.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/numbers/Reals.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/numbers/Reals.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/rings/EuclideanRings.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/rings/EuclideanRings.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/rings/EuclideanRings.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/rings/EuclideanRings.scala diff --git a/scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/rings/Rings.scala b/scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/rings/Rings.scala similarity index 100% rename from scala-libraries-5/src/main/scala-2/com/baeldung/scala/spire/rings/Rings.scala rename to scala-libraries-fp/src/main/scala/com/baeldung/scala/spire/rings/Rings.scala diff --git a/scala-libraries-fp/src/test/resources/fs2data.txt b/scala-libraries-fp/src/test/resources/fs2data.txt new file mode 100644 index 000000000..123ce0b97 --- /dev/null +++ b/scala-libraries-fp/src/test/resources/fs2data.txt @@ -0,0 +1 @@ +Scala Cats Effect FS2 Cats Typelevel Effect Scala \ No newline at end of file diff --git a/scala-libraries-fp/src/test/scala/com/baeldung/datavalidation/DataValidationUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/datavalidation/DataValidationUnitTest.scala new file mode 100644 index 000000000..39b2d37ea --- /dev/null +++ b/scala-libraries-fp/src/test/scala/com/baeldung/datavalidation/DataValidationUnitTest.scala @@ -0,0 +1,37 @@ +package com.baeldung.datavalidation + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +import cats.data.NonEmptyChain +import cats.data.Validated.Invalid + +class DataValidationUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks: + + "Version1 Scholarship" should "produce a Left[NonEmptyChain[String]]" in { + import Version1.{Scholarship} + Scholarship("Uganda", 23, 2.5) shouldBe Left( + NonEmptyChain.of("Invalid Age", "Invalid Cgpa") + ) + } + + "Version2 Scholarship" should "produce a Invalid[NonEmptyChain[String]]" in { + import Version2.{Scholarship} + Scholarship("Uganda", 23, 2.5) shouldBe Invalid( + NonEmptyChain.of("Invalid Age", "Invalid Cgpa") + ) + } + + "Version3 Scholarship" should "produce a Invalid[NonEmptyChain[ScholarshipValidationError]]" in { + import Version3.{Scholarship} + import Utilities2.ScholarshipValidationError.{ + AgeValidationError, + CgpaValdiationError + } + Scholarship("Uganda", 23, 2.5) shouldBe Invalid( + NonEmptyChain.of(AgeValidationError, CgpaValdiationError) + ) + } diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/cats/ShapeAreaSyntaxUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/ShapeAreaSyntaxUnitTest.scala similarity index 100% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/cats/ShapeAreaSyntaxUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/ShapeAreaSyntaxUnitTest.scala diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/cats/ShapeAreaUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/ShapeAreaUnitTest.scala similarity index 100% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/cats/ShapeAreaUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/ShapeAreaUnitTest.scala diff --git a/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/eithert/EitherTErrorHandling.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/eithert/EitherTErrorHandling.scala new file mode 100644 index 000000000..929b89d7f --- /dev/null +++ b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/eithert/EitherTErrorHandling.scala @@ -0,0 +1,86 @@ +package com.baeldung.scala.cats.eithert + +import cats.data.EitherT + +import scala.concurrent.Future + +object SimpleErrorHandling { + + case class User(userId: String, name: String, isActive: Boolean) + def getUserProfile(userId: String): Either[String, User] = ??? + def calculateDiscount(user: User): Either[String, Double] = ??? + def placeOrder( + itemId: String, + discount: Double, + user: User + ): Either[String, String] = ??? + + def performAction(userId: String, itemId: String): Either[String, String] = + for { + user <- getUserProfile(userId) + discount <- calculateDiscount(user) + orderId <- placeOrder(itemId, discount, user) + } yield orderId + +} + +object FutureErrorHandling { + + case class User(userId: String, name: String, isActive: Boolean) + def getUserProfile(userId: String): Future[Either[String, User]] = ??? + def calculateDiscount(user: User): Future[Either[String, Double]] = ??? + def placeOrder( + itemId: String, + discount: Double, + user: User + ): Future[Either[String, String]] = ??? + + import scala.concurrent.Future + import scala.concurrent.ExecutionContext.Implicits.global + + def performAction( + userId: String, + itemId: String + ): Future[Either[String, String]] = { + for { + userEither <- getUserProfile(userId) + result <- userEither match { + case Left(error) => Future.successful(Left(error)) + case Right(user) => + for { + discountEither <- calculateDiscount(user) + orderResult <- discountEither match { + case Left(error) => Future.successful(Left(error)) + case Right(discount) => placeOrder(itemId, discount, user) + } + } yield orderResult + } + } yield result + } + +} + +object EitherTErrorHandling { + + import scala.concurrent.ExecutionContext.Implicits.global + case class User(userId: String, name: String, isActive: Boolean) + def getUserProfile(userId: String): Future[Either[String, User]] = ??? + def calculateDiscount(user: User): Future[Either[String, Double]] = ??? + def placeOrder( + itemId: String, + discount: Double, + user: User + ): Future[Either[String, String]] = ??? + + def performAction( + userId: String, + itemId: String + ): Future[Either[String, String]] = { + (for { + user <- EitherT(getUserProfile(userId)) + discount <- EitherT(calculateDiscount(user)) + orderId <- EitherT(placeOrder(itemId, discount, user)) + } yield orderId).value + } + +} diff --git a/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/eithert/EitherTUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/eithert/EitherTUnitTest.scala new file mode 100644 index 000000000..d9e1e0683 --- /dev/null +++ b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/eithert/EitherTUnitTest.scala @@ -0,0 +1,94 @@ +package com.baeldung.scala.cats.eithert + +import cats.data.EitherT +import org.scalatest.flatspec.AsyncFlatSpec +import org.scalatest.matchers.should.Matchers + +import scala.concurrent.Future +import scala.util.{Failure, Try} + +class EitherTUnitTest extends AsyncFlatSpec with Matchers { + + it should "map over EitherT instance with Future" in { + val response: EitherT[Future, String, Int] = + EitherT(Future.successful(Right(100))) + response.map(_ * 5).value.map(_ shouldBe Right(500)) + } + + it should "map over EitherT instance with Try" in { + val opValue: Try[Either[String, Int]] = Try(Right(100)) + val response: EitherT[Try, String, Int] = EitherT(opValue) + val mappedValue: EitherT[Try, String, Int] = response.map(_ * 5) + val underlying: Try[Either[String, Int]] = mappedValue.value + underlying shouldBe Try(Right(500)) + } + + it should "map over error" in { + val response: EitherT[Try, String, Int] = + EitherT(Try(Left("invalid number!"))) + response.leftMap(_.toUpperCase).value shouldBe Try(Left("INVALID NUMBER!")) + } + + it should "be able to map on both side of either using bimap" in { + val success: EitherT[Try, String, Int] = EitherT(Try(Right(100))) + val biMappedSuccess = success.bimap(e => e.toUpperCase, s => s * 5) + biMappedSuccess.value shouldBe Try(Right(500)) + val error: EitherT[Try, String, Int] = EitherT(Try(Left("error"))) + val biMappedError = error.bimap(e => e.toUpperCase, s => s * 5) + biMappedError.value shouldBe Try(Left("ERROR")) + } + + it should "compose multiple EitherT instances together" in { + val num1: EitherT[Try, String, Int] = EitherT.right(Try(100)) + val num2: EitherT[Try, String, Int] = EitherT.liftF(Try(2)) + val divRes: EitherT[Try, String, Int] = for { + n1 <- num1 + n2 <- num2 + div = n1 / n2 + } yield div + divRes.value shouldBe Try(Right(50)) + } + + it should "compose multiple EitherT instances together when there is an error" in { + val num1: EitherT[Try, String, Int] = EitherT(Try(Right(100))) + val num2: EitherT[Try, String, Int] = EitherT.left(Try("zero")) + val divRes: EitherT[Try, String, Int] = for { + n1 <- num1 + n2 <- num2 + div = n1 / n2 + } yield div + divRes.value shouldBe Try(Left("zero")) + } + + it should "handle Try failure" in { + val failedOp: Try[Either[String, Int]] = + Try(throw new Exception("Operation failed!")) + val response: EitherT[Try, String, Int] = EitherT(failedOp) + response.value.isFailure shouldBe true + } + + it should "short circuit if one of the Try fails" in { + val num1: EitherT[Try, String, Int] = EitherT(Try(Right(100))) + val num2: EitherT[Try, String, Int] = + EitherT(Try(throw new Exception("Operation failed!"))) + val divRes: EitherT[Try, String, Int] = for { + n1 <- num1 + n2 <- num2 + div = n1 / n2 + } yield div + divRes.value.isFailure shouldBe true + } + + it should "lift an either into eitherT" in { + val either: Either[String, Int] = Right(100) + val eitherT: EitherT[Try, String, Int] = EitherT.fromEither(either) + eitherT.value.get shouldBe Right(100) + } + + it should "lift an option into eitherT" in { + val opt: Option[Int] = Some(100) + val eitherT: EitherT[Try, String, Int] = EitherT.fromOption(opt, "EMPTY") + eitherT.value.get shouldBe Right(100) + } + +} diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/cats/monoids/CollectionMonoidUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/monoids/CollectionMonoidUnitTest.scala similarity index 100% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/cats/monoids/CollectionMonoidUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/monoids/CollectionMonoidUnitTest.scala diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/cats/semigroups/CollectionSemigroupUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/semigroups/CollectionSemigroupUnitTest.scala similarity index 100% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/cats/semigroups/CollectionSemigroupUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/semigroups/CollectionSemigroupUnitTest.scala diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/cats/semigroups/CustomIntSemigroupUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/semigroups/CustomIntSemigroupUnitTest.scala similarity index 100% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/cats/semigroups/CustomIntSemigroupUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/semigroups/CustomIntSemigroupUnitTest.scala diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/cats/semigroups/IntSemigroupUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/semigroups/IntSemigroupUnitTest.scala similarity index 100% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/cats/semigroups/IntSemigroupUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/semigroups/IntSemigroupUnitTest.scala diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/cats/show/CustomInstanceUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/show/CustomInstanceUnitTest.scala similarity index 100% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/cats/show/CustomInstanceUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/show/CustomInstanceUnitTest.scala diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/cats/show/InterfaceSyntaxUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/show/InterfaceSyntaxUnitTest.scala similarity index 100% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/cats/show/InterfaceSyntaxUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/show/InterfaceSyntaxUnitTest.scala diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/cats/show/ShowImplUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/show/ShowImplUnitTest.scala similarity index 100% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/cats/show/ShowImplUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/show/ShowImplUnitTest.scala diff --git a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/cats/writer/WriterMonadUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/writer/WriterMonadUnitTest.scala similarity index 100% rename from scala-libraries-4/src/test/scala-2/com/baeldung/scala/cats/writer/WriterMonadUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/cats/writer/WriterMonadUnitTest.scala diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/fs2/FS2SampleTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/fs2/FS2SampleUnitTest.scala similarity index 78% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/fs2/FS2SampleTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/fs2/FS2SampleUnitTest.scala index 7f42e58a2..02fd156cc 100644 --- a/scala-libraries/src/test/scala-2/com/baeldung/scala/fs2/FS2SampleTest.scala +++ b/scala-libraries-fp/src/test/scala/com/baeldung/scala/fs2/FS2SampleUnitTest.scala @@ -7,11 +7,11 @@ import org.scalatest.matchers.should.Matchers import java.nio.file.Paths import scala.io.Source -class FS2SampleTest extends AsyncFlatSpec with AsyncIOSpec with Matchers { +class FS2SampleUnitTest extends AsyncFlatSpec with AsyncIOSpec with Matchers { it should "read from a file and calculate word count" in { val outFile = "wc-fs2-output.log" - val loc = "scala-libraries/src/test/resources/fs2data.txt" + val loc = "scala-libraries-fp/src/test/resources/fs2data.txt" val io = Fs2Examples.readAndWriteFile(loc, outFile).compile.drain val res = io.map { _ => Source.fromFile(outFile).getLines().toList diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/scalaz/ScalazExamplesUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/scalaz/ScalazExamplesUnitTest.scala similarity index 95% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/scalaz/ScalazExamplesUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/scalaz/ScalazExamplesUnitTest.scala index b89751aea..4aaa7155b 100644 --- a/scala-libraries/src/test/scala-2/com/baeldung/scala/scalaz/ScalazExamplesUnitTest.scala +++ b/scala-libraries-fp/src/test/scala/com/baeldung/scala/scalaz/ScalazExamplesUnitTest.scala @@ -1,13 +1,12 @@ package com.baeldung.scala.scalaz -import java.util.NoSuchElementException - -import com.baeldung.scala.scalaz.ScalazExamples._ +import com.baeldung.scala.scalaz.ScalazExamples.* +import org.junit.Assert.* import org.junit.Test -import org.junit.Assert._ +import scalaz.* +import scalaz.Scalaz.* -import scalaz.Scalaz._ -import scalaz._ +import java.util.NoSuchElementException class ScalazExamplesUnitTest { @@ -20,13 +19,15 @@ class ScalazExamplesUnitTest { assertTrue(val1 === val3) assertTrue(val1 =/= val2) } - @Test - def givenValuesOfDifferentTypes_whenComparedWithScala_thenItWorks: Unit = { - val intValue = 10 - val strValue = "10" - assertFalse(intValue == strValue) - } +// Scala 3 gives compilation error when wrong types are compared. +// @Test +// def givenValuesOfDifferentTypes_whenComparedWithScala_thenItWorks: Unit = { +// val intValue = 10 +// val strValue = "10" +// +// assertFalse(intValue == strValue) +// } @Test def givenScores_thenGreaterThanCheckWorks: Unit = { @@ -69,13 +70,13 @@ class ScalazExamplesUnitTest { @Test def givenCharRange_thenGenerateAsEnum: Unit = { - val enum = 'a' |-> 'g' - val enumAsList = enum.toList + val enumLike = 'a' |-> 'g' + val enumAsList = enumLike.toList val expectedResult = IList('a', 'b', 'c', 'd', 'e', 'f', 'g') val expectedResultAsList = List('a', 'b', 'c', 'd', 'e', 'f', 'g') - assertEquals(expectedResult, enum) + assertEquals(expectedResult, enumLike) assertEquals(expectedResultAsList, enumAsList) } diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/scalaz/principles/ScalazPrinciplesExamplesUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/scalaz/principles/ScalazPrinciplesExamplesUnitTest.scala similarity index 99% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/scalaz/principles/ScalazPrinciplesExamplesUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/scalaz/principles/ScalazPrinciplesExamplesUnitTest.scala index 0b3796400..82f8c262f 100644 --- a/scala-libraries/src/test/scala-2/com/baeldung/scala/scalaz/principles/ScalazPrinciplesExamplesUnitTest.scala +++ b/scala-libraries-fp/src/test/scala/com/baeldung/scala/scalaz/principles/ScalazPrinciplesExamplesUnitTest.scala @@ -1,6 +1,6 @@ package com.baeldung.scala.scalaz.principles -import com.baeldung.scala.scalaz.principles.ScalazPrinciplesExamples._ +import com.baeldung.scala.scalaz.principles.ScalazPrinciplesExamples.* import org.scalatest.flatspec.AnyFlatSpec class ScalazPrinciplesExamplesUnitTest extends AnyFlatSpec { diff --git a/scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/FractionalUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/FractionalUnitTest.scala similarity index 100% rename from scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/FractionalUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/FractionalUnitTest.scala diff --git a/scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/IntegralUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/IntegralUnitTest.scala similarity index 95% rename from scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/IntegralUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/IntegralUnitTest.scala index cc58caafa..70b740931 100644 --- a/scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/IntegralUnitTest.scala +++ b/scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/IntegralUnitTest.scala @@ -1,6 +1,6 @@ package com.baeldung.scala.spire -import com.baeldung.scala.spire.Integral._ +import com.baeldung.scala.spire.Integral.* import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec import spire.implicits._ diff --git a/scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/NumericUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/NumericUnitTest.scala similarity index 100% rename from scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/NumericUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/NumericUnitTest.scala diff --git a/scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/groups/TransactionGroupUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/groups/TransactionGroupUnitTest.scala similarity index 94% rename from scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/groups/TransactionGroupUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/groups/TransactionGroupUnitTest.scala index 1b8c32949..a838aa364 100644 --- a/scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/groups/TransactionGroupUnitTest.scala +++ b/scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/groups/TransactionGroupUnitTest.scala @@ -2,6 +2,8 @@ package com.baeldung.scala.spire.groups import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import spire.implicits._ +import spire.math._ class TransactionGroupUnitTest extends AnyWordSpec with Matchers { diff --git a/scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/monoids/AdditiveMonoidUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/monoids/AdditiveMonoidUnitTest.scala similarity index 88% rename from scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/monoids/AdditiveMonoidUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/monoids/AdditiveMonoidUnitTest.scala index dee1c549f..15d724d72 100644 --- a/scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/monoids/AdditiveMonoidUnitTest.scala +++ b/scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/monoids/AdditiveMonoidUnitTest.scala @@ -1,9 +1,10 @@ package com.baeldung.scala.spire.monoids -import com.baeldung.scala.spire.monoids.AdditiveMonoid._ +import com.baeldung.scala.spire.monoids.AdditiveMonoid.* import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec import spire.implicits._ +import spire.math._ class AdditiveMonoidUnitTest extends AnyWordSpec with Matchers { diff --git a/scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/rings/EuclideanRingUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/rings/EuclideanRingUnitTest.scala similarity index 88% rename from scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/rings/EuclideanRingUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/rings/EuclideanRingUnitTest.scala index ecc9638bf..de974daae 100644 --- a/scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/rings/EuclideanRingUnitTest.scala +++ b/scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/rings/EuclideanRingUnitTest.scala @@ -1,8 +1,9 @@ package com.baeldung.scala.spire.rings -import com.baeldung.scala.spire.rings.EuclideanRings._ +import com.baeldung.scala.spire.rings.EuclideanRings.* import org.scalatest.wordspec.AnyWordSpec import spire.implicits._ +import spire.math._ class EuclideanRingUnitTest extends AnyWordSpec { diff --git a/scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/rings/RingsUnitTest.scala b/scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/rings/RingsUnitTest.scala similarity index 91% rename from scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/rings/RingsUnitTest.scala rename to scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/rings/RingsUnitTest.scala index 8b6a6d261..ddc570a8b 100644 --- a/scala-libraries-5/src/test/scala-2/com/baeldung/scala/spire/rings/RingsUnitTest.scala +++ b/scala-libraries-fp/src/test/scala/com/baeldung/scala/spire/rings/RingsUnitTest.scala @@ -1,6 +1,9 @@ package com.baeldung.scala.spire.rings -import com.baeldung.scala.spire.rings.Rings._ + +import com.baeldung.scala.spire.rings.Rings.* import org.scalatest.wordspec.AnyWordSpec +import spire.implicits._ +import spire.math._ class RingsUnitTest extends AnyWordSpec { diff --git a/scala-libraries-os/src/main/scala-2/com/baeldung/scala/os/OsApp.scala b/scala-libraries-os/src/main/scala/com/baeldung/scala/os/OsApp.scala similarity index 100% rename from scala-libraries-os/src/main/scala-2/com/baeldung/scala/os/OsApp.scala rename to scala-libraries-os/src/main/scala/com/baeldung/scala/os/OsApp.scala diff --git a/scala-libraries-os/src/main/scala-2/com/baeldung/scala/os/ScalaSysApp.scala b/scala-libraries-os/src/main/scala/com/baeldung/scala/os/ScalaSysApp.scala similarity index 100% rename from scala-libraries-os/src/main/scala-2/com/baeldung/scala/os/ScalaSysApp.scala rename to scala-libraries-os/src/main/scala/com/baeldung/scala/os/ScalaSysApp.scala diff --git a/scala-libraries-persistence/README.md b/scala-libraries-persistence/README.md new file mode 100644 index 000000000..fa029a012 --- /dev/null +++ b/scala-libraries-persistence/README.md @@ -0,0 +1,7 @@ +### Relevant Articles: + +- [Introduction to Slick](https://www.baeldung.com/scala/slick-intro) +- [Introduction to Skunk – Scala Driver for PostgreSQL](https://www.baeldung.com/scala/skunk-postgresql-driver) +- [Introduction to doobie – a JDBC Layer for Scala](https://www.baeldung.com/scala/doobie-intro) +- [Introduction to Reactive Mongo](https://www.baeldung.com/scala/mongo-reactive-intro) +- [Postgres Specific Features in Slick Using Slick-PG](TBD) \ No newline at end of file diff --git a/scala-libraries-persistence/docker-compose.yaml b/scala-libraries-persistence/docker-compose.yaml new file mode 100644 index 000000000..9dad5d7f1 --- /dev/null +++ b/scala-libraries-persistence/docker-compose.yaml @@ -0,0 +1,14 @@ +services: + db: + image: postgres:16.4 + environment: + - POSTGRES_DB=baeldung + - POSTGRES_USER=baeldung + - POSTGRES_PASSWORD=password + ports: + - '5432:5432' + volumes: + - baeldung-postgresql-data:/var/lib/postgresql/data + +volumes: + baeldung-postgresql-data: \ No newline at end of file diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/reactivemongo/MongoEntities.scala b/scala-libraries-persistence/src/it/scala/com/baeldung/scala/reactivemongo/MongoEntities.scala similarity index 100% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/reactivemongo/MongoEntities.scala rename to scala-libraries-persistence/src/it/scala/com/baeldung/scala/reactivemongo/MongoEntities.scala diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/reactivemongo/ReactiveMongoUnitTest.scala b/scala-libraries-persistence/src/it/scala/com/baeldung/scala/reactivemongo/ReactiveMongoLiveTest.scala similarity index 96% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/reactivemongo/ReactiveMongoUnitTest.scala rename to scala-libraries-persistence/src/it/scala/com/baeldung/scala/reactivemongo/ReactiveMongoLiveTest.scala index 30d38f659..7b1128bba 100644 --- a/scala-libraries/src/test/scala-2/com/baeldung/scala/reactivemongo/ReactiveMongoUnitTest.scala +++ b/scala-libraries-persistence/src/it/scala/com/baeldung/scala/reactivemongo/ReactiveMongoLiveTest.scala @@ -1,9 +1,9 @@ package com.baeldung.scala.reactivemongo import akka.actor.ActorSystem -import akka.stream.ActorMaterializer +import akka.stream.Materializer import akka.stream.scaladsl.Sink -import com.baeldung.scala.reactivemongo.MongoEntityImplicits._ +import com.baeldung.scala.reactivemongo.MongoEntityImplicits.* import de.flapdoodle.embed.mongo.config.Net import de.flapdoodle.embed.mongo.distribution.Version import de.flapdoodle.embed.mongo.transitions.{ImmutableMongod, Mongod} @@ -11,10 +11,12 @@ import de.flapdoodle.reverse.transitions.Start import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AsyncWordSpec import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, FutureOutcome} +import reactivemongo.akkastream.cursorProducer import reactivemongo.api.Cursor import reactivemongo.api.bson.BSONDocument +import reactivemongo.api.bson.collection.BSONCollection -class ReactiveMongoUnitTest +class ReactiveMongoLiveTest extends AsyncWordSpec with Matchers with BeforeAndAfterAll @@ -29,7 +31,7 @@ class ReactiveMongoUnitTest .build() override def beforeAll(): Unit = { - mongodInstance.start(Version.Main.V4_0) + mongodInstance.start(Version.Main.V7_0) super.beforeAll() } @@ -208,9 +210,8 @@ class ReactiveMongoUnitTest "stream the movies and calculate total duration using akka stream api" in { // Note: This import(cursorProducer) is required for reactive mongo and akka stream integration - import reactivemongo.akkastream.cursorProducer implicit val system = ActorSystem("reactive-mongo-stream") - implicit val materializer = ActorMaterializer() + implicit val materializer = Materializer.matFromSystem(system) connection.getCollection("Movie").flatMap { col => val source = col .find(BSONDocument()) diff --git a/scala-libraries-persistence/src/it/scala/com/baeldung/scala/slick_pg/BaeldungPostgresManualTest.scala b/scala-libraries-persistence/src/it/scala/com/baeldung/scala/slick_pg/BaeldungPostgresManualTest.scala new file mode 100644 index 000000000..42311010d --- /dev/null +++ b/scala-libraries-persistence/src/it/scala/com/baeldung/scala/slick_pg/BaeldungPostgresManualTest.scala @@ -0,0 +1,68 @@ +package com.baeldung.scala.slick_pg + +import com.baeldung.scala.slick_pg.BaeldungPostgresProfile.api.* +import com.baeldung.scala.slick_pg.entity.BaeldungEntity +import com.baeldung.scala.slick_pg.table.baeldungEntityTable +import com.dimafeng.testcontainers.PostgreSQLContainer +import com.dimafeng.testcontainers.scalatest.TestContainerForAll +import org.json4s.native.JsonMethods +import org.scalatest.flatspec.AsyncFlatSpec +import org.testcontainers.utility.DockerImageName +import slick.dbio.DBIO + +import java.time.OffsetDateTime +import scala.concurrent.Future + +class BaeldungPostgresManualTest + extends AsyncFlatSpec + with TestContainerForAll { + + override val containerDef: PostgreSQLContainer.Def = PostgreSQLContainer.Def( + dockerImageName = DockerImageName.parse("postgres:16.4"), + databaseName = "baeldung", + username = "baeldung", + password = "baeldung" + ) + + "BaeldungPostgresManualTest" should "insert entity" in { + withContainers { container => + + val db: BaeldungPostgresProfile.backend.Database = + BaeldungPostgresProfile.backend.Database.forURL( + containerDef.start().jdbcUrl, + user = "baeldung", + password = "baeldung", + driver = "org.postgresql.Driver" + ) + + val createdAt = OffsetDateTime.now() + val res = db + .run( + DBIO.seq( + baeldungEntityTable.schema.createIfNotExists, + baeldungEntityTable += BaeldungEntity( + 1L, + createdAt, + List(1d, 5d, 2.24d), + JsonMethods.parse("""{"field1": 5, "field2": "test"}""") + ) + ) + ) + .flatMap(_ => db.run(baeldungEntityTable.result)) + + for (sequence <- res) yield { + sequence.foreach(entity => { + assert(entity.id.equals(1L)) + assert(entity.createdAt.equals(createdAt)) + assert(entity.prices.equals(List(1d, 5d, 2.24d))) + assert( + entity.metadata.equals( + JsonMethods.parse("""{"field1": 5, "field2": "test"}""") + ) + ) + }) + succeed + } + } + } +} diff --git a/scala-libraries-persistence/src/main/resources/application.conf b/scala-libraries-persistence/src/main/resources/application.conf new file mode 100644 index 000000000..0e6b89b44 --- /dev/null +++ b/scala-libraries-persistence/src/main/resources/application.conf @@ -0,0 +1,18 @@ +h2mem { + url = "jdbc:h2:mem:testDB" + driver = org.h2.Driver + keepAliveConnection = true + connectionPool = disabled +} + +postgres { + connectionPool = "HikariCP" + dataSourceClass = "org.postgresql.ds.PGSimpleDataSource" + properties = { + serverName = "localhost" + portNumber = "5432" + databaseName = "baeldung" + user = "baeldung" + password = "password" + } +} \ No newline at end of file diff --git a/doobie/src/main/scala-2/com/baeldung/scala/doobie/DoobieFragments.scala b/scala-libraries-persistence/src/main/scala/com/baeldung/doobie/DoobieFragments.scala similarity index 96% rename from doobie/src/main/scala-2/com/baeldung/scala/doobie/DoobieFragments.scala rename to scala-libraries-persistence/src/main/scala/com/baeldung/doobie/DoobieFragments.scala index a4b533db2..766ec2e30 100644 --- a/doobie/src/main/scala-2/com/baeldung/scala/doobie/DoobieFragments.scala +++ b/scala-libraries-persistence/src/main/scala/com/baeldung/doobie/DoobieFragments.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala.doobie +package com.baeldung.doobie import cats.effect.{IO, IOApp} import doobie.implicits._ diff --git a/doobie/src/main/scala-2/com/baeldung/scala/doobie/DoobieQuickStart.scala b/scala-libraries-persistence/src/main/scala/com/baeldung/doobie/DoobieQuickStart.scala similarity index 98% rename from doobie/src/main/scala-2/com/baeldung/scala/doobie/DoobieQuickStart.scala rename to scala-libraries-persistence/src/main/scala/com/baeldung/doobie/DoobieQuickStart.scala index 59f06af86..2f17e43da 100644 --- a/doobie/src/main/scala-2/com/baeldung/scala/doobie/DoobieQuickStart.scala +++ b/scala-libraries-persistence/src/main/scala/com/baeldung/doobie/DoobieQuickStart.scala @@ -1,4 +1,4 @@ -package com.baeldung.scala.doobie +package com.baeldung.doobie import cats.effect.{IO, IOApp} import doobie.implicits._ diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/reactivemongo/MongoDBConnection.scala b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/reactivemongo/MongoDBConnection.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/reactivemongo/MongoDBConnection.scala rename to scala-libraries-persistence/src/main/scala/com/baeldung/scala/reactivemongo/MongoDBConnection.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/slick/Connection.scala b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick/Connection.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/slick/Connection.scala rename to scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick/Connection.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/slick/Entities.scala b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick/Entities.scala similarity index 100% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/slick/Entities.scala rename to scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick/Entities.scala diff --git a/scala-libraries/src/main/scala-2/com/baeldung/scala/slick/SlickTables.scala b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick/SlickTables.scala similarity index 85% rename from scala-libraries/src/main/scala-2/com/baeldung/scala/slick/SlickTables.scala rename to scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick/SlickTables.scala index e296020f3..a3b4fa826 100644 --- a/scala-libraries/src/main/scala-2/com/baeldung/scala/slick/SlickTables.scala +++ b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick/SlickTables.scala @@ -7,7 +7,7 @@ import slick.jdbc.H2Profile.api._ object SlickTables { class PlayerTable(tag: Tag) extends Table[Player](tag, None, "Player") { - override def * = (id, name, country, dob) <> (Player.tupled, Player.unapply) + override def * = (id, name, country, dob).mapTo[Player] val id: Rep[Long] = column[Long]("player_id", O.AutoInc, O.PrimaryKey) val name: Rep[String] = column[String]("name") diff --git a/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/BaeldungPostgresProfile.scala b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/BaeldungPostgresProfile.scala new file mode 100644 index 000000000..2ef666642 --- /dev/null +++ b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/BaeldungPostgresProfile.scala @@ -0,0 +1,35 @@ +package com.baeldung.scala.slick_pg + +import com.github.tminglei.slickpg.* +import org.json4s.{JValue, JsonMethods} +import slick.jdbc.JdbcType + +trait BaeldungPostgresProfile + extends ExPostgresProfile + with PgDate2Support + with TimestamptzPostgresProfile + with PgArraySupport + with PgJson4sSupport { + + override protected def computeCapabilities: Set[slick.basic.Capability] = + super.computeCapabilities + slick.jdbc.JdbcCapabilities.insertOrUpdate + + override val api = BaeldungApi + + override val pgjson = "jsonb" + type DOCType = org.json4s.native.Document + override val jsonMethods = + org.json4s.native.JsonMethods.asInstanceOf[JsonMethods[DOCType]] + + object BaeldungApi + extends ExtPostgresAPI + with Date2DateTimeImplicitsDuration + with ArrayImplicits + with JsonImplicits { + + implicit val doubleListTypeMapper: JdbcType[List[Double]] = + new SimpleArrayJdbcType[Double]("DOUBLE PRECISION").to(_.toList) + } +} + +object BaeldungPostgresProfile extends BaeldungPostgresProfile diff --git a/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/PostgresConnection.scala b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/PostgresConnection.scala new file mode 100644 index 000000000..bc00c3dc5 --- /dev/null +++ b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/PostgresConnection.scala @@ -0,0 +1,10 @@ +package com.baeldung.scala.slick_pg + +import com.baeldung.scala.slick_pg +import com.baeldung.scala.slick_pg.BaeldungPostgresProfile.api.* + +object PostgresConnection { + + val db: slick_pg.BaeldungPostgresProfile.backend.Database = + Database.forConfig("postgres") +} diff --git a/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/TimestamptzPostgresProfile.scala b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/TimestamptzPostgresProfile.scala new file mode 100644 index 000000000..570d0a560 --- /dev/null +++ b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/TimestamptzPostgresProfile.scala @@ -0,0 +1,56 @@ +package com.baeldung.scala.slick_pg + +import slick.jdbc.PostgresProfile + +import java.sql.{PreparedStatement, ResultSet} +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatterBuilder +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoField + +trait TimestamptzPostgresProfile extends PostgresProfile { + + override val columnTypes: PostgresJdbcTypes = new PostgresJdbcTypes { + + override val offsetDateTimeType: OffsetDateTimeJdbcType = + new OffsetDateTimeJdbcType { + + override def sqlType: Int = { + java.sql.Types.TIMESTAMP_WITH_TIMEZONE + } + + private val formatter = { + new DateTimeFormatterBuilder() + .append(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + .optionalStart() + .appendFraction(ChronoField.NANO_OF_SECOND, 0, 6, true) + .optionalEnd() + .optionalStart() + .appendOffset("+HH:mm:ss", "+00") + .optionalEnd() + .toFormatter() + } + + override def setValue( + v: OffsetDateTime, + p: PreparedStatement, + idx: Int + ): Unit = { + p.setObject(idx, v) + } + override def getValue(r: ResultSet, idx: Int): OffsetDateTime = { + r.getString(idx) match { + case null => null + case date: String => OffsetDateTime.from(formatter.parse(date)) + } + } + override def updateValue( + v: OffsetDateTime, + r: ResultSet, + idx: Int + ): Unit = { + r.updateObject(idx, v) + } + } + } +} diff --git a/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/entity/BaeldungEntity.scala b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/entity/BaeldungEntity.scala new file mode 100644 index 000000000..e853cd6ca --- /dev/null +++ b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/entity/BaeldungEntity.scala @@ -0,0 +1,12 @@ +package com.baeldung.scala.slick_pg.entity + +import org.json4s.JValue + +import java.time.OffsetDateTime + +case class BaeldungEntity( + id: Long, + createdAt: OffsetDateTime, + prices: List[Double], + metadata: JValue +) diff --git a/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/table/BaeldungEntityTable.scala b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/table/BaeldungEntityTable.scala new file mode 100644 index 000000000..323dcd08d --- /dev/null +++ b/scala-libraries-persistence/src/main/scala/com/baeldung/scala/slick_pg/table/BaeldungEntityTable.scala @@ -0,0 +1,21 @@ +package com.baeldung.scala.slick_pg.table + +import com.baeldung.scala.slick_pg.BaeldungPostgresProfile.BaeldungApi.* +import com.baeldung.scala.slick_pg.entity.BaeldungEntity +import org.json4s.JValue + +import java.time.OffsetDateTime + +class BaeldungEntityTable(tag: Tag) + extends Table[BaeldungEntity](tag, None, "baeldung_entity") { + + val id = column[Long]("id", O.PrimaryKey, O.AutoInc, O.SqlType("BIGSERIAL")) + val createdAt = column[OffsetDateTime]("created_at", O.SqlType("TIMESTAMPTZ")) + val prices = column[List[Double]]("prices", O.SqlType("DOUBLE PRECISION[]")) + val metadata = column[JValue]("metadata", O.SqlType("JSONB")) + + override def * = (id, createdAt, prices, metadata).mapTo[BaeldungEntity] + +} + +val baeldungEntityTable = TableQuery[BaeldungEntityTable] diff --git a/scala-libraries-persistence/src/test/resources/logback-test.xml b/scala-libraries-persistence/src/test/resources/logback-test.xml new file mode 100644 index 000000000..f35180c79 --- /dev/null +++ b/scala-libraries-persistence/src/test/resources/logback-test.xml @@ -0,0 +1,24 @@ + + + + + myapp.log + false + + myapp_%d{yyyy-MM-dd}.log + + + [%date{ISO8601}] [%level] [%logger] [%marker] [%thread] - %msg MDC: {%mdc}%n + + + + + 8192 + true + + + + + + + \ No newline at end of file diff --git a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/skunk/Session.scala b/scala-libraries-persistence/src/test/scala/com/baeldung/scala/skunk/Session.scala similarity index 100% rename from scala-libraries-4/src/test/scala-2/com/baeldung/scala/skunk/Session.scala rename to scala-libraries-persistence/src/test/scala/com/baeldung/scala/skunk/Session.scala diff --git a/scala-libraries/src/test/scala-2/com/baeldung/scala/slick/PlayerServiceUnitTest.scala b/scala-libraries-persistence/src/test/scala/com/baeldung/scala/slick/PlayerServiceUnitTest.scala similarity index 99% rename from scala-libraries/src/test/scala-2/com/baeldung/scala/slick/PlayerServiceUnitTest.scala rename to scala-libraries-persistence/src/test/scala/com/baeldung/scala/slick/PlayerServiceUnitTest.scala index 1f2c63337..c4f3b8f1a 100644 --- a/scala-libraries/src/test/scala-2/com/baeldung/scala/slick/PlayerServiceUnitTest.scala +++ b/scala-libraries-persistence/src/test/scala/com/baeldung/scala/slick/PlayerServiceUnitTest.scala @@ -8,7 +8,6 @@ import org.scalatest.concurrent.ScalaFutures import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AsyncWordSpec import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, FutureOutcome} -import slick.jdbc.H2Profile.api._ import scala.concurrent.Future @@ -19,6 +18,7 @@ class PlayerServiceUnitTest with BeforeAndAfterAll with BeforeAndAfterEach { + import slick.jdbc.H2Profile.api._ private val playerTable = TableQuery[PlayerTable] lazy val db = Connection.db diff --git a/scala-libraries-standalone/README.md b/scala-libraries-standalone/README.md new file mode 100644 index 000000000..34c58c4d2 --- /dev/null +++ b/scala-libraries-standalone/README.md @@ -0,0 +1,5 @@ +## Relevant Articles + +- [Working with Redis in Scala](https://www.baeldung.com/scala/redis) +- [Introduction to Tapir](https://www.baeldung.com/scala/tapir) +- [Refining Existing Types in Scala With Refined](https://www.baeldung.com/scala/refined-types) diff --git a/scala-libraries-standalone/nscalatime/src/main/scala/com/baeldung/nscalatime/NscalaTime.scala b/scala-libraries-standalone/nscalatime/src/main/scala/com/baeldung/nscalatime/NscalaTime.scala new file mode 100644 index 000000000..0f137a433 --- /dev/null +++ b/scala-libraries-standalone/nscalatime/src/main/scala/com/baeldung/nscalatime/NscalaTime.scala @@ -0,0 +1,71 @@ +package com.baeldung.nscalatime + +import com.github.nscala_time.time.Imports.* +import java.util.Date +import java.util.Calendar +import java.util.Locale +import org.joda.time.chrono.EthiopicChronology +import org.joda.time.DateTime.Property +import com.github.nscala_time.time.DurationBuilder +import org.joda.time.format.DateTimeFormatter + +object NScalaTime: + // working with DateTime + val st: DateTime = DateTime.now() + val parsedDate: DateTime = DateTime.parse("2024-08-07") + val parsedDate2: DateTime = DateTime.parse("2024") + + // JDK interoperability + val jdkDate: Date = new Date() + val convertTo: DateTime = new DateTime(jdkDate) + + val jdkCal: Calendar = Calendar.getInstance() + val st2: DateTime = new DateTime(jdkCal) + + val st3: DateTime = new DateTime + val cal: Calendar = st3.gregorianCalendar + + // querrying Datetime + val dayOfWeek: Property = new DateTime().dayOfWeek() + val shortText: String = dayOfWeek.asShortText + val asText: String = dayOfWeek.asText + val monthOfYearString: String = DateTime.now().monthOfYear().getAsText() + val monthOfYearStringFrench: String = + DateTime.now().monthOfYear().getAsShortText(Locale.FRENCH) + val isLeapYear: Boolean = DateTime.now().year().isLeap() + val nowToTomorrow: Interval = DateTime.now().to(DateTime.tomorrow()) + + // Accessing fields + // Date fields + val st4: DateTime = new DateTime() + val era: String = st4.era.asText + val year2: String = st4.year.asText + val centOfEra: String = st4.centuryOfEra.asText + val yrOfEra: String = st4.yearOfEra.asText + val yrOfCent: String = st4.yearOfCentury.asText + val monthOfYr: String = st4.monthOfYear.asText + val dayOfYr: String = st4.dayOfYear.asText + val dayOfMonth: String = st4.dayOfMonth.asText + val dayOfWeek2: String = st4.dayOfWeek.asText + // Time fields + val hrOfDay: String = st4.hourOfDay.asText + val minOfHr: String = st4.minuteOfHour.asText + val secOfMin: String = st4.secondOfMinute.asText + + // Manipulating datetimes + val compare: Boolean = DateTime.now() < DateTime.lastMonth() + val addVarious: Period = 2.month + 5.hours + 6.millis + val less5: DateTime = DateTime.now() - 5.days + val sampleDuration: DurationBuilder = 4.hours + 30.minutes + 10.seconds + val datetimebeforeDuration: DateTime = sampleDuration.before(DateTime.now()) + val changeTZ: DateTime = less5.withZone(DateTimeZone.forID("Africa/Kampala")) + val chrono: DateTime = less5.withChronology(EthiopicChronology.getInstance()) + val ethioYear: Int = chrono.getYear() + + // formatters + val fmt: DateTimeFormatter = DateTimeFormat.forPattern("dd-MM-yyyy") + val st5: DateTime = fmt.parseDateTime("31-07-2024") + +import NScalaTime.* +@main def sTime(): Unit = + println(st5) diff --git a/scala-libraries-standalone/nscalatime/src/test/scala/com/baeldung/nscalatime/NscalaTimeUnitTest.scala b/scala-libraries-standalone/nscalatime/src/test/scala/com/baeldung/nscalatime/NscalaTimeUnitTest.scala new file mode 100644 index 000000000..8e52890c0 --- /dev/null +++ b/scala-libraries-standalone/nscalatime/src/test/scala/com/baeldung/nscalatime/NscalaTimeUnitTest.scala @@ -0,0 +1,106 @@ +package com.baeldung.nscalatime + +import com.github.nscala_time.time.Imports.* +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.Tables.* +import org.scalatest.prop.TableDrivenPropertyChecks +import org.scalatest.prop.TableFor1 +import java.util.Date +import java.util.Calendar +import com.github.nscala_time.time.DurationBuilder +import org.joda.time.format.DateTimeFormatter +import NScalaTime.* + +class NScalaTimeUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks: + + "DateTime checks" should "pass as DateTime objects" in: + val dateTimeObjects: TableFor1[DateTime] = + Table( + "datetimeobjects", + parsedDate, + parsedDate2, + convertTo, + st2, + st3, + st4, + less5, + changeTZ, + chrono, + st5 + ) + + forAll(dateTimeObjects) { n => + n shouldBe an[DateTime] + } + + "jdkDate" should "pass as a Date objects" in: + jdkDate shouldBe an[Date] + + "Calendar checks" should "pass as Calendar objects" in: + val calendarObjects: TableFor1[Calendar] = + Table( + "calendarobjects", + jdkCal, + cal + ) + + forAll(calendarObjects) { n => + n shouldBe an[Calendar] + } + + "strings checks" should "pass as String objects" in: + val stringObjects: TableFor1[String] = + Table( + "stringsobjects", + shortText, + asText, + monthOfYearString, + monthOfYearStringFrench, + era, + year2, + centOfEra, + yrOfEra, + yrOfCent, + monthOfYr, + dayOfYr, + dayOfMonth, + dayOfWeek2, + hrOfDay, + minOfHr, + secOfMin + ) + + forAll(stringObjects) { n => + n shouldBe an[String] + } + + "boolean checks" should "pass as Boolean objects" in: + val booleanObjects: TableFor1[Boolean] = + Table( + "booleanobjects", + isLeapYear, + compare + ) + + forAll(booleanObjects) { n => + n shouldBe an[Boolean] + } + + "nowToTomorrow" should "pass as an Interval object" in: + nowToTomorrow shouldBe an[Interval] + + "addVarious" should "pass as a Period object" in: + addVarious shouldBe an[Period] + + "sampleDuration" should "pass as a DurationBuilder object" in: + sampleDuration shouldBe an[DurationBuilder] + + "ethioYear" should "pass as an Int object" in: + ethioYear shouldBe an[Int] + + "fmt" should "pass as a DateTimeFormatter object" in: + fmt shouldBe an[DateTimeFormatter] diff --git a/scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/HelloRedisSpec.scala b/scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/HelloRedisManualTest.scala similarity index 66% rename from scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/HelloRedisSpec.scala rename to scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/HelloRedisManualTest.scala index 255235eff..ddc2affee 100644 --- a/scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/HelloRedisSpec.scala +++ b/scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/HelloRedisManualTest.scala @@ -1,9 +1,9 @@ package com.baeldung.redis -import com.baeldung.redis.util.RedisSpec +import com.baeldung.redis.util.RedisManualTest import org.scalatest.flatspec.AnyFlatSpec -class HelloRedisSpec extends AnyFlatSpec with RedisSpec { +class HelloRedisManualTest extends AnyFlatSpec with RedisManualTest { "The Hello Redis object" should "retrieve stored values" in { getJedis().set("key1", "value1") diff --git a/scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/cache/CacheThroughSpec.scala b/scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/cache/CacheThroughManualTest.scala similarity index 78% rename from scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/cache/CacheThroughSpec.scala rename to scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/cache/CacheThroughManualTest.scala index a2423abde..84e1edfc1 100644 --- a/scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/cache/CacheThroughSpec.scala +++ b/scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/cache/CacheThroughManualTest.scala @@ -1,12 +1,15 @@ package com.baeldung.redis.cache import com.baeldung.redis.db.{BooksDB, VirtualDatabase} -import com.baeldung.redis.util.RedisSpec +import com.baeldung.redis.util.RedisManualTest import org.mockito.Mockito._ import org.scalatest.flatspec.AnyFlatSpec import org.scalatestplus.mockito.MockitoSugar -class CacheThroughSpec extends AnyFlatSpec with RedisSpec with MockitoSugar { +class CacheThroughManualTest + extends AnyFlatSpec + with RedisManualTest + with MockitoSugar { "CacheThrough" should "fetch from DB only the first time" in { val mockDb = mock[VirtualDatabase] diff --git a/scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/geospatial/GeospatialSpec.scala b/scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/geospatial/GeospatialManualTest.scala similarity index 94% rename from scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/geospatial/GeospatialSpec.scala rename to scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/geospatial/GeospatialManualTest.scala index dc23152c4..d267f3cfc 100644 --- a/scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/geospatial/GeospatialSpec.scala +++ b/scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/geospatial/GeospatialManualTest.scala @@ -9,10 +9,10 @@ import com.baeldung.redis.geospatial.GeoPoints.{ MethanaIndex, Vromolimni } -import com.baeldung.redis.util.RedisSpec +import com.baeldung.redis.util.RedisManualTest import org.scalatest.flatspec.AnyFlatSpec -class GeospatialSpec extends AnyFlatSpec with RedisSpec { +class GeospatialManualTest extends AnyFlatSpec with RedisManualTest { private def loadGeopoints(geoSpatial: Geospatial): Unit = { geoSpatial.add(MethanaIndex)(Methana.name)(Methana.latLon) diff --git a/scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/leaderboard/LeaderboardSpec.scala b/scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/leaderboard/LeaderboardManualTest.scala similarity index 92% rename from scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/leaderboard/LeaderboardSpec.scala rename to scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/leaderboard/LeaderboardManualTest.scala index fa0f3e0bc..a2d297481 100644 --- a/scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/leaderboard/LeaderboardSpec.scala +++ b/scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/leaderboard/LeaderboardManualTest.scala @@ -1,10 +1,10 @@ package com.baeldung.redis.leaderboard import com.baeldung.redis.leaderboard.model.EmployDB -import com.baeldung.redis.util.RedisSpec +import com.baeldung.redis.util.RedisManualTest import org.scalatest.flatspec.AnyFlatSpec -class LeaderboardSpec extends AnyFlatSpec with RedisSpec { +class LeaderboardManualTest extends AnyFlatSpec with RedisManualTest { "Leaderboard#count" should "return correct counts for Employees after plus one calls" in { val commitLeaderboardKey = "commits" diff --git a/scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/ratelimit/RateLimitSpec.scala b/scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/ratelimit/RateLimitManualTest.scala similarity index 82% rename from scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/ratelimit/RateLimitSpec.scala rename to scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/ratelimit/RateLimitManualTest.scala index 1a395f22b..d7a995889 100644 --- a/scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/ratelimit/RateLimitSpec.scala +++ b/scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/ratelimit/RateLimitManualTest.scala @@ -1,12 +1,12 @@ package com.baeldung.redis.ratelimit -import com.baeldung.redis.util.RedisSpec +import com.baeldung.redis.util.RedisManualTest import org.scalatest.flatspec.AnyFlatSpec import scala.concurrent.duration._ import scala.language.postfixOps -class RateLimitSpec extends AnyFlatSpec with RedisSpec { +class RateLimitManualTest extends AnyFlatSpec with RedisManualTest { "RateLimit" should "respond with not allowed for the given duration" in { val limitedKey = "limited-key" diff --git a/scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/util/RedisSpec.scala b/scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/util/RedisManualTest.scala similarity index 74% rename from scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/util/RedisSpec.scala rename to scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/util/RedisManualTest.scala index 2e4eb4058..d260e5b88 100644 --- a/scala-libraries-4/src/redis-intro/src/test/scala/com/baeldung/redis/util/RedisSpec.scala +++ b/scala-libraries-standalone/redis-intro/src/it/scala/com/baeldung/redis/util/RedisManualTest.scala @@ -3,7 +3,10 @@ package com.baeldung.redis.util import org.scalatest.{BeforeAndAfter, BeforeAndAfterEach, Suite} import redis.clients.jedis.args.FlushMode -trait RedisSpec extends WithRedis with BeforeAndAfter with BeforeAndAfterEach { +trait RedisManualTest + extends WithRedis + with BeforeAndAfter + with BeforeAndAfterEach { this: Suite => diff --git a/scala-libraries-4/src/redis-intro/docker-compose.yml b/scala-libraries-standalone/redis-intro/src/main/resources/docker-compose.yml similarity index 100% rename from scala-libraries-4/src/redis-intro/docker-compose.yml rename to scala-libraries-standalone/redis-intro/src/main/resources/docker-compose.yml diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/cache/CacheThrough.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/cache/CacheThrough.scala similarity index 100% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/cache/CacheThrough.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/cache/CacheThrough.scala diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/db/BooksDB.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/db/BooksDB.scala similarity index 100% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/db/BooksDB.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/db/BooksDB.scala diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/db/VirtualDatabase.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/db/VirtualDatabase.scala similarity index 100% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/db/VirtualDatabase.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/db/VirtualDatabase.scala diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/geospatial/GeoPoints.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/geospatial/GeoPoints.scala similarity index 100% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/geospatial/GeoPoints.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/geospatial/GeoPoints.scala diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/geospatial/Geospatial.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/geospatial/Geospatial.scala similarity index 100% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/geospatial/Geospatial.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/geospatial/Geospatial.scala diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/LeaderBoard.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/LeaderBoard.scala similarity index 79% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/LeaderBoard.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/LeaderBoard.scala index 24ae2a497..4f90eacb3 100644 --- a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/LeaderBoard.scala +++ b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/LeaderBoard.scala @@ -15,9 +15,9 @@ class LeaderBoard(hllKey: String, jedis: Jedis) { def plusN(key: LeaderboardKey, n: Int): Unit = { val randValues = (0 until n) map (_ => random()) - jedis.pfadd(s"$hllKey:${key.firstLevelKey}", randValues: _*) - jedis.pfadd(s"$hllKey:${key.secondLevelKey}", randValues: _*) - jedis.pfadd(s"$hllKey:${key.thirdLevelKey}", randValues: _*) + jedis.pfadd(s"$hllKey:${key.firstLevelKey}", randValues*) + jedis.pfadd(s"$hllKey:${key.secondLevelKey}", randValues*) + jedis.pfadd(s"$hllKey:${key.thirdLevelKey}", randValues*) } def count(key: String): Long = { diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/model/EmployDB.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/model/EmployDB.scala similarity index 100% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/model/EmployDB.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/model/EmployDB.scala diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/model/EmployeeKey.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/model/EmployeeKey.scala similarity index 100% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/model/EmployeeKey.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/leaderboard/model/EmployeeKey.scala diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/pubsub/Publisher.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/pubsub/Publisher.scala similarity index 100% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/pubsub/Publisher.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/pubsub/Publisher.scala diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/pubsub/Subscriber.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/pubsub/Subscriber.scala similarity index 100% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/pubsub/Subscriber.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/pubsub/Subscriber.scala diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/ratelimit/RateLimit.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/ratelimit/RateLimit.scala similarity index 100% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/ratelimit/RateLimit.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/ratelimit/RateLimit.scala diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/util/RedisClients.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/util/RedisClients.scala similarity index 100% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/util/RedisClients.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/util/RedisClients.scala diff --git a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/util/WithRedis.scala b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/util/WithRedis.scala similarity index 80% rename from scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/util/WithRedis.scala rename to scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/util/WithRedis.scala index ffe18e955..54b417fc6 100644 --- a/scala-libraries-4/src/redis-intro/src/main/scala/com/baeldung/redis/util/WithRedis.scala +++ b/scala-libraries-standalone/redis-intro/src/main/scala/com/baeldung/redis/util/WithRedis.scala @@ -4,7 +4,7 @@ import redis.clients.jedis.Jedis trait WithRedis { - private var clients: RedisClients = _ + private var clients: RedisClients = scala.compiletime.uninitialized protected def initialize(): Unit = { clients = new RedisClients("localhost", 6379) diff --git a/scala-libraries-standalone/refined/src/main/scala/com/baeldung/refined/RefinedIntro.scala b/scala-libraries-standalone/refined/src/main/scala/com/baeldung/refined/RefinedIntro.scala new file mode 100644 index 000000000..d4a8c1491 --- /dev/null +++ b/scala-libraries-standalone/refined/src/main/scala/com/baeldung/refined/RefinedIntro.scala @@ -0,0 +1,77 @@ +package com.baeldung.refined + +import eu.timepit.refined._ +import eu.timepit.refined.api.Refined +import eu.timepit.refined.auto._ +import eu.timepit.refined.numeric._ +import eu.timepit.refined.string._ +import eu.timepit.refined.collection._ +import eu.timepit.refined.char._ +import eu.timepit.refined.boolean._ +import eu.timepit.refined.api.Validate + +object RefinedIntro { + // val oddNumber: Refined[Int, Odd] = 8 + // val oddNumber: Int Refined Odd = 8 + + val age: Int Refined Less[35] = 30 + val ageInterval: Int Refined Interval.Closed[30, 35] = 35 + val age2: Int Refined GreaterEqual[35] = 35 + val ageInput: Int = 36 + val ageCheck = refineV[GreaterEqual[35]](ageInput) + + val myDigit: Char Refined Digit = '8' + val myLetter: Char Refined Letter = 'H' + + val myName: String Refined StartsWith["S"] = "Sandra" + val myName2: String Refined EndsWith["t"] = "Herbert" + // val myName3: String Refined StartsWith["s"] = "Sandra" + // val myAddr: String Refined IPv6 = "127.0.0.1" + + type myRegex = MatchesRegex["""[A-Za-z0-9]+"""] + val accessCode: String Refined myRegex = "DC13h" + + type myIntRegex = myRegex And ValidInt + val accessCode2: String Refined myIntRegex = "97426" + // val accessCode3: String Refined myIntRegex = "9742B" + + val fruits = List("Banana", "Orange", "Lemon", "Guava") + val contains = refineV[Contains["Berry"]](fruits) + val forall = refineV[Forall[Trimmed]](fruits) + val last = refineV[Last[Uuid]](fruits) + val size = refineV[Size[Less[5]]](fruits) + + case class Person(name: String, height: Double) + case class Tall() + case class Average() + case class Short() + + implicit val tallValidate: Validate.Plain[Person, Tall] = + Validate.fromPredicate( + p => p.height >= 6.0, + p => s"(${p.name} is tall)", + Tall() + ) + + implicit val averageValidate: Validate.Plain[Person, Average] = + Validate.fromPredicate( + p => p.height >= 5.0 && p.height < 6.0, + p => s"(${p.name} is average)", + Average() + ) + + implicit val shortValidate: Validate.Plain[Person, Short] = + Validate.fromPredicate( + p => p.height < 5.0, + p => s"(${p.name} is short)", + Short() + ) + + val tall = refineV[Tall](Person("Herbert", 5.5)) + val average = refineV[Average](Person("Herbert", 5.5)) +} + +object Hello extends App { + import RefinedIntro._ + println(refineV[Short](Person("Herbert", 4.0))) +} diff --git a/scala-libraries-standalone/refined/src/test/scala/com/baeldung/refined/RefinedUnitTest.scala b/scala-libraries-standalone/refined/src/test/scala/com/baeldung/refined/RefinedUnitTest.scala new file mode 100644 index 000000000..ba3ae0fbc --- /dev/null +++ b/scala-libraries-standalone/refined/src/test/scala/com/baeldung/refined/RefinedUnitTest.scala @@ -0,0 +1,75 @@ +package com.baeldung.refined + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import eu.timepit.refined._ +import eu.timepit.refined.api.Refined +import eu.timepit.refined.auto._ +import eu.timepit.refined.numeric._ +import eu.timepit.refined.string._ +import eu.timepit.refined.collection._ +import eu.timepit.refined.char._ +import eu.timepit.refined.boolean._ +import eu.timepit.refined.api.Validate +import scala.util.{Right, Left} + +class RefinedUnitTest extends AnyFlatSpec with Matchers { + import RefinedIntro.{size => sizeTest, _} + + "Int checks" should "pass with refined types" in { + assert(refineV[Odd](8) == Left("Predicate (8 % 2 == 0) did not fail.")) + age shouldBe an[Int Refined Less[35]] + ageInterval shouldBe an[Int Refined Interval.Closed[30, 35]] + age2 shouldBe an[Int Refined GreaterEqual[35]] + ageInput shouldBe an[Int] + ageCheck shouldBe an[Either[String, Refined[Int, GreaterEqual[35]]]] + } + + "Char checks" should "pass for refined types" in { + assert(myDigit.isInstanceOf[Char Refined Digit]) + assert(myLetter.isInstanceOf[Char Refined Letter]) + } + + "String checks" should "pass for refined types" in { + assert( + refineV[StartsWith["s"]]("Sandra") == Left( + "Predicate failed: \"Sandra\".startsWith(\"s\")." + ) + ) + assert( + refineV[IPv6]("127.0.0.1") == Left( + "Predicate failed: 127.0.0.1 is a valid IPv6." + ) + ) + assert(myName.isInstanceOf[String Refined StartsWith["S"]]) + assert(myName2.isInstanceOf[String Refined EndsWith["t"]]) + } + + "Regex checks" should "pass for refined types" in { + assert( + refineV[myIntRegex]("9742B") == Left( + "Right predicate of (\"9742B\".matches(\"[A-Za-z0-9]+\") && isValidValidInt(\"9742B\")) failed: ValidInt predicate failed: For input string: \"9742B\"" + ) + ) + assert(accessCode.isInstanceOf[String Refined myRegex]) + + assert(accessCode2.isInstanceOf[String Refined myIntRegex]) + } + + "collection checks" should "pass for refined types" in { + assert( + forall + .isInstanceOf[Either[String, Refined[List[String], Forall[Trimmed]]]] + ) + assert(last.isInstanceOf[Either[String, Refined[List[String], Last[Uuid]]]]) + assert( + sizeTest + .isInstanceOf[Either[String, Refined[List[String], Size[Less[5]]]]] + ) + } + + "Person checks" should "pass for custom refined types" in { + assert(tall.isInstanceOf[Either[String, Refined[Person, Tall]]]) + assert(average.isInstanceOf[Either[String, Refined[Person, Average]]]) + } +} diff --git a/scala-libraries-3/src/tapir/build.sbt b/scala-libraries-standalone/tapir/build.sbt similarity index 97% rename from scala-libraries-3/src/tapir/build.sbt rename to scala-libraries-standalone/tapir/build.sbt index d074e5e81..773b820ae 100644 --- a/scala-libraries-3/src/tapir/build.sbt +++ b/scala-libraries-standalone/tapir/build.sbt @@ -1,5 +1,5 @@ val scalaV = "2.12.16" -val tapirV = "1.0.3" +val tapirV = "1.12.6" ThisBuild / scalaVersion := scalaV ThisBuild / version := "1.0.0" ThisBuild / organization := "com.baeldung" diff --git a/scala-libraries-3/src/tapir/client/src/main/scala/com/baeldung/tapir/client/Client.scala b/scala-libraries-standalone/tapir/client/src/main/scala/com/baeldung/tapir/client/Client.scala similarity index 100% rename from scala-libraries-3/src/tapir/client/src/main/scala/com/baeldung/tapir/client/Client.scala rename to scala-libraries-standalone/tapir/client/src/main/scala/com/baeldung/tapir/client/Client.scala diff --git a/scala-libraries-3/src/tapir/endpoint/src/main/scala/com/baeldung/tapir/endpoint/AnimalEndpoints.scala b/scala-libraries-standalone/tapir/endpoint/src/main/scala/com/baeldung/tapir/endpoint/AnimalEndpoints.scala similarity index 100% rename from scala-libraries-3/src/tapir/endpoint/src/main/scala/com/baeldung/tapir/endpoint/AnimalEndpoints.scala rename to scala-libraries-standalone/tapir/endpoint/src/main/scala/com/baeldung/tapir/endpoint/AnimalEndpoints.scala diff --git a/scala3-lang-2/project/build.properties b/scala-libraries-standalone/tapir/project/build.properties similarity index 100% rename from scala3-lang-2/project/build.properties rename to scala-libraries-standalone/tapir/project/build.properties diff --git a/scala-libraries-3/src/tapir/server/src/main/scala/com/baeldung/tapir/server/BaseAkkaServer.scala b/scala-libraries-standalone/tapir/server/src/main/scala/com/baeldung/tapir/server/BaseAkkaServer.scala similarity index 100% rename from scala-libraries-3/src/tapir/server/src/main/scala/com/baeldung/tapir/server/BaseAkkaServer.scala rename to scala-libraries-standalone/tapir/server/src/main/scala/com/baeldung/tapir/server/BaseAkkaServer.scala diff --git a/scala-libraries-3/src/tapir/server/src/main/scala/com/baeldung/tapir/server/Database.scala b/scala-libraries-standalone/tapir/server/src/main/scala/com/baeldung/tapir/server/Database.scala similarity index 100% rename from scala-libraries-3/src/tapir/server/src/main/scala/com/baeldung/tapir/server/Database.scala rename to scala-libraries-standalone/tapir/server/src/main/scala/com/baeldung/tapir/server/Database.scala diff --git a/scala-libraries-3/src/tapir/server/src/main/scala/com/baeldung/tapir/server/KittensServer.scala b/scala-libraries-standalone/tapir/server/src/main/scala/com/baeldung/tapir/server/KittensServer.scala similarity index 100% rename from scala-libraries-3/src/tapir/server/src/main/scala/com/baeldung/tapir/server/KittensServer.scala rename to scala-libraries-standalone/tapir/server/src/main/scala/com/baeldung/tapir/server/KittensServer.scala diff --git a/scala-libraries-testing/README.md b/scala-libraries-testing/README.md new file mode 100644 index 000000000..b66635f35 --- /dev/null +++ b/scala-libraries-testing/README.md @@ -0,0 +1,7 @@ +### Relevant Articles: + +- [Introduction to ScalaCheck](https://www.baeldung.com/scala/scalacheck) +- [Introduction to ScalaMock](https://www.baeldung.com/scala/scalamock) +- [Introduction to uTest](https://www.baeldung.com/scala/utest-intro) +- [Introduction to MUnit](https://www.baeldung.com/scala/munit-introduction) +- [Making Integration Testing Easier With TestContainers-scala](https://www.baeldung.com/scala/testcontainers-scala) diff --git a/scala-libraries-4/src/it/resources/docker-compose.yml b/scala-libraries-testing/src/it/resources/docker-compose.yml similarity index 100% rename from scala-libraries-4/src/it/resources/docker-compose.yml rename to scala-libraries-testing/src/it/resources/docker-compose.yml diff --git a/scala-libraries-4/src/it/resources/s3-test.txt b/scala-libraries-testing/src/it/resources/s3-test.txt similarity index 100% rename from scala-libraries-4/src/it/resources/s3-test.txt rename to scala-libraries-testing/src/it/resources/s3-test.txt diff --git a/scala-libraries-4/src/it/scala-2/com/baeldung/scala/testcontainers/DockerComposeManualTest.scala b/scala-libraries-testing/src/it/scala/com/baeldung/scala/testcontainers/DockerComposeManualTest.scala similarity index 96% rename from scala-libraries-4/src/it/scala-2/com/baeldung/scala/testcontainers/DockerComposeManualTest.scala rename to scala-libraries-testing/src/it/scala/com/baeldung/scala/testcontainers/DockerComposeManualTest.scala index dd541c3f7..91824696a 100644 --- a/scala-libraries-4/src/it/scala-2/com/baeldung/scala/testcontainers/DockerComposeManualTest.scala +++ b/scala-libraries-testing/src/it/scala/com/baeldung/scala/testcontainers/DockerComposeManualTest.scala @@ -39,7 +39,7 @@ class DockerComposeManualTest getClass.getClassLoader.getResource("s3-test.txt").getFile ) - override lazy val containerDef: DockerComposeContainer.Def = { + override val containerDef: DockerComposeContainer.Def = { DockerComposeContainer.Def( new File( this.getClass.getClassLoader.getResource("docker-compose.yml").getFile @@ -90,7 +90,7 @@ class DockerComposeManualTest ).fold( { case ex: NoSuchKeyException => fail("File not found: " + ex) - case _ => fail + case _ => fail() }, _ => succeed ) diff --git a/scala-libraries-4/src/it/scala-2/com/baeldung/scala/testcontainers/GenericContainerManualTest.scala b/scala-libraries-testing/src/it/scala/com/baeldung/scala/testcontainers/GenericContainerManualTest.scala similarity index 98% rename from scala-libraries-4/src/it/scala-2/com/baeldung/scala/testcontainers/GenericContainerManualTest.scala rename to scala-libraries-testing/src/it/scala/com/baeldung/scala/testcontainers/GenericContainerManualTest.scala index 63c924bd4..4773f1eb9 100644 --- a/scala-libraries-4/src/it/scala-2/com/baeldung/scala/testcontainers/GenericContainerManualTest.scala +++ b/scala-libraries-testing/src/it/scala/com/baeldung/scala/testcontainers/GenericContainerManualTest.scala @@ -107,7 +107,7 @@ class GenericContainerManualTest ).fold( { case _: NoSuchKeyException => fail("File not found") - case _ => fail + case _ => fail() }, _ => succeed ) diff --git a/scala-libraries-4/src/it/scala-2/com/baeldung/scala/testcontainers/LocalstackModuleManualTest.scala b/scala-libraries-testing/src/it/scala/com/baeldung/scala/testcontainers/LocalstackModuleManualTest.scala similarity index 97% rename from scala-libraries-4/src/it/scala-2/com/baeldung/scala/testcontainers/LocalstackModuleManualTest.scala rename to scala-libraries-testing/src/it/scala/com/baeldung/scala/testcontainers/LocalstackModuleManualTest.scala index 2ddc89912..c95e51292 100644 --- a/scala-libraries-4/src/it/scala-2/com/baeldung/scala/testcontainers/LocalstackModuleManualTest.scala +++ b/scala-libraries-testing/src/it/scala/com/baeldung/scala/testcontainers/LocalstackModuleManualTest.scala @@ -67,7 +67,7 @@ class LocalstackModuleManualTest ).fold( { case _: NoSuchKeyException => fail("File not found") - case _ => fail + case _ => fail() }, _ => succeed ) diff --git a/scala-libraries-4/src/main/scala-2/com/baeldung/scala/testcontainers/SimpleS3Uploader.scala b/scala-libraries-testing/src/main/scala/com/baeldung/scala/testcontainers/SimpleS3Uploader.scala similarity index 100% rename from scala-libraries-4/src/main/scala-2/com/baeldung/scala/testcontainers/SimpleS3Uploader.scala rename to scala-libraries-testing/src/main/scala/com/baeldung/scala/testcontainers/SimpleS3Uploader.scala diff --git a/scala3-libraries/src/test/scala/com/baeldung/munit/MUnitTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/munit/MUnitTest.scala similarity index 95% rename from scala3-libraries/src/test/scala/com/baeldung/munit/MUnitTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/munit/MUnitTest.scala index f4664e0a6..61c47964e 100644 --- a/scala3-libraries/src/test/scala/com/baeldung/munit/MUnitTest.scala +++ b/scala-libraries-testing/src/test/scala/com/baeldung/munit/MUnitTest.scala @@ -2,7 +2,6 @@ package com.baeldung.munit import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global -import munit.GenericBeforeEach class MUnitTest extends munit.FunSuite { @@ -66,7 +65,7 @@ class MUnitTest extends munit.FunSuite { ) } -class MacOnlyTest extends munit.FunSuite { +class MacOnlyUnitTest extends munit.FunSuite { override def munitIgnore: Boolean = !scala.util.Properties.isWin test("This is a mac only test") { println("mac only test") diff --git a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/AsyncTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/AsyncUnitTest.scala similarity index 88% rename from scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/AsyncTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/AsyncUnitTest.scala index 2949b18df..80f8e7589 100644 --- a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/AsyncTest.scala +++ b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/AsyncUnitTest.scala @@ -5,7 +5,7 @@ import utest._ import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global -object AsyncTest extends TestSuite { +object AsyncUnitTest extends TestSuite { def getFromDB(): Future[Int] = Future { 42 } override def tests: Tests = Tests { diff --git a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/BeforeAfterTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/BeforeAfterUnitTest.scala similarity index 88% rename from scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/BeforeAfterTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/BeforeAfterUnitTest.scala index cf4df8f97..96b753f40 100644 --- a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/BeforeAfterTest.scala +++ b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/BeforeAfterUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.utest import utest._ -object BeforeAfterTest extends TestSuite { +object BeforeAfterUnitTest extends TestSuite { println("This is executed before the tests") override def utestAfterAll() = { println("This method will be executed after all the tests") diff --git a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/ExceptionHandling.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/ExceptionHandlingUnitTest.scala similarity index 85% rename from scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/ExceptionHandling.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/ExceptionHandlingUnitTest.scala index c9a97ad01..6b4b5f20c 100644 --- a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/ExceptionHandling.scala +++ b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/ExceptionHandlingUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.utest import utest._ -object ExceptionHandling extends TestSuite { +object ExceptionHandlingUnitTest extends TestSuite { override def tests: Tests = Tests { def funnyMethod: String = throw new RuntimeException("Uh oh...") test("Handle an exception") { diff --git a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/NestedTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/NestedUnitTest.scala similarity index 92% rename from scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/NestedTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/NestedUnitTest.scala index ba2226212..68cc92236 100644 --- a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/NestedTest.scala +++ b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/NestedUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.utest import utest._ -object NestedTest extends TestSuite { +object NestedUnitTest extends TestSuite { override def tests: Tests = Tests { test("outer test") - { val list = List(1, 2) diff --git a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/RetryTestSuite.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/RetryTestUnitTest.scala similarity index 78% rename from scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/RetryTestSuite.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/RetryTestUnitTest.scala index 8beb3180a..ffd2bb034 100644 --- a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/RetryTestSuite.scala +++ b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/RetryTestUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.utest import utest._ -object RetryTestSuite extends TestSuite with TestSuite.Retries { +object RetryTestUnitTest extends TestSuite with TestSuite.Retries { override def utestRetryCount: Int = 3 override def tests: Tests = Tests { test("retryable test 1") { diff --git a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/RetryTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/RetryUnitTest.scala similarity index 88% rename from scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/RetryTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/RetryUnitTest.scala index 497116eb0..535e9fa0f 100644 --- a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/RetryTest.scala +++ b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/RetryUnitTest.scala @@ -4,7 +4,7 @@ import utest._ import scala.util.Random -object RetryTest extends TestSuite { +object RetryUnitTest extends TestSuite { override def tests: Tests = Tests { def flakyMethod: Int = Random.nextInt(2) // change this value to test failure diff --git a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/SimpleUTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/SimpleUnitTest.scala similarity index 86% rename from scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/SimpleUTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/SimpleUnitTest.scala index 546a90457..0f58fb048 100644 --- a/scala-libraries-4/src/test/scala-2/com/baeldung/scala/utest/SimpleUTest.scala +++ b/scala-libraries-testing/src/test/scala/com/baeldung/scala/utest/SimpleUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.utest import utest._ -object SimpleUTest extends TestSuite { +object SimpleUnitTest extends TestSuite { override def tests: Tests = Tests { test("str") { val name = "Baeldung" diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/CommandsUnitTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/CommandsUnitTest.scala similarity index 97% rename from scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/CommandsUnitTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/CommandsUnitTest.scala index b3d3eeab5..844653995 100644 --- a/scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/CommandsUnitTest.scala +++ b/scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/CommandsUnitTest.scala @@ -8,7 +8,7 @@ import java.time.Clock import java.util.UUID import scala.util.{Success, Try} -object TrafficLightCommandsTest extends Properties("TrafficLightCommands") { +object TrafficLightCommandsUnitTest extends Properties("TrafficLightCommands") { property("TrafficLight") = CommandsUnitTest.property() } diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/GeneratorsUnitTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/GeneratorsUnitTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/GeneratorsUnitTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/GeneratorsUnitTest.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/PropertiesUnitTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/PropertiesUnitTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/PropertiesUnitTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/PropertiesUnitTest.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/PropertiesWithCustomParametersUnitTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/PropertiesWithCustomParametersUnitTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/PropertiesWithCustomParametersUnitTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/PropertiesWithCustomParametersUnitTest.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/model/Simple.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/model/Simple.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/model/Simple.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/model/Simple.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/model/SystemUnderTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/model/SystemUnderTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/model/SystemUnderTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/model/SystemUnderTest.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/model/TrafficLight.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/model/TrafficLight.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/scalacheck/model/TrafficLight.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scalacheck/model/TrafficLight.scala diff --git a/scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/ScalamockFeaturesTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scalamock/ScalamockFeaturesUnitTest.scala similarity index 83% rename from scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/ScalamockFeaturesTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scalamock/ScalamockFeaturesUnitTest.scala index 9ec760dfa..a823121dc 100644 --- a/scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/ScalamockFeaturesTest.scala +++ b/scala-libraries-testing/src/test/scala/com/baeldung/scalamock/ScalamockFeaturesUnitTest.scala @@ -6,14 +6,14 @@ import org.scalamock.matchers.ArgCapture.CaptureOne import org.scalamock.scalatest.MockFactory import org.scalatest.wordspec.AnyWordSpec -class ScalamockFeaturesTest extends AnyWordSpec with MockFactory { +class ScalamockFeaturesUnitTest extends AnyWordSpec with MockFactory { "ArgumentMatching" should { "call Service1 doSomething result" in { val m1 = Model1(1L, "Jim") val m2 = Model2(2L, "Timmy") val mockUnitService1 = mock[UnitService1] - (mockUnitService1.doSomething _).expects(m1, m2) + (mockUnitService1.doSomething).expects(m1, m2) val unitService2 = new UnitService2Impl(mockUnitService1) unitService2.doSomething(m1, m2) succeed @@ -23,7 +23,7 @@ class ScalamockFeaturesTest extends AnyWordSpec with MockFactory { val m1 = Model1(1L, "Jim") val m2 = Model2(2L, "Timmy") val mockUnitService1 = mock[UnitService1] - (mockUnitService1.doSomething _).expects(m1, *) + (mockUnitService1.doSomething).expects(m1, *) val unitService2 = new UnitService2Impl(mockUnitService1) unitService2.doSomething(m1, m2) succeed @@ -33,7 +33,7 @@ class ScalamockFeaturesTest extends AnyWordSpec with MockFactory { val m1 = Model1(1L, "Jim") val m2 = Model2(2L, "Timmy") val mockUnitService1 = mock[UnitService1] - (mockUnitService1.doSomething _).expects(where { + (mockUnitService1.doSomething).expects(where { (_m1: Model1, _m2: Model2) => _m1.id == 1L && _m2.id == 2L }) val unitService2 = new UnitService2Impl(mockUnitService1) @@ -49,7 +49,7 @@ class ScalamockFeaturesTest extends AnyWordSpec with MockFactory { } "match close numbers" in { val mockedModel3 = mock[Model3] - (mockedModel3.numFunc _).expects(~71.5f) + (mockedModel3.numFunc).expects(~71.5f) mockedModel3.numFunc(71.50002f) // success // mockedModel3.numFunc(71.502f) // failure succeed @@ -62,8 +62,8 @@ class ScalamockFeaturesTest extends AnyWordSpec with MockFactory { val m2 = Model2(2L, "Timmy") val mockUnitService1 = mock[UnitService1] inSequence { - (mockUnitService1.doSomething _).expects(m1, m2) - (mockUnitService1.doSomethingElse _).expects(m1, m2) + (mockUnitService1.doSomething).expects(m1, m2) + (mockUnitService1.doSomethingElse).expects(m1, m2) } val unitService2 = new UnitService2Impl(mockUnitService1) unitService2.doManyThings(m1, m2) // success @@ -76,8 +76,8 @@ class ScalamockFeaturesTest extends AnyWordSpec with MockFactory { val m2 = Model2(2L, "Timmy") val mockUnitService1 = mock[UnitService1] inAnyOrder { - (mockUnitService1.doSomething _).expects(m1, m2) - (mockUnitService1.doSomethingElse _).expects(m1, m2) + (mockUnitService1.doSomething).expects(m1, m2) + (mockUnitService1.doSomethingElse).expects(m1, m2) } val unitService2 = new UnitService2Impl(mockUnitService1) // unitService2.doManyThings(m1, m2) // also success @@ -92,7 +92,7 @@ class ScalamockFeaturesTest extends AnyWordSpec with MockFactory { } "verify the number of calls" in { val mockedModel3 = mock[Model3] - (mockedModel3.emptyFunc _).expects().once() + ((() => mockedModel3.emptyFunc())).expects().once() // (mockedModel3.emptyFunc _).expects().twice() // exactly 2 times // (mockedModel3.emptyFunc _).expects().never() // never called // (mockedModel3.emptyFunc _).expects().repeat(4) // exactly 4 times @@ -108,7 +108,7 @@ class ScalamockFeaturesTest extends AnyWordSpec with MockFactory { } "verify the number of calls" in { val mockedModel3 = mock[Model3] - (mockedModel3.getInt _).expects().returning(12) + ((() => mockedModel3.getInt())).expects().returning(12) assert(mockedModel3.getInt() === 12) } } @@ -119,7 +119,7 @@ class ScalamockFeaturesTest extends AnyWordSpec with MockFactory { } "throw exception on mock call" in { val mockedModel3 = mock[Model3] - (mockedModel3.getInt _) + ((() => mockedModel3.getInt())) .expects() .throwing(new RuntimeException("getInt called")) assertThrows[RuntimeException](mockedModel3.getInt()) @@ -132,7 +132,7 @@ class ScalamockFeaturesTest extends AnyWordSpec with MockFactory { } "return argument plus 1" in { val mockedModel3 = mock[Model3] - (mockedModel3.get _).expects(*).onCall((i: Int) => i + 1) + (mockedModel3.get).expects(*).onCall((i: Int) => i + 1) assert(mockedModel3.get(4) === 5) } } @@ -144,7 +144,7 @@ class ScalamockFeaturesTest extends AnyWordSpec with MockFactory { "capture arguments" in { val mockedOneArg = mock[OneArg] val captor = CaptureOne[Int]() - (mockedOneArg.func _).expects(capture(captor)).atLeastOnce() + (mockedOneArg.func).expects(capture(captor)).atLeastOnce() mockedOneArg.func(32) assert(captor.value === 32) } @@ -156,7 +156,7 @@ class ScalamockFeaturesTest extends AnyWordSpec with MockFactory { def call[A](arg: A): A } val mockPolymorphic = mock[Polymorphic] - (mockPolymorphic.call[Int] _).expects(1).onCall((i: Int) => i * 2) + (mockPolymorphic.call[Int]).expects(1).onCall((i: Int) => i * 2) assert(mockPolymorphic.call(1) === 2) } } diff --git a/scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/ScalamockFunctionTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scalamock/ScalamockFunctionUnitTest.scala similarity index 96% rename from scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/ScalamockFunctionTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scalamock/ScalamockFunctionUnitTest.scala index c2be9996e..7478b2193 100644 --- a/scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/ScalamockFunctionTest.scala +++ b/scala-libraries-testing/src/test/scala/com/baeldung/scalamock/ScalamockFunctionUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scalamock import org.scalamock.scalatest.MockFactory import org.scalatest.wordspec.AnyWordSpec -class ScalamockFunctionTest extends AnyWordSpec with MockFactory { +class ScalamockFunctionUnitTest extends AnyWordSpec with MockFactory { "MockedFunction" should { @@ -20,7 +20,7 @@ class ScalamockFunctionTest extends AnyWordSpec with MockFactory { def call(f: Int => String, i: Int): String } val mockedFoo = mock[Foo] - (mockedFoo.call _) + (mockedFoo.call) .expects(*, *) .onCall((f: Int => String, i: Int) => Range(0, i).mkString(",")) assert(mockedFoo.call(_ => "bla", 3) === "0,1,2") diff --git a/scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/ScalamockMockingStylesTest.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scalamock/ScalamockMockingStylesUnitTest.scala similarity index 66% rename from scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/ScalamockMockingStylesTest.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scalamock/ScalamockMockingStylesUnitTest.scala index d13da6e26..1e38f0d74 100644 --- a/scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/ScalamockMockingStylesTest.scala +++ b/scala-libraries-testing/src/test/scala/com/baeldung/scalamock/ScalamockMockingStylesUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scalamock import org.scalamock.scalatest.MockFactory import org.scalatest.wordspec.AnyWordSpec -class ScalamockMockingStylesTest extends AnyWordSpec with MockFactory { +class ScalamockMockingStylesUnitTest extends AnyWordSpec with MockFactory { "MockingStyle" should { trait MockitoWannabe { @@ -11,9 +11,9 @@ class ScalamockMockingStylesTest extends AnyWordSpec with MockFactory { } "record and then verify" in { val mockedWannabe = stub[MockitoWannabe] - (mockedWannabe.foo _).when(*).onCall((i: Int) => i * 2) + (mockedWannabe.foo).when(*).onCall((i: Int) => i * 2) assert(mockedWannabe.foo(12) === 24) - (mockedWannabe.foo _).verify(12) + (mockedWannabe.foo).verify(12) } } diff --git a/scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/model/Models.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scalamock/model/Models.scala similarity index 100% rename from scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/model/Models.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scalamock/model/Models.scala diff --git a/scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/service/Services.scala b/scala-libraries-testing/src/test/scala/com/baeldung/scalamock/service/Services.scala similarity index 100% rename from scala-libraries-3/src/test/scala-2/com/baeldung/scalamock/service/Services.scala rename to scala-libraries-testing/src/test/scala/com/baeldung/scalamock/service/Services.scala diff --git a/scala-libraries/README.md b/scala-libraries/README.md index 5f7efb735..40012dfc6 100644 --- a/scala-libraries/README.md +++ b/scala-libraries/README.md @@ -1,12 +1,10 @@ -### Relevant Articles: +## Relevant Articles: -- [Introduction to Optics in Scala Using Monocle](https://www.baeldung.com/scala/monocle-optics) -- [Building REST APIs in Scala with Finch](https://www.baeldung.com/scala/finch-rest-apis) -- [Introduction to Slick](https://www.baeldung.com/scala/slick-intro) -- [The Principles Behind Scalaz](https://www.baeldung.com/scala/scalaz-principles) -- [Scala – Introduction to Cats](https://www.baeldung.com/scala/cats-intro) -- [Introduction to Scalaz](https://www.baeldung.com/scala/scalaz-intro) -- [Introduction to FS2: Functional Streams for Scala](https://www.baeldung.com/scala/fs2-functional-streams) -- [Introduction to Generic Programming in Scala with shapeless](https://www.baeldung.com/scala/generic-programming) -- [Actor Lifecycle in Akka](https://www.baeldung.com/scala/akka-actor-lifecycle) -- [Introduction to Reactive Mongo](https://www.baeldung.com/scala/mongo-reactive-intro) +- [Parsing JSON with Circe](https://www.baeldung.com/scala/circe-json) +- [Database Access with Play](https://www.baeldung.com/scala/play-database-access) +- [Simple REST Requests Using Requests-Scala](https://www.baeldung.com/scala/rest-with-requests-scala) +- [Guide to elastic4s – Elasticsearch Scala Client](https://www.baeldung.com/scala/elastic4s-elasticsearch-client) +- [A Guide to the Scala Retry Library](https://www.baeldung.com/scala/retry-library) +- [Introduction to Apache Log4j in Scala](https://www.baeldung.com/scala/apache-log4j) +- [Logging in Scala Applications Using Scala-Logging](https://www.baeldung.com/scala/scala-logging) +- [AWScala – AWS SDK for Scala](https://www.baeldung.com/scala/awscala-aws-sdk-for-scala) \ No newline at end of file diff --git a/scala-libraries-2/src/it/scala-2/com/baeldung/requests/RequestsScalaHttpClientLiveTest.scala b/scala-libraries/src/it/scala/com/baeldung/requests/RequestsScalaHttpClientLiveTest.scala similarity index 97% rename from scala-libraries-2/src/it/scala-2/com/baeldung/requests/RequestsScalaHttpClientLiveTest.scala rename to scala-libraries/src/it/scala/com/baeldung/requests/RequestsScalaHttpClientLiveTest.scala index 0d5133001..f6715ffb9 100644 --- a/scala-libraries-2/src/it/scala-2/com/baeldung/requests/RequestsScalaHttpClientLiveTest.scala +++ b/scala-libraries/src/it/scala/com/baeldung/requests/RequestsScalaHttpClientLiveTest.scala @@ -24,7 +24,7 @@ class RequestsScalaHttpClientLiveTest "Requests" should { "invoke a simple GET request" in { val r: Response = requests.get("https://api.github.com/users/baeldung") - assert(r.text.contains("http://www.baeldung.com")) + assert(r.text().contains("http://www.baeldung.com")) assert(r.statusCode == 200) assert(r.contentType.exists(_.contains("application/json"))) assert(r.is2xx) @@ -134,7 +134,7 @@ class RequestsScalaHttpClientLiveTest requests.MultiItem("hint", "This is file upload") ) ) - assert(r.text contains ("multipart/form-data")) + assert(r.text() `contains` ("multipart/form-data")) } } diff --git a/scala-libraries/src/main/resources/application.conf b/scala-libraries/src/main/resources/application.conf index 2272bb414..25ff922a8 100644 --- a/scala-libraries/src/main/resources/application.conf +++ b/scala-libraries/src/main/resources/application.conf @@ -1,17 +1,11 @@ -h2mem { - url = "jdbc:h2:mem:testDB" - driver = org.h2.Driver - keepAliveConnection = true - connectionPool = disabled -} - -#postgres { -# dataSourceClass = "org.postgresql.ds.PGSimpleDataSource" -# properties = { -# serverName = "localhost" -# portNumber = "5432" -# databaseName = "slick-tutorial" -# user = "postgres" -# password = "admin" -# } -#} \ No newline at end of file +slick.dbs.default { + profile = "slick.jdbc.PostgresProfile$" + db { + driver = "org.postgresql.Driver" + url="jdbc:postgres://localhost:5432/postgres" + user=postgres + password=postgres + numThreads=20 + maxConnections=20 + } +} \ No newline at end of file diff --git a/scala-libraries/src/main/resources/elasticmq.conf b/scala-libraries/src/main/resources/elasticmq.conf new file mode 100644 index 000000000..99649fe40 --- /dev/null +++ b/scala-libraries/src/main/resources/elasticmq.conf @@ -0,0 +1,52 @@ +# What is the outside visible address of this ElasticMQ node +# Used to create the queue URL (may be different from bind address!) +node-address { + protocol = http + host = localhost + port = 9324 + context-path = "" +} + +rest-sqs { + enabled = true + bind-port = 9324 + bind-hostname = "0.0.0.0" + // Possible values: relaxed, strict + sqs-limits = strict +} + +rest-stats { + enabled = true + bind-port = 9325 + bind-hostname = "0.0.0.0" +} + +// Should the node-address be generated from the bind port/hostname +// Set this to true e.g. when assigning port automatically by using port 0. +generate-node-address = false + +queues { + queue1 { + defaultVisibilityTimeout = 10 seconds + delay = 0 seconds + receiveMessageWait = 0 seconds + deadLettersQueue { + name = "queue1-dead-letters" + maxReceiveCount = 3 // from 1 to 1000 + } + fifo = false + contentBasedDeduplication = false + tags { + tag1 = "tagged1" + tag2 = "tagged2" + } + } + queue1-dead-letters { } +} + +elastic-mq { + region = "elasticMQ" + endPoint = "http://localhost:9325" + access-key-id = "your aws access key id" + secret-access-key = "secret-access-token" +} \ No newline at end of file diff --git a/scala-libraries-5/src/main/resources/log4j2.xml b/scala-libraries/src/main/resources/log4j2.xml similarity index 100% rename from scala-libraries-5/src/main/resources/log4j2.xml rename to scala-libraries/src/main/resources/log4j2.xml diff --git a/scala-libraries/src/main/resources/persons.csv b/scala-libraries/src/main/resources/persons.csv new file mode 100644 index 000000000..164284cec --- /dev/null +++ b/scala-libraries/src/main/resources/persons.csv @@ -0,0 +1,4 @@ +id,name +1,Jim +2,Bob +3,Mary diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/circe/JSONConversions.scala b/scala-libraries/src/main/scala/com/baeldung/circe/JSONConversions.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/circe/JSONConversions.scala rename to scala-libraries/src/main/scala/com/baeldung/circe/JSONConversions.scala diff --git a/scala-libraries/src/main/scala/com/baeldung/csv/reader/ApacheCommonsCSVReader.scala b/scala-libraries/src/main/scala/com/baeldung/csv/reader/ApacheCommonsCSVReader.scala new file mode 100644 index 000000000..ccbf47f0d --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/csv/reader/ApacheCommonsCSVReader.scala @@ -0,0 +1,24 @@ +package com.baeldung.csv.reader + +import org.apache.commons.csv.CSVFormat + +import java.io.{File, FileInputStream, InputStreamReader} +import scala.jdk.CollectionConverters.IterableHasAsScala + +class ApacheCommonsCSVReader extends CommaSeparatedValuesReader { + + override def read(file: File): CSVReadDigest = { + val in = new InputStreamReader(new FileInputStream(file)) + val csvParser = CSVFormat.DEFAULT + .builder() + .setHeader() + .build() + .parse(in) + val result = CSVReadDigest( + csvParser.getHeaderNames.asScala.toSeq, + csvParser.getRecords.asScala.map(r => r.values().toSeq).toSeq + ) + csvParser.close() + result + } +} diff --git a/scala-libraries/src/main/scala/com/baeldung/csv/reader/CSVReaders.scala b/scala-libraries/src/main/scala/com/baeldung/csv/reader/CSVReaders.scala new file mode 100644 index 000000000..662b6908a --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/csv/reader/CSVReaders.scala @@ -0,0 +1,23 @@ +package com.baeldung.csv.reader + +import java.io.File + +object CSVReaders { + + def main(args: Array[String]): Unit = { + + val apacheCommonsCSVReader = new ApacheCommonsCSVReader + val simpleCSVReader = new SimpleCSVReader + val scalaCSVReader = new ScalaCSVReader + val openCSVReader = new OpenCSVReader + + val file = new File(getClass.getResource("/persons.csv").getFile) + + println(apacheCommonsCSVReader.read(file)) + println(simpleCSVReader.read(file)) + println(scalaCSVReader.read(file)) + println(openCSVReader.read(file)) + + } + +} diff --git a/scala-libraries/src/main/scala/com/baeldung/csv/reader/CommaSeparatedValuesReader.scala b/scala-libraries/src/main/scala/com/baeldung/csv/reader/CommaSeparatedValuesReader.scala new file mode 100644 index 000000000..0beafbc5c --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/csv/reader/CommaSeparatedValuesReader.scala @@ -0,0 +1,10 @@ +package com.baeldung.csv.reader + +import java.io.File + +case class CSVReadDigest(headers: Seq[String], rows: Seq[Seq[String]]) +trait CommaSeparatedValuesReader { + + def read(file: File): CSVReadDigest + +} diff --git a/scala-libraries/src/main/scala/com/baeldung/csv/reader/OpenCSVReader.scala b/scala-libraries/src/main/scala/com/baeldung/csv/reader/OpenCSVReader.scala new file mode 100644 index 000000000..521b631ec --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/csv/reader/OpenCSVReader.scala @@ -0,0 +1,35 @@ +package com.baeldung.csv.reader + +import com.opencsv.CSVReader + +import java.io.{File, FileInputStream, InputStreamReader} +import scala.annotation.tailrec + +class OpenCSVReader extends CommaSeparatedValuesReader { + + override def read(file: File): CSVReadDigest = { + + val reader = new CSVReader( + new InputStreamReader(new FileInputStream(file)) + ) + + @tailrec + def readLinesRecursively( + currentReader: CSVReader, + result: Seq[Seq[String]] + ): Seq[Seq[String]] = { + currentReader.readNext() match { + case null => result + case line => readLinesRecursively(currentReader, result :+ line.toSeq) + } + } + + val csvLines = readLinesRecursively(reader, List()) + reader.close() + + CSVReadDigest( + csvLines.head, + csvLines.tail + ) + } +} diff --git a/scala-libraries/src/main/scala/com/baeldung/csv/reader/ScalaCSVReader.scala b/scala-libraries/src/main/scala/com/baeldung/csv/reader/ScalaCSVReader.scala new file mode 100644 index 000000000..492a4c16c --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/csv/reader/ScalaCSVReader.scala @@ -0,0 +1,15 @@ +package com.baeldung.csv.reader + +import com.github.tototoshi.csv.CSVReader + +import java.io.File + +class ScalaCSVReader extends CommaSeparatedValuesReader { + + override def read(file: File): CSVReadDigest = { + val reader = CSVReader.open(file) + val all = reader.all() + reader.close() + CSVReadDigest(all.head, all.tail) + } +} diff --git a/scala-libraries/src/main/scala/com/baeldung/csv/reader/SimpleCSVReader.scala b/scala-libraries/src/main/scala/com/baeldung/csv/reader/SimpleCSVReader.scala new file mode 100644 index 000000000..d1a0b6f92 --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/csv/reader/SimpleCSVReader.scala @@ -0,0 +1,36 @@ +package com.baeldung.csv.reader + +import java.io.{BufferedReader, File, FileInputStream, InputStreamReader} +import scala.annotation.tailrec + +class SimpleCSVReader extends CommaSeparatedValuesReader { + + override def read(file: File): CSVReadDigest = { + val in = new InputStreamReader(new FileInputStream(file)) + val bufferedReader = new BufferedReader(in) + + @tailrec + def readLinesRecursively( + currentBufferedReader: BufferedReader, + result: Seq[Seq[String]] + ): Seq[Seq[String]] = { + currentBufferedReader.readLine() match { + case null => result + case line => + readLinesRecursively( + currentBufferedReader, + result :+ line.split(",").toSeq + ) + } + } + + val csvLines = readLinesRecursively(bufferedReader, List()) + + bufferedReader.close() + + CSVReadDigest( + csvLines.head, + csvLines.tail + ) + } +} diff --git a/scala-libraries/src/main/scala/com/baeldung/csv/writer/ApacheCommonsCSVWriter.scala b/scala-libraries/src/main/scala/com/baeldung/csv/writer/ApacheCommonsCSVWriter.scala new file mode 100644 index 000000000..57b4699a0 --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/csv/writer/ApacheCommonsCSVWriter.scala @@ -0,0 +1,25 @@ +package com.baeldung.csv.writer +import org.apache.commons.csv.{CSVFormat, CSVPrinter} + +import java.io.{File, FileWriter} +import scala.util.Try + +class ApacheCommonsCSVWriter extends CommaSeparatedValuesWriter { + + override def write( + file: File, + headers: Seq[String], + rows: Seq[Seq[String]] + ): Try[Unit] = Try { + + val csvFormat = CSVFormat.DEFAULT + .builder() + .setHeader(headers: _*) + .build() + + val out = new FileWriter(file) + val printer = new CSVPrinter(out, csvFormat) + rows.foreach(row => printer.printRecord(row: _*)) + printer.close() + } +} diff --git a/scala-libraries/src/main/scala/com/baeldung/csv/writer/CommaSeparatedValuesWriter.scala b/scala-libraries/src/main/scala/com/baeldung/csv/writer/CommaSeparatedValuesWriter.scala new file mode 100644 index 000000000..1c49bcc17 --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/csv/writer/CommaSeparatedValuesWriter.scala @@ -0,0 +1,14 @@ +package com.baeldung.csv.writer + +import java.io.File +import scala.util.Try + +trait CommaSeparatedValuesWriter { + + def write( + file: File, + headers: Seq[String], + rows: Seq[Seq[String]] + ): Try[Unit] + +} diff --git a/scala-libraries/src/main/scala/com/baeldung/csv/writer/CsvWriters.scala b/scala-libraries/src/main/scala/com/baeldung/csv/writer/CsvWriters.scala new file mode 100644 index 000000000..e6d6e92a5 --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/csv/writer/CsvWriters.scala @@ -0,0 +1,51 @@ +package com.baeldung.csv.writer + +import java.io.File +import scala.util.{Failure, Success, Try} + +object CsvWriters { + + def main(args: Array[String]): Unit = { + + val fileName = "foo.csv" + val headers = List("id", "name") + val rows = List( + List("1", "Manolis"), + List("2", "Thanasis"), + List("3", "Stefanos") + ) + + val simpleCSVWriter = new SimpleCSVWriter + val openCSVWriter = new OpenCSVWriter + val scalaCSVWriter = new ScalaCSVWriter + val apacheCommonsCSVWriter = new ApacheCommonsCSVWriter + + handleFailure( + simpleCSVWriter.write(new File(s"simple-$fileName"), headers, rows) + ) + handleFailure( + openCSVWriter.write(new File(s"openCSV-$fileName"), headers, rows) + ) + handleFailure( + scalaCSVWriter.write(new File(s"scalaCSV-$fileName"), headers, rows) + ) + handleFailure( + apacheCommonsCSVWriter.write( + new File(s"apacheCommons-$fileName"), + headers, + rows + ) + ) + } + + private def handleFailure(tryWrite: Try[Unit]): Unit = { + tryWrite match { + case Success(_) => + case Failure(exception) => + println( + s"Something went wrong during CSV writing: ${exception.getMessage}" + ) + } + } + +} diff --git a/scala-libraries/src/main/scala/com/baeldung/csv/writer/OpenCSVWriter.scala b/scala-libraries/src/main/scala/com/baeldung/csv/writer/OpenCSVWriter.scala new file mode 100644 index 000000000..fd36108ac --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/csv/writer/OpenCSVWriter.scala @@ -0,0 +1,26 @@ +package com.baeldung.csv.writer + +import com.opencsv.CSVWriter + +import java.io.{BufferedWriter, File, FileWriter} +import scala.jdk.CollectionConverters.IterableHasAsJava +import scala.util.Try + +class OpenCSVWriter extends CommaSeparatedValuesWriter { + + override def write( + file: File, + headers: Seq[String], + rows: Seq[Seq[String]] + ): Try[Unit] = Try( + new CSVWriter(new BufferedWriter(new FileWriter(file))) + ).flatMap((csvWriter: CSVWriter) => + Try { + csvWriter.writeAll( + (headers +: rows).map(_.toArray).asJava, + false + ) + csvWriter.close() + } + ) +} diff --git a/scala-libraries/src/main/scala/com/baeldung/csv/writer/ScalaCSVWriter.scala b/scala-libraries/src/main/scala/com/baeldung/csv/writer/ScalaCSVWriter.scala new file mode 100644 index 000000000..6d25b6d0c --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/csv/writer/ScalaCSVWriter.scala @@ -0,0 +1,20 @@ +package com.baeldung.csv.writer + +import com.github.tototoshi.csv.CSVWriter + +import java.io.File +import scala.util.Try + +class ScalaCSVWriter extends CommaSeparatedValuesWriter { + + override def write( + file: File, + headers: Seq[String], + rows: Seq[Seq[String]] + ): Try[Unit] = Try { + val writer = CSVWriter.open(file) + writer.writeRow(headers) + writer.writeAll(rows) + writer.close() + } +} diff --git a/scala-libraries/src/main/scala/com/baeldung/csv/writer/SimpleCSVWriter.scala b/scala-libraries/src/main/scala/com/baeldung/csv/writer/SimpleCSVWriter.scala new file mode 100644 index 000000000..1c0d73ff4 --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/csv/writer/SimpleCSVWriter.scala @@ -0,0 +1,19 @@ +package com.baeldung.csv.writer + +import java.io.{File, PrintWriter} +import scala.util.Try + +class SimpleCSVWriter extends CommaSeparatedValuesWriter { + + override def write( + file: File, + headers: Seq[String], + rows: Seq[Seq[String]] + ): Try[Unit] = Try { + val writer = new PrintWriter(file) + writer.println(headers.mkString(",")) + rows.foreach(row => writer.println(row.mkString(","))) + writer.close() + } + +} diff --git a/scala-libraries-2/src/main/scala-2/com/baeldung/elastic4s/Main.scala b/scala-libraries/src/main/scala/com/baeldung/elastic4s/Main.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/com/baeldung/elastic4s/Main.scala rename to scala-libraries/src/main/scala/com/baeldung/elastic4s/Main.scala diff --git a/scala-libraries/src/main/scala/com/baeldung/elasticmq/AsyncSQSClient.scala b/scala-libraries/src/main/scala/com/baeldung/elasticmq/AsyncSQSClient.scala new file mode 100644 index 000000000..80b584cd8 --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/elasticmq/AsyncSQSClient.scala @@ -0,0 +1,221 @@ +package com.baeldung.elasticmq + +import software.amazon.awssdk.auth.credentials.{ + AwsBasicCredentials, + AwsCredentialsProviderChain, + StaticCredentialsProvider +} +import software.amazon.awssdk.regions.Region +import software.amazon.awssdk.services.sqs.model.* +import software.amazon.awssdk.services.sqs.{ + SqsAsyncClient, + SqsAsyncClientBuilder +} + +import java.net.URI +import java.util.UUID + +import scala.concurrent.{ExecutionContext, Future} +import scala.jdk.FutureConverters.* +import scala.jdk.CollectionConverters.* + +class SQSAsyncClient( + queueURL: String, + region: String, + endpoint: String +)(implicit executionContext: ExecutionContext): + + private val sqsAsyncClient: SqsAsyncClient = + SqsAsyncClient + .builder() + .region(Region.of(region)) + .credentialsProvider( + AwsCredentialsProviderChain + .builder() + .credentialsProviders( + StaticCredentialsProvider.create( + AwsBasicCredentials.create( + ElasticMQConfig.ELASTIC_MQ_ACCESS_KEY, + ElasticMQConfig.ELASTIC_MQ_SECRET_ACCESS_KEY + ) + ) + ) + .build() + ) + .endpointOverride(URI.create(endpoint)) + .build() + + def createStandardQueue(queueName: String): Future[CreateQueueResponse] = + val request = CreateQueueRequest.builder.queueName(queueName).build + + sqsAsyncClient.createQueue(request).asScala + + final lazy val createFIFOQueueAttributes = Map( + (QueueAttributeName.FIFO_QUEUE, "true") + ).asJava + + def createFIFOQueue(queueName: String): Future[CreateQueueResponse] = + val createQueueRequest = CreateQueueRequest.builder + .queueName(queueName) + .attributes(createFIFOQueueAttributes) + .build + + sqsAsyncClient.createQueue(createQueueRequest).asScala + + def deleteQueue(): Future[DeleteQueueResponse] = + val request = DeleteQueueRequest.builder().queueUrl(queueURL).build() + + sqsAsyncClient.deleteQueue(request).asScala + + def sendMessage(message: String): Future[SendMessageResponse] = + val request = SendMessageRequest + .builder() + .messageBody(message) + .queueUrl(queueURL) + .build() + + sqsAsyncClient.sendMessage(request).asScala + + def sendMessagesInBatch( + messages: List[String] + ): Future[SendMessageBatchResponse] = + val batchRequestEntry = messages + .map( + SendMessageBatchRequestEntry + .builder() + .messageBody(_) + .id(UUID.randomUUID().toString) + .build() + ) + .asJava + val sendMessageBatchRequest = SendMessageBatchRequest + .builder() + .queueUrl(queueURL) + .entries(batchRequestEntry) + .build() + + sqsAsyncClient.sendMessageBatch(sendMessageBatchRequest).asScala + + // maxNumberOfMessages must be less than 10. + def receiveMessages( + maxNumberOfMessages: Int + ): Future[ReceiveMessageResponse] = + val receiveMessageRequest = + ReceiveMessageRequest + .builder() + .maxNumberOfMessages(maxNumberOfMessages) + .queueUrl(queueURL) + .waitTimeSeconds(10) + .build() + + sqsAsyncClient.receiveMessage(receiveMessageRequest).asScala + + def deleteMessage(receiptHandle: String): Future[DeleteMessageResponse] = + val deleteMessageRequest = DeleteMessageRequest + .builder() + .queueUrl(queueURL) + .receiptHandle(receiptHandle) + .build() + + sqsAsyncClient.deleteMessage(deleteMessageRequest).asScala + + def deleteMessageInBatch( + messages: List[Message] + ): Future[DeleteMessageBatchResponse] = + val listDeleteMessageBatchRequestEntry = messages + .map(message => + DeleteMessageBatchRequestEntry + .builder() + .receiptHandle(message.receiptHandle()) + .build() + ) + .asJava + val deleteMessageBatchRequest = DeleteMessageBatchRequest + .builder() + .queueUrl(queueURL) + .entries(listDeleteMessageBatchRequestEntry) + .build() + + sqsAsyncClient.deleteMessageBatch(deleteMessageBatchRequest).asScala + + def getQueueURL(queueName: String): Future[GetQueueUrlResponse] = + val getQueueUrlRequest = + GetQueueUrlRequest.builder().queueName(queueName).build() + + sqsAsyncClient.getQueueUrl(getQueueUrlRequest).asScala + + def listQueues(): Future[ListQueuesResponse] = + sqsAsyncClient.listQueues().asScala + + def listQueuesStartingFromPrefix(prefix: String): Future[ListQueuesResponse] = + val listQueueStartingFromPrefixRequest = + ListQueuesRequest.builder().queueNamePrefix(prefix).build() + + sqsAsyncClient.listQueues(listQueueStartingFromPrefixRequest).asScala + + def changeMessageVisibility( + message: Message + ): Future[ChangeMessageVisibilityResponse] = + val changeMessageVisibilityRequest = ChangeMessageVisibilityRequest + .builder() + .queueUrl(queueURL) + .receiptHandle(message.receiptHandle()) + .visibilityTimeout(30) + .build() + + sqsAsyncClient + .changeMessageVisibility(changeMessageVisibilityRequest) + .asScala + + def changeMessageVisibilityOfBatch( + messages: List[Message] + ): Future[ChangeMessageVisibilityBatchResponse] = + val changeMessageVisibilityBatchRequestEntry = messages + .map(message => + ChangeMessageVisibilityBatchRequestEntry + .builder() + .receiptHandle(message.receiptHandle()) + .visibilityTimeout(30) + .build() + ) + .asJava + val changeMessageVisibilityRequest = ChangeMessageVisibilityBatchRequest + .builder() + .queueUrl(queueURL) + .entries(changeMessageVisibilityBatchRequestEntry) + .build() + + sqsAsyncClient + .changeMessageVisibilityBatch(changeMessageVisibilityRequest) + .asScala + + final lazy val purgeQueueRequest = + PurgeQueueRequest.builder().queueUrl(queueURL).build() + def purgeQueue(): Future[PurgeQueueResponse] = + sqsAsyncClient.purgeQueue(purgeQueueRequest).asScala + + def setQueueAttributes( + attributes: Map[QueueAttributeName, String] + ): Future[SetQueueAttributesResponse] = + val setQueueAttributesRequest = SetQueueAttributesRequest + .builder() + .queueUrl(queueURL) + .attributes(attributes.asJava) + .build() + + sqsAsyncClient.setQueueAttributes(setQueueAttributesRequest).asScala + + def tagQueue(tags: Map[String, String]): Future[TagQueueResponse] = + val tagQueueRequest = + TagQueueRequest.builder().queueUrl(queueURL).tags(tags.asJava).build() + + sqsAsyncClient.tagQueue(tagQueueRequest).asScala + + def untagQueue(listOfTagsToRemove: List[String]): Future[UntagQueueResponse] = + val untagQueueRequest = UntagQueueRequest + .builder() + .queueUrl(queueURL) + .tagKeys(listOfTagsToRemove.asJava) + .build() + + sqsAsyncClient.untagQueue(untagQueueRequest).asScala diff --git a/scala-libraries/src/main/scala/com/baeldung/elasticmq/ElasticMQConfig.scala b/scala-libraries/src/main/scala/com/baeldung/elasticmq/ElasticMQConfig.scala new file mode 100644 index 000000000..58ac2f5e4 --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/elasticmq/ElasticMQConfig.scala @@ -0,0 +1,15 @@ +package com.baeldung.elasticmq + +import com.typesafe.config.{Config, ConfigFactory} + +object ElasticMQConfig: + + private final val config: Config = ConfigFactory.load("elasticmq.conf") + + final val ELASTIC_MQ_ACCESS_KEY: String = + config.getString("elastic-mq.access-key-id") + final val ELASTIC_MQ_SECRET_ACCESS_KEY: String = + config.getString("elastic-mq.secret-access-key") + + final val ELASTIC_MQ_REGION = config.getString("elastic-mq.region") + final val ELASTIC_MQ_ENDPOINT = config.getString("elastic-mq.endPoint") diff --git a/scala-libraries/src/main/scala/com/baeldung/elasticmq/ElasticMQService.scala b/scala-libraries/src/main/scala/com/baeldung/elasticmq/ElasticMQService.scala new file mode 100644 index 000000000..64b99420e --- /dev/null +++ b/scala-libraries/src/main/scala/com/baeldung/elasticmq/ElasticMQService.scala @@ -0,0 +1,56 @@ +package com.baeldung.elasticmq + +import org.elasticmq.rest.sqs.SQSRestServerBuilder +import org.elasticmq.server.ElasticMQServer +import org.elasticmq.server.config.ElasticMQServerConfig + +import com.typesafe.config.ConfigFactory + +import org.apache.pekko.actor.{Actor, ActorRef, ActorSystem} +import org.apache.pekko.event.LoggingAdapter + +import scala.util.{Failure, Success} + +object ElasticMQService extends App: + + implicit val actorSystem: ActorSystem = ActorSystem.create() + implicit val executionContext: concurrent.ExecutionContextExecutor = + actorSystem.dispatcher + implicit val m_logger: LoggingAdapter = actorSystem.log + + final val ElasticMQ_URL = s"http://localhost:9324/000000000000/" + + val endpoint = "http://localhost:9325" + val region = "elasticmq" + + val server = SQSRestServerBuilder + .withPort(9325) + .withInterface("localhost") + .start() + + val elasticMQClient = new SQSAsyncClient(ElasticMQ_URL, region, endpoint) + + val uselessWorkflow = + for + _ <- elasticMQClient.createStandardQueue("standardQueueForTest") + testQueueClient = new SQSAsyncClient( + ElasticMQ_URL + "standardQueueForTest", + region, + endpoint + ) + _ <- testQueueClient.createFIFOQueue("fifoQueue.fifo") + _ <- testQueueClient.listQueues() + _ <- testQueueClient.sendMessage("Hi") + _ <- testQueueClient.sendMessagesInBatch( + List("Follow", "Baeldung", "on", "LinkedIn") + ) + _ <- testQueueClient.receiveMessages(5) + _ <- testQueueClient.purgeQueue() + yield () + + uselessWorkflow + .andThen(_ => server.stopAndWait()) + .onComplete: + case Success(_) => m_logger.info("queue created") + case Failure(exception) => + m_logger.error(exception, "exception in uselessWorkflow") diff --git a/scala-libraries-4/src/main/scala-2/com/baeldung/scala/awscala/Main.scala b/scala-libraries/src/main/scala/com/baeldung/scala/awscala/Main.scala similarity index 93% rename from scala-libraries-4/src/main/scala-2/com/baeldung/scala/awscala/Main.scala rename to scala-libraries/src/main/scala/com/baeldung/scala/awscala/Main.scala index da4704041..56854124b 100644 --- a/scala-libraries-4/src/main/scala-2/com/baeldung/scala/awscala/Main.scala +++ b/scala-libraries/src/main/scala/com/baeldung/scala/awscala/Main.scala @@ -23,7 +23,8 @@ object Main extends App { .build() Try { - s3.createBucket { req: CreateBucketRequest.Builder => req.bucket(name) } + + s3.createBucket(CreateBucketRequest.builder().bucket(name).build()) s3.headBucket( HeadBucketRequest.builder().bucket(name).build() diff --git a/scala-libraries-3/src/main/scala-2/com/baeldung/scala/log4j/LoggingApp.scala b/scala-libraries/src/main/scala/com/baeldung/scala/log4j/LoggingApp.scala similarity index 100% rename from scala-libraries-3/src/main/scala-2/com/baeldung/scala/log4j/LoggingApp.scala rename to scala-libraries/src/main/scala/com/baeldung/scala/log4j/LoggingApp.scala diff --git a/scala-libraries-4/src/main/scala-2/com/baeldung/scala/logging/ScalaLoggingSample.scala b/scala-libraries/src/main/scala/com/baeldung/scala/logging/ScalaLoggingSample.scala similarity index 100% rename from scala-libraries-4/src/main/scala-2/com/baeldung/scala/logging/ScalaLoggingSample.scala rename to scala-libraries/src/main/scala/com/baeldung/scala/logging/ScalaLoggingSample.scala diff --git a/scala-libraries-3/src/main/scala-2/com/baeldung/scala/retry/PrimeNumberRetry.scala b/scala-libraries/src/main/scala/com/baeldung/scala/retry/PrimeNumberRetry.scala similarity index 100% rename from scala-libraries-3/src/main/scala-2/com/baeldung/scala/retry/PrimeNumberRetry.scala rename to scala-libraries/src/main/scala/com/baeldung/scala/retry/PrimeNumberRetry.scala diff --git a/scala-libraries-2/src/main/scala-2/play_db/PlayDbExample.scala b/scala-libraries/src/main/scala/play_db/PlayDbExample.scala similarity index 100% rename from scala-libraries-2/src/main/scala-2/play_db/PlayDbExample.scala rename to scala-libraries/src/main/scala/play_db/PlayDbExample.scala diff --git a/scala-libraries/src/test/resources/application.conf b/scala-libraries/src/test/resources/application.conf deleted file mode 100644 index b6b07d594..000000000 --- a/scala-libraries/src/test/resources/application.conf +++ /dev/null @@ -1,13 +0,0 @@ -mongo-async-driver { - akka { - loggers = ["akka.event.slf4j.Slf4jLogger"] - loglevel = DEBUG - } -} -akka { - # Loggers to register at boot time (akka.event.Logging$DefaultLogger logs - # to STDOUT) - loggers = ["akka.event.Logging$DefaultLogger"] - # Options: OFF, ERROR, WARNING, INFO, DEBUG - loglevel = "INFO" -} \ No newline at end of file diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/circe/JSONConversionsTest.scala b/scala-libraries/src/test/scala/com/baeldung/circe/JSONConversionsUnitTest.scala similarity index 86% rename from scala-libraries-2/src/test/scala-2/com/baeldung/circe/JSONConversionsTest.scala rename to scala-libraries/src/test/scala/com/baeldung/circe/JSONConversionsUnitTest.scala index 496d6e4ca..d68956764 100644 --- a/scala-libraries-2/src/test/scala-2/com/baeldung/circe/JSONConversionsTest.scala +++ b/scala-libraries/src/test/scala/com/baeldung/circe/JSONConversionsUnitTest.scala @@ -5,7 +5,7 @@ import io.circe.parser.parse import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should -class JSONConversionsTest extends AnyFlatSpec with should.Matchers { +class JSONConversionsUnitTest extends AnyFlatSpec with should.Matchers { "Convert Json String" should "successfully" in { val parseResult = parse(stringJson) assert(parseResult.isRight) diff --git a/scala-libraries/src/test/scala/com/baeldung/csv/reader/ScalaCSVReadersUnitTest.scala b/scala-libraries/src/test/scala/com/baeldung/csv/reader/ScalaCSVReadersUnitTest.scala new file mode 100644 index 000000000..fa89a871e --- /dev/null +++ b/scala-libraries/src/test/scala/com/baeldung/csv/reader/ScalaCSVReadersUnitTest.scala @@ -0,0 +1,63 @@ +package com.baeldung.csv.reader + +import org.scalatest.matchers.should +import org.scalatest.wordspec.AnyWordSpec + +import java.io.{File, PrintWriter} +import java.nio.file.{Files, Path} +import java.util.UUID + +class ScalaCSVReadersUnitTest extends AnyWordSpec with should.Matchers { + + "SimpleCSVReader" should { + "read a csv file" in { + commonTestBody(new SimpleCSVReader, randomTmpFile().toFile) + } + } + + "ScalaCSVReader" should { + "read a csv file" in { + commonTestBody(new ScalaCSVReader, randomTmpFile().toFile) + } + } + + "OpenCSVReader" should { + "read a csv file" in { + commonTestBody(new OpenCSVReader, randomTmpFile().toFile) + } + } + + "ApacheCommonsCSVReader" should { + "read a csv file" in { + commonTestBody(new ApacheCommonsCSVReader, randomTmpFile().toFile) + } + } + + private def commonTestBody( + testee: CommaSeparatedValuesReader, + file: File + ): Unit = { + val result = testee.read(file) + assert(result.headers === List("column1", "column2")) + assert( + result.rows === List( + List("1.1", "1.2"), + List("2.1", "2.2") + ) + ) + } + + private def randomTmpFile(): Path = { + val path = Files.createTempFile( + s"persons-${UUID.randomUUID().toString.take(8)}", + "csv" + ) + val writer = new PrintWriter(path.toFile) + writer.println(List("column1", "column2").mkString(",")) + writer.println(List("1.1", "1.2").mkString(",")) + writer.println(List("2.1", "2.2").mkString(",")) + writer.close() + path + } + +} diff --git a/scala-libraries/src/test/scala/com/baeldung/csv/writer/ScalaCSVWritersUnitTest.scala b/scala-libraries/src/test/scala/com/baeldung/csv/writer/ScalaCSVWritersUnitTest.scala new file mode 100644 index 000000000..20e4b0e61 --- /dev/null +++ b/scala-libraries/src/test/scala/com/baeldung/csv/writer/ScalaCSVWritersUnitTest.scala @@ -0,0 +1,88 @@ +package com.baeldung.csv.writer + +import org.scalatest.matchers.should +import org.scalatest.wordspec.AnyWordSpec + +import java.io.{BufferedReader, File, FileInputStream, InputStreamReader} +import java.nio.file.{Files, Path} +import java.util.UUID +import scala.annotation.tailrec; + +class ScalaCSVWritersUnitTest extends AnyWordSpec with should.Matchers { + + "SimpleCSVWriter" should { + "write a csv file" in { + commonTestBody(new SimpleCSVWriter, randomTmpFile().toFile) + } + } + + "ScalaCSVWriter" should { + "write a csv file" in { + commonTestBody(new ScalaCSVWriter, randomTmpFile().toFile) + } + } + + "OpenCSVWriter" should { + "write a csv file" in { + commonTestBody(new OpenCSVWriter, randomTmpFile().toFile) + } + } + + "ApacheCommonsCSVWriter" should { + "write a csv file" in { + commonTestBody(new ApacheCommonsCSVWriter, randomTmpFile().toFile) + } + } + + private def commonTestBody( + testee: CommaSeparatedValuesWriter, + tmpFile: File + ): Unit = { + testee.write( + tmpFile, + List("column1", "column2"), + List( + List("c1.1", "c2.1"), + List("c1.2", "c2.2") + ) + ) + val result = read(tmpFile) + assert(result.headOption.contains("column1,column2")) + assert(result.tail.headOption.contains("c1.1,c2.1")) + assert(result.tail.tail.headOption.contains("c1.2,c2.2")) + } + + private def read(file: File): Seq[String] = { + val in = new InputStreamReader(new FileInputStream(file)) + val bufferedReader = new BufferedReader(in) + + @tailrec + def readLinesRecursively( + currentBufferedReader: BufferedReader, + result: Seq[String] + ): Seq[String] = { + currentBufferedReader.readLine() match { + case null => result + case line => + readLinesRecursively( + currentBufferedReader, + result :+ line + ) + } + } + + val csvLines = readLinesRecursively(bufferedReader, List()) + + bufferedReader.close() + + csvLines + } + + private def randomTmpFile(): Path = { + Files.createTempFile( + s"persons-${UUID.randomUUID().toString.take(8)}", + "csv" + ) + } + +} diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/scala/circe/CirceUnitTest.scala b/scala-libraries/src/test/scala/com/baeldung/scala/circe/CirceUnitTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/scala/circe/CirceUnitTest.scala rename to scala-libraries/src/test/scala/com/baeldung/scala/circe/CirceUnitTest.scala diff --git a/scala-libraries-2/src/test/scala-2/com/baeldung/scala/circe/CustomDecoderUnitTest.scala b/scala-libraries/src/test/scala/com/baeldung/scala/circe/CustomDecoderUnitTest.scala similarity index 100% rename from scala-libraries-2/src/test/scala-2/com/baeldung/scala/circe/CustomDecoderUnitTest.scala rename to scala-libraries/src/test/scala/com/baeldung/scala/circe/CustomDecoderUnitTest.scala diff --git a/scala-libraries-3/src/test/scala-2/com/baeldung/scala/retry/PrimeNumberRetryTest.scala b/scala-libraries/src/test/scala/com/baeldung/scala/retry/PrimeNumberRetryUnitTest.scala similarity index 96% rename from scala-libraries-3/src/test/scala-2/com/baeldung/scala/retry/PrimeNumberRetryTest.scala rename to scala-libraries/src/test/scala/com/baeldung/scala/retry/PrimeNumberRetryUnitTest.scala index 3509fea05..2c5f6a2cd 100644 --- a/scala-libraries-3/src/test/scala-2/com/baeldung/scala/retry/PrimeNumberRetryTest.scala +++ b/scala-libraries/src/test/scala/com/baeldung/scala/retry/PrimeNumberRetryUnitTest.scala @@ -7,7 +7,7 @@ import org.scalatest.wordspec.AsyncWordSpec import java.util.concurrent.atomic.AtomicInteger import scala.concurrent.Future -class PrimeNumberRetryTest extends AsyncWordSpec with Matchers { +class PrimeNumberRetryUnitTest extends AsyncWordSpec with Matchers { "PrimeNumberRetry" should { "FailFast when an NumberFormatException is thrown" in { diff --git a/scala-native/src/main/scala-2/com/baeldung/native/NativeApp.scala b/scala-native/src/main/scala/com/baeldung/native/NativeApp.scala similarity index 93% rename from scala-native/src/main/scala-2/com/baeldung/native/NativeApp.scala rename to scala-native/src/main/scala/com/baeldung/native/NativeApp.scala index 2f552b7c4..62d8c6781 100644 --- a/scala-native/src/main/scala-2/com/baeldung/native/NativeApp.scala +++ b/scala-native/src/main/scala/com/baeldung/native/NativeApp.scala @@ -41,10 +41,10 @@ object NativeApp { } def main(args: Array[String]): Unit = { - printFromNative - simpleNative - nativeFromCFile - testCurl + printFromNative() + simpleNative() + nativeFromCFile() + testCurl() } } diff --git a/scala-python/src/main/scala-2/com/baeldung/scalapy/ScalaPySample.scala b/scala-python/src/main/scala/com/baeldung/scalapy/ScalaPySample.scala similarity index 100% rename from scala-python/src/main/scala-2/com/baeldung/scalapy/ScalaPySample.scala rename to scala-python/src/main/scala/com/baeldung/scalapy/ScalaPySample.scala diff --git a/scala-sbt/README.md b/scala-sbt/README.md index 1867e73ce..23c345b48 100644 --- a/scala-sbt/README.md +++ b/scala-sbt/README.md @@ -3,3 +3,5 @@ - [Introduction to SBT](https://www.baeldung.com/scala/sbt-intro) - [Setting up Global Configurations in SBT](https://www.baeldung.com/scala/sbt-global-configurations) - [Safer Scala Code by Using WartRemover](https://www.baeldung.com/scala/wartremover) +- [Integration Testing with SBT](https://www.baeldung.com/scala/sbt-integration-testing) +- [Introduction to Scalafix](https://www.baeldung.com/scala/scalafix) diff --git a/app-packaging/README.md b/scala-sbt/app-packaging/README.md similarity index 100% rename from app-packaging/README.md rename to scala-sbt/app-packaging/README.md diff --git a/app-packaging/build.sbt b/scala-sbt/app-packaging/build.sbt similarity index 100% rename from app-packaging/build.sbt rename to scala-sbt/app-packaging/build.sbt diff --git a/app-packaging/project/build.properties b/scala-sbt/app-packaging/project/build.properties similarity index 100% rename from app-packaging/project/build.properties rename to scala-sbt/app-packaging/project/build.properties diff --git a/app-packaging/project/plugins.sbt b/scala-sbt/app-packaging/project/plugins.sbt similarity index 100% rename from app-packaging/project/plugins.sbt rename to scala-sbt/app-packaging/project/plugins.sbt diff --git a/app-packaging/scalacli-app/ScalaCliApp.scala b/scala-sbt/app-packaging/scalacli-app/ScalaCliApp.scala similarity index 88% rename from app-packaging/scalacli-app/ScalaCliApp.scala rename to scala-sbt/app-packaging/scalacli-app/ScalaCliApp.scala index 17341f95d..8cff03a13 100644 --- a/app-packaging/scalacli-app/ScalaCliApp.scala +++ b/scala-sbt/app-packaging/scalacli-app/ScalaCliApp.scala @@ -1,6 +1,6 @@ -//> using scala "3.1.0" +//> using scala 3.4.2 +//> using dep com.lihaoyi::os-lib:0.10.1 package com.baeldung.scalacli -import $dep.`com.lihaoyi::os-lib:0.7.8` import os._ object ScalaCliApp { diff --git a/app-packaging/src/main/scala/com/baeldung/packaging/Main.scala b/scala-sbt/app-packaging/src/main/scala/com/baeldung/packaging/Main.scala similarity index 100% rename from app-packaging/src/main/scala/com/baeldung/packaging/Main.scala rename to scala-sbt/app-packaging/src/main/scala/com/baeldung/packaging/Main.scala diff --git a/scala-sbt/integration-tests-post-1-9-0/build.sbt b/scala-sbt/integration-tests-post-1-9-0/build.sbt new file mode 100644 index 000000000..551cdbe92 --- /dev/null +++ b/scala-sbt/integration-tests-post-1-9-0/build.sbt @@ -0,0 +1,13 @@ +name := "sbt-integration-tests-post-1-9-0" +version := "1.0.0" + +ThisBuild / scalaVersion := "3.2.0" + +lazy val root = (project in file(".")) + +lazy val integration = (project in file("integration")) + .dependsOn(root) + .settings( + publish / skip := true, + libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.17" % Test + ) diff --git a/scala-sbt/integration-tests-post-1-9-0/integration/src/test/scala/com/baeldung/it/SampleUnitTest.scala b/scala-sbt/integration-tests-post-1-9-0/integration/src/test/scala/com/baeldung/it/SampleUnitTest.scala new file mode 100644 index 000000000..96a490681 --- /dev/null +++ b/scala-sbt/integration-tests-post-1-9-0/integration/src/test/scala/com/baeldung/it/SampleUnitTest.scala @@ -0,0 +1,9 @@ +package com.baeldung.it + +import org.scalatest.flatspec.AnyFlatSpec + +class SampleUnitTest extends AnyFlatSpec { + "The sample test" should "pass" in { + assert(1 + 1 == 2) + } +} diff --git a/scala-sbt/integration-tests-post-1-9-0/project/build.properties b/scala-sbt/integration-tests-post-1-9-0/project/build.properties new file mode 100644 index 000000000..abbbce5da --- /dev/null +++ b/scala-sbt/integration-tests-post-1-9-0/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.9.8 diff --git a/scala-sbt/integration-tests-pre-1-9-0/build.sbt b/scala-sbt/integration-tests-pre-1-9-0/build.sbt new file mode 100644 index 000000000..dad925273 --- /dev/null +++ b/scala-sbt/integration-tests-pre-1-9-0/build.sbt @@ -0,0 +1,11 @@ +name := "sbt-integration-tests-pre-1-9-0" +version := "1.0.0" + +ThisBuild / scalaVersion := "3.2.0" + +lazy val root = (project in file(".")) + .configs(IntegrationTest) + .settings( + Defaults.itSettings, + libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.17" % "it" + ) diff --git a/scala-sbt/integration-tests-pre-1-9-0/project/build.properties b/scala-sbt/integration-tests-pre-1-9-0/project/build.properties new file mode 100644 index 000000000..abbbce5da --- /dev/null +++ b/scala-sbt/integration-tests-pre-1-9-0/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.9.8 diff --git a/scala-sbt/integration-tests-pre-1-9-0/src/it/scala/com/baeldung/it/SampleTest.scala b/scala-sbt/integration-tests-pre-1-9-0/src/it/scala/com/baeldung/it/SampleTest.scala new file mode 100644 index 000000000..e804cd765 --- /dev/null +++ b/scala-sbt/integration-tests-pre-1-9-0/src/it/scala/com/baeldung/it/SampleTest.scala @@ -0,0 +1,9 @@ +package com.baeldung.it + +import org.scalatest.flatspec.AnyFlatSpec + +class SampleTest extends AnyFlatSpec { + "The sample test" should "pass" in { + assert(1 + 1 == 2) + } +} diff --git a/scala-sbt/intro-to-sbt/build.sbt b/scala-sbt/intro-to-sbt/build.sbt index 341518c66..b5602f056 100644 --- a/scala-sbt/intro-to-sbt/build.sbt +++ b/scala-sbt/intro-to-sbt/build.sbt @@ -1,6 +1,6 @@ import Dependencies._ -ThisBuild / scalaVersion := "2.13.12" +ThisBuild / scalaVersion := "3.4.1" ThisBuild / version := "0.1.0-SNAPSHOT" lazy val printHello = taskKey[Unit]("prints hello") diff --git a/scala-sbt/intro-to-sbt/project/build.properties b/scala-sbt/intro-to-sbt/project/build.properties index c8fcab543..01a16ed14 100644 --- a/scala-sbt/intro-to-sbt/project/build.properties +++ b/scala-sbt/intro-to-sbt/project/build.properties @@ -1 +1 @@ -sbt.version=1.6.2 +sbt.version=1.11.7 diff --git a/scala-sbt/intro-to-sbt/src/test/scala/com/baeldung/HelloSpec.scala b/scala-sbt/intro-to-sbt/src/test/scala/com/baeldung/HelloUnitTest.scala similarity index 78% rename from scala-sbt/intro-to-sbt/src/test/scala/com/baeldung/HelloSpec.scala rename to scala-sbt/intro-to-sbt/src/test/scala/com/baeldung/HelloUnitTest.scala index 320d1e067..bd44c437b 100644 --- a/scala-sbt/intro-to-sbt/src/test/scala/com/baeldung/HelloSpec.scala +++ b/scala-sbt/intro-to-sbt/src/test/scala/com/baeldung/HelloUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class HelloSpec extends AnyFlatSpec with Matchers { +class HelloUnitTest extends AnyFlatSpec with Matchers { "The Hello object" should "say hello" in { Hello.greeting shouldEqual "hello" } diff --git a/scala-sbt/sbt-info/build.sbt b/scala-sbt/sbt-info/build.sbt index af02ed737..6e4a68128 100644 --- a/scala-sbt/sbt-info/build.sbt +++ b/scala-sbt/sbt-info/build.sbt @@ -37,7 +37,6 @@ buildInfoOptions := Seq( ) //settings for scoverage -ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always ThisBuild / coverageEnabled := true diff --git a/scala-sbt/sbt-info/project/plugins.sbt b/scala-sbt/sbt-info/project/plugins.sbt index 005b9257e..594df223e 100644 --- a/scala-sbt/sbt-info/project/plugins.sbt +++ b/scala-sbt/sbt-info/project/plugins.sbt @@ -1,5 +1,3 @@ addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.10.0") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.8") - -ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always diff --git a/scala-sbt/scalafix/.scalafix.conf b/scala-sbt/scalafix/.scalafix.conf new file mode 100644 index 000000000..2adf80f27 --- /dev/null +++ b/scala-sbt/scalafix/.scalafix.conf @@ -0,0 +1,8 @@ +rules = [ + DisableSyntax, + RemoveUnused, +] + +DisableSyntax.noVars = true +DisableSyntax.noThrows = true +DisableSyntax.noNulls = true diff --git a/scala-sbt/scalafix/build.sbt b/scala-sbt/scalafix/build.sbt new file mode 100644 index 000000000..83058e4c4 --- /dev/null +++ b/scala-sbt/scalafix/build.sbt @@ -0,0 +1,9 @@ +lazy val root = (project in file(".")) + .settings( + name := "scalafix", + scalaVersion := "3.4.2", + version := "1.0.0", + semanticdbEnabled := true, + semanticdbVersion := scalafixSemanticdb.revision, + scalacOptions += "-Wunused:all" + ) diff --git a/scala-sbt/scalafix/project/build.properties b/scala-sbt/scalafix/project/build.properties new file mode 100644 index 000000000..7be021262 --- /dev/null +++ b/scala-sbt/scalafix/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.11.7 \ No newline at end of file diff --git a/scala-sbt/scalafix/project/plugins.sbt b/scala-sbt/scalafix/project/plugins.sbt new file mode 100644 index 000000000..32c80251b --- /dev/null +++ b/scala-sbt/scalafix/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.12.1") \ No newline at end of file diff --git a/scala-sbt/scalafix/src/main/scala/com/baeldung/scala/scalafix/DisableSyntaxDemo.scala b/scala-sbt/scalafix/src/main/scala/com/baeldung/scala/scalafix/DisableSyntaxDemo.scala new file mode 100644 index 000000000..97dfd72e3 --- /dev/null +++ b/scala-sbt/scalafix/src/main/scala/com/baeldung/scala/scalafix/DisableSyntaxDemo.scala @@ -0,0 +1,15 @@ +package com.baeldung.scala.scalafix + +object DisableSyntaxDemo: + var myVariable = null + + def validateMyVariable(): Boolean = + if (myVariable == null) throw Exception("myVariable Is Null") + + return true + +object DisableSyntaxDemoRewritten: + val myVariable = Option.empty[Unit] + + def validateMyVariable(): Either[String, Unit] = + myVariable.toRight("myVariable Is Null") diff --git a/scala-sbt/scalafix/src/main/scala/com/baeldung/scala/scalafix/RemoveUnusedDemo.scala b/scala-sbt/scalafix/src/main/scala/com/baeldung/scala/scalafix/RemoveUnusedDemo.scala new file mode 100644 index 000000000..1ccd8ab71 --- /dev/null +++ b/scala-sbt/scalafix/src/main/scala/com/baeldung/scala/scalafix/RemoveUnusedDemo.scala @@ -0,0 +1,21 @@ +package com.baeldung.scala.scalafix + +import scala.List + +object RemoveUnusedDemo: + val myNumber = 10 + + def greeting(name: String): String = { + val newName = s"$name $myNumber" + s"Hello, $name!" + } + +/* Rewritten: +object RemoveUnusedDemo: + val myNumber = 10 + + def greeting(name: String): String = { + s"$name $myNumber" + s"Hello, $name!" + } + */ diff --git a/scala-sbt/wart-remover/build.sbt b/scala-sbt/wart-remover/build.sbt index 01424ac7f..1063892c7 100644 --- a/scala-sbt/wart-remover/build.sbt +++ b/scala-sbt/wart-remover/build.sbt @@ -1,5 +1,5 @@ lazy val customWarts = (project in file("custom-warts")).settings( - scalaVersion := "2.13.12", + scalaVersion := "2.13.18", name := "CustomWarts", version := "1.0.0", exportJars := true, @@ -10,7 +10,7 @@ lazy val root = (project in file(".")) .dependsOn(customWarts) .settings( name := "wart-remover", - scalaVersion := "2.13.12", + scalaVersion := "2.13.18", version := "1.0.0", wartremoverWarnings ++= Seq(Wart.AsInstanceOf, Wart.Null), wartremoverExcluded += baseDirectory.value / "src" / "main" / "scala" / "com" / "baeldung" / "scala" / "wartremover" / "Excluded.scala", diff --git a/scala-sbt/wart-remover/custom-warts/build.sbt b/scala-sbt/wart-remover/custom-warts/build.sbt index b37b4686e..299ca4bf6 100644 --- a/scala-sbt/wart-remover/custom-warts/build.sbt +++ b/scala-sbt/wart-remover/custom-warts/build.sbt @@ -1,6 +1,6 @@ /*lazy val root = (project in file(".")).settings( name := "custom-warts", - scalaVersion := "2.13.12", + scalaVersion := "2.13.18", organization := "com.baeldung", version := "1.0.0-SNAPSHOT", libraryDependencies ++= Seq( diff --git a/scala-strings/README.md b/scala-strings/README.md index e826b3731..07a72f801 100644 --- a/scala-strings/README.md +++ b/scala-strings/README.md @@ -9,3 +9,8 @@ This module contains articles about Scala's core features - [String Comparison in Scala](https://www.baeldung.com/scala/string-comparison) - [Convert a String to Camel Case in Scala](https://www.baeldung.com/scala/string-to-camel-case) - [Convert Byte Array to String in Scala](https://www.baeldung.com/scala/convert-byte-array-to-string) +- [Scala Keyword Matching: A Library-First Guide](https://www.baeldung.com/scala/keyword-matching) +- [Generate Secure Random Passwords in Scala](https://www.baeldung.com/scala/generate-secure-random-passwords) +- [Generate an Acronym for a Given String in Scala](https://www.baeldung.com/scala/string-derive-acronym) +- [Capitalizing Every Word in a Sentence in Scala](https://www.baeldung.com/scala/capitalize-each-word) +- [Remove Special Characters From a String in Scala](https://www.baeldung.com/scala/string-delete-special-characters) diff --git a/scala-strings/src/main/scala/com/baeldung/scala/strings/acronym/AcronymGenerator.scala b/scala-strings/src/main/scala/com/baeldung/scala/strings/acronym/AcronymGenerator.scala new file mode 100644 index 000000000..ec96e93b9 --- /dev/null +++ b/scala-strings/src/main/scala/com/baeldung/scala/strings/acronym/AcronymGenerator.scala @@ -0,0 +1,13 @@ +package com.baeldung.scala.strings.acronym + +object AcronymGenerator { + def acronymUsingSplit(text: String): String = { + text + .split("\\s") + .filterNot(_.trim.isEmpty) + .map(_.head) + .filter(_.isLetter) + .mkString + .toUpperCase + } +} diff --git a/scala-strings/src/main/scala-2/com/baeldung/scala/strings/bytes/ByteArrayToString.scala b/scala-strings/src/main/scala/com/baeldung/scala/strings/bytes/ByteArrayToString.scala similarity index 100% rename from scala-strings/src/main/scala-2/com/baeldung/scala/strings/bytes/ByteArrayToString.scala rename to scala-strings/src/main/scala/com/baeldung/scala/strings/bytes/ByteArrayToString.scala diff --git a/scala-strings/src/main/scala-2/com/baeldung/scala/strings/camelcase/StringWrapper.scala b/scala-strings/src/main/scala/com/baeldung/scala/strings/camelcase/StringWrapper.scala similarity index 98% rename from scala-strings/src/main/scala-2/com/baeldung/scala/strings/camelcase/StringWrapper.scala rename to scala-strings/src/main/scala/com/baeldung/scala/strings/camelcase/StringWrapper.scala index 5c3cc425b..ca0ddfb15 100644 --- a/scala-strings/src/main/scala-2/com/baeldung/scala/strings/camelcase/StringWrapper.scala +++ b/scala-strings/src/main/scala/com/baeldung/scala/strings/camelcase/StringWrapper.scala @@ -19,7 +19,7 @@ object StringWrapper { val useMapReduce: String => String = { spacedString => val first :: rest = - spacedString.split(Array(' ', '_')).toList.map(_.toLowerCase) + spacedString.split(Array(' ', '_')).toList.map(_.toLowerCase): @unchecked val changedRest = rest.map(w => w.take(1).toUpperCase + w.drop(1)) val reunited = first :: changedRest reunited.mkString diff --git a/scala-strings/src/main/scala/com/baeldung/scala/strings/capitalize/CapitalizeWords.scala b/scala-strings/src/main/scala/com/baeldung/scala/strings/capitalize/CapitalizeWords.scala new file mode 100644 index 000000000..d83d1b0ad --- /dev/null +++ b/scala-strings/src/main/scala/com/baeldung/scala/strings/capitalize/CapitalizeWords.scala @@ -0,0 +1,28 @@ +package com.baeldung.scala.strings.capitalize + +object CapitalizeWords { + + def capitalizeWords(sentence: String): String = { + sentence.split("\\s+").map(_.capitalize).mkString(" ") + } + + def capitalizeTitleCase(sentence: String): String = { + val exclusions = Set("is", "in", "to", "a", "an", "the") + sentence + .split("\\s+") + .zipWithIndex + .map { (word, index) => + if (index != 0 && exclusions.contains(word.toLowerCase)) word + else word.capitalize + } + .mkString(" ") + } + + def capitalizeWordsPreserveSpaces(sentence: String): String = { + sentence.zipWithIndex.map { (char, index) => + if (index == 0) char.toUpper + else if (sentence.charAt(index - 1).isSpaceChar) char.toUpper + else char + }.mkString + } +} diff --git a/scala-strings/src/main/scala-2/com/baeldung/scala/strings/definition/DefiningStringsExamples.scala b/scala-strings/src/main/scala/com/baeldung/scala/strings/definition/DefiningStringsExamples.scala similarity index 100% rename from scala-strings/src/main/scala-2/com/baeldung/scala/strings/definition/DefiningStringsExamples.scala rename to scala-strings/src/main/scala/com/baeldung/scala/strings/definition/DefiningStringsExamples.scala diff --git a/scala-strings/src/main/scala-2/com/baeldung/scala/strings/interpolation/CustomInterpolatorObj.scala b/scala-strings/src/main/scala/com/baeldung/scala/strings/interpolation/CustomInterpolatorObj.scala similarity index 100% rename from scala-strings/src/main/scala-2/com/baeldung/scala/strings/interpolation/CustomInterpolatorObj.scala rename to scala-strings/src/main/scala/com/baeldung/scala/strings/interpolation/CustomInterpolatorObj.scala diff --git a/scala-strings/src/main/scala-2/com/baeldung/scala/strings/keywordsearch/KeywordCounter.scala b/scala-strings/src/main/scala/com/baeldung/scala/strings/keywordsearch/KeywordCounter.scala similarity index 100% rename from scala-strings/src/main/scala-2/com/baeldung/scala/strings/keywordsearch/KeywordCounter.scala rename to scala-strings/src/main/scala/com/baeldung/scala/strings/keywordsearch/KeywordCounter.scala diff --git a/scala-strings/src/main/scala/com/baeldung/scala/strings/passwordgen/RandomPasswordGenerator.scala b/scala-strings/src/main/scala/com/baeldung/scala/strings/passwordgen/RandomPasswordGenerator.scala new file mode 100644 index 000000000..aca154ba8 --- /dev/null +++ b/scala-strings/src/main/scala/com/baeldung/scala/strings/passwordgen/RandomPasswordGenerator.scala @@ -0,0 +1,81 @@ +package com.baeldung.scala.strings.passwordgen + +import java.security.SecureRandom +import scala.util.Random + +object RandomPasswordGenerator { + + def randomAlphaNumericString: String = { + Random.alphanumeric.take(16).mkString + } + + def randomString: String = { + Iterator.continually(Random.nextPrintableChar()).take(16).mkString + } + + def randomStringWithSpecificSpecialChars: String = { + def isSupportedChar(char: Char): Boolean = + char.isLetterOrDigit || Set('*', '_', '&', '@').contains(char) + Iterator + .continually(Random.nextPrintableChar()) + .filter(isSupportedChar) + .take(16) + .mkString + } + + def randomAlphaNumericSecurePwd: String = { + val alphanumericChars = ('0' to '9') ++ ('A' to 'Z') ++ ('a' to 'z') + val random = new SecureRandom() + Iterator + .continually(alphanumericChars(random.nextInt(alphanumericChars.length))) + .take(16) + .mkString + } + + def randomSecurePwdWithSpecialChars: String = { + val printableChars = (33 to 126).map(_.toChar) // ASCII printable characters + val random = new SecureRandom() + Iterator + .continually(printableChars(random.nextInt(printableChars.length))) + .take(16) + .mkString + } + + def randomSecurePwdWithExclusions: String = { + val printableChars = (33 to 126).map(_.toChar) // ASCII printable characters + val excludedChars = Set('O', 'o', 'l', '1', '0', '`') + val random = new SecureRandom() + Iterator + .continually(printableChars(random.nextInt(printableChars.length))) + .filterNot(excludedChars.contains) + .take(16) + .mkString + } + + def randomSecurePwdWithMix: String = { + val lowerLetters = 'a' to 'z' + val upperLetters = 'A' to 'Z' + val numbers = '0' to '9' + val specialChars = + IndexedSeq('!', '@', '#', '$', '&', '*', '?', '^', '(', ')') + val fullCharset = lowerLetters ++ upperLetters ++ numbers ++ specialChars + val random = new SecureRandom() + + def pickRandomChars(charSet: IndexedSeq[Char], size: Int): List[Char] = { + Iterator + .continually(charSet(random.nextInt(charSet.length))) + .take(size) + .toList + } + + val passwordChars: List[Char] = pickRandomChars(lowerLetters, 1) ++ + pickRandomChars(upperLetters, 1) ++ + pickRandomChars(numbers, 1) ++ + pickRandomChars(specialChars, 1) ++ + pickRandomChars(fullCharset, 12) + + Random.shuffle(passwordChars).mkString + + } + +} diff --git a/scala-strings/src/test/scala/com/baeldung/scala/strings/acronym/AcronymGeneratorUnitTest.scala b/scala-strings/src/test/scala/com/baeldung/scala/strings/acronym/AcronymGeneratorUnitTest.scala new file mode 100644 index 000000000..72ee0c7c2 --- /dev/null +++ b/scala-strings/src/test/scala/com/baeldung/scala/strings/acronym/AcronymGeneratorUnitTest.scala @@ -0,0 +1,61 @@ +package com.baeldung.scala.strings.acronym + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import org.scalacheck.Gen +import org.scalatest.matchers.should.Matchers +import AcronymGenerator.* + +class AcronymGeneratorUnitTest + extends AnyFlatSpec + with ScalaCheckPropertyChecks + with Matchers { + + val sentenceGenerator: Gen[String] = for { + charPart <- Gen.alphaStr + numPart <- Gen.listOfN(5, Gen.numChar).map(_.mkString) + specialCharPart <- Gen + .listOfN(1, Gen.oneOf(Seq('@', "_", "*"))) + .map(_.mkString) + alphaNum <- Gen.alphaNumStr + } yield s"$charPart $numPart $specialCharPart$charPart $alphaNum" + + it should "generate acronym for random generated string correctly" in { + forAll(sentenceGenerator) { sentence => + println("sentence -=> " + sentence) + val acronym = AcronymGenerator.acronymUsingSplit(sentence) + withClue(s"Gen sentence = $sentence , acronym = $acronym") { + acronym.forall(_.isLetter) shouldBe true + acronym.exists(_ == ' ') shouldBe false + acronym.forall(_.isUpper) shouldBe true + } + } + } + + it should "generate empty acronym for empty string" in { + val acronym = acronymUsingSplit(" ") + acronym shouldBe empty + } + + it should "generate correct acronym for alpha numeric string" in { + val acronym = acronymUsingSplit("This is a sentence with 9numbers") + acronym shouldBe "TIASW" + } + + it should "generate empty acronym for numeric/special char starting string" in { + val acronym = acronymUsingSplit("1is 7m @num") + acronym shouldBe empty + } + + it should "generate acronym for FAQ" in { + val acronym = acronymUsingSplit("Frequently Asked Questions") + acronym shouldBe "FAQ" + } + + it should "generate acronym for NASA with extra spaces in between" in { + val acronym = + acronymUsingSplit("National Aeronautics & Space Administration ") + acronym shouldBe "NASA" + } + +} diff --git a/scala-strings/src/test/scala-2/com/baeldung/scala/strings/bytes/ByteArrayToStringTest.scala b/scala-strings/src/test/scala/com/baeldung/scala/strings/bytes/ByteArrayToStringUnitTest.scala similarity index 91% rename from scala-strings/src/test/scala-2/com/baeldung/scala/strings/bytes/ByteArrayToStringTest.scala rename to scala-strings/src/test/scala/com/baeldung/scala/strings/bytes/ByteArrayToStringUnitTest.scala index 4a54e0c5e..61c9e2e97 100644 --- a/scala-strings/src/test/scala-2/com/baeldung/scala/strings/bytes/ByteArrayToStringTest.scala +++ b/scala-strings/src/test/scala/com/baeldung/scala/strings/bytes/ByteArrayToStringUnitTest.scala @@ -4,7 +4,7 @@ import com.baeldung.scala.strings.bytes.ByteArrayToString._ import org.scalatest.matchers.must.Matchers import org.scalatest.wordspec.AnyWordSpec -class ByteArrayToStringTest extends AnyWordSpec with Matchers { +class ByteArrayToStringUnitTest extends AnyWordSpec with Matchers { "byte array" should { val helloInUtf8 = Array[Byte](104, 101, 108, 108, 111) val helloInUtf16Le = Array[Byte](104, 0, 101, 0, 108, 0, 108, 0, 111, 0) diff --git a/scala-strings/src/test/scala-2/com/baeldung/scala/strings/camelcase/CamelCaseSpec.scala b/scala-strings/src/test/scala/com/baeldung/scala/strings/camelcase/CamelCaseUnitTest.scala similarity index 93% rename from scala-strings/src/test/scala-2/com/baeldung/scala/strings/camelcase/CamelCaseSpec.scala rename to scala-strings/src/test/scala/com/baeldung/scala/strings/camelcase/CamelCaseUnitTest.scala index ac6707781..37aaf0d14 100644 --- a/scala-strings/src/test/scala-2/com/baeldung/scala/strings/camelcase/CamelCaseSpec.scala +++ b/scala-strings/src/test/scala/com/baeldung/scala/strings/camelcase/CamelCaseUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.strings.camelcase import org.scalatest.wordspec.AnyWordSpec -class CamelCaseSpec extends AnyWordSpec { +class CamelCaseUnitTest extends AnyWordSpec { import StringWrapper._ diff --git a/scala-strings/src/test/scala/com/baeldung/scala/strings/capitalize/CapitalizeWordsUnitTest.scala b/scala-strings/src/test/scala/com/baeldung/scala/strings/capitalize/CapitalizeWordsUnitTest.scala new file mode 100644 index 000000000..520b1eb58 --- /dev/null +++ b/scala-strings/src/test/scala/com/baeldung/scala/strings/capitalize/CapitalizeWordsUnitTest.scala @@ -0,0 +1,56 @@ +package com.baeldung.scala.strings.capitalize + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks + +class CapitalizeWordsUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + val table = Table( + ("Input", "Expected"), + ("This is scala 3", "This Is Scala 3"), + ("hello world", "Hello World"), + ("baeldung articles", "Baeldung Articles"), + (" ", ""), + ("1000", "1000") + ) + it should "capitalize every words of a sentence" in { + forAll(table) { (input, expected) => + CapitalizeWords.capitalizeWords(input) shouldBe expected + } + } + + val tableWithExclusions = Table( + ("Input", "Expected"), + ("This is scala 3", "This is Scala 3"), + ("baeldung articles", "Baeldung Articles"), + (" ", ""), + ( + "the quick brown fox jumps over the lazy Dog", + "The Quick Brown Fox Jumps Over the Lazy Dog" + ) + ) + it should "capitalize every word of a sentence with exclusion" in { + forAll(tableWithExclusions) { (input, expected) => + CapitalizeWords.capitalizeTitleCase(input) shouldBe expected + } + } + + val tablePreservingSpace = Table( + ("Input", "Expected"), + ("This is scala 3", "This Is Scala 3"), + ("hello world", "Hello World"), + ("baeldung articles", "Baeldung Articles"), + (" ", " "), + ("1000", "1000") + ) + it should "capitalize every words of a sentence preserving the spaces" in { + forAll(tablePreservingSpace) { (input, expected) => + CapitalizeWords.capitalizeWordsPreserveSpaces(input) shouldBe expected + } + } + +} diff --git a/scala-strings/src/test/scala-2/com/baeldung/scala/strings/comparison/StringComparisonUnitTest.scala b/scala-strings/src/test/scala/com/baeldung/scala/strings/comparison/StringComparisonUnitTest.scala similarity index 100% rename from scala-strings/src/test/scala-2/com/baeldung/scala/strings/comparison/StringComparisonUnitTest.scala rename to scala-strings/src/test/scala/com/baeldung/scala/strings/comparison/StringComparisonUnitTest.scala diff --git a/scala-strings/src/test/scala-2/com/baeldung/scala/strings/interpolation/strings/definition/UsingRegexUnitTest.scala b/scala-strings/src/test/scala/com/baeldung/scala/strings/interpolation/strings/definition/UsingRegexUnitTest.scala similarity index 100% rename from scala-strings/src/test/scala-2/com/baeldung/scala/strings/interpolation/strings/definition/UsingRegexUnitTest.scala rename to scala-strings/src/test/scala/com/baeldung/scala/strings/interpolation/strings/definition/UsingRegexUnitTest.scala diff --git a/scala-strings/src/test/scala-2/com/baeldung/scala/strings/interpolation/strings/interpolation/CustomInterpolatorObjUnitTest.scala b/scala-strings/src/test/scala/com/baeldung/scala/strings/interpolation/strings/interpolation/CustomInterpolatorObjUnitTest.scala similarity index 100% rename from scala-strings/src/test/scala-2/com/baeldung/scala/strings/interpolation/strings/interpolation/CustomInterpolatorObjUnitTest.scala rename to scala-strings/src/test/scala/com/baeldung/scala/strings/interpolation/strings/interpolation/CustomInterpolatorObjUnitTest.scala diff --git a/scala-strings/src/test/scala-2/com/baeldung/scala/strings/interpolation/strings/interpolation/EscapeUsingMetaCharUnitTest.scala b/scala-strings/src/test/scala/com/baeldung/scala/strings/interpolation/strings/interpolation/InterpolationEscapesUnitTest.scala similarity index 92% rename from scala-strings/src/test/scala-2/com/baeldung/scala/strings/interpolation/strings/interpolation/EscapeUsingMetaCharUnitTest.scala rename to scala-strings/src/test/scala/com/baeldung/scala/strings/interpolation/strings/interpolation/InterpolationEscapesUnitTest.scala index 9dea700d7..2ffbdf645 100644 --- a/scala-strings/src/test/scala-2/com/baeldung/scala/strings/interpolation/strings/interpolation/EscapeUsingMetaCharUnitTest.scala +++ b/scala-strings/src/test/scala/com/baeldung/scala/strings/interpolation/strings/interpolation/InterpolationEscapesUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.strings.interpolation.strings.interpolation import org.scalatest.flatspec.AnyFlatSpec -class InterpolationEscapesSpec extends AnyFlatSpec { +class InterpolationEscapesUnitTest extends AnyFlatSpec { val DOUBLE_QUOTE: Char = '"' diff --git a/scala-strings/src/test/scala-2/com/baeldung/scala/strings/keywordsearch/KeywordMatchingSpec.scala b/scala-strings/src/test/scala/com/baeldung/scala/strings/keywordsearch/KeywordMatchingUnitTest.scala similarity index 94% rename from scala-strings/src/test/scala-2/com/baeldung/scala/strings/keywordsearch/KeywordMatchingSpec.scala rename to scala-strings/src/test/scala/com/baeldung/scala/strings/keywordsearch/KeywordMatchingUnitTest.scala index 630d97185..86a02ac1c 100644 --- a/scala-strings/src/test/scala-2/com/baeldung/scala/strings/keywordsearch/KeywordMatchingSpec.scala +++ b/scala-strings/src/test/scala/com/baeldung/scala/strings/keywordsearch/KeywordMatchingUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.strings.keywordsearch import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class KeywordMatchingSpec extends AnyFlatSpec with Matchers { +class KeywordMatchingUnitTest extends AnyFlatSpec with Matchers { "Keyword Matching" should "detect keywords in a string using brute-force" in { val text = "May the Force be with you" diff --git a/scala-strings/src/test/scala/com/baeldung/scala/strings/passwordgen/RandomPasswordGeneratorUnitTest.scala b/scala-strings/src/test/scala/com/baeldung/scala/strings/passwordgen/RandomPasswordGeneratorUnitTest.scala new file mode 100644 index 000000000..2277d663b --- /dev/null +++ b/scala-strings/src/test/scala/com/baeldung/scala/strings/passwordgen/RandomPasswordGeneratorUnitTest.scala @@ -0,0 +1,79 @@ +package com.baeldung.scala.strings.passwordgen + +import org.scalacheck.Gen +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + +class RandomPasswordGeneratorUnitTest + extends AnyWordSpec + with Matchers + with ScalaCheckPropertyChecks { + + "RandomPasswordGenerator" should { + "generate alpha numeric random string" in { + forAll(Gen.const(())) { _ => + val pwd = RandomPasswordGenerator.randomAlphaNumericString + pwd should have size 16 + pwd.forall(_.isLetterOrDigit) shouldBe true + } + } + + "generate random string - might contain alpha-numeric-special" in { + forAll(Gen.const(())) { _ => + val pwd = RandomPasswordGenerator.randomString + pwd should have size 16 + pwd.exists(_ == ' ') shouldBe false + } + } + + "generate random string - might contain alpha-numeric and only special characters *, _, &, @" in { + forAll(Gen.const(())) { _ => + val pwd = RandomPasswordGenerator.randomStringWithSpecificSpecialChars + withClue(s"Failed for generated password : $pwd ") { + pwd should have size 16 + pwd.exists(_ == ' ') shouldBe false + pwd.exists(Set('!', ')', '(', '^').contains(_)) shouldBe false + } + } + } + + "generate alpha numeric secure password" in { + forAll { (_: Int) => + val password = RandomPasswordGenerator.randomAlphaNumericSecurePwd + password.length shouldBe 16 + password.forall(_.isLetterOrDigit) shouldBe true + } + } + + "generate secure password including special characters" in { + forAll { (_: Unit) => + val password = RandomPasswordGenerator.randomSecurePwdWithSpecialChars + password.length shouldBe 16 + password.forall(c => (33 to 126).contains(c.toInt)) shouldBe true + } + } + + "generate secure password excluding some special characters" in { + forAll { (_: Unit) => + val password = RandomPasswordGenerator.randomSecurePwdWithExclusions + password.length shouldBe 16 + password should not contain oneOf('O', 'o', 'l', '1', '0', '`') + } + } + + "generate secure password with mix of condition" in { + forAll { (_: Unit) => + val password = RandomPasswordGenerator.randomSecurePwdWithMix + password.length shouldBe 16 + password.exists(_.isLower) shouldBe true + password.exists(_.isUpper) shouldBe true + password.exists(_.isDigit) shouldBe true + password.exists( + IndexedSeq('!', '@', '#', '$', '&', '*', '?', '^', '(', ')').contains + ) shouldBe true + } + } + } + +} diff --git a/scala-strings/src/test/scala/com/baeldung/scala/strings/removespecialchars/RemoveSpecialCharactersUnitTest.scala b/scala-strings/src/test/scala/com/baeldung/scala/strings/removespecialchars/RemoveSpecialCharactersUnitTest.scala new file mode 100644 index 000000000..e53a7578a --- /dev/null +++ b/scala-strings/src/test/scala/com/baeldung/scala/strings/removespecialchars/RemoveSpecialCharactersUnitTest.scala @@ -0,0 +1,105 @@ +package com.baeldung.scala.strings.removespecialchars + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks + +class RemoveSpecialCharactersUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + def removeAllSpecialCharUsingRegex(text: String): String = { + text.replaceAll("[^a-zA-Z0-9]", "") + } + + def removeSomeSpecialCharUsingRegex(text: String): String = { + text.replaceAll("[^a-zA-Z0-9_]", "") + } + + def removeSpecialCharUsingWordRegex(text: String): String = { + text.replaceAll("\\W", "") + } + + def removeSpecialCharUsingAnotherWordRegex(text: String): String = { + text.replaceAll("[^\\w]", "") + } + + def removeAllSpecialCharsUsingFilter(text: String): String = { + text.filter(_.isLetterOrDigit) + } + + def removeSpecialCharsUsingCollect(text: String): String = { + text.collect { + case c if c.isLetterOrDigit || Set(' ', '_').contains(c) => c + } + } + + private val removeAllSpecialTable = Table( + ("text", "expected"), + ("Special_Chars**Text $#^{} Here 2", "SpecialCharsTextHere2"), + (" ", ""), + ("!@#$.... ", ""), + ("baeldung", "baeldung") + ) + + it should "remove all the special characters using regex" in { + forAll(removeAllSpecialTable) { (text, expected) => + removeAllSpecialCharUsingRegex(text) shouldBe expected + } + } + + it should "remove all the special characters using filter" in { + forAll(removeAllSpecialTable) { (text, expected) => + removeAllSpecialCharsUsingFilter(text) shouldBe expected + } + } + + it should "remove all special characters using removeAllSpecialCharUsingRegex" in { + assert( + removeAllSpecialCharUsingRegex("Hello Baeldung_!") == "HelloBaeldung" + ) + } + + it should "remove all special characters using removeAllSpecialCharsUsingFilter" in { + assert( + removeAllSpecialCharsUsingFilter("Hello Baeldung_!") == "HelloBaeldung" + ) + } + + it should "remove some special characters using removeSomeSpecialCharUsingRegex" in { + assert( + removeSomeSpecialCharUsingRegex("Hello Baeldung_!") == "HelloBaeldung_" + ) + } + + it should "remove special characters using removeSpecialCharUsingWordRegex" in { + assert( + removeSpecialCharUsingWordRegex( + "Hello Baeldung_*()!" + ) == "HelloBaeldung_" + ) + } + + it should "remove special characters using removeSpecialCharsUsingCollect" in { + assert( + removeSpecialCharsUsingCollect("Hello Baeldung_*()!") == "Hello Baeldung_" + ) + } + + it should "remove special characters using removeSpecialCharUsingAnotherWordRegex" in { + assert( + removeSpecialCharUsingAnotherWordRegex( + "Hello Baeldung_*()!" + ) == "HelloBaeldung_" + ) + } + + it should "filter only required character from string" in { + val text = "Hello Baeldung_!*&$" + val sanitized = + text.filter(c => c.isLetterOrDigit || Set(' ', '_').contains(c)) + assert(sanitized == "Hello Baeldung_") + } + +} diff --git a/scala-test-2/README.md b/scala-test-2/README.md new file mode 100644 index 000000000..033b6435f --- /dev/null +++ b/scala-test-2/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: +- [Comparing Double Values in ScalaTest](https://www.baeldung.com/scala/scalatest-compare-double-values) +- [Ignore Tests Conditionally in ScalaTest](https://www.baeldung.com/scala/scalatest-ignore-conditionally) diff --git a/scala-test-2/src/test/scala/com/baeldung/scala/comparedouble/CompareDoubleUnitTest.scala b/scala-test-2/src/test/scala/com/baeldung/scala/comparedouble/CompareDoubleUnitTest.scala new file mode 100644 index 000000000..b91887340 --- /dev/null +++ b/scala-test-2/src/test/scala/com/baeldung/scala/comparedouble/CompareDoubleUnitTest.scala @@ -0,0 +1,18 @@ +package com.baeldung.scala.comparedouble + +import org.scalactic.TolerantNumerics +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class CompareDoubleUnitTest extends AnyFlatSpec with Matchers { + val doubleToTest: Double = 100.123 + + "double" should "pass test with implicit tolerance" in { + implicit val doubleEquality = TolerantNumerics.tolerantDoubleEquality(0.001) + assert(doubleToTest === 100.122) + } + + "double" should "pass test with tolerance" in { + assert(doubleToTest === 100.122 +- 0.001) + } +} diff --git a/scala-test-2/src/test/scala/com/baeldung/scala/conditionaltests/ConditionalUnitTest.scala b/scala-test-2/src/test/scala/com/baeldung/scala/conditionaltests/ConditionalUnitTest.scala new file mode 100644 index 000000000..cc85acbff --- /dev/null +++ b/scala-test-2/src/test/scala/com/baeldung/scala/conditionaltests/ConditionalUnitTest.scala @@ -0,0 +1,64 @@ +package com.baeldung.scala.conditionaltests + +import org.scalatest.{Assertion, Ignore, Tag} +import org.scalatest.wordspec.AnyWordSpec + +object ServiceChecker { + def isServiceAvailable: Boolean = false +} + +object RequiresAvailableService extends Tag("RequiresAvailableService") +object RequiresAvailableServiceConditional + extends Tag( + if (ServiceChecker.isServiceAvailable) "" else classOf[Ignore].getName + ) + +object AlwaysIgnoreTest extends Tag("org.scalatest.Ignore") + +class ConditionalUnitTest extends AnyWordSpec { + + private def skipTestIfServerUnavailable(test: => Assertion): Assertion = { + if (ServiceChecker.isServiceAvailable) { + test + } else cancel("Not executing test since the service is not available") + } + + "Conditional Tests" should { + "run this test only if the service is available by checking tag" taggedAs (RequiresAvailableService) in { + succeed + } + + "run this test anyways" in { + succeed + } + + "run this test using the conditional if-else tag if service is available" taggedAs (RequiresAvailableServiceConditional) in { + succeed + } + + "run this test using simple if-else condition" in { + if (!ServiceChecker.isServiceAvailable) { + cancel("excluding this test due to service not available") + } + succeed + } + + "run this test based on assume" in { + assume(ServiceChecker.isServiceAvailable) + succeed + } + + "run this test using skipTestIfServerUnavailable method" in skipTestIfServerUnavailable { + succeed + } + + "always ignore this test since Ignore name is used in Tag" taggedAs (AlwaysIgnoreTest) in { + fail("this fails, but it should never reaches here due to the tag") + } + + "ignore this test using the ignore keyword conditionally" ignore { + fail() + } + } + +} diff --git a/scala-test-2/src/test/scala/com/baeldung/scala/eventually/EventuallyUnitTest.scala b/scala-test-2/src/test/scala/com/baeldung/scala/eventually/EventuallyUnitTest.scala new file mode 100644 index 000000000..e286162fd --- /dev/null +++ b/scala-test-2/src/test/scala/com/baeldung/scala/eventually/EventuallyUnitTest.scala @@ -0,0 +1,64 @@ +package com.baeldung.scala.eventually + +import org.scalatest.concurrent.Eventually +import org.scalatest.concurrent.Eventually.eventually +import org.scalatest.concurrent.PatienceConfiguration.{Interval, Timeout} +import org.scalatest.exceptions.TestFailedDueToTimeoutException +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.time.{Millis, Seconds, Span} + +class AsyncOperation(waitTime: Int) { + private var database: Map[String, String] = Map.empty + + def writeToDatabase(key: String, value: String): Unit = { + new Thread(() => { + Thread.sleep(waitTime) // Simulate a delay in writing to the database + database += (key -> value) + }).start() + } + def readFromDatabase(key: String): Option[String] = database.get(key) +} +class EventuallyUnitTest extends AnyFlatSpec with Matchers with Eventually { + it should "retry the save to database multiple times in eventually with default timeout" in { + val op = AsyncOperation(100) + op.writeToDatabase("key", "value") + eventually { + op.readFromDatabase("key") shouldBe Some("value") + } + } + + it should "throw exception if the result is not available within the expected timeout in eventually" in { + val op = AsyncOperation(300) + op.writeToDatabase("key", "value") + assertThrows[TestFailedDueToTimeoutException] { + eventually { + op.readFromDatabase("key") shouldBe Some("value") + } + } + } + + it should "retry with newly configured timeout value" in { + given patienceConfig: PatienceConfig = + PatienceConfig( + timeout = scaled(Span(2, Seconds)), + interval = scaled(Span(5, Millis)) + ) + val op = AsyncOperation(400) + op.writeToDatabase("new-key", "value") + eventually { + op.readFromDatabase("new-key") shouldBe Some("value") + } + } + + it should "retry with explicit timeout value" in { + val op = AsyncOperation(400) + op.writeToDatabase("new-key", "value") + eventually( + timeout = Timeout(Span(2, Seconds)), + interval = Interval(Span(50, Millis)) + ) { + op.readFromDatabase("new-key") shouldBe Some("value") + } + } +} diff --git a/scala-test-junit4/src/test/scala-2/com/baeldung/scala/junit4/IntJunitTests.scala b/scala-test-junit4/src/test/scala/com/baeldung/scala/junit4/IntJunitUnitTest.scala similarity index 64% rename from scala-test-junit4/src/test/scala-2/com/baeldung/scala/junit4/IntJunitTests.scala rename to scala-test-junit4/src/test/scala/com/baeldung/scala/junit4/IntJunitUnitTest.scala index 53be1ac73..bf3d104a5 100644 --- a/scala-test-junit4/src/test/scala-2/com/baeldung/scala/junit4/IntJunitTests.scala +++ b/scala-test-junit4/src/test/scala/com/baeldung/scala/junit4/IntJunitUnitTest.scala @@ -3,15 +3,15 @@ package com.baeldung.scala.junit4 import org.junit.Test import org.junit.Assert._ -class IntJunitTests { +class IntJunitUnitTest { @Test - def testOneIsPositive { + def testOneIsPositive = { assertTrue(1 > 0) } @Test - def testMinusOneIsNegative { + def testMinusOneIsNegative = { assertTrue(-1 < 0) } } diff --git a/scala-test-junit4/src/test/scala-2/com/baeldung/scala/junit4/STAssertionsTests.scala b/scala-test-junit4/src/test/scala/com/baeldung/scala/junit4/STAssertionsUnitTest.scala similarity index 77% rename from scala-test-junit4/src/test/scala-2/com/baeldung/scala/junit4/STAssertionsTests.scala rename to scala-test-junit4/src/test/scala/com/baeldung/scala/junit4/STAssertionsUnitTest.scala index 7767dc4d5..3b0a4331d 100644 --- a/scala-test-junit4/src/test/scala-2/com/baeldung/scala/junit4/STAssertionsTests.scala +++ b/scala-test-junit4/src/test/scala/com/baeldung/scala/junit4/STAssertionsUnitTest.scala @@ -4,11 +4,11 @@ import org.scalatestplus.junit.AssertionsForJUnit import org.junit.Assert._ import org.junit.Test -class STAssertionsTests extends AssertionsForJUnit { +class STAssertionsUnitTest extends AssertionsForJUnit { private final val myInt = 1 @Test - def testAssertJUnitStyle() { + def testAssertJUnitStyle(): Unit = { assertEquals(myInt, 1) assertTrue(myInt > 0) @@ -21,7 +21,7 @@ class STAssertionsTests extends AssertionsForJUnit { } @Test - def testAssertScalaTestStyle() { + def testAssertScalaTestStyle(): Unit = { assert(myInt == 1) assert(myInt.isValidInt) intercept[ArithmeticException] { diff --git a/scala-test-junit4/src/test/scala-2/com/baeldung/scala/junit4/StringJunitTests.scala b/scala-test-junit4/src/test/scala/com/baeldung/scala/junit4/StringJunitUnitTest.scala similarity index 66% rename from scala-test-junit4/src/test/scala-2/com/baeldung/scala/junit4/StringJunitTests.scala rename to scala-test-junit4/src/test/scala/com/baeldung/scala/junit4/StringJunitUnitTest.scala index 8d1feb6bd..e4a62173b 100644 --- a/scala-test-junit4/src/test/scala-2/com/baeldung/scala/junit4/StringJunitTests.scala +++ b/scala-test-junit4/src/test/scala/com/baeldung/scala/junit4/StringJunitUnitTest.scala @@ -3,10 +3,10 @@ package com.baeldung.scala.junit4 import org.junit.Test import org.junit.Assert._ -class StringJunitTests { +class StringJunitUnitTest { @Test - def testEmptyStringLengthIsZero { + def testEmptyStringLengthIsZero = { assertEquals("".length, 0) } } diff --git a/scala-test-junit4/src/test/scala-2/com/baeldung/scala/junit4/TypesTestSuite.scala b/scala-test-junit4/src/test/scala/com/baeldung/scala/junit4/TypesTestUnitTest.scala similarity index 52% rename from scala-test-junit4/src/test/scala-2/com/baeldung/scala/junit4/TypesTestSuite.scala rename to scala-test-junit4/src/test/scala/com/baeldung/scala/junit4/TypesTestUnitTest.scala index 92919b3e5..147738d12 100644 --- a/scala-test-junit4/src/test/scala-2/com/baeldung/scala/junit4/TypesTestSuite.scala +++ b/scala-test-junit4/src/test/scala/com/baeldung/scala/junit4/TypesTestUnitTest.scala @@ -4,5 +4,7 @@ import org.junit.runner.RunWith import org.junit.runners.Suite @RunWith(classOf[Suite]) -@Suite.SuiteClasses(Array(classOf[IntJunitTests], classOf[StringJunitTests])) -class TypesTestSuite +@Suite.SuiteClasses( + Array(classOf[IntJunitUnitTest], classOf[StringJunitUnitTest]) +) +class TypesTestUnitTest diff --git a/scala-test-junit5/build.sbt b/scala-test-junit5/build.sbt index 3163ad18d..c329c0966 100644 --- a/scala-test-junit5/build.sbt +++ b/scala-test-junit5/build.sbt @@ -3,8 +3,6 @@ organization := "com.baeldung" version := "1.0-SNAPSHOT" -scalaVersion := "2.13.12" - resolvers += Resolver.jcenterRepo libraryDependencies += "net.aichler" % "jupiter-interface" % JupiterKeys.jupiterVersion.value % Test diff --git a/scala-test-junit5/src/test/scala-2/com/baeldung/junit5/ExampleTests.scala b/scala-test-junit5/src/test/scala-2/com/baeldung/junit5/ExampleUnitTest.scala similarity index 100% rename from scala-test-junit5/src/test/scala-2/com/baeldung/junit5/ExampleTests.scala rename to scala-test-junit5/src/test/scala-2/com/baeldung/junit5/ExampleUnitTest.scala diff --git a/scala-test/README.md b/scala-test/README.md index c355d8d7f..ef154e02a 100644 --- a/scala-test/README.md +++ b/scala-test/README.md @@ -11,3 +11,6 @@ This module contains articles about the ScalaTest framework in Scala. - [Using the ScalaTest Runner](https://www.baeldung.com/scala/scalatest-runner) - [How to Check If Code Compiles in ScalaTest](https://www.baeldung.com/scala/scalatest-check-compilation) - [Case-Insensitive String Comparison Scala](https://www.baeldung.com/scala/case-insensitive-comparison) +- [ScalaTest Assertions: A Comprehensive Guide](https://www.baeldung.com/scala/scalatest-assertions) +- [Test for Exceptions in ScalaTest](https://www.baeldung.com/scala/scalatest-test-exceptions) +- [Guide to Parameterized Tests in ScalaTest](https://www.baeldung.com/scala/scalatest-parameterized-tests) diff --git a/scala-test/project/build.properties b/scala-test/project/build.properties deleted file mode 100644 index 06703e34d..000000000 --- a/scala-test/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.3.9 diff --git a/scala-test/src/main/scala-2/com/baeldung/scala/assertions/CurrencyDSL.scala b/scala-test/src/main/scala/com/baeldung/scala/assertions/CurrencyDSL.scala similarity index 100% rename from scala-test/src/main/scala-2/com/baeldung/scala/assertions/CurrencyDSL.scala rename to scala-test/src/main/scala/com/baeldung/scala/assertions/CurrencyDSL.scala diff --git a/scala-test/src/main/scala-2/com/baeldung/scala/bddtesting/BankAccount.scala b/scala-test/src/main/scala/com/baeldung/scala/bddtesting/BankAccount.scala similarity index 100% rename from scala-test/src/main/scala-2/com/baeldung/scala/bddtesting/BankAccount.scala rename to scala-test/src/main/scala/com/baeldung/scala/bddtesting/BankAccount.scala diff --git a/scala-test/src/main/scala-2/com/baeldung/scala/scalatest/mockito/InventoryService.scala b/scala-test/src/main/scala/com/baeldung/scala/scalatest/mockito/InventoryService.scala similarity index 100% rename from scala-test/src/main/scala-2/com/baeldung/scala/scalatest/mockito/InventoryService.scala rename to scala-test/src/main/scala/com/baeldung/scala/scalatest/mockito/InventoryService.scala diff --git a/scala-test/src/main/scala-2/com/baeldung/scala/scalatest/mockito/InventoryTransactionDao.scala b/scala-test/src/main/scala/com/baeldung/scala/scalatest/mockito/InventoryTransactionDao.scala similarity index 100% rename from scala-test/src/main/scala-2/com/baeldung/scala/scalatest/mockito/InventoryTransactionDao.scala rename to scala-test/src/main/scala/com/baeldung/scala/scalatest/mockito/InventoryTransactionDao.scala diff --git a/scala-test/src/main/scala-2/com/baeldung/scala/scalatest/mockito/Models.scala b/scala-test/src/main/scala/com/baeldung/scala/scalatest/mockito/Models.scala similarity index 100% rename from scala-test/src/main/scala-2/com/baeldung/scala/scalatest/mockito/Models.scala rename to scala-test/src/main/scala/com/baeldung/scala/scalatest/mockito/Models.scala diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/.scala-build/ide-inputs.json b/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/.scala-build/ide-inputs.json deleted file mode 100644 index b716a44b0..000000000 --- a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/.scala-build/ide-inputs.json +++ /dev/null @@ -1 +0,0 @@ -{"args":["/home/mboizi/Documents/code/scala/scala-tutorials/scala-test/src/test/scala-2/com/baeldung/scala/scalatest"]} \ No newline at end of file diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/.scala-build/ide-options-v2.json b/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/.scala-build/ide-options-v2.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/.scala-build/ide-options-v2.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ScalaMockFlatSpecUnitTest.scala b/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ScalaMockFlatSpecUnitTest.scala deleted file mode 100644 index 6da769be5..000000000 --- a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ScalaMockFlatSpecUnitTest.scala +++ /dev/null @@ -1,19 +0,0 @@ -package com.baeldung.scala.scalatest - -import org.scalamock.scalatest.MockFactory -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers - -class ScalaMockFlatSpec extends AnyFlatSpec with MockFactory with Matchers { - - "A mocked Foo" should "return a mocked bar value" in { - val mockFoo = mock[Foo] - (mockFoo.bar _).expects().returning(6) - - mockFoo.bar should be(6) - } -} - -class Foo { - def bar = 100 -} diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/assertions/AssertionsTests.scala b/scala-test/src/test/scala/com/baeldung/scala/assertions/AssertionsUnitTest.scala similarity index 95% rename from scala-test/src/test/scala-2/com/baeldung/scala/assertions/AssertionsTests.scala rename to scala-test/src/test/scala/com/baeldung/scala/assertions/AssertionsUnitTest.scala index 2f59d985c..8bb6bd6c8 100644 --- a/scala-test/src/test/scala-2/com/baeldung/scala/assertions/AssertionsTests.scala +++ b/scala-test/src/test/scala/com/baeldung/scala/assertions/AssertionsUnitTest.scala @@ -4,7 +4,7 @@ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import org.scalatest.exceptions.TestFailedException -class AssertionsTests extends AnyFlatSpec with Matchers { +class AssertionsUnitTest extends AnyFlatSpec with Matchers { def parseAge(ageString: String): Int = { if (ageString.isEmpty) diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/assertions/CurrencyTests.scala b/scala-test/src/test/scala/com/baeldung/scala/assertions/CurrencyUnitTest.scala similarity index 90% rename from scala-test/src/test/scala-2/com/baeldung/scala/assertions/CurrencyTests.scala rename to scala-test/src/test/scala/com/baeldung/scala/assertions/CurrencyUnitTest.scala index cc70c6c74..b979b5510 100644 --- a/scala-test/src/test/scala-2/com/baeldung/scala/assertions/CurrencyTests.scala +++ b/scala-test/src/test/scala/com/baeldung/scala/assertions/CurrencyUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.assertions import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class CurrencyTests extends AnyFlatSpec with Matchers { +class CurrencyUnitTest extends AnyFlatSpec with Matchers { import Currency.RichDouble "Currency DSL" should "allow adding amounts of the same currency" in { diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/bddtesting/BDDBankUnitTest.scala b/scala-test/src/test/scala/com/baeldung/scala/bddtesting/BDDBankUnitTest.scala similarity index 100% rename from scala-test/src/test/scala-2/com/baeldung/scala/bddtesting/BDDBankUnitTest.scala rename to scala-test/src/test/scala/com/baeldung/scala/bddtesting/BDDBankUnitTest.scala diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ExampleFlatSpecWithMatchersUnitTest.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/ExampleFlatSpecWithMatchersUnitTest.scala similarity index 95% rename from scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ExampleFlatSpecWithMatchersUnitTest.scala rename to scala-test/src/test/scala/com/baeldung/scala/scalatest/ExampleFlatSpecWithMatchersUnitTest.scala index 2ecb7b076..ed3339a81 100644 --- a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ExampleFlatSpecWithMatchersUnitTest.scala +++ b/scala-test/src/test/scala/com/baeldung/scala/scalatest/ExampleFlatSpecWithMatchersUnitTest.scala @@ -64,8 +64,8 @@ class ExampleFlatSpecWithMatchersUnitTest extends AnyFlatSpec with Matchers { } it should "let us check the type of an object" in { - List(1, 2, 3) shouldBe a[List[_]] - List(1, 2, 3) should not be a[Map[_, _]] + List(1, 2, 3) shouldBe a[List[?]] + List(1, 2, 3) should not be a[Map[?, ?]] } } diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/IfCompilesTest.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/IfCompilesUnitTest.scala similarity index 80% rename from scala-test/src/test/scala-2/com/baeldung/scala/scalatest/IfCompilesTest.scala rename to scala-test/src/test/scala/com/baeldung/scala/scalatest/IfCompilesUnitTest.scala index a738a305b..82485656b 100644 --- a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/IfCompilesTest.scala +++ b/scala-test/src/test/scala/com/baeldung/scala/scalatest/IfCompilesUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.scalatest import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class IfCompilesTest extends AnyFlatSpec with Matchers { +class IfCompilesUnitTest extends AnyFlatSpec with Matchers { "val x: Int = 2" should compile diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ListFlatSpecUnitTest.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/ListFlatSpecUnitTest.scala similarity index 100% rename from scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ListFlatSpecUnitTest.scala rename to scala-test/src/test/scala/com/baeldung/scala/scalatest/ListFlatSpecUnitTest.scala diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ListFunSpecUnitTest.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/ListFunSpecUnitTest.scala similarity index 100% rename from scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ListFunSpecUnitTest.scala rename to scala-test/src/test/scala/com/baeldung/scala/scalatest/ListFunSpecUnitTest.scala diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ListFunSuite.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/ListFunUnitTest.scala similarity index 90% rename from scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ListFunSuite.scala rename to scala-test/src/test/scala/com/baeldung/scala/scalatest/ListFunUnitTest.scala index c7628d0ac..33d7880d8 100644 --- a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/ListFunSuite.scala +++ b/scala-test/src/test/scala/com/baeldung/scala/scalatest/ListFunUnitTest.scala @@ -2,7 +2,7 @@ package com.baeldung.scala.scalatest import org.scalatest.funsuite.AnyFunSuite -class ListFunSuite extends AnyFunSuite { +class ListFunUnitTest extends AnyFunSuite { test("An empty List should have size 0") { assert(List.empty.size == 0) diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/StringFlatSpecWithBeforeAndAfterUnitTest.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/StringFlatSpecWithBeforeAndAfterUnitTest.scala similarity index 100% rename from scala-test/src/test/scala-2/com/baeldung/scala/scalatest/StringFlatSpecWithBeforeAndAfterUnitTest.scala rename to scala-test/src/test/scala/com/baeldung/scala/scalatest/StringFlatSpecWithBeforeAndAfterUnitTest.scala diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/TaggedFlatSpecUnitTest.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/TaggedFlatSpecUnitTest.scala similarity index 100% rename from scala-test/src/test/scala-2/com/baeldung/scala/scalatest/TaggedFlatSpecUnitTest.scala rename to scala-test/src/test/scala/com/baeldung/scala/scalatest/TaggedFlatSpecUnitTest.scala diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/collectiontest/CollectionTest.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/collectiontest/CollectionUnitTest.scala similarity index 93% rename from scala-test/src/test/scala-2/com/baeldung/scala/scalatest/collectiontest/CollectionTest.scala rename to scala-test/src/test/scala/com/baeldung/scala/scalatest/collectiontest/CollectionUnitTest.scala index e50256ed2..1f2ee5891 100644 --- a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/collectiontest/CollectionTest.scala +++ b/scala-test/src/test/scala/com/baeldung/scala/scalatest/collectiontest/CollectionUnitTest.scala @@ -5,7 +5,7 @@ import org.scalatest.matchers.should.Matchers import scala.util.Random -class CollectionTest extends AnyFlatSpec with Matchers { +class CollectionUnitTest extends AnyFlatSpec with Matchers { it should "compare equality for a list of numbers in same order" in { val intList = List(1, 2, 3, 4) @@ -73,8 +73,8 @@ class CollectionTest extends AnyFlatSpec with Matchers { val cities2 = List("Barcelona", "Hamburg") Random.shuffle(cities) should contain theSameElementsAs (cities2) cities shouldBe cities2 - cities ++ cities should contain only (cities2: _*) // but duplicates are allowed - cities should contain only (cities2: _*) + cities ++ cities should contain only ("Barcelona", "Hamburg") // but duplicates are allowed + cities should contain only ("Barcelona", "Hamburg") } it should "pass if both collections contains exactly same elements in same order" in { diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/collectiontest/Models.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/collectiontest/Models.scala similarity index 100% rename from scala-test/src/test/scala-2/com/baeldung/scala/scalatest/collectiontest/Models.scala rename to scala-test/src/test/scala/com/baeldung/scala/scalatest/collectiontest/Models.scala diff --git a/scala-test/src/test/scala/com/baeldung/scala/scalatest/exceptions/ExceptionHandlingUnitTest.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/exceptions/ExceptionHandlingUnitTest.scala new file mode 100644 index 000000000..72b9889e7 --- /dev/null +++ b/scala-test/src/test/scala/com/baeldung/scala/scalatest/exceptions/ExceptionHandlingUnitTest.scala @@ -0,0 +1,81 @@ +package com.baeldung.scala.scalatest.exceptions + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AsyncWordSpec + +import scala.concurrent.Future +import scala.util.Try + +object ExceptionHandlingMethods { + def explodingMethod(): Unit = { + throw new RuntimeException("Boom boom!") + } + + def getLastDigit(num: Int): Try[Int] = { + Try { + require(num > 0, "Only positive numbers supported!") + num % 10 + } + } + + def getDBResult(): Future[Int] = { + Future.failed(new RuntimeException("Unexpected error occurred!")) + } +} + +class ExceptionHandlingUnitTest extends AsyncWordSpec with Matchers { + import ExceptionHandlingMethods._ + + "Scalatest exception handler" should { + + "intercept thrown exception successfully" in { + val exception = intercept[RuntimeException](explodingMethod()) + exception shouldBe a[RuntimeException] + exception.getMessage shouldBe "Boom boom!" + } + + "intercept thrown exception successfully using the parent type" in { + val exception = intercept[Exception](explodingMethod()) + exception shouldBe a[Exception] + exception shouldBe a[RuntimeException] + exception.getMessage shouldBe "Boom boom!" + } + + "use assertThrows to handle thrown exception successfully" in { + assertThrows[RuntimeException](explodingMethod()) + } + + "use assertThrows to handle thrown exception successfully by using parent type" in { + assertThrows[Exception](explodingMethod()) + } + + "handle Try failures correctly" in { + val result = getLastDigit(-100) + result.failed.get shouldBe a[IllegalArgumentException] + result.failed.get.getMessage should include( + "Only positive numbers supported!" + ) + } + + "handle future failures correctly" in { + val futureResult = getDBResult() + futureResult.failed.map { ex => + ex shouldBe a[RuntimeException] + ex.getMessage shouldBe "Unexpected error occurred!" + } + } + + "use recoverToExceptionIf method for futures" in { + val futureResult = getDBResult() + recoverToExceptionIf[RuntimeException](futureResult).map { rt => + rt.getMessage shouldBe "Unexpected error occurred!" + } + } + + "use recoverToSucceededIf method for futures" in { + val futureResult = getDBResult() + recoverToSucceededIf[RuntimeException](futureResult) + } + } + +} diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/mockito/InventoryServiceTest.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/mockito/InventoryServiceUnitTest.scala similarity index 99% rename from scala-test/src/test/scala-2/com/baeldung/scala/scalatest/mockito/InventoryServiceTest.scala rename to scala-test/src/test/scala/com/baeldung/scala/scalatest/mockito/InventoryServiceUnitTest.scala index 4d5a1efc6..60d9db53c 100644 --- a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/mockito/InventoryServiceTest.scala +++ b/scala-test/src/test/scala/com/baeldung/scala/scalatest/mockito/InventoryServiceUnitTest.scala @@ -11,7 +11,7 @@ import org.scalatestplus.mockito.MockitoSugar import java.time.LocalDateTime import scala.concurrent.Future -class InventoryServiceTest +class InventoryServiceUnitTest extends AnyWordSpec with MockitoSugar with ScalaFutures diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/runner/ScalaTestRunnerTests.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/runner/ScalaTestRunnerUnitTest.scala similarity index 91% rename from scala-test/src/test/scala-2/com/baeldung/scala/scalatest/runner/ScalaTestRunnerTests.scala rename to scala-test/src/test/scala/com/baeldung/scala/scalatest/runner/ScalaTestRunnerUnitTest.scala index d20e27088..976f021b0 100644 --- a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/runner/ScalaTestRunnerTests.scala +++ b/scala-test/src/test/scala/com/baeldung/scala/scalatest/runner/ScalaTestRunnerUnitTest.scala @@ -10,7 +10,7 @@ import org.scalatest.wordspec.AnyWordSpec object BooleanTests extends Tag("BooleanTests") -class ScalaTestRunnerTests extends AnyWordSpec with Matchers { +class ScalaTestRunnerUnitTest extends AnyWordSpec with Matchers { "Scalatest runnner" should { "convert string true to boolean true" taggedAs (BooleanTests) in { diff --git a/scala-test/src/test/scala-2/com/baeldung/scala/scalatest/stringtest/CaseInsensitiveStringComparisonUnitTest.scala b/scala-test/src/test/scala/com/baeldung/scala/scalatest/stringtest/CaseInsensitiveStringComparisonUnitTest.scala similarity index 100% rename from scala-test/src/test/scala-2/com/baeldung/scala/scalatest/stringtest/CaseInsensitiveStringComparisonUnitTest.scala rename to scala-test/src/test/scala/com/baeldung/scala/scalatest/stringtest/CaseInsensitiveStringComparisonUnitTest.scala diff --git a/scala-test/src/test/scala/com/baeldung/scala/tabledata/TableDrivenUnitTest.scala b/scala-test/src/test/scala/com/baeldung/scala/tabledata/TableDrivenUnitTest.scala new file mode 100644 index 000000000..bd797c3a0 --- /dev/null +++ b/scala-test/src/test/scala/com/baeldung/scala/tabledata/TableDrivenUnitTest.scala @@ -0,0 +1,48 @@ +package com.baeldung.scala.tabledata + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks + +class TableDrivenUnitTest + extends AnyFlatSpec + with Matchers + with TableDrivenPropertyChecks { + + def isPalindrome(str: String): Boolean = { + val sanitizedStr = str.replaceAll("[^A-Za-z0-9]", "").toLowerCase + sanitizedStr == sanitizedStr.reverse + } + + private val palindromeTable = Table( + ("Text", "Is Palindrome"), + ("madam", true), + ("Madam", true), + ("Tacocat", true), + ("TACO, CAT", true), + ("Hello", false) + ) + + it should "check for palindrome" in { + forAll(palindromeTable) { (str, expectedResult) => + isPalindrome(str) shouldBe expectedResult + } + } + + it should "check for palindrome using forEvery" in { + forEvery(palindromeTable) { (str, expectedResult) => + isPalindrome(str) shouldBe expectedResult + } + } + + it should "check for palindrome for string 'madam'" in { + isPalindrome("madam") shouldBe true + } + it should "check for palindrome for string 'Tacocat' with special character" in { + isPalindrome("Taco, cat") shouldBe true + } + it should "check for palindrome for string 'Hello'" in { + isPalindrome("Hello") shouldBe false + } + +} diff --git a/scala-with-maven/README.md b/scala-with-maven/README.md new file mode 100644 index 000000000..351eabd0d --- /dev/null +++ b/scala-with-maven/README.md @@ -0,0 +1 @@ +- [Building Scala Projects With Maven](https://www.baeldung.com/scala/maven) diff --git a/scala-with-maven/hello-rest/pom.xml b/scala-with-maven/hello-rest/pom.xml new file mode 100644 index 000000000..90998e2b1 --- /dev/null +++ b/scala-with-maven/hello-rest/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + + com.baeldung + scala-with-maven + ${revision} + ../pom.xml + + + hello-rest + + + + + org.scala-lang + scala3-library_3 + + + org.typelevel + cats-effect_3 + 3.6.1 + + + org.http4s + http4s-blaze-server_3 + 1.0.0-M39 + + + org.http4s + http4s-dsl_3 + 1.0.0-M41 + + + + \ No newline at end of file diff --git a/scala-with-maven/hello-rest/src/main/scala/HelloRest.scala b/scala-with-maven/hello-rest/src/main/scala/HelloRest.scala new file mode 100644 index 000000000..2eeb09043 --- /dev/null +++ b/scala-with-maven/hello-rest/src/main/scala/HelloRest.scala @@ -0,0 +1,7 @@ +import org.http4s.HttpRoutes +import org.http4s.dsl.io._ +import cats.effect._ + +val helloWorldService = HttpRoutes.of[IO] { case GET -> Root / "hello" => + Ok("Hello, World!") +} diff --git a/scala-with-maven/hello-world/pom.xml b/scala-with-maven/hello-world/pom.xml new file mode 100644 index 000000000..91b215485 --- /dev/null +++ b/scala-with-maven/hello-world/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + + com.baeldung + scala-with-maven + ${revision} + ../pom.xml + + + hello-world + + + + org.scala-lang + scala3-library_3 + ${scala.version} + + + org.scalatest + scalatest_3 + 3.2.19 + test + + + + + + + org.scalatest + scalatest-maven-plugin + + + + + \ No newline at end of file diff --git a/scala-with-maven/hello-world/src/main/scala/HelloWorld.scala b/scala-with-maven/hello-world/src/main/scala/HelloWorld.scala new file mode 100644 index 000000000..30b197e49 --- /dev/null +++ b/scala-with-maven/hello-world/src/main/scala/HelloWorld.scala @@ -0,0 +1,9 @@ +package com.baeldung.scalawithmaven + +object HelloWorld { + + def message: String = "Hello, World!" + + def main(args: Array[String]): Unit = println(message) + +} diff --git a/scala-with-maven/hello-world/src/test/scala/HelloWorldUnitTest.scala b/scala-with-maven/hello-world/src/test/scala/HelloWorldUnitTest.scala new file mode 100644 index 000000000..edb69636b --- /dev/null +++ b/scala-with-maven/hello-world/src/test/scala/HelloWorldUnitTest.scala @@ -0,0 +1,12 @@ +package com.baeldung.scalawithmaven + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class HelloWorldUnitTest extends AnyFlatSpec with Matchers { + + "The HelloWorld object" should "say hello" in { + val greeting = HelloWorld.message + greeting shouldEqual "Hello, World!" + } +} diff --git a/scala-with-maven/pom.xml b/scala-with-maven/pom.xml new file mode 100644 index 000000000..e4f777f75 --- /dev/null +++ b/scala-with-maven/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + com.baeldung + scala-with-maven + ${revision} + pom + + + 1.0-SNAPSHOT + 3.4.0 + + + + + + org.scala-lang + scala3-library_3 + ${scala.version} + + + org.scalatest + scalatest_3 + 3.2.19 + + + + + + hello-world + hello-rest + + + + src/main/scala + src/test/scala + + + + org.scalatest + scalatest-maven-plugin + 2.2.0 + + + + test + + + + + + + + + net.alchim31.maven + scala-maven-plugin + 4.8.1 + + incremental + ${scala.version} + + -Xlint:unchecked + -Xlint:deprecation + + + + + + compile + testCompile + + + + + + + + \ No newline at end of file diff --git a/scala3-lang-3/build.sbt b/scala3-lang-3/build.sbt deleted file mode 100644 index b955a8259..000000000 --- a/scala3-lang-3/build.sbt +++ /dev/null @@ -1,9 +0,0 @@ -val scala3Version = "3.2.2" - -scalaVersion := scala3Version - -/* extra runtime checks to find ill-formed trees or types as soon as they are created - * and check compiler invariants for tree well-formedness - */ -//scalacOptions += "-Xcheck-macros" -//scalacOptions += "-Ycheck:all" diff --git a/scala3-lang/build.sbt b/scala3-lang/build.sbt deleted file mode 100644 index ebe44f530..000000000 --- a/scala3-lang/build.sbt +++ /dev/null @@ -1,3 +0,0 @@ -val scala3Version = "3.2.2" - -scalaVersion := scala3Version diff --git a/scala3-lang/project/build.properties b/scala3-lang/project/build.properties deleted file mode 100644 index e64c208ff..000000000 --- a/scala3-lang/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.5.8 diff --git a/scala3-libraries/README.md b/scala3-libraries/README.md deleted file mode 100644 index 2d4c63290..000000000 --- a/scala3-libraries/README.md +++ /dev/null @@ -1,4 +0,0 @@ - -### Relevant Articles: -- [Scala App Configurations With Clarity Using ClearConfig](https://www.baeldung.com/scala/clearconfig) -- [Introduction to MUnit](https://www.baeldung.com/scala/munit-introduction) diff --git a/scalatra/build.sbt b/scalatra/build.sbt index a0636b504..9ff59b635 100644 --- a/scalatra/build.sbt +++ b/scalatra/build.sbt @@ -1,6 +1,6 @@ val ScalatraVersion = "2.8.4" -ThisBuild / scalaVersion := "2.13.12" +ThisBuild / scalaVersion := "2.13.18" ThisBuild / organization := "baeldung" name := "scalatratutorial" @@ -8,16 +8,16 @@ version := "0.1.0-SNAPSHOT" libraryDependencies ++= Seq( "org.scalatra" %% "scalatra" % ScalatraVersion, "org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test", - "ch.qos.logback" % "logback-classic" % "1.2.3" % "runtime", - "org.eclipse.jetty" % "jetty-webapp" % "9.4.52.v20230823" % "container", + "ch.qos.logback" % "logback-classic" % "1.5.17" % "runtime", + "org.eclipse.jetty" % "jetty-webapp" % "11.0.25" % "container", "javax.servlet" % "javax.servlet-api" % "4.0.1" % "provided", "org.scalatra" %% "scalatra-auth" % ScalatraVersion, "org.scalatra" %% "scalatra-json" % "2.8.4", - "org.json4s" %% "json4s-jackson" % "4.0.6", - "com.typesafe.slick" %% "slick" % "3.3.3", - "com.h2database" % "h2" % "1.4.196", - "com.mchange" % "c3p0" % "0.9.5.5" + "io.github.json4s" %% "json4s-jackson" % "4.1.0", + "com.typesafe.slick" %% "slick" % "3.6.1", + "com.h2database" % "h2" % "2.1.214", + "com.mchange" % "c3p0" % "0.11.2" ) - +libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always enablePlugins(SbtTwirl) enablePlugins(JettyPlugin) diff --git a/scalatra/project/build.properties b/scalatra/project/build.properties index c06db1bb2..01a16ed14 100644 --- a/scalatra/project/build.properties +++ b/scalatra/project/build.properties @@ -1 +1 @@ -sbt.version=1.4.5 +sbt.version=1.11.7 diff --git a/scalatra/project/plugins.sbt b/scalatra/project/plugins.sbt index 8b1378917..884bcac19 100644 --- a/scalatra/project/plugins.sbt +++ b/scalatra/project/plugins.sbt @@ -1 +1,5 @@ +addSbtPlugin("org.playframework.twirl" % "sbt-twirl" % "2.0.9") +addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") + +libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always diff --git a/scalatra/src/main/scala-2/scalatra/tutorial/model/DbSupport.scala b/scalatra/src/main/scala-2/scalatra/tutorial/model/DbSupport.scala index ef04324a5..7523830e0 100644 --- a/scalatra/src/main/scala-2/scalatra/tutorial/model/DbSupport.scala +++ b/scalatra/src/main/scala-2/scalatra/tutorial/model/DbSupport.scala @@ -5,7 +5,7 @@ import slick.jdbc.H2Profile.api._ trait DbSupport extends FutureSupport { - protected implicit def executor = + protected implicit def executor: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global def db: Database diff --git a/scalatra/src/test/scala-2/scalatra/tutorial/MyScalatraServletTests.scala b/scalatra/src/test/scala-2/scalatra/tutorial/MyScalatraServletUnitTest.scala similarity index 82% rename from scalatra/src/test/scala-2/scalatra/tutorial/MyScalatraServletTests.scala rename to scalatra/src/test/scala-2/scalatra/tutorial/MyScalatraServletUnitTest.scala index e57601936..9b734ed29 100644 --- a/scalatra/src/test/scala-2/scalatra/tutorial/MyScalatraServletTests.scala +++ b/scalatra/src/test/scala-2/scalatra/tutorial/MyScalatraServletUnitTest.scala @@ -3,7 +3,7 @@ package scalatra.tutorial import org.scalatra.test.scalatest._ import scalatra.tutorial.servlet.MyScalatraServlet -class MyScalatraServletTests extends ScalatraFunSuite { +class MyScalatraServletUnitTest extends ScalatraFunSuite { addServlet(classOf[MyScalatraServlet], "/*") diff --git a/spark-scala/src/test/scala-2/com/baeldung/scala/spark/FirstNRowsTest.scala b/spark-scala/src/test/scala-2/com/baeldung/scala/spark/FirstNRowsUnitTest.scala similarity index 96% rename from spark-scala/src/test/scala-2/com/baeldung/scala/spark/FirstNRowsTest.scala rename to spark-scala/src/test/scala-2/com/baeldung/scala/spark/FirstNRowsUnitTest.scala index 7c8671f4b..9eca5ba18 100644 --- a/spark-scala/src/test/scala-2/com/baeldung/scala/spark/FirstNRowsTest.scala +++ b/spark-scala/src/test/scala-2/com/baeldung/scala/spark/FirstNRowsUnitTest.scala @@ -8,7 +8,7 @@ import org.apache.spark.sql.Dataset import info.{data, spark} import spark.implicits._ -class FirstNRowsSpec extends AnyFlatSpec with Matchers { +class FirstNRowsUnitTest extends AnyFlatSpec with Matchers { "The data Dataframe" should "contain 6 elements" in { data.count() shouldBe (6) diff --git a/spark-scala/src/test/scala-2/com/baeldung/scala/spark/RDDToDataframeTest.scala b/spark-scala/src/test/scala-2/com/baeldung/scala/spark/RDDToDataframeUnitTest.scala similarity index 93% rename from spark-scala/src/test/scala-2/com/baeldung/scala/spark/RDDToDataframeTest.scala rename to spark-scala/src/test/scala-2/com/baeldung/scala/spark/RDDToDataframeUnitTest.scala index ed391d67d..0dd52a58b 100644 --- a/spark-scala/src/test/scala-2/com/baeldung/scala/spark/RDDToDataframeTest.scala +++ b/spark-scala/src/test/scala-2/com/baeldung/scala/spark/RDDToDataframeUnitTest.scala @@ -10,7 +10,7 @@ import org.apache.spark.sql.types.{ } import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should -class RDDToDataframeTest extends AnyFlatSpec with should.Matchers { +class RDDToDataframeUnitTest extends AnyFlatSpec with should.Matchers { val spark: SparkSession = SparkSession.builder.master("local").getOrCreate "convertRowRDDToDataframe" should "be able to convert an RDD[Row] and return a DataFrame" in { diff --git a/spark-scala/src/test/scala-2/com/baeldung/scala/spark/rdd/PrintRDDTest.scala b/spark-scala/src/test/scala-2/com/baeldung/scala/spark/rdd/PrintRDDUnitTest.scala similarity index 94% rename from spark-scala/src/test/scala-2/com/baeldung/scala/spark/rdd/PrintRDDTest.scala rename to spark-scala/src/test/scala-2/com/baeldung/scala/spark/rdd/PrintRDDUnitTest.scala index 31bc24a0c..ee78d3de7 100644 --- a/spark-scala/src/test/scala-2/com/baeldung/scala/spark/rdd/PrintRDDTest.scala +++ b/spark-scala/src/test/scala-2/com/baeldung/scala/spark/rdd/PrintRDDUnitTest.scala @@ -4,7 +4,7 @@ import org.apache.spark.sql.SparkSession import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.must.Matchers -class PrintRDDTest extends AnyFlatSpec with Matchers { +class PrintRDDUnitTest extends AnyFlatSpec with Matchers { val spark: SparkSession = SparkSession.builder.master("local").getOrCreate "default rdd" should "have size six" in { diff --git a/spark-scala/src/test/scala-2/com/baeldung/scala/spark/rdd/RDDTutorialTest.scala b/spark-scala/src/test/scala-2/com/baeldung/scala/spark/rdd/RDDTutorialUnitTest.scala similarity index 96% rename from spark-scala/src/test/scala-2/com/baeldung/scala/spark/rdd/RDDTutorialTest.scala rename to spark-scala/src/test/scala-2/com/baeldung/scala/spark/rdd/RDDTutorialUnitTest.scala index 82578f632..eef5dbfaf 100644 --- a/spark-scala/src/test/scala-2/com/baeldung/scala/spark/rdd/RDDTutorialTest.scala +++ b/spark-scala/src/test/scala-2/com/baeldung/scala/spark/rdd/RDDTutorialUnitTest.scala @@ -4,7 +4,7 @@ import org.apache.spark.rdd.RDD import org.apache.spark.sql.SparkSession import org.scalatest.flatspec.AnyFlatSpec -class RDDTutorialTest extends AnyFlatSpec { +class RDDTutorialUnitTest extends AnyFlatSpec { val spark: SparkSession = SparkSession.builder.master("local").getOrCreate val sc = spark.sparkContext diff --git a/spark-scala/src/test/scala-2/com/baeldung/scala/sparkjoin/SparkJoinsTest.scala b/spark-scala/src/test/scala-2/com/baeldung/scala/sparkjoin/SparkJoinsUnitTest.scala similarity index 97% rename from spark-scala/src/test/scala-2/com/baeldung/scala/sparkjoin/SparkJoinsTest.scala rename to spark-scala/src/test/scala-2/com/baeldung/scala/sparkjoin/SparkJoinsUnitTest.scala index 9c32ddf56..8c9279d1c 100644 --- a/spark-scala/src/test/scala-2/com/baeldung/scala/sparkjoin/SparkJoinsTest.scala +++ b/spark-scala/src/test/scala-2/com/baeldung/scala/sparkjoin/SparkJoinsUnitTest.scala @@ -7,7 +7,7 @@ import org.apache.spark.sql.SparkSession import org.apache.spark.sql.Column import com.baeldung.scala.sparkjoin.SparkJoins._ -class SparkJoinsSpec extends AnyFlatSpec with Matchers { +class SparkJoinsUnitTest extends AnyFlatSpec with Matchers { val spark = SparkSession.builder().appName("Joins").master("local").getOrCreate() diff --git a/validate.sbt b/validate.sbt new file mode 100644 index 000000000..f7dcaae2a --- /dev/null +++ b/validate.sbt @@ -0,0 +1,46 @@ +lazy val validateUnitTestNames = + taskKey[Int]("Validate test naming convention for unit tests") +val inAllUnitTest = ScopeFilter(inAnyProject, inConfigurations(Test)) +validateUnitTestNames := { + val testFileFullPath: Seq[Seq[String]] = + definedTestNames.all(inAllUnitTest).value + val log = streams.value.log + val invalidTestName = testFileFullPath + .flatMap(_.filterNot(_.endsWith("UnitTest"))) + .filterNot(_.trim.isEmpty) + val invalidTestNameFormatted = invalidTestName.mkString("\n") + if (invalidTestName.nonEmpty) { + log.error( + s""" *************************************************** + |Found unit test classes not matching with naming standards. Unit test class must end with `UnitTest`. + |Refer to README.md file in the GitHub for more information. + |Invalid test classes: + |${invalidTestNameFormatted} + |*************************************************** + |""".stripMargin + ) + } + require( + invalidTestName.isEmpty, + s"Found ${invalidTestName.size} tests that doesn't follow naming convention, check the logs above for list of test classes!" + ) + 0 +} + +// for integration tests +// +//lazy val validateIntegrationTestNames = taskKey[Int]("Validate test naming convention for unit tests") +//val inAllIntegrationTest = ScopeFilter(inAnyProject, inConfigurations(IntegrationTest)) +//validateIntegrationTestNames := { +// val testFileFullPath: Seq[Seq[String]] = definedTestNames.all(inAllIntegrationTest).value +// val log = streams.value.log +// val invalidTestName = testFileFullPath.flatMap(_.filterNot(_.endsWith("IntegrationTest"))).filterNot(_.trim.isEmpty) +// val invalidTestNameFormatted = invalidTestName.mkString("\n") +// log.error( +// s"""Found integration test files not matching with naming standards. Integration test files must end with IntegrationTest.scala. +// | Invalid files: +// | ${invalidTestNameFormatted} +// |""".stripMargin) +// require(invalidTestName.isEmpty, s"Found ${invalidTestName.size} tests that doesn't follow naming convention!") +// 0 +//} diff --git a/zio-2/README.md b/zio-2/README.md new file mode 100644 index 000000000..d908add26 --- /dev/null +++ b/zio-2/README.md @@ -0,0 +1,4 @@ +### Relevant Articles: +- [Test Aspects in ZIO Test](https://www.baeldung.com/scala/zio-test-aspects) +- [Introduction to ZIO Json](https://www.baeldung.com/scala/zio-json) +- [Logging in ZIO Applications](https://www.baeldung.com/scala/zio-logging) diff --git a/scala-libraries-4/src/main/resources/logback.xml b/zio-2/src/main/resources/logback.xml similarity index 77% rename from scala-libraries-4/src/main/resources/logback.xml rename to zio-2/src/main/resources/logback.xml index 1a457dba4..08d24db32 100644 --- a/scala-libraries-4/src/main/resources/logback.xml +++ b/zio-2/src/main/resources/logback.xml @@ -1,10 +1,12 @@ + + - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - \ No newline at end of file + diff --git a/zio-2/src/main/scala/com/baeldung/scala/zio/json/BaeldungZIOJson.scala b/zio-2/src/main/scala/com/baeldung/scala/zio/json/BaeldungZIOJson.scala new file mode 100644 index 000000000..525e1ddfc --- /dev/null +++ b/zio-2/src/main/scala/com/baeldung/scala/zio/json/BaeldungZIOJson.scala @@ -0,0 +1,29 @@ +package com.baeldung.scala.zio.json + +import zio.json.* + +sealed trait Command +case class Start(timeout: Long) extends Command +case object Stop extends Command +case class Kill(reason: String, force: Boolean) extends Command + +@jsonDiscriminator("type") +sealed trait Command2 +case class Start2(timeout: Long) extends Command2 +case object Stop2 extends Command2 +case class Kill2(reason: String, force: Boolean) extends Command2 + +object Start { + implicit val encoder: JsonEncoder[Start] = DeriveJsonEncoder.gen[Start] + implicit val decoder: JsonDecoder[Start] = DeriveJsonDecoder.gen[Start] +} + +object Command { + implicit val encoder: JsonEncoder[Command] = DeriveJsonEncoder.gen[Command] + implicit val decoder: JsonDecoder[Command] = DeriveJsonDecoder.gen[Command] +} + +object Command2 { + implicit val encoder: JsonEncoder[Command2] = DeriveJsonEncoder.gen[Command2] + implicit val decoder: JsonDecoder[Command2] = DeriveJsonDecoder.gen[Command2] +} diff --git a/zio-2/src/main/scala/com/baeldung/scala/zio/logging/BaeldungLogger.scala b/zio-2/src/main/scala/com/baeldung/scala/zio/logging/BaeldungLogger.scala new file mode 100644 index 000000000..eeaa3e2cb --- /dev/null +++ b/zio-2/src/main/scala/com/baeldung/scala/zio/logging/BaeldungLogger.scala @@ -0,0 +1,49 @@ +package com.baeldung.scala.zio.logging + +import zio.* +import zio.logging.backend.SLF4J +import zio.logging.{ + LogColor, + LogFilter, + LogFormat, + LoggerNameExtractor, + console, + fileJson +} +import zio.logging.LogFormat.* + +import java.nio.file.Path +import java.time.format.DateTimeFormatter + +object BaeldungLogger { + + private val filter = + LogFilter.logLevelByName( + LogLevel.Trace, + ("com.baeldung.scala.zio.logging", LogLevel.Info) + ) + + private val logFormat: LogFormat = timestamp( + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssAZ") + ).highlight(_ => LogColor.BLUE) + |-| bracketStart |-| LogFormat.loggerName( + LoggerNameExtractor.trace + ) |-| text(":") |-| traceLine |-| bracketEnd |-| + fiberId |-| level.highlight |-| label("message", quoted(line)).highlight + + val consoleLogger: ULayer[Unit] = + Runtime.removeDefaultLoggers >>> console(logFormat, filter) + + val fileLogger: ULayer[Unit] = Runtime.removeDefaultLoggers >>> fileJson( + Path.of("baeldung-zio-logging.log"), + logFormat, + LogLevel.Debug + ) + + val slf4jLogger: ULayer[Unit] = Runtime.removeDefaultLoggers >>> SLF4J.slf4j + +// val slf4jBridgeLogger: ULayer[Unit] = Runtime.removeDefaultLoggers >>> Slf4jBridge.initialize + + val combinedLogger: ULayer[Unit] = consoleLogger ++ fileLogger + +} diff --git a/zio-2/src/main/scala/com/baeldung/scala/zio/logging/BaeldungMetricsLogger.scala b/zio-2/src/main/scala/com/baeldung/scala/zio/logging/BaeldungMetricsLogger.scala new file mode 100644 index 000000000..8f986ec6f --- /dev/null +++ b/zio-2/src/main/scala/com/baeldung/scala/zio/logging/BaeldungMetricsLogger.scala @@ -0,0 +1,26 @@ +package com.baeldung.scala.zio.logging + +import zio.* +import zio.logging.* +import zio.metrics.connectors.MetricsConfig +import zio.metrics.connectors.prometheus.{ + PrometheusPublisher, + prometheusLayer, + publisherLayer +} + +import java.io.IOException + +object BaeldungMetricsLogger { + + private val prometheusConnector + : ZLayer[Unit, IOException, PrometheusPublisher] = (ZLayer.succeed( + MetricsConfig(10.millis) + ) ++ publisherLayer) >+> prometheusLayer + val metricsLogger: ZLayer[Unit, IOException, PrometheusPublisher] = + logMetrics ++ logMetricsWith( + "custom_log_counter", + "log_level" + ) ++ prometheusConnector + +} diff --git a/zio-2/src/main/scala/com/baeldung/scala/zio/logging/BaeldungZIOLoggingApp.scala b/zio-2/src/main/scala/com/baeldung/scala/zio/logging/BaeldungZIOLoggingApp.scala new file mode 100644 index 000000000..c059443dc --- /dev/null +++ b/zio-2/src/main/scala/com/baeldung/scala/zio/logging/BaeldungZIOLoggingApp.scala @@ -0,0 +1,23 @@ +package com.baeldung.scala.zio.logging + +import zio.* +import zio.metrics.connectors.prometheus.PrometheusPublisher + +object BaeldungZIOLoggingApp extends ZIOAppDefault { + + val app: ZIO[PrometheusPublisher, Any, Unit] = for { + - <- ZIO.logTrace("This is a trace message") + _ <- ZIO.logDebug("This is a debug message") + _ <- ZIO.logInfo("This is an info message") + _ <- ZIO.logWarning("This is a warning message") + _ <- ZIO.logError("This is an error message") + _ <- ZIO.sleep(500.millis) + metricValues <- ZIO.serviceWithZIO[PrometheusPublisher](_.get) + _ <- Console.printLine(metricValues) + } yield () + + override def run: ZIO[ZIOAppArgs & Scope, Any, Any] = + app.provideLayer( + BaeldungLogger.slf4jLogger >>> BaeldungMetricsLogger.metricsLogger + ) +} diff --git a/zio-2/src/test/scala/com/baeldung/scala/zio/aspects/ZAspectsUnitTest.scala b/zio-2/src/test/scala/com/baeldung/scala/zio/aspects/ZAspectsUnitTest.scala new file mode 100644 index 000000000..446b088a8 --- /dev/null +++ b/zio-2/src/test/scala/com/baeldung/scala/zio/aspects/ZAspectsUnitTest.scala @@ -0,0 +1,34 @@ +package com.baeldung.scala.zio.aspects + +import com.baeldung.scala.zio.aspects.ZAspectsUnitTest.test +import zio.* +import zio.test.* +import zio.test.TestAspect.{aroundWith, ignore, nonFlaky, timeout} + +object ZAspectsUnitTest extends ZIOSpecDefault { + override def spec = suite("ZAspectSpec")( + test("Tautology test") { + assertTrue(true) + }, + test("Tautology test") { + assertTrue(true) + } @@ ignore, + test("Run effects around a test") { + for { + plt <- System.env("PLATFORM") + } yield assertTrue(plt.contains("Baeldung")) + } @@ aroundWith { + val platform = "Baeldung" + TestSystem.putEnv("PLATFORM", platform) *> ZIO.succeed(platform) + }(envVar => ZIO.debug(s"Platform: $envVar")), + test("Non flaky") { + assertTrue(true) + } @@ nonFlaky(10) + /* Commented out as otherwise the test suite will fail + test("timeouts") { + for { + _ <- ZIO.succeed("Baeldung").forever + } yield assertTrue(true) + } @@ timeout(1.second)*/ + ) +} diff --git a/zio-2/src/test/scala/com/baeldung/scala/zio/json/DecodingUnitTest.scala b/zio-2/src/test/scala/com/baeldung/scala/zio/json/DecodingUnitTest.scala new file mode 100644 index 000000000..b0e2a17a7 --- /dev/null +++ b/zio-2/src/test/scala/com/baeldung/scala/zio/json/DecodingUnitTest.scala @@ -0,0 +1,65 @@ +package com.baeldung.scala.zio.json + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec +import zio.json.* + +class DecodingUnitTest extends AnyWordSpec with Matchers { + "zio-json" should { + "decode a JSON object to a case class" in { + """{"timeout":789}""".fromJson[Start] shouldBe Right(Start(789)) + } + + "return a decoding error if the JSON is not correct" in { + """{"duration":789}""".fromJson[Start] shouldBe Left(".timeout(missing)") + } + + "decode a JSON object with extra fields to a case class" in { + """{"timeout":789, "extra": "field"}""".fromJson[Start] shouldBe Right( + Start(789) + ) + } + + "decode an ADT to JSON" in { + """{"Start":{"timeout":100}}""".fromJson[Command] shouldBe Right( + Start(100) + ) + """{"Stop":{}}""".fromJson[Command] shouldBe Right(Stop) + """{"Kill":{"reason":"Random reason","force":false}}""" + .fromJson[Command] shouldBe Right( + Kill("Random reason", false) + ) + } + + "decode Stop to JSON" in { + implicit val decoder: JsonDecoder[Stop.type] = + implicitly[JsonDecoder[String]].map(_ => Stop) + + """{"Start":{"timeout":100}}""".fromJson[Command] shouldBe Right( + Start(100) + ) + """"Stop"""".fromJson[Stop.type] shouldBe Right(Stop) + """{"Kill":{"reason":"Random reason","force":false}}""" + .fromJson[Command] shouldBe Right( + Kill("Random reason", false) + ) + } + + "use a discriminator" in { + """{"type":"Start2","timeout":100}""".fromJson[Command2] shouldBe Right( + Start2(100) + ) + """{"type":"Stop2"}""".fromJson[Command2] shouldBe Right(Stop2) + """{"type":"Kill2","reason":"Random reason","force":false}""" + .fromJson[Command2] shouldBe Right( + Kill2("Random reason", false) + ) + } + + "fail if there's no discriminator" in { + """{"timeout":100}""".fromJson[Command2] shouldBe Left( + "(missing hint 'type')" + ) + } + } +} diff --git a/zio-2/src/test/scala/com/baeldung/scala/zio/json/EncodingUnitTest.scala b/zio-2/src/test/scala/com/baeldung/scala/zio/json/EncodingUnitTest.scala new file mode 100644 index 000000000..c6c547e3c --- /dev/null +++ b/zio-2/src/test/scala/com/baeldung/scala/zio/json/EncodingUnitTest.scala @@ -0,0 +1,45 @@ +package com.baeldung.scala.zio.json + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec +import zio.json.* + +class EncodingUnitTest extends AnyWordSpec with Matchers { + "zio-json" should { + "encode a case class to JSON" in { + Start(100).toJson shouldBe """{"timeout":100}""" + } + + "encode an ADT to JSON" in { + (Start(100): Command).toJson shouldBe """{"Start":{"timeout":100}}""" + (Stop: Command).toJson shouldBe """{"Stop":{}}""" + (Kill( + reason = "Random reason", + force = false + ): Command).toJson shouldBe """{"Kill":{"reason":"Random reason","force":false}}""" + } + + "encode Stop to JSON" in { + implicit val encoder: JsonEncoder[Stop.type] = + implicitly[JsonEncoder[String]].contramap(_.toString()) + + (Start(100): Command).toJson shouldBe """{"Start":{"timeout":100}}""" + Stop.toJson shouldBe """"Stop"""" + (Kill( + reason = "Random reason", + force = false + ): Command).toJson shouldBe """{"Kill":{"reason":"Random reason","force":false}}""" + } + + "use a discriminator" in { + (Start2( + 100 + ): Command2).toJson shouldBe """{"type":"Start2","timeout":100}""" + (Stop2: Command2).toJson shouldBe """{"type":"Stop2"}""" + (Kill2( + reason = "Random reason", + force = false + ): Command2).toJson shouldBe """{"type":"Kill2","reason":"Random reason","force":false}""" + } + } +} diff --git a/zio-2/src/test/scala/com/baeldung/scala/zio/logging/BaeldungZIOLoggingUnitTest.scala b/zio-2/src/test/scala/com/baeldung/scala/zio/logging/BaeldungZIOLoggingUnitTest.scala new file mode 100644 index 000000000..aeda8d8e5 --- /dev/null +++ b/zio-2/src/test/scala/com/baeldung/scala/zio/logging/BaeldungZIOLoggingUnitTest.scala @@ -0,0 +1,45 @@ +package com.baeldung.scala.zio.logging + +import zio.{Chunk, LogLevel, ZIO} +import zio.test.Assertion.* +import zio.test.* +import zio.* + +object BaeldungZIOLoggingUnitTest extends ZIOSpecDefault { + + override def spec: Spec[TestEnvironment, Any] = + suite("BaeldungZIOLoggingSpec")( + test("log trace level") { + for { + - <- ZIO.logTrace("This is a trace message") + _ <- ZIO.logDebug("This is a debug message") + _ <- ZIO.logInfo("This is an info message") + _ <- ZIO.logWarning("This is a warning message") + _ <- ZIO.logError("This is an error message") + loggerOutput <- ZTestLogger.logOutput + } yield assertTrue(loggerOutput.size == 5) && + assert(loggerOutput.map(_.logLevel))( + equalTo( + Chunk( + LogLevel.Trace, + LogLevel.Debug, + LogLevel.Info, + LogLevel.Warning, + LogLevel.Error + ) + ) + ) && + assert(loggerOutput.map(_.message()))( + equalTo( + Chunk( + "This is a trace message", + "This is a debug message", + "This is an info message", + "This is a warning message", + "This is an error message" + ) + ) + ) + } + ).provideLayer(Runtime.removeDefaultLoggers >>> ZTestLogger.default) +} diff --git a/zio/README.md b/zio/README.md index fe03f82ee..59036189a 100644 --- a/zio/README.md +++ b/zio/README.md @@ -8,3 +8,5 @@ - [Repeat and Retry in ZIO](https://www.baeldung.com/scala/zio-repeat-retry) - [Introduction to Fibers in ZIO](https://www.baeldung.com/scala/zio-fibers-tutorial) - [Error Handling in ZIO](https://www.baeldung.com/scala/zio-error-handling) +- [Introduction to zio-kafka](https://www.baeldung.com/scala/zio-kafka-intro) +- [Functional Abstraction in Scala Using ZIO Prelude](https://www.baeldung.com/scala/zio-prelude-functional-abstraction) diff --git a/zio/project/build.properties b/zio/project/build.properties new file mode 100644 index 000000000..19479ba46 --- /dev/null +++ b/zio/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.5.2 diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/AcquireReleaseExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/AcquireReleaseExample.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/AcquireReleaseExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/AcquireReleaseExample.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/AsynchronousProgrammingExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/AsynchronousProgrammingExample.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/AsynchronousProgrammingExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/AsynchronousProgrammingExample.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/ClassicSTMTransferExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/ClassicSTMTransferExample.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/ClassicSTMTransferExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/ClassicSTMTransferExample.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/CounterExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/CounterExample.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/CounterExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/CounterExample.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/FiberExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/FiberExample.scala similarity index 90% rename from zio/src/main/scala-2/com/baeldung/scala/zio/FiberExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/FiberExample.scala index c9c1abc0f..67d660e6e 100644 --- a/zio/src/main/scala-2/com/baeldung/scala/zio/FiberExample.scala +++ b/zio/src/main/scala/com/baeldung/scala/zio/FiberExample.scala @@ -22,5 +22,5 @@ object FiberExample extends zio.ZIOAppDefault { result <- (fiber1 <*> fiber2).join } yield result - override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = myApp + override def run: ZIO[Any & ZIOAppArgs & Scope, Any, Any] = myApp } diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/HelloWorld.scala b/zio/src/main/scala/com/baeldung/scala/zio/HelloWorld.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/HelloWorld.scala rename to zio/src/main/scala/com/baeldung/scala/zio/HelloWorld.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/ModulePatternExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/ModulePatternExample.scala similarity index 96% rename from zio/src/main/scala-2/com/baeldung/scala/zio/ModulePatternExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/ModulePatternExample.scala index faff48f29..b11ed810a 100644 --- a/zio/src/main/scala-2/com/baeldung/scala/zio/ModulePatternExample.scala +++ b/zio/src/main/scala/com/baeldung/scala/zio/ModulePatternExample.scala @@ -25,7 +25,7 @@ trait Logging { // Companion object containing accessor methods object Logging { def log(line: String): URIO[Logging, Unit] = - ZIO.serviceWith[Logging](_.log(line)) + ZIO.serviceWithZIO[Logging](_.log(line)) } // Live implementation of Logging service diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/ParallelForeachExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/ParallelForeachExample.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/ParallelForeachExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/ParallelForeachExample.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/SequentialEffectComposition.scala b/zio/src/main/scala/com/baeldung/scala/zio/SequentialEffectComposition.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/SequentialEffectComposition.scala rename to zio/src/main/scala/com/baeldung/scala/zio/SequentialEffectComposition.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/createeffects/CreateZIOEffects.scala b/zio/src/main/scala/com/baeldung/scala/zio/createeffects/CreateZIOEffects.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/createeffects/CreateZIOEffects.scala rename to zio/src/main/scala/com/baeldung/scala/zio/createeffects/CreateZIOEffects.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/errorhandling/ErrorHandling.scala b/zio/src/main/scala/com/baeldung/scala/zio/errorhandling/ErrorHandling.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/errorhandling/ErrorHandling.scala rename to zio/src/main/scala/com/baeldung/scala/zio/errorhandling/ErrorHandling.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/fibers/PastaCookingFiberAppExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/fibers/PastaCookingFiberAppExample.scala similarity index 96% rename from zio/src/main/scala-2/com/baeldung/scala/zio/fibers/PastaCookingFiberAppExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/fibers/PastaCookingFiberAppExample.scala index e65042147..d0e04aa38 100644 --- a/zio/src/main/scala-2/com/baeldung/scala/zio/fibers/PastaCookingFiberAppExample.scala +++ b/zio/src/main/scala/com/baeldung/scala/zio/fibers/PastaCookingFiberAppExample.scala @@ -61,5 +61,5 @@ object PastaCookingFiberAppExample extends ZIOAppDefault { _ <- orderFiber.join.ensuring(cleanup) } yield () - override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = pastaApp + override def run: ZIO[Any & ZIOAppArgs & Scope, Any, Any] = pastaApp } diff --git a/zio/src/main/scala/com/baeldung/scala/zio/prelude/averages/CombinedAverage.scala b/zio/src/main/scala/com/baeldung/scala/zio/prelude/averages/CombinedAverage.scala new file mode 100644 index 000000000..bad859cdb --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/prelude/averages/CombinedAverage.scala @@ -0,0 +1,28 @@ +package com.baeldung.scala.zio.prelude.averages + +import zio.prelude._ + +/** A combined average class that takes into consideration the need of carrying + * the count of elements. + * + * @param sum + * average of elements + * @param count + * number of elements + */ +case class CombinedAverage(sum: Double, count: Int) + +object CombinedAverage { + implicit val AvgAssociative: Associative[CombinedAverage] = { + new Associative[CombinedAverage] { + override def combine( + l: => CombinedAverage, + r: => CombinedAverage + ): CombinedAverage = { + val sum = l.sum * l.count + r.sum * r.count + val count = l.count + r.count + CombinedAverage(sum / count, count) + } + } + } +} diff --git a/zio/src/main/scala/com/baeldung/scala/zio/prelude/mapreduce/MapReduce.scala b/zio/src/main/scala/com/baeldung/scala/zio/prelude/mapreduce/MapReduce.scala new file mode 100644 index 000000000..0232e62be --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/prelude/mapreduce/MapReduce.scala @@ -0,0 +1,31 @@ +package com.baeldung.scala.zio.prelude.mapreduce + +import zio.prelude._ + +object MapReduce { + implicit val intIdentity: Identity[Int] = Identity.make(0, (a, b) => a + b) + + implicit val intAssociative: Associative[Int] = + new Associative[Int] { + def combine(left: => Int, right: => Int): Int = + left + right + } + + def mapReduce[A, B](as: List[A])(f: A => B)(implicit + identity: Identity[B] + ): B = + as.map(f).toNonEmptyList.map(_.reduce1).getOrElse(identity.identity) + + def mapReduce2[F[+_]: ForEach, A, B: Identity](as: F[A])(f: A => B): B = + as.foldMap(f) + + private val wordsPerLine: String => Int = _.split(" ").length + + def wordCount(lines: List[String]): Int = lines.map(wordsPerLine).sum + + def wordCountAbstract(as: List[String]): Int = mapReduce(as)(wordsPerLine) + + def wordCountVeryAbstract(as: List[String]): Int = + mapReduce2(as)(wordsPerLine) + +} diff --git a/zio/src/main/scala/com/baeldung/scala/zio/prelude/typeclass/Demo.scala b/zio/src/main/scala/com/baeldung/scala/zio/prelude/typeclass/Demo.scala new file mode 100644 index 000000000..0e3865b97 --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/prelude/typeclass/Demo.scala @@ -0,0 +1,12 @@ +package com.baeldung.scala.zio.prelude.typeclass + +import com.baeldung.scala.zio.prelude.typeclass.Printable._ + +object Demo extends App { + private def printAny[A](value: A)(implicit printable: Printable[A]): Unit = { + println(printable.format(value)) + } + + printAny("Hello") // Prints "Hello" + printAny(10) // Prints "10" +} diff --git a/zio/src/main/scala/com/baeldung/scala/zio/prelude/typeclass/Printable.scala b/zio/src/main/scala/com/baeldung/scala/zio/prelude/typeclass/Printable.scala new file mode 100644 index 000000000..bb0494a8b --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/prelude/typeclass/Printable.scala @@ -0,0 +1,34 @@ +package com.baeldung.scala.zio.prelude.typeclass + +/** Type class that instructs implementing types to be able to be formatted as + * strings. + * + * @tparam A + * type that needs to implement the trait + */ +trait Printable[A] { + + /** Any type A that implements this trait must be able to present itself as a + * string. + * + * @param value + * instance of type [[A]] that implements the type class + * @return + * string representation of the parameter `value` + */ + def format(value: A): String +} + +/** Provides implementations of the [[Printable]] type class for types + * [[String]] and [[Int]]. + */ +object Printable { + + /** Implementation of the [[Printable]] type class for [[String]]. + */ + implicit val stringPrintable: Printable[String] = (value: String) => value + + /** Implementation of the [[Printable]] type class for [[Int]]. + */ + implicit val intPrintable: Printable[Int] = (value: Int) => value.toString +} diff --git a/zio/src/main/scala/com/baeldung/scala/zio/prelude/voting/Voting.scala b/zio/src/main/scala/com/baeldung/scala/zio/prelude/voting/Voting.scala new file mode 100644 index 000000000..a518cfbbe --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/prelude/voting/Voting.scala @@ -0,0 +1,28 @@ +package com.baeldung.scala.zio.prelude.voting + +import zio.prelude._ + +object Voting { + + object Votes extends Subtype[Int] { + implicit val associativeVotes: Associative[Votes] = + new Associative[Votes] { + override def combine(l: => Votes, r: => Votes): Votes = + Votes(l + r) + } + } + + private type Votes = Votes.Type + + object Topic extends Subtype[String] + + private type Topic = Topic.Type + + final case class VoteState(map: Map[Topic, Votes]) { + self => + def combine(that: VoteState): VoteState = + VoteState(self.map combine that.map) + + } + +} diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/repeatretry/RepeatSamples.scala b/zio/src/main/scala/com/baeldung/scala/zio/repeatretry/RepeatSamples.scala similarity index 91% rename from zio/src/main/scala-2/com/baeldung/scala/zio/repeatretry/RepeatSamples.scala rename to zio/src/main/scala/com/baeldung/scala/zio/repeatretry/RepeatSamples.scala index d3e812334..78abe0ee5 100644 --- a/zio/src/main/scala-2/com/baeldung/scala/zio/repeatretry/RepeatSamples.scala +++ b/zio/src/main/scala/com/baeldung/scala/zio/repeatretry/RepeatSamples.scala @@ -5,7 +5,7 @@ import zio.{Schedule, Scope, Task, ZIO, ZIOAppArgs, ZIOAppDefault} import scala.util.Random object RepeatSamples extends ZIOAppDefault { - override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = { + override def run: ZIO[Any & ZIOAppArgs & Scope, Any, Any] = { val simpleZio: ZIO[Any, Nothing, Unit] = ZIO.succeed(println("Hello ZIO!")) val aFailingZio = ZIO.fail(new Exception("failed!")) @@ -40,7 +40,7 @@ object RepeatSamples extends ZIOAppDefault { } object FailingRepeatSamples extends ZIOAppDefault { - override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = { + override def run: ZIO[Any & ZIOAppArgs & Scope, Any, Any] = { val aFailingZIO = ZIO.attempt { println("A failing action here") throw new Exception("Failure block") diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/repeatretry/RetrySamples.scala b/zio/src/main/scala/com/baeldung/scala/zio/repeatretry/RetrySamples.scala similarity index 94% rename from zio/src/main/scala-2/com/baeldung/scala/zio/repeatretry/RetrySamples.scala rename to zio/src/main/scala/com/baeldung/scala/zio/repeatretry/RetrySamples.scala index 533e54ec0..d118d7713 100644 --- a/zio/src/main/scala-2/com/baeldung/scala/zio/repeatretry/RetrySamples.scala +++ b/zio/src/main/scala/com/baeldung/scala/zio/repeatretry/RetrySamples.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.zio.repeatretry import zio.{Random, Schedule, Scope, ZIO, ZIOAppArgs, ZIOAppDefault} object RetrySamples extends ZIOAppDefault { - override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = { + override def run: ZIO[Any & ZIOAppArgs & Scope, Any, Any] = { val mayBeFailingZIO = for { num <- Random.nextIntBounded(100) _ <- zio.Console.printLine("Calculating with number: " + num) diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/resources/ResourceHandling.scala b/zio/src/main/scala/com/baeldung/scala/zio/resources/ResourceHandling.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/resources/ResourceHandling.scala rename to zio/src/main/scala/com/baeldung/scala/zio/resources/ResourceHandling.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/streams/FailureRecoveryExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/streams/FailureRecoveryExample.scala similarity index 86% rename from zio/src/main/scala-2/com/baeldung/scala/zio/streams/FailureRecoveryExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/streams/FailureRecoveryExample.scala index 7a01dbc2d..d511d76cf 100644 --- a/zio/src/main/scala-2/com/baeldung/scala/zio/streams/FailureRecoveryExample.scala +++ b/zio/src/main/scala/com/baeldung/scala/zio/streams/FailureRecoveryExample.scala @@ -9,7 +9,7 @@ object FailureRecoveryExample extends ZIOAppDefault { ZStream.range(6, 10) val recoveryStream: ZStream[Any, Throwable, Int] = ZStream.range(10, 15) - override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = { + override def run: ZIO[Any & ZIOAppArgs & Scope, Any, Any] = { ZStream.range(0, 5).runSum } diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/streams/FailureRetryExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/streams/FailureRetryExample.scala similarity index 89% rename from zio/src/main/scala-2/com/baeldung/scala/zio/streams/FailureRetryExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/streams/FailureRetryExample.scala index 51ee5fe2b..bab986480 100644 --- a/zio/src/main/scala-2/com/baeldung/scala/zio/streams/FailureRetryExample.scala +++ b/zio/src/main/scala/com/baeldung/scala/zio/streams/FailureRetryExample.scala @@ -15,6 +15,6 @@ object FailureRetryExample extends ZIOAppDefault { val failingStream: ZStream[Any, Throwable, Int] = ZStream.range(0, 5).flatMap(_ => ZStream.fromZIO(Generator.getNext())) - override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = + override def run: ZIO[Any & ZIOAppArgs & Scope, Any, Any] = failingStream.retry(Schedule.once).runSum } diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/streams/FileProcessingExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/streams/FileProcessingExample.scala similarity index 82% rename from zio/src/main/scala-2/com/baeldung/scala/zio/streams/FileProcessingExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/streams/FileProcessingExample.scala index 70ed27c90..ed13fa703 100644 --- a/zio/src/main/scala-2/com/baeldung/scala/zio/streams/FileProcessingExample.scala +++ b/zio/src/main/scala/com/baeldung/scala/zio/streams/FileProcessingExample.scala @@ -14,9 +14,9 @@ object FileProcessingExample extends ZIOAppDefault { val fileSink = ZSink.fromFile(new File("outputFile.txt")) val fileOutputStream = fileInputStream .intersperse("\n") - .flatMap(line => ZStream(line.getBytes.toList: _*)) + .flatMap(line => ZStream(line.getBytes.toList*)) .run(fileSink) - override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = + override def run: ZIO[Any & ZIOAppArgs & Scope, Any, Any] = fileOutputStream } diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/streams/ProcessAllSinkExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/streams/ProcessAllSinkExample.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/streams/ProcessAllSinkExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/streams/ProcessAllSinkExample.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/streams/ProcessSomeSinkExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/streams/ProcessSomeSinkExample.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/streams/ProcessSomeSinkExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/streams/ProcessSomeSinkExample.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/streams/SimpleStreamExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/streams/SimpleStreamExample.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/streams/SimpleStreamExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/streams/SimpleStreamExample.scala diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/streams/ZPipelineExample.scala b/zio/src/main/scala/com/baeldung/scala/zio/streams/ZPipelineExample.scala similarity index 90% rename from zio/src/main/scala-2/com/baeldung/scala/zio/streams/ZPipelineExample.scala rename to zio/src/main/scala/com/baeldung/scala/zio/streams/ZPipelineExample.scala index 4cf36cdb8..c68f7d80d 100644 --- a/zio/src/main/scala-2/com/baeldung/scala/zio/streams/ZPipelineExample.scala +++ b/zio/src/main/scala/com/baeldung/scala/zio/streams/ZPipelineExample.scala @@ -16,5 +16,5 @@ object ZPipelineExample extends ZIOAppDefault { val totalSum: ZIO[Any, Nothing, Int] = firstStreamMapped.concat(secondStreamMapped).runSum - override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = totalSum + override def run: ZIO[Any & ZIOAppArgs & Scope, Any, Any] = totalSum } diff --git a/zio/src/main/scala-2/com/baeldung/scala/zio/testingapplications/TestingApplicationsExamples.scala b/zio/src/main/scala/com/baeldung/scala/zio/testingapplications/TestingApplicationsExamples.scala similarity index 100% rename from zio/src/main/scala-2/com/baeldung/scala/zio/testingapplications/TestingApplicationsExamples.scala rename to zio/src/main/scala/com/baeldung/scala/zio/testingapplications/TestingApplicationsExamples.scala diff --git a/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/CustomMessage.scala b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/CustomMessage.scala new file mode 100644 index 000000000..e2123e9a8 --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/CustomMessage.scala @@ -0,0 +1,13 @@ +package com.baeldung.scala.zio.zio_kafka + +import zio.json._ + +case class CustomMessage(id: Long, message: String, sender: String) + +object CustomMessage { + implicit val encoder: JsonEncoder[CustomMessage] = + DeriveJsonEncoder.gen[CustomMessage] + + implicit val decoder: JsonDecoder[CustomMessage] = + DeriveJsonDecoder.gen[CustomMessage] +} diff --git a/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/CustomMessageSerde.scala b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/CustomMessageSerde.scala new file mode 100644 index 000000000..82afdc327 --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/CustomMessageSerde.scala @@ -0,0 +1,17 @@ +package com.baeldung.scala.zio.zio_kafka + +import zio._ +import zio.json._ +import zio.kafka.serde._ + +object CustomMessageSerde { + val key: Serde[Any, String] = + Serde.string + + val value: Serde[Any, CustomMessage] = + Serde.string.inmapM[Any, CustomMessage](s => + ZIO + .fromEither(s.fromJson[CustomMessage]) + .mapError(e => new RuntimeException(e)) + )(r => ZIO.succeed(r.toJson)) +} diff --git a/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/stream/KafkaStreamConsumer.scala b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/stream/KafkaStreamConsumer.scala new file mode 100644 index 000000000..26388bb8b --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/stream/KafkaStreamConsumer.scala @@ -0,0 +1,19 @@ +package com.baeldung.scala.zio.zio_kafka.stream + +import zio.ZLayer +import zio.kafka.consumer.{Consumer, ConsumerSettings} + +object KafkaStreamConsumer { + + def consumerLayer( + bootstrapServers: List[String], + groupId: String + ): ZLayer[Any, Throwable, Consumer] = + ZLayer.scoped( + Consumer.make( + ConsumerSettings(bootstrapServers) + .withGroupId(groupId) + ) + ) + +} diff --git a/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/stream/KafkaStreamProducer.scala b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/stream/KafkaStreamProducer.scala new file mode 100644 index 000000000..c666a59ce --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/stream/KafkaStreamProducer.scala @@ -0,0 +1,16 @@ +package com.baeldung.scala.zio.zio_kafka.stream + +import zio.ZLayer +import zio.kafka.producer.{Producer, ProducerSettings} + +object KafkaStreamProducer { + + def producerLayer( + bootstrapServers: List[String] + ): ZLayer[Any, Throwable, Producer] = + ZLayer.scoped( + Producer.make( + ProducerSettings(bootstrapServers) + ) + ) +} diff --git a/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/stream/KafkaStreamWorkflowApp.scala b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/stream/KafkaStreamWorkflowApp.scala new file mode 100644 index 000000000..ab5b0e40b --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/stream/KafkaStreamWorkflowApp.scala @@ -0,0 +1,63 @@ +package com.baeldung.scala.zio.zio_kafka.stream + +import com.baeldung.scala.zio.zio_kafka.{CustomMessage, CustomMessageSerde} +import org.apache.kafka.clients.producer.ProducerRecord +import zio._ +import zio.kafka.consumer._ +import zio.ZIOAppDefault +import zio.kafka.producer.Producer +import zio.stream.ZStream + +object KafkaStreamWorkflowApp extends ZIOAppDefault { + + private val BOOTSTRAP_SERVERS = List("localhost:9092") + private val KAFKA_TOPIC = "baeldung" + + val producer: ZLayer[Any, Throwable, Producer] = + KafkaStreamProducer.producerLayer(BOOTSTRAP_SERVERS) + val consumer: ZLayer[Any, Throwable, Consumer] = + KafkaStreamConsumer.consumerLayer( + BOOTSTRAP_SERVERS, + "baeldung-consumer-group" + ) + + override def run = { + val prod: ZStream[Producer, Throwable, Nothing] = + ZStream + .repeat("key") + .schedule(Schedule.spaced(1.second)) + .map(key => + new ProducerRecord( + KAFKA_TOPIC, + key, + CustomMessage(1, "Hello", "Baeldung") + ) + ) + .via( + Producer.produceAll(CustomMessageSerde.key, CustomMessageSerde.value) + ) + .drain + + val cons: ZStream[Consumer, Throwable, Nothing] = + Consumer + .plainStream( + Subscription.topics(KAFKA_TOPIC), + CustomMessageSerde.key, + CustomMessageSerde.value + ) + .tap(record => + Console + .printLine( + s"Consumed message with key: ${record.key} and value: ${record.value}" + ) + .orDie + ) + .map(_.offset) + .aggregateAsync(Consumer.offsetBatches) + .mapZIO(_.commit) + .drain + + (prod merge cons).runDrain.provide(producer, consumer) + } + +} diff --git a/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/workflow/KafkaConsumer.scala b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/workflow/KafkaConsumer.scala new file mode 100644 index 000000000..d3aa68e25 --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/workflow/KafkaConsumer.scala @@ -0,0 +1,26 @@ +package com.baeldung.scala.zio.zio_kafka.workflow + +import com.baeldung.scala.zio.zio_kafka.CustomMessageSerde +import zio._ +import zio.kafka.consumer.{Consumer, ConsumerSettings, Subscription} + +object KafkaConsumer { + def consume( + bootstrapServers: List[String], + groupId: String, + topic: String + ): RIO[Any, Unit] = + Consumer.consumeWith( + settings = ConsumerSettings(bootstrapServers) + .withGroupId(groupId), + subscription = Subscription.topics(topic), + keyDeserializer = CustomMessageSerde.key, + valueDeserializer = CustomMessageSerde.value + )(record => + Console + .printLine( + s"Consumed message with key: ${record.key()} and value: ${record.value()}" + ) + .orDie + ) +} diff --git a/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/workflow/KafkaProducer.scala b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/workflow/KafkaProducer.scala new file mode 100644 index 000000000..0527b92dc --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/workflow/KafkaProducer.scala @@ -0,0 +1,30 @@ +package com.baeldung.scala.zio.zio_kafka.workflow + +import com.baeldung.scala.zio.zio_kafka.{CustomMessage, CustomMessageSerde} +import org.apache.kafka.clients.producer.RecordMetadata +import zio.{RIO, ZLayer} +import zio.kafka.producer.{Producer, ProducerSettings} + +object KafkaProducer { + def produce( + topic: String, + key: String, + value: CustomMessage + ): RIO[Any & Producer, RecordMetadata] = + Producer.produce[Any, String, CustomMessage]( + topic = topic, + key = key, + value = value, + keySerializer = CustomMessageSerde.key, + valueSerializer = CustomMessageSerde.value + ) + + def producerLayer( + bootstrapServers: List[String] + ): ZLayer[Any, Throwable, Producer] = + ZLayer.scoped( + Producer.make( + ProducerSettings().withBootstrapServers(bootstrapServers) + ) + ) +} diff --git a/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/workflow/KafkaWorkflowApp.scala b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/workflow/KafkaWorkflowApp.scala new file mode 100644 index 000000000..c7babbfda --- /dev/null +++ b/zio/src/main/scala/com/baeldung/scala/zio/zio_kafka/workflow/KafkaWorkflowApp.scala @@ -0,0 +1,23 @@ +package com.baeldung.scala.zio.zio_kafka.workflow + +import com.baeldung.scala.zio.zio_kafka.CustomMessage +import zio.ZIOAppDefault + +object KafkaWorkflowApp extends ZIOAppDefault { + + private val BOOTSTRAP_SERVERS = List("localhost:9092") + private val KAFKA_TOPIC = "baeldung" + + override def run = + for { + consumer <- KafkaConsumer + .consume(BOOTSTRAP_SERVERS, "baeldung-consumer-group", KAFKA_TOPIC) + .fork + _ <- KafkaProducer + .produce(KAFKA_TOPIC, "key", CustomMessage(1, "Hello", "Baeldung")) + .provide(KafkaProducer.producerLayer(BOOTSTRAP_SERVERS)) + .repeatN(10) + _ <- consumer.join + } yield () + +} diff --git a/zio/src/test/scala-2/com/baeldung/scala/zio/createeffects/CreateZIOEffectsSpec.scala b/zio/src/test/scala/com/baeldung/scala/zio/createeffects/CreateZIOEffectsUnitTest.scala similarity index 98% rename from zio/src/test/scala-2/com/baeldung/scala/zio/createeffects/CreateZIOEffectsSpec.scala rename to zio/src/test/scala/com/baeldung/scala/zio/createeffects/CreateZIOEffectsUnitTest.scala index 7dbc414ab..9a6f577b6 100644 --- a/zio/src/test/scala-2/com/baeldung/scala/zio/createeffects/CreateZIOEffectsSpec.scala +++ b/zio/src/test/scala/com/baeldung/scala/zio/createeffects/CreateZIOEffectsUnitTest.scala @@ -7,7 +7,7 @@ import com.baeldung.scala.zio.createeffects.CreateZIOEffects._ import scala.concurrent.Future import scala.util.Try -object CreateZIOEffectsSpec extends ZIOSpecDefault { +object CreateZIOEffectsUnitTest extends ZIOSpecDefault { override def spec = suite("CreateZIOEffects")( test("fromValue") { for { diff --git a/zio/src/test/scala-2/com/baeldung/scala/zio/errorhandling/ErrorHandlingSpec.scala b/zio/src/test/scala/com/baeldung/scala/zio/errorhandling/ErrorHandlingUnitTest.scala similarity index 91% rename from zio/src/test/scala-2/com/baeldung/scala/zio/errorhandling/ErrorHandlingSpec.scala rename to zio/src/test/scala/com/baeldung/scala/zio/errorhandling/ErrorHandlingUnitTest.scala index c19cd66c7..54af05577 100644 --- a/zio/src/test/scala-2/com/baeldung/scala/zio/errorhandling/ErrorHandlingSpec.scala +++ b/zio/src/test/scala/com/baeldung/scala/zio/errorhandling/ErrorHandlingUnitTest.scala @@ -4,10 +4,10 @@ import zio.{Scope, ZIO} import zio.test.{Spec, TestEnvironment, ZIOSpecDefault} import zio.test._ -object ErrorHandlingSpec extends ZIOSpecDefault { +object ErrorHandlingUnitTest extends ZIOSpecDefault { val successResult = "success" - override def spec: Spec[TestEnvironment with Scope, Any] = - suite("ErrorHandlingSpec")( + override def spec: Spec[TestEnvironment & Scope, Any] = + suite("ErrorHandlingUnitTest")( test("usingEither returns Either of String") { for { result <- ErrorHandling.usingEither diff --git a/zio/src/test/scala/com/baeldung/scala/zio/prelude/averages/CombinedAverageUnitTest.scala b/zio/src/test/scala/com/baeldung/scala/zio/prelude/averages/CombinedAverageUnitTest.scala new file mode 100644 index 000000000..296765c47 --- /dev/null +++ b/zio/src/test/scala/com/baeldung/scala/zio/prelude/averages/CombinedAverageUnitTest.scala @@ -0,0 +1,33 @@ +package com.baeldung.scala.zio.prelude.averages + +import com.baeldung.scala.zio.prelude.averages.CombinedAverage.* +import org.scalatest.wordspec.AnyWordSpec +import zio.prelude.AssociativeOps + +class CombinedAverageUnitTest extends AnyWordSpec { + val avg1: Double = (2.0 + 3.0 + 4.0) / 3 // avg1 is 3.0 + val avg2: Double = (10.0 + 11.0) / 2 // avg2 is 10.5 + + "a simple average" should { + "sum its elements and divide them by their count" in { + assertResult(3.0)(avg1) + assertResult(10.5)(avg2) + } + "not be associative" in { + val avgAvg = (avg1 + avg2) / 2 + + // if "average" were associative, the average of the two averages + // would be 6, but it's 6.75 instead! + assert(avgAvg != 6.0) + } + } + + "a smarter average" should { + "properly combine partial averages" in { + val avg1 = CombinedAverage(3.0, 3) + val avg2 = CombinedAverage(10.5, 2) + val avgAvg = avg1 <> avg2 + assertResult(CombinedAverage(6.0, 5))(avgAvg) + } + } +} diff --git a/zio/src/test/scala/com/baeldung/scala/zio/prelude/mapreduce/MapReduceUnitTest.scala b/zio/src/test/scala/com/baeldung/scala/zio/prelude/mapreduce/MapReduceUnitTest.scala new file mode 100644 index 000000000..407233f1e --- /dev/null +++ b/zio/src/test/scala/com/baeldung/scala/zio/prelude/mapreduce/MapReduceUnitTest.scala @@ -0,0 +1,29 @@ +package com.baeldung.scala.zio.prelude.mapreduce + +import org.scalatest.wordspec.AnyWordSpec + +class MapReduceUnitTest extends AnyWordSpec { + + import MapReduce._ + + val book: List[String] = + "This eBook is for the use of anyone anywhere at no cost and with" :: + "almost no restrictions whatsoever. You may copy it, give it away or" :: + "re-use it under the terms of the Project Gutenberg License included" :: + "with this eBook or online at www.gutenberg.org" :: Nil + + "MapReduce" should { + "count correctly words without abstractions" in { + val n = wordCount(book) + assertResult(45)(n) + } + "count correctly words abstracting the map function" in { + val n = wordCountAbstract(book) + assertResult(45)(n) + } + "count correctly words abstracting also the data structure" in { + val n = wordCountVeryAbstract(book) + assertResult(45)(n) + } + } +} diff --git a/zio/src/test/scala/com/baeldung/scala/zio/prelude/voting/VotingUnitTest.scala b/zio/src/test/scala/com/baeldung/scala/zio/prelude/voting/VotingUnitTest.scala new file mode 100644 index 000000000..064008bca --- /dev/null +++ b/zio/src/test/scala/com/baeldung/scala/zio/prelude/voting/VotingUnitTest.scala @@ -0,0 +1,26 @@ +package com.baeldung.scala.zio.prelude.voting + +import org.scalatest.wordspec.AnyWordSpec + +class VotingUnitTest extends AnyWordSpec { + + import Voting._ + + "a voting object" should { + "correctly combine vote states" in { + val zioHttp = Topic("zio-http") + val uziHttp = Topic("uzi-http") + val zioTlsHttp = Topic("zio-tls-http") + + val leftVotes = VoteState(Map(zioHttp -> Votes(4), uziHttp -> Votes(2))) + val rightVotes = + VoteState(Map(zioHttp -> Votes(2), zioTlsHttp -> Votes(2))) + + val totalVotes = leftVotes `combine` rightVotes + + assertResult(Votes(6))(totalVotes.map(zioHttp)) + assertResult(Votes(2))(totalVotes.map(uziHttp)) + assertResult(Votes(2))(totalVotes.map(zioTlsHttp)) + } + } +} diff --git a/zio/src/test/scala-2/com/baeldung/scala/zio/repeatretry/RepeatRetryTest.scala b/zio/src/test/scala/com/baeldung/scala/zio/repeatretry/RepeatRetryUnitTest.scala similarity index 94% rename from zio/src/test/scala-2/com/baeldung/scala/zio/repeatretry/RepeatRetryTest.scala rename to zio/src/test/scala/com/baeldung/scala/zio/repeatretry/RepeatRetryUnitTest.scala index 17e096c67..08c3fde1e 100644 --- a/zio/src/test/scala-2/com/baeldung/scala/zio/repeatretry/RepeatRetryTest.scala +++ b/zio/src/test/scala/com/baeldung/scala/zio/repeatretry/RepeatRetryUnitTest.scala @@ -3,7 +3,7 @@ package com.baeldung.scala.zio.repeatretry import zio._ import zio.test._ -object RepeatRetryTest extends ZIOSpecDefault { +object RepeatRetryUnitTest extends ZIOSpecDefault { override def spec = suite("CreateZIOEffects")( test("execute retry and end with default handler") { val failingZIO = ZIO.fail(new Exception("Fail!")) diff --git a/zio/src/test/scala-2/com/bealdung/scala/zio/testingapplications/TestingApplicationsExamplesSpec.scala b/zio/src/test/scala/com/bealdung/scala/zio/testingapplications/TestingApplicationsExamplesUnitTest.scala similarity index 92% rename from zio/src/test/scala-2/com/bealdung/scala/zio/testingapplications/TestingApplicationsExamplesSpec.scala rename to zio/src/test/scala/com/bealdung/scala/zio/testingapplications/TestingApplicationsExamplesUnitTest.scala index cd6ac6a8b..0085f04ab 100644 --- a/zio/src/test/scala-2/com/bealdung/scala/zio/testingapplications/TestingApplicationsExamplesSpec.scala +++ b/zio/src/test/scala/com/bealdung/scala/zio/testingapplications/TestingApplicationsExamplesUnitTest.scala @@ -5,8 +5,8 @@ import zio.test._ import zio.test.Assertion._ import com.baeldung.scala.zio.testingapplications.TestingApplicationsExamples._ -object TestingApplicationsExamplesSpec extends ZIOSpecDefault { - override def spec = suite("TestingApplicationsExamplesSpec")( +object TestingApplicationsExamplesUnitTest extends ZIOSpecDefault { + override def spec = suite("TestingApplicationsExamplesUnitTest")( test("returnString correctly returns string") { val testString = "Hello World!" for { diff --git a/zio3/README.md b/zio3/README.md new file mode 100644 index 000000000..4a46e2f83 --- /dev/null +++ b/zio3/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [Introduction to ZIO-HTTP](https://www.baeldung.com/scala/zio-http) diff --git a/zio3/project/build.properties b/zio3/project/build.properties new file mode 100644 index 000000000..e8a1e246e --- /dev/null +++ b/zio3/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.9.7 diff --git a/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/HelloApp.scala b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/HelloApp.scala new file mode 100644 index 000000000..cf015927c --- /dev/null +++ b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/HelloApp.scala @@ -0,0 +1,19 @@ +package com.baeldung.scala.zio.httpapp + +import com.baeldung.scala.zio.httpapp.MainApp.Environment +import zio.* +import zio.http.* + +object HelloApp extends ZIOAppDefault: + private object HelloHttpApp: + def apply(): Http[Any, Nothing, Request, Response] = + Http.collect[Request] { case Method.GET -> Root / "hello" => + Response.text(s"Hello World!") + } + + def run: ZIO[Environment with ZIOAppArgs with Scope, Throwable, Any] = + Server + .serve(HelloHttpApp()) + .provide( + Server.defaultWithPort(8080) + ) diff --git a/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/MainApp.scala b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/MainApp.scala new file mode 100644 index 000000000..d0bc14c8d --- /dev/null +++ b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/MainApp.scala @@ -0,0 +1,35 @@ +package com.baeldung.scala.zio.httpapp + +import com.baeldung.scala.zio.httpapp.app.{ + IngredientWebsocketApp, + RecipeHttpApp, + SpeedHttpApp +} +import com.baeldung.scala.zio.httpapp.service.RecipeService +import com.baeldung.scala.zio.httpapp.repo.InMemoryRecipeRepo +import com.baeldung.scala.zio.httpapp.repo.RecipeRepo +import zio.http.{RequestHandlerMiddlewares, Server} +import zio.{Ref, Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer} + +object MainApp extends ZIOAppDefault: + def run: ZIO[Environment with ZIOAppArgs with Scope, Throwable, Any] = + + val headerMiddleware = + RequestHandlerMiddlewares.addHeader("X-Environment", "Dev") + + val loggingMiddleware = RequestHandlerMiddlewares.requestLogging( + logRequestBody = true, + logResponseBody = true + ) + + val recipeApp = RecipeHttpApp() @@ headerMiddleware @@ loggingMiddleware + + Server + .serve( + (recipeApp ++ IngredientWebsocketApp()).withDefaultErrorResponse + ) + .provide( + Server.defaultWithPort(8080), + InMemoryRecipeRepo.layer, + RecipeService.layer + ) diff --git a/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/ZioEffectApp.scala b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/ZioEffectApp.scala new file mode 100644 index 000000000..89c07be5c --- /dev/null +++ b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/ZioEffectApp.scala @@ -0,0 +1,33 @@ +package com.baeldung.scala.zio.httpapp + +import com.baeldung.scala.zio.httpapp.HelloApp.Environment +import zio.* +import zio.http.* + +object ZioEffectApp extends ZIOAppDefault: + private object CounterHttpApp: + def apply(): Http[Ref[Int], Nothing, Request, Response] = + Http.collectZIO[Request] { + case Method.GET -> Root / "up" => + ZIO.serviceWithZIO[Ref[Int]](cRef => + response(cRef.updateAndGet(_ + 1)) + ) + case Method.GET -> Root / "get" => + ZIO.serviceWithZIO[Ref[Int]](cRef => response(cRef.get)) + case Method.GET -> Root / "reset" => + ZIO.serviceWithZIO[Ref[Int]](cRef => + response(cRef.updateAndGet(_ => 0)) + ) + } + + private def response(counterUio: UIO[Int]) = counterUio + .map(_.toString) + .map(Response.text) + + def run: ZIO[Environment with ZIOAppArgs with Scope, Throwable, Any] = + Server + .serve(CounterHttpApp()) + .provide( + ZLayer.fromZIO(Ref.make(0)), + Server.defaultWithPort(8080) + ) diff --git a/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/app/IngredientWebsocketApp.scala b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/app/IngredientWebsocketApp.scala new file mode 100644 index 000000000..789999a43 --- /dev/null +++ b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/app/IngredientWebsocketApp.scala @@ -0,0 +1,24 @@ +package com.baeldung.scala.zio.httpapp.app + +import zio.* +import zio.http.* + +object IngredientWebsocketApp: + + private val appContext = "ingredients" + + def apply(): Http[Any, Nothing, Request, Response] = + + val socket = Handler.webSocket { channel => + channel.receiveAll { + case ChannelEvent.Read(WebSocketFrame.Text(input)) => + channel.send(ChannelEvent.Read(WebSocketFrame.text(input * 2))) + case _ => + ZIO.unit + } + } + + Http.collectZIO[Request] { + case Method.GET -> Root / IngredientWebsocketApp.appContext => + socket.toResponse + } diff --git a/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/app/Recipe.scala b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/app/Recipe.scala new file mode 100644 index 000000000..6f2b33a5e --- /dev/null +++ b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/app/Recipe.scala @@ -0,0 +1,10 @@ +package com.baeldung.scala.zio.httpapp.app + +import java.util.UUID +import zio.json._ + +case class Recipe(id: Long, name: String, ingredients: List[String]) + +object Recipe: + given JsonEncoder[Recipe] = DeriveJsonEncoder.gen[Recipe] + given JsonDecoder[Recipe] = DeriveJsonDecoder.gen[Recipe] diff --git a/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/app/RecipeHttpApp.scala b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/app/RecipeHttpApp.scala new file mode 100644 index 000000000..09ec14b2b --- /dev/null +++ b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/app/RecipeHttpApp.scala @@ -0,0 +1,94 @@ +package com.baeldung.scala.zio.httpapp.app + +import com.baeldung.scala.zio.httpapp.service.RecipeService +import com.baeldung.scala.zio.httpapp.repo.RecipeRepo +import zio.* +import zio.http.{Root, *} +import zio.json.* + +import scala.util.{Failure, Success, Try} + +object RecipeHttpApp: + + val appContext = "recipes" + + private type RecipeEffect = ZIO[RecipeService, Throwable, Response] + + private def jsonErrorResponse(error: String) = { + ZIO + .debug(s"Failed to parse the input: $error") + .as( + Response.text(error).withStatus(Status.BadRequest) + ) + } + + val postHandler: PartialFunction[Request, RecipeEffect] = { + case req @ (Method.POST -> Root / RecipeHttpApp.appContext) => + (for { + u <- req.body.asString.map(_.fromJson[Recipe]) + response <- u match { + case Left(e) => jsonErrorResponse(e) + case Right(recipe) => + ZIO + .serviceWithZIO[RecipeService](_.save(recipe)) + .map(recipe => Response.json(recipe.toJson)) + } + } yield response).orDie + } + + val getHandler: PartialFunction[Request, RecipeEffect] = { + case Method.GET -> Root / RecipeHttpApp.appContext / id => + (for { + idLong <- ZIO.fromTry(Try(id.toLong)) + response <- ZIO + .serviceWithZIO[RecipeService](_.find(idLong)) + .map({ + case Some(recipe) => Response.json(recipe.toJson) + case None => Response.status(Status.NotFound) + }) + } yield response).orDie + } + + private val putHandler: PartialFunction[Request, RecipeEffect] = { + case req @ Method.PUT -> Root / RecipeHttpApp.appContext => + (for { + u <- req.body.asString.map(_.fromJson[Recipe]) + response <- u match { + case Left(e) => jsonErrorResponse(e) + case Right(recipe) => + ZIO + .serviceWithZIO[RecipeService](_.update(recipe)) + .map({ + case Some(recipe) => Response.json(recipe.toJson) + case None => Response.status(Status.NotFound) + }) + } + } yield response).orDie + } + + private val deleteHandler: PartialFunction[Request, RecipeEffect] = { + case Method.DELETE -> Root / RecipeHttpApp.appContext / id => + (for { + idLong <- ZIO.fromTry(Try(id.toLong)) + response <- ZIO + .serviceWithZIO[RecipeService](_.delete(idLong)) + .map({ + case Some(recipe) => Response.json(recipe.toJson) + case None => Response.status(Status.NotFound) + }) + } yield response).orDie + } + + def apply(): Http[RecipeService, Throwable, Request, Response] = + Http.collectZIO[Request] { + postHandler + } ++ + Http.collectZIO[Request] { + getHandler + } ++ + Http.collectZIO[Request] { + putHandler + } ++ + Http.collectZIO[Request] { + deleteHandler + } diff --git a/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/app/SpeedHttpApp.scala b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/app/SpeedHttpApp.scala new file mode 100644 index 000000000..971cb2c5a --- /dev/null +++ b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/app/SpeedHttpApp.scala @@ -0,0 +1,30 @@ +package com.baeldung.scala.zio.httpapp.app + +import zio.{Ref, ZIO} +import zio.http._ + +object SpeedHttpApp: + + val appContext = "speed" + + def apply(): Http[Ref[Int], Nothing, Request, Response] = + Http.collectZIO[Request] { + case Method.GET -> Root / SpeedHttpApp.appContext / "add" => + ZIO.serviceWithZIO[Ref[Int]] { ref => + ref + .updateAndGet(_ + 1) + .map(_.toString) + .map(Response.text) + } + case Method.GET -> Root / SpeedHttpApp.appContext / "reduce" => + ZIO.serviceWithZIO[Ref[Int]] { ref => + ref + .updateAndGet(_ - 1) + .map(_.toString) + .map(Response.text) + } + case Method.GET -> Root / SpeedHttpApp.appContext / "get" => + ZIO.serviceWithZIO[Ref[Int]](ref => + ref.get.map(_.toString).map(Response.text) + ) + } diff --git a/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/client/HttpClient.scala b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/client/HttpClient.scala new file mode 100644 index 000000000..38d58139e --- /dev/null +++ b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/client/HttpClient.scala @@ -0,0 +1,32 @@ +package com.baeldung.scala.zio.httpapp.client + +import zio._ + +import zio.http.* + +object HttpClient extends ZIOAppDefault: + val url = "http://localhost:8080/recipes" + + private val program = for { + postRes <- Client.request( + url, + Method.POST, + content = Body.fromString("""{ + |"id": 1, + |"name": "burger", + |"ingredients": ["beef", "salt", "pepper"] + |}""".stripMargin) + ) + data <- postRes.body.asString + _ <- Console.printLine(s"posted: $data") + getRes <- Client.request( + s"$url/1", + Method.GET + ) + gotData <- getRes.body.asString + _ <- Console.printLine(s"gotData: $gotData") + + } yield () + + def run: ZIO[Environment with ZIOAppArgs with Scope, Any, Any] = + program.provide(Client.default) diff --git a/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/repo/InMemoryRecipeRepo.scala b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/repo/InMemoryRecipeRepo.scala new file mode 100644 index 000000000..05d96b91b --- /dev/null +++ b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/repo/InMemoryRecipeRepo.scala @@ -0,0 +1,31 @@ +package com.baeldung.scala.zio.httpapp.repo + +import com.baeldung.scala.zio.httpapp.app.Recipe +import zio.{Ref, Task, UIO, ZLayer} + +case class InMemoryRecipeRepo(map: Ref[Map[Long, Recipe]]) extends RecipeRepo: + def save(recipe: Recipe): UIO[Recipe] = + for _ <- map.update(_ + (recipe.id -> recipe)) + yield recipe + + def find(id: Long): UIO[Option[Recipe]] = + map.get.map(_.get(id)) + + def update(recipe: Recipe): Task[Option[Recipe]] = + for + _ <- map.update(_ + (recipe.id -> recipe)) + recipeOpt <- map.get.map(_.get(recipe.id)) + yield recipeOpt + + def delete(id: Long): Task[Option[Recipe]] = + for + recipe <- map.get.map(_.get(id)) + _ <- map.update(_ - id) + yield recipe + +object InMemoryRecipeRepo { + def layer: ZLayer[Any, Nothing, RecipeRepo] = + ZLayer.fromZIO( + Ref.make(Map.empty[Long, Recipe]).map(new InMemoryRecipeRepo(_)) + ) +} diff --git a/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/repo/RecipeRepo.scala b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/repo/RecipeRepo.scala new file mode 100644 index 000000000..6b9672533 --- /dev/null +++ b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/repo/RecipeRepo.scala @@ -0,0 +1,13 @@ +package com.baeldung.scala.zio.httpapp.repo + +import com.baeldung.scala.zio.httpapp.app.Recipe +import zio._ + +trait RecipeRepo: + def save(recipe: Recipe): Task[Recipe] + + def find(id: Long): Task[Option[Recipe]] + + def update(recipe: Recipe): Task[Option[Recipe]] + + def delete(id: Long): Task[Option[Recipe]] diff --git a/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/service/RecipeService.scala b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/service/RecipeService.scala new file mode 100644 index 000000000..992c22925 --- /dev/null +++ b/zio3/src/main/scala-3/com/baeldung/scala/zio/httpapp/service/RecipeService.scala @@ -0,0 +1,34 @@ +package com.baeldung.scala.zio.httpapp.service + +import com.baeldung.scala.zio.httpapp.app.Recipe +import com.baeldung.scala.zio.httpapp.repo.RecipeRepo +import zio.{Ref, Task, ZLayer, ZIO} + +case class RecipeServiceImpl(recipeRepo: RecipeRepo) extends RecipeService: + + def save(recipe: Recipe): Task[Recipe] = recipeRepo.save(recipe) + + def find(id: Long): Task[Option[Recipe]] = recipeRepo.find(id) + + def update(recipe: Recipe): Task[Option[Recipe]] = recipeRepo.update(recipe) + + def delete(id: Long): Task[Option[Recipe]] = recipeRepo.delete(id) + +trait RecipeService: + + def save(recipe: Recipe): Task[Recipe] + + def find(id: Long): Task[Option[Recipe]] + + def update(recipe: Recipe): Task[Option[Recipe]] + + def delete(id: Long): Task[Option[Recipe]] + +object RecipeService { + def layer: ZLayer[RecipeRepo, Nothing, RecipeService] = + ZLayer { + for { + recipeRepo <- ZIO.service[RecipeRepo] + } yield RecipeServiceImpl(recipeRepo) + } +} diff --git a/zio3/src/test/scala-3/com/baeldung/scala/zio/httpapp/app/RecipeHttpAppUnitTest.scala b/zio3/src/test/scala-3/com/baeldung/scala/zio/httpapp/app/RecipeHttpAppUnitTest.scala new file mode 100644 index 000000000..13923024b --- /dev/null +++ b/zio3/src/test/scala-3/com/baeldung/scala/zio/httpapp/app/RecipeHttpAppUnitTest.scala @@ -0,0 +1,82 @@ +package com.baeldung.scala.zio.httpapp.app + +import com.baeldung.scala.zio.httpapp.repo.InMemoryRecipeRepo +import com.baeldung.scala.zio.httpapp.service.RecipeService +import zio.test.* +import zio.http.* +import zio.* +import zio.http.netty.server.NettyDriver + +object RecipeHttpAppSpec extends ZIOSpecDefault: + + def status(response: Response): Status = response.status + + override def spec = suite("RecipeHttpApp")( + test("get should return 404 for non existent recipes") { + for { + client <- ZIO.service[Client] + testRequest <- getRecipe1 + _ <- TestServer.addHandler { + RecipeHttpApp.getHandler + } + response1 <- client.request(testRequest) + } yield assertTrue(status(response1) == Status.NotFound) + }.provideSome[Client with Driver]( + InMemoryRecipeRepo.layer, + RecipeService.layer, + TestServer.layer, + Scope.default + ), + test("post should create recipes") { + for { + client <- ZIO.service[Client] + testRequest1 <- postRecipe1 + _ <- TestServer.addHandler { + RecipeHttpApp.getHandler + } + _ <- TestServer.addHandler { + RecipeHttpApp.postHandler + } + response1 <- client.request(testRequest1) + testRequest2 <- getRecipe1 + response2 <- client.request(testRequest2) + } yield assertTrue( + status(response1) == Status.Ok, + status(response2) == Status.Ok + ) + }.provideSome[Client with Driver]( + InMemoryRecipeRepo.layer, + RecipeService.layer, + TestServer.layer, + Scope.default + ) + ).provide( + ZLayer.succeed(Server.Config.default.onAnyOpenPort), + Client.default, + Driver.default + ) + + private def getRecipe1 = + for { + port <- ZIO.serviceWith[Server](_.port) + } yield Request + .get( + URL(Path.root / "recipes" / "1").withPort(port) + ) + .addHeaders(Headers(Header.Accept(MediaType.text.`plain`))) + + private def postRecipe1 = + for { + port <- ZIO.serviceWith[Server](_.port) + } yield Request + .post( + Body.fromString(""" + |{ + | "id": 1, + | "name": "test-recipe", + | "ingredients": ["ingr1", "ingr2"] + |} + |""".stripMargin), + url = URL(Path.root / "recipes").withPort(port) + ) + .addHeaders(Headers(Header.Accept(MediaType.text.`plain`)))