Skip to content

Instantly share code, notes, and snippets.

@poetix
Created January 14, 2013 17:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save poetix/4531651 to your computer and use it in GitHub Desktop.
Save poetix/4531651 to your computer and use it in GitHub Desktop.
A monad which captures dynamic method invocations against multiple targets (for use in implementing object builders / matchers)
package com.youdevise.lofty
import org.specs2.mutable._
import scala.language.dynamics
trait State[S, A] {
val runState: Function[S, (S, A)]
def flatMap[B](f: A => Function[S, (S, B)]):Function[S, (S, B)] = (state:S) => {
val (newState, value) = runState(state)
f(value)(newState)
}
def map[B](f: A => B): Function[S, (S, B)] = flatMap { (a:A) => (s:S) => (s, f(a)) }
}
case class RecordedCall(target:RecorderTarget, methodName:String, parameters:Seq[Any])
case class Recording(val calls:Seq[RecordedCall])
object Recording {
val emptyRecording = Recording(Seq.empty[RecordedCall])
def newTarget(): State[Recording, RecorderTarget] = new State[Recording, RecorderTarget] {
val runState: Function[Recording, (Recording, RecorderTarget)] = (recording:Recording) => (recording, new RecorderTarget())
}
}
import Recording._
class RecorderTarget() extends Dynamic {
def applyDynamic(name:String)(args:Any*):State[Recording, RecorderTarget] = {
val target = this
new State[Recording, RecorderTarget] {
val runState: Function[Recording, (Recording, RecorderTarget)] = (recording:Recording) => (
new Recording(recording.calls :+ RecordedCall(target, name, args.toSeq)),
target)
}
}
}
class RecorderSpec extends Specification {
"""A recorder""" should {
"""Capture all method calls within the recorder monad""" in {
val recorder = for {
t1 <- newTarget()
_ <- t1.hey()
_ <- t1.iJustMet("you")
_ <- t1.andThis("is", "crazy")
t2 <- newTarget()
_ <- t2.receive(t1)
} yield (t1, t2)
val (recording:Recording, (t1: RecorderTarget, t2: RecorderTarget)) = recorder(emptyRecording)
recording.calls must beEqualTo(Seq(RecordedCall(t1, "hey", Seq()),
RecordedCall(t1, "iJustMet", Seq("you")),
RecordedCall(t1, "andThis", Seq("is", "crazy")),
RecordedCall(t2, "receive", Seq(t1))))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment