1
votes

We use the cake pattern to abstract Components (DB, Mock) with self-type annotation, that are Injected at the top level call.

In one case class we want to be able to enrich it's behavior by extending common trait. But how to do it if we want this case class to call an abstract component? Refactoring the case class as abstract will remove all the implementation of apply, unapply, copy ... Things that we need to do the mapping between database and model (for example using Slick).

Here is an exemple representing Plants into some Blocks as Resources owned by an Account:

trait Resource {
  def getAccount: Future[Account]
}

case class Account(id: Int)

case class Block(id: Int, name:String, accountId: Int) extends Resource

case class Plant(id: Int, name: String, blockId: Int) extends Resource {
  this: PlantDBComponent => 

 override def getAccount: Future[Account] = plantDB.getAccount(this)

}

trait PlantDBComponent {
  def plantDB: PlantDB

  trait PlantDB {
    def getAccount(plant: Plant): Future[Account]
  }
}

trait SlickPlantDBComponent {
  def blockDB = SlickPlantDB()

  trait SlickPlantDB extends PlantDB {
    def getAccount(block: Block): Future[Account] = {
      val q = for {
        block <- BlockTable if block.id === plant.blockId
        account <- AccountTable if account.id === block.accountId
      } yield account

      db.run(q.result.head)
    }
  }
}

Any idea on how to do it without reimplementing all the case class boilerplate? Adding getAccount in a Resource companion object with case class pattern matching will not solve the problem

1
Show me in code what you want to achievepamu
Why do you want to make Account a Resource ?pamu
Adding a more concrete example of code. Resource is a generic trait intended to be used for permission access. Users (not showed here) belong to an Account and we want to check for all kind of resources (Block, Plant, ...) if a user belong to the same account than the Resource with the getAccount methodmlardeur

1 Answers

0
votes

We choose to implement a ResourceService that, according to resource Type, return the Account the resource belong to.

Here is the trait component:

trait ResourceServiceComponent {

  val resourceService: ResourceService

  trait ResourceService {
    def getAccount(resource: Resource): Future[Account] = {
      resource match {
        case plant: Plant => blockDB.getAccount(plant.blockId)
        case ...
      }
    }
  }
}