Last active
August 29, 2015 13:56
-
-
Save jedws/9086375 to your computer and use it in GitHub Desktop.
An Actor equivalent of SafeApp
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 stuff | |
import akka.actor.{ Actor, actorRef2Scala } | |
import kadai.log.Logging | |
import spray.http.{ HttpFailure, HttpResponse } | |
import spray.http.HttpEntity.apply | |
trait IOActor extends Actor { | |
log: Logging => | |
def receiveIO: PartialFunction[Any, IOResult[Unit]] | |
val handle = (err: Invalid) => log.error(err) | |
override final def receive = { | |
case any if (receiveIO.isDefinedAt(any)) => | |
receiveIO(any).run.unsafePerformIO.swap.foreach { handle } | |
case unknown => log.error(unknown) | |
} | |
case class Handle[A](io: IOResult[A]) { | |
def onFail(f: Invalid => (HttpFailure, String)): IOResult[A] = | |
io.bimap( | |
err => { | |
val (status, response) = f(err) | |
sender ! HttpResponse(status, response) | |
s"Failed to process store request: HTTP[$status.intValue}] => [$response]".invalid | |
}, | |
identity | |
) | |
} | |
} |
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
import scalaz._ | |
import syntax.applicative._ | |
import syntax.id._ | |
import Scalaz.Id | |
import effect._ | |
import util.control.NonFatal | |
package object stuff { | |
type Invalid = kadai.Invalid | |
val Invalid = kadai.Invalid | |
/** | |
* The Result monad transformer: essentially specializing Scalaz's EitherT to have | |
* the left type be an Invalid | |
*/ | |
type ResultT[F[+_], +A] = EitherT[F, Invalid, A] | |
object ResultT { | |
def apply[F[+_], A](a: F[Invalid \/ A]): ResultT[F, A] = | |
EitherT.eitherT(a) | |
/** Construct a left disjunction value. */ | |
def left[F[+_], A](a: F[Invalid])(implicit F: Functor[F]): ResultT[F, A] = | |
EitherT.left(a) | |
/** Construct a right disjunction value. */ | |
def right[F[+_], A](b: F[A])(implicit F: Functor[F]): ResultT[F, A] = | |
EitherT.right(b) | |
} | |
/** The "basic" version of ResultT. There are no other monads involved except Id */ | |
type Result[+A] = ResultT[Id, A] | |
object Result extends EitherTInstances with EitherTFunctions { | |
def fromOption[A](b: Option[A]): Result[A] = | |
b.fold[Result[A]]("Empty Option".invalidResult)(x => Result(\/.right(x))) | |
def right[A](a: => A): Result[A] = | |
ResultT.right(a.point[Id]) | |
def left[A](i: Invalid): Result[A] = | |
ResultT.left(i.point[Id]) | |
def apply[A](a: Invalid \/ A): Result[A] = | |
ResultT(a.point[Id]) | |
} | |
/** Evaluate the given value, which might throw an exception. */ | |
def catchingToResult[A](a: => A): Result[A] = | |
try ResultT.right(a.point[Id]) // need to do this explicitly due to thunk expansion, otherwise weird error in scalac about implicit conversions | |
catch { | |
case NonFatal(e) => e.invalidResult | |
} | |
implicit val EachResult = | |
new Each[Result] { | |
def each[A](fa: Result[A])(f: A => Unit) = fa foreach f | |
} | |
implicit def resultMonoid[A : Monoid] = new Monoid[Result[A]] { | |
def zero: Result[A] = | |
Result.right(Monoid[A].zero) | |
def append(r0: Result[A], r1: => Result[A]): Result[A] = | |
for { | |
a0 <- r0 | |
a1 <- r1 | |
} yield implicitly[Monoid[A]].append(a0, a1) | |
} | |
/** A Result wrapped in IO, using the ResultT transformer **/ | |
type IOResult[+A] = ResultT[IO, A] | |
object IOResult extends EitherTInstances with EitherTFunctions { | |
def apply[A](a: => Result[A]): IOResult[A] = | |
ResultT { | |
IO { | |
{ | |
try a | |
catch { case NonFatal(e) => e.invalidResult[A] } | |
}.run | |
} | |
} | |
def right[A](a: => A): IOResult[A] = | |
ResultT.right { IO { a } } | |
def left[A](a: => Invalid): IOResult[A] = | |
ResultT.left { IO { a } } | |
def mustExist[A](a: Option[A], missing: => String): IOResult[A] = | |
IOResult { | |
a.fold[Result[A]](missing.invalidResult)(Result.right(_)) | |
} | |
} | |
def catchingIO[A](a: => A): IOResult[A] = | |
IOResult { catchingToResult { a } } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment