[scaldi-play 0.5.8] Providing an Injector for Model Classes

44 views
Skip to first unread message

Neil Chaudhuri

unread,
Sep 25, 2015, 11:16:05 PM9/25/15
to scaldi
I have a Play 2.4 application with Scaldi bindings established the usual way with a Module class registered in application.conf. I need one of the bindings for some of my model classes like so:

import scaldi.Injectable._
import scaldi.Injector

case class File(key: String, fileName: String) 

object File {
  def apply(key: String)(implicit inj: Injector): File = File(key = key, fileName = inject[BoundClass].fileName(key))
}

First, is such a thing even possible?

Second, if so, how do I get access to the injector configured in application.conf? When I define a new module, I get a StackOverflowError, which makes sense to me since I define the same binding twice. Everything else I try leads to compile-time errors where Play complains there is no injector to be found.

Any insight is appreciated. Thanks.

Oleg Ilyenko

unread,
Sep 26, 2015, 9:50:32 AM9/26/15
to scaldi
Sure, it should be possible the way you did it. The only thing you need to take care of here is: you pass implicit `Injector` through all intermediate classes down to the class that injects something (`File` in this case). If you don't do this you will get a compile error. One implicit instance if injector is already provided inside of a `Module` by the library, so all other classes, that require implicit injector, should be instantiated there via bindings. I would assume that you have a circular dependency somewhere, that's why you are getting stack overflow. Maybe the `BoundClass` itself uses the `File` directly or indirectly during an initialization?

Neil Chaudhuri

unread,
Sep 26, 2015, 10:41:52 AM9/26/15
to scaldi
That makes sense and explains at least one part of my problem. The File model is buried in the Person model, and I am currently not passing an injector to Person. Fixing that is easier said than done though since I am using an established ReactiveMongo trait to define the class that deserializes the data in MongoDB into the Person instance:

implicit object PersonReader extends BSONDocumentReader[Person] {
    def read(doc: BSONDocument): Person = {
      ....
      val file = doc.getAs[File]("file")
    }
}

I don't see how I can add (implicit inj: Injector) anywhere in here without violating the contract of BSONDocumentReader. Is there a way?

Oleg Ilyenko

unread,
Sep 26, 2015, 12:38:52 PM9/26/15
to scaldi
hmmm... one way to handle this issue is to define `PersonReader` in a scope where you have implicit `Injector` and not as a global singleton. Another approach would be to do some implicit magic like this (this assumes that reactive mongo also uses type-class based deserialization):

case class Person(....)

object Person {
  implicit def personReader(implicit inj: Injector) = new BSONDocumentReader[Person] {
    def read(doc: BSONDocument): Person = {
      ....
      val file = doc.getAs[File]("file")
    }
  }
}

class SomeServiceClass(implicit inj: Injector) {

  def findPersonById(id: String) = 
    db.loadPersonSomehow(id).deserialize[Person]

}

Will this approach help in your case?

Neil Chaudhuri

unread,
Sep 26, 2015, 10:58:12 PM9/26/15
to scaldi
So something interesting happened.

The code I have is actually more complicated than I described--with case classes mixing in traits and having methods that need an injected value and so on. But I expanded on your excellent ideas and got things working in my initials tests. Sort of.

When I first run Play after making the changes inspired by your comments, I get a StackOverflowError as before, but I looked deeper. The error originates when Scaldi tries to initialize a completely unrelated class--at least unrelated in the sense that it has nothing to do with my model classes I wrote about in the first place. 

And not just that. The error originates where I inject the mode with val mode = inject[Mode]. When I change this line to val mode = Dev just for the time being to see what happens, there is no StackOverflowError and everything seems to work--including the stuff that I asked about in the first place.

Could this be a bug? Or perhaps I am injecting the mode the wrong way?

Neil Chaudhuri

unread,
Sep 27, 2015, 1:30:29 AM9/27/15
to scaldi
Incidentally, in this same class unrelated to models where the StackOverflowError occurs, which is just a static asset resolver class that simply figures out whether to rely on Play to serve static assets in development or a CDN in production, I can inject a configuration string just fine. But when I inject Mode or Application, I get the StackOverflowError. If I inject Configuration, which I don't need but just thought I'd try, it works.

This code was working before two major changes I made--1) adding support for injection into models as we have been discussing and 2) upgrading to the latest version of Scaldi. I know correlation isn't causation, but is it possible the new version might have an issue with loading Application? That would explain my observations where Mode and Application fail while Configuration and a config string succeed.
Reply all
Reply to author
Forward
0 new messages