Last active
May 20, 2018 17:04
-
-
Save wsargent/a2d3b4a35c27404296d45c4e911bf524 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package repository | |
import java.util.UUID | |
import cats._ | |
import cats.implicits._ | |
import scala.util._ | |
/** | |
* Demonstrates exposing capabilities as facets of a repository, so individual elements are exposed. | |
* | |
* The capabilities use tagless final to show how you can use different effects with capabilities. | |
* | |
* For example, the Id effect is an identity, so a failure will cause an exception. | |
* | |
* The Try effect is a disjoint union with Exception, so a failure will return Failure(Exception) as a result. | |
*/ | |
object Main { | |
val ID = UUID.fromString("c31d34e2-5892-4a2d-9fd5-3ce2e0efedf7") | |
import ItemRepository._ | |
def main(args: Array[String]): Unit = { | |
val itemRepository = new ItemRepository() | |
changeWithId(itemRepository) | |
changeWithTry(itemRepository) | |
} | |
def changeWithId(itemRepository: ItemRepository): Unit = { | |
val idAccess = new ItemRepository.IdAccess() | |
val idNameChanger = new NameChanger[Id]( idAccess.finder(itemRepository), idAccess.updater(itemRepository), _.map(identity)) | |
val idResult = idNameChanger.changeName(ID, "new name") | |
println(s"id result = $idResult") | |
} | |
def changeWithTry(itemRepository: ItemRepository): Unit = { | |
val tryAccess = new ItemRepository.TryAccess() | |
val tryNameChanger = new NameChanger[Try](tryAccess.finder(itemRepository), tryAccess.updater(itemRepository), { | |
case Success(Some(result)) => result.map(Some(_)) | |
case Success(None) => Success(None) | |
case Failure(ex) => Failure(ex) | |
}) | |
val tryResult = tryNameChanger.changeName(ID, "new name") | |
println(s"try result = $tryResult") | |
} | |
class NameChanger[G[_]: Functor](finder: Finder[G], updater: Updater[G], transform: G[Option[G[UpdateResult]]] => G[Option[UpdateResult]]) { | |
def changeName(id: UUID, newName: String): G[Option[UpdateResult]] = { | |
val saved: G[Option[G[UpdateResult]]] = finder.find(id).map { maybeItem: Option[Item] => | |
maybeItem.map { item => | |
updater.update(item.copy(name = newName)) | |
} | |
} | |
transform(saved) | |
} | |
} | |
// #repository | |
case class Item(id: UUID, name: String) | |
class ItemRepository { | |
import ItemRepository._ | |
private var items = Seq(Item(ID, "item name")) | |
private def find(id: UUID): Option[Item] = items.find(_.id == id) | |
private def update(u: Item): UpdateResult = UpdateResult(s"item $u updated") | |
private object capabilities { | |
val finder: Finder[Id] = new Finder[Id]() { | |
override def find(id: UUID): Id[Option[Item]] = ItemRepository.this.find(id) | |
} | |
val updater: Updater[Id] = new Updater[Id]() { | |
override def update(item: Item): Id[UpdateResult] = ItemRepository.this.update(item) | |
} | |
} | |
} | |
object ItemRepository { | |
sealed trait Finder[F[_]] { | |
def find(id: UUID): F[Option[Item]] | |
} | |
sealed trait Updater[F[_]] { | |
def update(item: Item): F[UpdateResult] | |
} | |
case class UpdateResult(message: String) | |
class IdAccess { | |
def finder(repo: ItemRepository): Finder[Id] = repo.capabilities.finder | |
def updater(repo: ItemRepository): Updater[Id] = repo.capabilities.updater | |
} | |
class TryAccess { | |
def finder(repo: ItemRepository): Finder[Try] = new Finder[Try] { | |
override def find(id: UUID): Try[Option[Item]] = Try(repo.capabilities.finder.find(id)) | |
} | |
def updater(repo: ItemRepository): Updater[Try] = new Updater[Try] { | |
override def update(item: Item): Try[UpdateResult] = Try(repo.capabilities.updater.update(item)) | |
} | |
} | |
} | |
// #repository | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment