Scala reflection and type parameters
I am trying to build a reflection based selector that checks if the value
of the field in the case class or result of parameterless method matches
required value.
Case class hierarchy:
trait Event {
def eventType: String
def eventName: String = this.getClass.getSimpleName
}
trait VCSEvent extends Event {
def eventType: String = "VCS"
}
case class BranchAdded(branch: String) extends VCSEvent
So, BranchAdded("develop").eventName == "BranchAdded", and
BranchAdded("develop").branch == "develop"
The matcher itself is pretty simple:
case class EventSelector(ops: List[EventSelectorOp]) {
def matches(event: Event): Boolean = {
ops.map {
op Ë op.matches(event)
}.reduceLeft(_ & _)
}
}
trait EventSelectorOp {
def matches[T <: Event](event: T)(implicit tag: ru.TypeTag[T], classtag:
ClassTag[T]): Boolean
}
case class FieldEventSelectorOp(field: String, operation: Symbol, value:
Any) extends EventSelectorOp {
def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T]
def matches[T <: Event](event: T)(implicit tag: ru.TypeTag[T], classtag:
ClassTag[T]): Boolean = {
val mirror = ru.runtimeMirror(event.getClass.getClassLoader)
val memberSymbol = tag.tpe.member(ru.newTermName(field))
if (memberSymbol.name.decoded.equals("<none>"))
return false
val fieldValue = if (memberSymbol.isMethod) {
mirror.reflect(event).reflectMethod(memberSymbol.asMethod).apply()
} else {
mirror.reflect(event).reflectField(memberSymbol.asTerm).get
}
operation match {
case 'eq Ë fieldValue.equals(value)
case _ Ë false
}
}
}
Now, the magic starts in testing:
class EventSelectorOpSpec extends WordSpec with ShouldMatchers {
"FieldEventSelectorOp" should {
"match correct paremeterless method" in {
FieldEventSelectorOp("eventName", 'eq,
"BranchAdded").matches(BranchAdded("develop")) should be(true)
}
"not match incorrect parameterless method" in {
FieldEventSelectorOp("eventName", 'eq,
"BranchAdded").matches(Initialize) should be(false)
}
"match field by name" in {
FieldEventSelectorOp("branch", 'eq,
"develop").matches(BranchAdded("develop")) should be(true)
}
"not match existing field with wrong value" in {
FieldEventSelectorOp("branch", 'eq,
"master").matches(BranchAdded("develop")) should be(false)
}
"not match non-existing field by name" in {
FieldEventSelectorOp("branch2", 'eq,
"develop").matches(BranchAdded("develop")) should be(false)
}
}
"EventSelector" should {
"match when all of multiple selectors match" in {
val op1 = FieldEventSelectorOp("eventName", 'eq, "BranchAdded")
val op2 = FieldEventSelectorOp("branch" , 'eq, "develop")
val event = BranchAdded("develop")
val selector = EventSelector(List(op1, op2))
selector.matches(event) should be(true)
}
}
}
In the spec above, all tests in FieldEventSelectorOp part pass, at the
same time last test fails - memberSymbol.name.decoded in the last test is
"" and the tag.tpe.erasure is "Event"
Why is this happening?
No comments:
Post a Comment