add selfChannel and createChildren

This commit is contained in:
Roland 2012-12-31 18:38:06 +01:00
parent db40d51c05
commit 8a4fca72f7
2 changed files with 94 additions and 5 deletions

View file

@ -13,7 +13,7 @@ import scala.tools.reflect.ToolBoxError
object ChannelSpec {
trait Msg
trait A extends Msg
object A extends A
object A1 extends A
@ -21,10 +21,10 @@ object ChannelSpec {
trait B extends Msg
object B extends B
trait C extends Msg
object C extends C
trait D extends Msg
object D extends D
@ -49,6 +49,24 @@ object ChannelSpec {
x ! C
}
}
// pos compile test for children
class Children extends Channels[Parent[TNil], Channel[A, B] :=: Channel[C, D] :=: TNil] {
val c = createChild(new Channels[Parent[A :-: TNil], Channel[B, C]:=: TNil] {
channel[B] { case (B, s) s ! C }
})
var client: ActorRef = _
channel[A] {
case (A, s) c ! B; client = sender
}
channel[C] {
case (C, _) client ! C
}
createChild(new Channels[Parent[C :-: TNil], TNil])
createChild(new Channels[Parent[A :-: C :-: TNil], TNil])
}
}
class ChannelSpec extends AkkaSpec with ImplicitSender {
@ -151,6 +169,36 @@ class ChannelSpec extends AkkaSpec with ImplicitSender {
}.message must include("This ChannelRef does not support messages of type akka.channels.ChannelSpec.C.type")
}
"not permit Nothing children" in {
intercept[ToolBoxError] {
eval("""
|import akka.channels._
|import ChannelSpec._
|new Channels[Parent[TNil], Channel[A, B] :=: Channel[C, D] :=: TNil] {
| createChild(new Channels)
|}
""".stripMargin)
}.message must include("Parent argument must not be Nothing")
}
"not permit too demanding children" in {
intercept[ToolBoxError] {
eval("""
|import akka.channels._
|import ChannelSpec._
|new Channels[Parent[TNil], Channel[A, B] :=: Channel[C, D] :=: TNil] {
| createChild(new Channels[Parent[B :-: TNil], TNil])
|}
""".stripMargin)
}.message must include("This actor cannot support a child requiring channels akka.channels.ChannelSpec.B")
}
"have a working selfChannel" in {
val ref = ChannelExt(system).actorOf(new Children)
ref ! A
expectMsg(C)
}
}
}

View file

@ -10,11 +10,16 @@ import scala.reflect.macros.Context
import scala.reflect.runtime.universe.TypeTag
import scala.reflect.macros.Universe
import scala.runtime.AbstractPartialFunction
import akka.actor.Props
trait Channels[P <: Parent[_], C <: ChannelList] extends Actor {
class Channels[P <: Parent[_], C <: ChannelList: TypeTag] extends Actor {
import Channels._
def createChild[Pa <: Parent[_], Ch <: ChannelList](factory: Channels[Pa, Ch]): ChannelRef[Ch] = macro createChildImpl[C, Pa, Ch]
implicit val selfChannel = new ChannelRef[C](self)
/*
* Warning Ugly Hack Ahead
*
@ -27,7 +32,7 @@ trait Channels[P <: Parent[_], C <: ChannelList] extends Actor {
*/
private var behavior = List.empty[Recv[Any, ChannelList]]
def channel[T]: Channels[P, C]#Behaviorist[T, _ <: ChannelList] = macro Channels.channelImpl[T, C, P, ChannelList]
def channel[T]: Channels[P, C]#Behaviorist[T, _ <: ChannelList] = macro channelImpl[T, C, P, ChannelList]
protected def _channel[T, Ch <: ChannelList] = new Behaviorist[T, Ch]
protected class Behaviorist[T, Ch <: ChannelList] {
@ -87,6 +92,42 @@ object Channels {
}
}
def createChildImpl[C <: ChannelList: c.WeakTypeTag, Pa <: Parent[_]: c.WeakTypeTag, Ch <: ChannelList: c.WeakTypeTag](
c: Context {
type PrefixType = Channels[_, C]
})(factory: c.Expr[Channels[Pa, Ch]]): c.Expr[ChannelRef[Ch]] = {
import c.universe._
if (weakTypeOf[Pa] =:= weakTypeOf[Nothing]) {
c.abort(c.enclosingPosition, "Parent argument must not be Nothing")
}
if (weakTypeOf[Ch] =:= weakTypeOf[Nothing]) {
c.abort(c.enclosingPosition, "channel list must not be Nothing")
}
val missing = missingChannels(c.universe)(weakTypeOf[C], parentChannels(c.universe)(weakTypeOf[Pa]))
if (missing.isEmpty) {
implicit val t = c.TypeTag[Ch](c.weakTypeOf[Ch])
reify(new ChannelRef[Ch](c.prefix.splice.context.actorOf(Props(factory.splice))))
} else {
c.error(c.enclosingPosition, s"This actor cannot support a child requiring channels ${missing mkString ", "}")
reify(null)
}
}
/**
* get all required channels from a Parent[_]
*/
final def parentChannels(u: Universe)(list: u.Type): List[u.Type] = {
import u._
def rec(l: u.Type, acc: List[u.Type]): List[u.Type] = l match {
case TypeRef(_, _, ch :: tail :: Nil) rec(tail, ch :: acc)
case _ acc.reverse
}
list match {
case TypeRef(_, _, ch :: Nil) rec(ch, Nil)
}
}
/**
* find all input channels matching the given message type and return a
* list of their respective reply channels