Typesafe Activator

Scaldi Play Example

Scaldi Play Example

scaldi
Source
December 18, 2014
scaldi playframework scala dependency-injection basics

Scaldi is lightweight Scala dependency injection library. In this project you will find an example of Play 2 application that uses Scaldi for dependency injection.

How to get "Scaldi Play Example" on your computer

There are several ways to get this template.

Option 1: Choose scaldi-play-example in the Typesafe Activator UI.

Already have Typesafe Activator (get it here)? Launch the UI then search for scaldi-play-example in the list of templates.

Option 2: Download the scaldi-play-example project as a zip archive

If you haven't installed Activator, you can get the code by downloading the template bundle for scaldi-play-example.

  1. Download the Template Bundle for "Scaldi Play Example"
  2. Extract the downloaded zip file to your system
  3. The bundle includes a small bootstrap script that can start Activator. To start Typesafe Activator's UI:

    In your File Explorer, navigate into the directory that the template was extracted to, right-click on the file named "activator.bat", then select "Open", and if prompted with a warning, click to continue:

    Or from a command line:

     C:\Users\typesafe\scaldi-play-example> activator ui 
    This will start Typesafe Activator and open this template in your browser.

Option 3: Create a scaldi-play-example project from the command line

If you have Typesafe Activator, use its command line mode to create a new project from this template. Type activator new PROJECTNAME scaldi-play-example on the command line.

Option 4: View the template source

The creator of this template maintains it at https://github.com/scaldi/scaldi-play-example#master.

Option 5: Preview the tutorial below

We've included the text of this template's tutorial below, but it may work better if you view it inside Activator on your computer. Activator tutorials are often designed to be interactive.

Preview the tutorial

Scaldi - lightweight dependency injection library

Scaldi is dependency injection library for Scala. It's very lightweight (without any dependencies) and provides nice Scala DSL for binding dependencies and injecting them.

There are only 3 most important traits that you need to know, in order to make dependency injection with Scaldi:

  • Injector - it's a container for the bindings, that you have defined in the module.
  • Module - gives you nice syntax to create bindings with bind and binding. Module also extends Injector trait and implicit Injector instance always available when you are defining your bindings
  • Injectable - the only responsibility of this trait is to provide you with inject function (so it just provides nice syntax for injecting dependencies). It's important to understand, that it's the only the purpose of it. So it completely stateless and knows nothing about actual bindings you have defined in the module. In order to actually find and inject dependencies, inject function always takes an implicit parameter of type Injector

Defining Managed Classes

Let's take a small example. Suppose you have have MessageService trait like this:

trait MessageService {
    def getGreetMessage(name: String): String
}

Now let's define one implementation of it, where we will inject greeting string:

class OfficialMessageService(implicit inj: Injector) extends MessageService with Injectable {
    val officialGreeting = inject [String] (identified by "greeting.official")

    def getGreetMessage(name: String) = s"$officialGreeting, $name!"
}

You have probably noticed 2 things, that are required for injection to work:

  • We extended Injectable in order to make inject function available
  • We declared implicit parameter of type Injector in order to provide bindings to the inject function.

You are not required to always extend Injectable in order to use inject - you can just import it. This will work as good as in previous example:

import scaldi.Injectable._

class SomeService(implicit inj: Injector) extends MessageService {
    val dep = inject [SomeOtherService]
}

Creating a Module

Now that we've created a managed class, we need to add it to the Module:

class UserModule extends Module {
    bind [MessageService] to new OfficialMessageService

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

In the module context you are able to instantiate OfficialMessageService because it always have implicit Injector available in a scope, as I mentioned earlier. I also defined another binding with identifier greeting.official because OfficialMessageService needs it and will try to inject it at some point.

Integration with Play App

Now we will try to integrate our new module in the Play app. The first thing you need to do is to add dependency on scaldi-play in the project file (build.sbt). Something like this:

name := "scaldi-play-example"

version := "1.0-SNAPSHOT"

libraryDependencies ++= Seq(
    "com.github.scaldi" %% "scaldi-play" % "0.2.2"
)

play.Project.playScalaSettings

Now you are able to use Scaldi in the project. Play application normally has it's initialization logic in the Global object, so we need to add ScaldiSupport in it:

object Global extends GlobalSettings with ScaldiSupport {
    def applicationModule = new UserModule
}

Nice! Your Play application is now uses Scaldi for the dependency injection, but unfortunately it doesn't do anything, so let's fix it. Let's create a simple index page (index.scala.html) that will show greeting message to the user:

@(message: String)

@main("Test Page") {
<h1>@message</h1>
}

Now we need to create a controller for it. But instead of creating a singleton object, let's make a managed class (simmilat to the OfficialMessageService class):

class Application(implicit inj: Injector) extends Controller with Injectable {
    val messageService = inject [MessageService]

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

As you can see, MessageService is injected and its used to produce nice greet message.

As you probably already aware, Play only works with singleton controllers, which means, that it requires Application controller to be an object instead of class. Thankfully Play 2.1 introduced new feature, which makes integration with dependency injection framework like Scaldi possible. In order to use this feature, you need to prefix managed controllers with @ in the routes file. So let's make it:

GET  /                 @controllers.Application.index

The one last thing that remains is to add controller to the new module:

class WebModule extends Module {
    binding to new Application
}

and to compose WebModule with UserModule in the Global object:

object Global extends GlobalSettings with ScaldiSupport {
    def applicationModule = new WebModule :: new UserModule
}

At this point you should be able to run Play app and view the index page, that will show you: Welcome, Test User!.

Injecting Play Configuration

scaldi-play provides integration with Play configuration (application.conf) out of the box. So you can, for example, define greeting.official property there:

greeting.official = Welcome

and then just remove one extra binding for it from the UserModule:

class UserModule extends Module {
    bind [MessageService] to new OfficialMessageService
}

It will continue to work as before. You can also inject other primitive types like Int or Boolean and not only String. If you would like to use configuration object directly, then you need inject it like this:

val config = inject [play.api.Configuration]

Distinguishing Between Modes

Suppose we have another implementation of MessageService that we want to use when Play app is in the dev or test mode:

class SimpleMessageService extends MessageService {
    def getGreetMessage(name: String) = s"Hi, $name"
}

With Scaldi it's pretty easy to make. You just need to define UserModule like this:

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

inDevMode, inTestMode and inProdMode are just functions that produce Condition objects. Conditions are used by Scaldi to decide, whether binding is available for injection.

Hope you enjoyed!

I hope you liked this small introduction to scaldi and scaldi-play. You can find example application, that I described here, in this github repo:

https://github.com/scaldi/scaldi-play-example

Please feel free to fork and hack it :) Also feel free to give a feedback in case of problems.

comments powered by Disqus