Advertisement
NLinker

Isolating side effects for the store call

Jun 29th, 2018
278
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Scala 2.16 KB | None | 0 0
  1. package nick
  2.  
  3. import java.time.Instant
  4. import java.util.concurrent.atomic.AtomicInteger
  5.  
  6. import scala.util.{Failure, Random, Success, Try}
  7.  
  8. case class Dto(name: String)
  9.  
  10. case class Entity(
  11.   var id: Int,
  12.   var name: String,
  13.   var createdAt: Instant
  14. )
  15.  
  16. // like true Dao sometimes fails
  17. class Dao {
  18.   val random = new Random()
  19.   var currentId = new AtomicInteger(0)
  20.  
  21.   def store(e: Entity): Unit = {
  22.     if (random.nextInt(10) < 1) {
  23.       throw new Exception("Database error")
  24.     } else {
  25.       e.id = currentId.incrementAndGet()
  26.     }
  27.   }
  28. }
  29.  
  30. class MyService {
  31.   val dao = new Dao()
  32.  
  33.   // this method requires to mock dao to be tested
  34.   def createMetadataBefore(dto: Dto): Dto = {
  35.     val e = convertD2E(dto)
  36.     e.createdAt = Instant.now()
  37.     dao.store(e)
  38.     convertE2D(e)
  39.   }
  40.  
  41.   // split the effectful method into 2 pure functions
  42.   // one is before store call
  43.   def createMetadata1(dto: Dto, storeFun: Entity ⇒ Try[Entity]): Try[Entity] = {
  44.     val e = convertD2E(dto)
  45.     e.createdAt = Instant.now()
  46.     storeFun(e)
  47.   }
  48.  
  49.   // one is after store call
  50.   def createMetadata2(e: Entity): Dto = {
  51.     convertE2D(e)
  52.   }
  53.  
  54.   def createMetadataAfter(dto: Dto): Dto = {
  55.     val storeFun: Entity ⇒ Try[Entity] = e ⇒ {
  56.       try {
  57.         val e1 = e.copy() // to not spoil the argument
  58.         dao.store(e1)
  59.         Success(e1)
  60.       } catch {
  61.         case x: Exception ⇒
  62.           Failure(x)
  63.       }
  64.     }
  65.     val result = createMetadata1(dto, storeFun)
  66.     result match {
  67.       case Success(e) ⇒ createMetadata2(e)
  68.       case Failure(ex)throw ex
  69.     }
  70.   }
  71.  
  72.   def convertE2D(e: Entity): Dto = Dto(e.name)
  73.   def convertD2E(d: Dto): Entity = Entity(0, d.name, Instant.MIN)
  74. }
  75.  
  76. object MyServiceTest extends App {
  77.   val service = new MyService
  78.  
  79.   def test(): Unit = {
  80.     val now = Instant.now()
  81.     val storingFun: Entity ⇒ Try[Entity] =
  82.       e ⇒ Success(e.copy(id = 1, createdAt = now))
  83.     val dto = Dto("Hello!")
  84.     val r1 = service.createMetadata1(dto, storingFun)
  85.     assert(r1 == Success(Entity(1, "Hello!", now)))
  86.     val r2 = service.createMetadata2(r1.get)
  87.     assert(r2 == Dto("Hello!"))
  88.   }
  89.  
  90.   test()
  91. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement