owned this note
owned this note
Published
Linked with GitHub
# Lab #1 - Scala Basics
>Themes: sbt, library dependencies
###### tags: `scala` `scala-innopolis` `lab`
## Links
- Intellij IDEA setup: https://docs.scala-lang.org/getting-started/intellij-track/getting-started-with-scala-in-intellij.html, https://www.baeldung.com/scala/intellij-with-sbt
- Scala project structure: https://docs.scala-lang.org/getting-started/intellij-track/building-a-scala-project-with-intellij-and-sbt.html
- Testing: https://docs.scala-lang.org/getting-started/intellij-track/testing-scala-in-intellij-with-scalatest.html
- Sbt basic commands: https://www.scala-sbt.org/1.x/docs/Running.html
### Labs submission
- this lab - zip archive without .idea and target directory in moodle
- from the next lab we will use GitHub Classroom, where you will get assigments in individual repos. REMEMBER: Do not load all code just in master. You should create a new branch, push all code to it and open pull request onto master. Only in this case Github Action job with autotests will run.
- Since we will use github you all should have a github account by the next lab
### Intellij IDEA
- jdk 8/11/17
- env setup
- sbt build settings in IDE
### sbt
- basics commands
- deps
- plugins(scalafmt, dependencyTree)
- tests
### Exercise
#### Step 1 - Setup Intellij
- Install JDK 8/11/16 as you wish, Scala does not take much advantage from jdk 8+ features.
-- [Generic system](https://openjdk.java.net/install/)
-- [Mac OS Brew](https://formulae.brew.sh/formula/openjdk)
- Download and Install IntelliJ IDEA. [Link](https://www.jetbrains.com/idea/download/)
- Open Intellij IDEA Plugins and install Scala plugin, or download it from Web Intellij Marketplace [Link](https://plugins.jetbrains.com/plugin/1347-scala)
#### Step 2 - Setup Project
- Create a new project from File -> New -> Project -> Scala -> sbt: Next button -> Project with name 'Currency Converter', choose you JDK, sbt: 1.3.13, Scala: 2.13.6 with sources.
- After project creation open sbt window from the right sidebar, then its settings and check that options project reload and builds under 'sbt shell used for' are activated. Also disable 'Allow overriding sbt version' option

- Create a package `<your-name>.<your-surname>` in `src/main/scala`
- Take some time to explore project's structure
### Step 3 - Currency converter
You task is to implement simple currency converter that can convert one currency to another.
```scala=
object Currencies {
val SupportedCurrencies = List("RUB", "USD", "EUR")
}
class MoneyAmountShouldBePositiveException extends Exception
class UnsupportedCurrencyException extends Exception
class WrongCurrencyException extends Exception
case class Money private (amount: BigDecimal, currency: String) {
def +(other: Money): Money = ???
def -(other: Money): Money = ???
def isSameCurrency(other: Money): Boolean = ???
}
class CurrencyConverter(ratesDictionary: Map[String, Map[String, BigDecimal]]) {
def exchange(money: Money, toCurrency: String): Money = ???
}
object CurrencyConverter {
import Currencies.SupportedCurrencies
def apply(ratesDictionary: Map[String, Map[String, BigDecimal]]) = {
val fromCurrencies = ratesDictionary.keys
val toCurrencies = ratesDictionary.values
if (fromCurrencies.toSet.subsetOf(SupportedCurrencies) && toCurrencies.forall(_.keys.toSet.subsetOf(SupportedCurrencies)))
new CurrencyConverter(ratesDictionary)
else throw new UnsupportedCurrencyException
}
}
```
- Create new file `CurrencyConverter.scala` in your package
- Copy the provided code snippet
- Implement all functions with `???` instead of bodies. Money cannot have negative amount. Money operations cannot be applied to two money objects with different currencies.
- Create an `object Money`, a so called companion object, and implement method `def apply(amount: BigDecimal, currency: String)` in it. It will be a `Money` constructor, that should check:
-- `amount` should be positive or throw `MoneyAmountShouldBePositiveException`
-- `currency` should be contained in `CurrencyRate.SupportedCurrencies`, otherwise throw `UnsupportedCurrencyException`
- Exchange cannot be called on the same currencies
### Step 4 - Tests
- Now its time to test our converter
- Open build.sbt and add [ScalaTest](https://www.scalatest.org) library by `libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.8" % Test`
- Create the same package `<your-name>.<your-surname>` in `src/test/scala`
- Create `CurrencyConverterSpec.scala` file and fill it with the snippet
```scala=
class CurrencyConverter extends AnyFlatSpec with Matchers {
"exchange" should "convert money for supported currencies" in {
val rates = Map(
"USD" -> Map("RUB" -> BigDecimal(72.5)),
"RUB" -> Map("USD" -> BigDecimal(1/72.5))
)
val converted = CurrencyConverter(rates)
val exchangedRub = converted.exchange(new Money(2, "USD"), "RUB")
val exchangedUsd = converted.exchange(new Money(10, "RUB"), "USD")
exchangedRub.amount shouldEqual 145
exchangedRub.currency shouldEqual "RUB"
exchangedUsd.amount shouldEqual BigDecimal(1/7.25)
exchangedUsd.currency shouldEqual "USD"
}
"converted constructor" should "throw UnsupportedCurrencyException if rates dictionary contains wrong currency" in {
val rates = Map(
"EUR" -> Map("RUB" -> BigDecimal(85))
)
assertThrows[UnsupportedCurrencyException] {
CurrencyConverterO(rates)
}
}
}
```
You can run tests via your IDE – notice the green 'Play' button next to class name and individual tests, as well as via your sbt shell – run test command in the panel sbt shell below. Try to do it both ways.
- Write tests for others possible outcomes of `exchange` method and `Money` methods and constructor. Do not forger about possible exceptions