Merge pull request #25108 from akka/wip-18785-orElse-patriknw
add Behavior.orElse, #18785
This commit is contained in:
commit
5fbf68f23d
4 changed files with 110 additions and 1 deletions
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) ⇒
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue