Last active
March 28, 2016 09:03
-
-
Save etorreborre/2ef77cbef35e349d3303 to your computer and use it in GitHub Desktop.
Pseudo-code for doing DI on a service using an actor with 2 different implementations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait MyActor | |
case class InMemoryActor() extends MyActor | |
case class ProductionActor(connection: DBConnection) extends MyActor | |
case class MyService(actor: MyActor) | |
object MyService { | |
def fromConfig(config: Config): ConfigError Xor MyActor = | |
??? // read application.conf | |
} | |
class MySpec extends Specification with ForEach[MyService] { def is = s2""" | |
The service should do this $e1 | |
""" | |
def e1 = { service => | |
// replace the actor implementation using the ConfigRewriter | |
val actor: MyActor = InMemoryActor() | |
service.replace[MyActor](actor) | |
// test the service here | |
ok | |
} | |
def foreach[R : AsResult](f: MyService => R) = { | |
MyService.fromConfig(ConfigFactory.load("application-test.conf")).fold( | |
e => Failure(ConfigError.render(e)), | |
s => f(s) | |
) | |
} | |
} | |
/** | |
* This is using: https://bitbucket.org/inkytonik/kiama | |
*/ | |
trait ConfigRewriter { | |
/** | |
* Take the first value of a given type (approximated with a ClassTag) | |
* and replace it everywhere in the graph | |
*/ | |
def singleton[S : ClassTag, G](graph: G): G = | |
replaceWithStrategy(singletonStrategy[S], graph) | |
/** | |
* Replace all values of type S, with the same value | |
*/ | |
def replace[S : ClassTag, G](s: S, graph: G): G = | |
replaceWithStrategy(replaceStrategy[S](s), graph) | |
/** | |
* Replace with a given strategy | |
*/ | |
def replaceWithStrategy[G](strategy: Strategy, graph: G): G = | |
rewrite(everywheretd(strategy))(graph) | |
/** | |
* Replace with a given partial function | |
*/ | |
def replaceWith[G, T](s: T ==> Option[T], graph: G): G = | |
replaceWithStrategy(strategy(s), graph) | |
def singletonStrategy[S : ClassTag]: Strategy = { | |
var s: Option[S] = None | |
strategy[Any] { | |
case v if implicitly[ClassTag[S]].runtimeClass.isInstance(v) => | |
s match { | |
case Some(singleton) => Some(singleton) | |
case None => s = Some(v.asInstanceOf[S]) | |
Some(v) | |
} | |
case other => None | |
} | |
} | |
def replaceStrategy[S : ClassTag](s: S): Strategy = | |
strategy[Any] { | |
case v if Reflect.implements(v) => Some(s) | |
case other => None | |
} | |
} | |
object ConfigRewriter extends ConfigRewriter with ConfigRewriterSyntax | |
/** | |
* Syntactic sugar for rewriting nodes in a graph | |
* | |
* The methods here allow to chain various replacements: | |
* | |
* g.singleton[T].replace[S](s) | |
*/ | |
trait ConfigRewriterSyntax { | |
implicit class Rewrite[G](graph: G) { | |
def singleton[S : ClassTag]: G = | |
ConfigRewriter.singleton[S, G](graph) | |
def replace[S : ClassTag](s: S): G = | |
ConfigRewriter.replace[S, G](s, graph) | |
def replaceWith[T](s: T ==> Option[T]): G = | |
ConfigRewriter.replaceWith(s, graph) | |
} | |
} | |
object ConfigRewriterSyntax extends ConfigRewriterSyntax |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment