Scaldi – Lightweight Dependency Injection Library



Scaldi – Lightweight Dependency Injection Library

0 3


scaldi-presentation

Dependency injection in Play and Akka with Scaldi Presentation

On Github OlegIlyenko / scaldi-presentation

Scaldi

Lightweight Dependency Injection Library

Created by  Oleg Ilyenko  @easyangel

I think It all started here: https://groups.google.com/forum/#!topic/liftweb/Sau5aCEvJ0M

Only 3 Concepts

1  Injector

A container for the bindings

2  Module

  • A place where you define the bindings
  • DSL for bindings
  • extends Injector

3  Injectable

DSL for injection
inject [SomeClass]
        
... or this one ...
inject [Database] (identified by 'remote and by default new Riak)
        

Injector  and  Injectable

class OfficialMessageService(implicit inj: Injector)
          extends MessageService with Injectable {

  val officialGreeting =
    inject [String] (identified by "greeting.official")

  def getGreetMessage(name: String) =
    s"$officialGreeting, $name!"
}
        
and Module of course
class UserModule extends Module {
  bind [MessageService] to new OfficialMessageService

  binding identifiedBy "greeting.official" to "Welcome"
}
        

★ Something Special ★

Injector Composition

def tokenModule = new Module {
  bind [Tokens] to new TokenRepo(db = inject [Database])
}

def dbModule = new Module {
  bind [Database] to new Riak
}

def appModule = tokenModule :: dbModule
        
and then test it
def mocksModule = new Module {
  bind [Database] to new InMemoryDb
}

implicit val testModule = mocksModule :: appModule
        

Injector Types

  • Mutable Injectors
  • Immutable Injectors
  • Properties Injector
  • Play Configuration Injector
  • Create you own, it's easy
trait Injector {
  def getBinding(identifiers: List[Identifier]): Option[Binding]
  def getBindings(identifiers: List[Identifier]): List[Binding]

  // ...
}
        
Tell about Play controller injector.

Generics

bind
binding identifiedBy "intAdder" to
  ((a: Int, b: Int) => a + b)

binding identifiedBy "mapping" to Map(
  "scala" -> "http://scala-lang.org",
  "play" -> "http://www.playframework.com",
  "akka" -> "http://akka.io"
)
        
inject
val intAdder = inject [(Int, Int) => Int]

val mapping = inject [Map[String, String]]
        

Conditions

class UserModule extends Module {
  bind [MessageService] when (inDevMode or inTestMode) to
    new SimpleMessageService

  bind [MessageService] when inProdMode to
    new OfficialMessageService
}
        
def inDevMode(implicit inj: Injector) = {
  val mode = inject [Mode]
  Condition(mode == Dev)
}
        

Binding Lifecycle

class MyModule extends Module {
  bind [ActorSystem] to
    ActorSystem("ScaldiExample") destroyWith (_.shutdown())

  bind [Database] to new Mongo initWith (_.start())
}
        
implicit val injector = new MyModule

// do stuff ...

injector.destroy()
        

Extensibility

  • Extend almost any part of the library
    • Injector
    • Condition
    • Binding and BindingWithLifecycle
    • Identifier - create your own and define how they match
  • Type classes
    • CanCompose - defines how Injectors can be composed together
    • CanBeIdentifier

Create  a  Controller

Finally you can develop with classes and not singleton objects.
class Application(implicit inj: Injector)
          extends Controller with Injectable {

  val messageService = inject [MessageService]

  def index = Action {
    Ok(views.html.index(
      messageService.getGreetMessage("Test User")))
  }
}
        

Then  Bind  it

class MyModule extends Module {
  binding to new Application

  bind [MessageService] to new OfficialMessageService
}
        

Define  Global

object Global extends GlobalSettings with ScaldiSupport {
  def applicationModule =
          new MyModule :: new SomeOtherModule
}
        

Configure  routes

Don't forget @
GET  /   @controllers.Application.index
        

Play  Extras

Bindings
inject [Application]
inject [Mode]
inject [Configuration]
        
Conditions
bind [Thing] when (inDevMode or inTestMode or inProdMode) to
  new SomeThing
        
All configuration properties are also available as bindings
inject [String] (identified by "db.host")
inject [Int] (identified by "db.port")
        

Akka  Injectable

  • injectActorProps
  • injectActorRef
class Receptionist(implicit inj: Injector)
    extends Actor with AkkaInjectable {

  val orderProcessorProps = injectActorProps [OrderProcessor]

  val priceCalculator = injectActorRef [PriceCalculator]

  def receive = {
    case PlaceOrder(userName, itemId, netAmount) =>
      val processor = context.actorOf(orderProcessorProps)
      // ...
  }
}
        

Actor Ref  Factory

  • Should be implicitly available
    • ActorContext (within an actor)
    • ActorSystem (outside of actor)
  • Creates and configures an actor
    • Setups correct actor hierarchy
  • Integrates with Scaldi via Props

Actor  Bindings

Don't forget to bind with toProvider
class OrderModule extends Module {
  bind [ActorSystem] to ActorSystem("ScaldiExample")
    destroyWith (_.shutdown())

  binding toProvider new Receptionist
  binding toProvider new OrderProcessor
  binding toProvider new PriceCalculator
}
        

More Information

EOF

Thank you!

Questions?