Merge pull request #25108 from akka/wip-18785-orElse-patriknw

add Behavior.orElse, #18785
This commit is contained in:
Patrik Nordwall 2018-05-21 11:22:36 +02:00 committed by GitHub
commit 5fbf68f23d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 110 additions and 1 deletions

View file

@ -0,0 +1,79 @@
/**
* Copyright (C) 2017-2018 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.actor.typed
import scala.util.control.NoStackTrace
import akka.actor.typed.scaladsl.Behaviors
import akka.testkit.typed.TestKitSettings
import akka.testkit.typed.scaladsl._
import org.scalatest.WordSpecLike
object OrElseSpec {
sealed trait Ping
case class Ping1(replyTo: ActorRef[Pong]) extends Ping
case class Ping2(replyTo: ActorRef[Pong]) extends Ping
case class Ping3(replyTo: ActorRef[Pong]) extends Ping
case class Pong(counter: Int)
def ping(counters: Map[String, Int]): Behavior[Ping] = {
val ping1: Behavior[Ping] = Behaviors.receiveMessagePartial {
case Ping1(replyTo: ActorRef[Pong])
val newCounters = counters.updated("ping1", counters.getOrElse("ping1", 0) + 1)
replyTo ! Pong(newCounters("ping1"))
ping(newCounters)
}
val ping2: Behavior[Ping] = Behaviors.receiveMessage {
case Ping2(replyTo: ActorRef[Pong])
val newCounters = counters.updated("ping2", counters.getOrElse("ping2", 0) + 1)
replyTo ! Pong(newCounters("ping2"))
ping(newCounters)
case _ Behaviors.unhandled
}
val ping3: Behavior[Ping] = Behaviors.receiveMessagePartial {
case Ping3(replyTo: ActorRef[Pong])
val newCounters = counters.updated("ping3", counters.getOrElse("ping3", 0) + 1)
replyTo ! Pong(newCounters("ping3"))
ping(newCounters)
}
ping1.orElse(ping2).orElse(ping3)
}
}
class OrElseSpec extends WordSpecLike with TypedAkkaSpec {
import OrElseSpec._
"Behavior.orElse" must {
"use first matching behavior" in {
val inbox = TestInbox[Pong]("reply")
val testkit = BehaviorTestKit(ping(Map.empty))
testkit.run(Ping1(inbox.ref))
inbox.receiveMessage() should ===(Pong(1))
testkit.run(Ping1(inbox.ref))
inbox.receiveMessage() should ===(Pong(2))
testkit.run(Ping2(inbox.ref))
inbox.receiveMessage() should ===(Pong(1))
testkit.run(Ping3(inbox.ref))
inbox.receiveMessage() should ===(Pong(1))
testkit.run(Ping2(inbox.ref))
inbox.receiveMessage() should ===(Pong(2))
testkit.run(Ping3(inbox.ref))
inbox.receiveMessage() should ===(Pong(2))
testkit.run(Ping1(inbox.ref))
inbox.receiveMessage() should ===(Pong(3))
}
}
}

View file

@ -6,8 +6,9 @@ package akka.actor.typed
import akka.actor.InvalidMessageException
import akka.actor.typed.internal.BehaviorImpl
import scala.annotation.tailrec
import akka.actor.typed.internal.BehaviorImpl.OrElseBehavior
import akka.util.{ LineNumbers, OptionVal }
import akka.annotation.{ DoNotInherit, InternalApi }
import akka.actor.typed.scaladsl.{ ActorContext SAC }
@ -41,6 +42,15 @@ sealed abstract class Behavior[T] { behavior ⇒
* (which cannot be expressed directly due to type inference problems).
*/
final def narrow[U <: T]: Behavior[U] = this.asInstanceOf[Behavior[U]]
/**
* Composes this `Behavior with a fallback `Behavior` which
* is used when this `Behavior` doesn't handle the message or signal, i.e.
* when `unhandled` is returned.
*
* @param that the fallback `Behavior`
*/
final def orElse(that: Behavior[T]): Behavior[T] = new OrElseBehavior[T](this, that)
}
/**

View file

@ -178,4 +178,20 @@ import scala.reflect.ClassTag
override def toString = s"$toStringPrefix(${LineNumbers(beforeOnMessage)},${LineNumbers(beforeOnSignal)},$behavior)"
}
class OrElseBehavior[T](first: Behavior[T], second: Behavior[T]) extends ExtensibleBehavior[T] {
override def receive(ctx: AC[T], msg: T): Behavior[T] = {
Behavior.interpretMessage(first, ctx, msg) match {
case _: UnhandledBehavior.type Behavior.interpretMessage(second, ctx, msg)
case handled handled
}
}
override def receiveSignal(ctx: AC[T], msg: Signal): Behavior[T] = {
Behavior.interpretSignal(first, ctx, msg) match {
case _: UnhandledBehavior.type Behavior.interpretSignal(second, ctx, msg)
case handled handled
}
}
}
}

View file

@ -115,6 +115,8 @@ object Behaviors {
/**
* Construct an immutable actor behavior from a partial message handler which treats undefined messages as unhandled.
*
* Behaviors can also be composed with [[Behavior#orElse]].
*/
def receivePartial[T](onMessage: PartialFunction[(ActorContext[T], T), Behavior[T]]): Receive[T] =
Behaviors.receive[T] { (ctx, t)
@ -123,6 +125,8 @@ object Behaviors {
/**
* Construct an immutable actor behavior from a partial message handler which treats undefined messages as unhandled.
*
* Behaviors can also be composed with [[Behavior#orElse]].
*/
def receiveMessagePartial[T](onMessage: PartialFunction[T, Behavior[T]]): Receive[T] =
Behaviors.receive[T] { (_, t)