Skip to content

Commit 939454f

Browse files
authored
Merge pull request #97 from barbasa/master
SCALA-49: Introduction to Optics with Monocole
2 parents 3b27d77 + 0e544e0 commit 939454f

File tree

3 files changed

+156
-0
lines changed

3 files changed

+156
-0
lines changed

build.sbt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,15 @@ lazy val scala_test = (project in file("scala-test"))
4444
libraryDependencies += "org.scalamock" %% "scalamock" % "4.4.0" % Test,
4545
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
4646
)
47+
48+
val monocleVersion = "2.0.4"
49+
lazy val scala_libraries = (project in file("scala-libraries"))
50+
.settings(
51+
name := "scala-libraries",
52+
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % Test,
53+
libraryDependencies ++= Seq(
54+
"com.github.julien-truffaut" %% "monocle-core" % monocleVersion,
55+
"com.github.julien-truffaut" %% "monocle-macro" % monocleVersion,
56+
"com.github.julien-truffaut" %% "monocle-law" % monocleVersion % "test"
57+
)
58+
)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.baeldung.scala.monocle
2+
3+
import monocle.{Iso, Lens, Prism, Optional}
4+
import monocle.macros.GenLens
5+
import monocle.macros.Lenses
6+
7+
object OpticsExamples {
8+
9+
trait Discount
10+
case class NoDiscount() extends Discount
11+
case class PercentageOff(value: Double) extends Discount
12+
case class FixPriceOff(value: Double) extends Discount
13+
14+
case class User(name: String, cart: Cart)
15+
case class Cart(id: String, item: Item, quantity: Int)
16+
17+
case class Item(
18+
sku: String,
19+
price: Double,
20+
leftInStock: Int,
21+
discount: Discount
22+
)
23+
24+
def updateStockWithoutLenses(user: User): User = {
25+
user.copy(
26+
cart = user.cart.copy(
27+
item = user.cart.item.copy(leftInStock = user.cart.item.leftInStock - 1)
28+
)
29+
)
30+
}
31+
32+
def updateStockWithLenses(user: User): User = {
33+
34+
val cart: Lens[User, Cart] = GenLens[User](_.cart)
35+
val item: Lens[Cart, Item] = GenLens[Cart](_.item)
36+
val leftInStock: Lens[Item, Int] = GenLens[Item](_.leftInStock)
37+
38+
(cart composeLens item composeLens leftInStock).modify(_ - 1)(user)
39+
}
40+
41+
def updateDiscountedItemsPrice(cart: Cart, newDiscount: Double): Cart = {
42+
val discountLens: Lens[Item, Discount] = GenLens[Item](_.discount)
43+
val onlyPctDiscount = Prism.partial[Discount, Double] {
44+
case PercentageOff(p) => p
45+
}(PercentageOff)
46+
47+
val newItem =
48+
(discountLens composePrism onlyPctDiscount set newDiscount)(cart.item)
49+
50+
cart.copy(item = newItem)
51+
}
52+
53+
def getDiscountValue(discount: Discount): Option[Double] = {
54+
val maybeDiscountValue = Optional[Discount, Double] {
55+
case pctOff: PercentageOff => Some(pctOff.value)
56+
case fixOff: FixPriceOff => Some(fixOff.value)
57+
case _ => None
58+
} { discountValue => discount =>
59+
discount match {
60+
case pctOff: PercentageOff => pctOff.copy(value = discountValue)
61+
case fixOff: FixPriceOff => fixOff.copy(value = discountValue)
62+
case _ => discount
63+
}
64+
}
65+
66+
maybeDiscountValue.getOption(discount)
67+
68+
}
69+
70+
case class PriceEUR(value: Double)
71+
case class PriceGBP(value: Double)
72+
73+
val tranformCurrency = Iso[PriceEUR, PriceGBP] { eur =>
74+
PriceGBP(eur.value * 0.9)
75+
} { gbp =>
76+
PriceEUR(gbp.value / 0.9)
77+
}
78+
79+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.baeldung.scala.monocle
2+
3+
import org.scalatest._
4+
import org.scalatest.Assertions._
5+
import scala.util.{Success, Failure}
6+
import OpticsExamples._
7+
8+
class OpticsExamplesUnitTest extends FlatSpec with Matchers {
9+
10+
"OpticExamples" should "update stock without Lenses" in {
11+
val currentStock = 10
12+
val item = Item("123sku", 35L, currentStock, NoDiscount())
13+
val cart = Cart("abc123", item, 1)
14+
val user = User("joe", cart)
15+
16+
val newUser = updateStockWithoutLenses(user)
17+
assert(newUser.cart.item.leftInStock == currentStock - 1)
18+
}
19+
20+
it should "update stock with Lenses" in {
21+
val currentStock = 10
22+
val item = Item("123sku", 35L, currentStock, NoDiscount())
23+
val cart = Cart("abc123", item, 1)
24+
val user = User("joe", cart)
25+
26+
val newUser = updateStockWithLenses(user)
27+
assert(newUser.cart.item.leftInStock == currentStock - 1)
28+
}
29+
30+
it should "update discount percentage values" in {
31+
val originalDiscount = 10L
32+
val newDiscount = 5L
33+
val updatedCart = updateDiscountedItemsPrice(
34+
Cart("abc", Item("item123", 23L, 1, PercentageOff(originalDiscount)), 1),
35+
newDiscount
36+
)
37+
assert(updatedCart.item.discount == PercentageOff(newDiscount))
38+
}
39+
40+
it should "not update discount fix price values" in {
41+
val originalDiscount = 10L
42+
val newDiscount = 5L
43+
val updatedCart = updateDiscountedItemsPrice(
44+
Cart("abc", Item("item123", 23L, 1, FixPriceOff(originalDiscount)), 1),
45+
newDiscount
46+
)
47+
assert(updatedCart.item.discount == FixPriceOff(originalDiscount))
48+
}
49+
50+
it should "return the Fix Off discount value" in {
51+
val value = 3L
52+
assert(getDiscountValue(FixPriceOff(value)) == Some(value))
53+
}
54+
55+
it should "return no discount value" in {
56+
assert(getDiscountValue(NoDiscount()) == None)
57+
}
58+
59+
it should "transform GBP to EUR correctly" in {
60+
val x =
61+
tranformCurrency.modify(gbp => gbp.copy(gbp.value + 90L))(PriceEUR(1000L))
62+
assert(x.value == 1100L)
63+
}
64+
65+
}

0 commit comments

Comments
 (0)