NLinker icon

Isolating side effects for the store call

NLinker | PRO | 06/29/18 03:19:12 PM UTC | 0 ⭐ | 6894 👁️ | Never ⏰ | []
Scala |

2.16 KB

|

None

|

0 👍

/

0 👎

package nick
 
import java.time.Instant
import java.util.concurrent.atomic.AtomicInteger
 
import scala.util.{Failure, Random, Success, Try}
 
case class Dto(name: String)
 
case class Entity(
  var id: Int,
  var name: String,
  var createdAt: Instant
)
 
// like true Dao sometimes fails
class Dao {
  val random = new Random()
  var currentId = new AtomicInteger(0)
 
  def store(e: Entity): Unit = {
    if (random.nextInt(10) < 1) {
      throw new Exception("Database error")
    } else {
      e.id = currentId.incrementAndGet()
    }
  }
}
 
class MyService {
  val dao = new Dao()
 
  // this method requires to mock dao to be tested
  def createMetadataBefore(dto: Dto): Dto = {
    val e = convertD2E(dto)
    e.createdAt = Instant.now()
    dao.store(e)
    convertE2D(e)
  }
 
  // split the effectful method into 2 pure functions
  // one is before store call
  def createMetadata1(dto: Dto, storeFun: Entity ⇒ Try[Entity]): Try[Entity] = {
    val e = convertD2E(dto)
    e.createdAt = Instant.now()
    storeFun(e)
  }
 
  // one is after store call
  def createMetadata2(e: Entity): Dto = {
    convertE2D(e)
  }
 
  def createMetadataAfter(dto: Dto): Dto = {
    val storeFun: Entity ⇒ Try[Entity] = e ⇒ {
      try {
        val e1 = e.copy() // to not spoil the argument
        dao.store(e1)
        Success(e1)
      } catch {
        case x: Exception ⇒
          Failure(x)
      }
    }
    val result = createMetadata1(dto, storeFun)
    result match {
      case Success(e) ⇒ createMetadata2(e)
      case Failure(ex) ⇒ throw ex
    }
  }
 
  def convertE2D(e: Entity): Dto = Dto(e.name)
  def convertD2E(d: Dto): Entity = Entity(0, d.name, Instant.MIN)
}
 
object MyServiceTest extends App {
  val service = new MyService
 
  def test(): Unit = {
    val now = Instant.now()
    val storingFun: Entity ⇒ Try[Entity] =
      e ⇒ Success(e.copy(id = 1, createdAt = now))
    val dto = Dto("Hello!")
    val r1 = service.createMetadata1(dto, storingFun)
    assert(r1 == Success(Entity(1, "Hello!", now)))
    val r2 = service.createMetadata2(r1.get)
    assert(r2 == Dto("Hello!"))
  }
 
  test()
}

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎