0

I'm working on my first Scala application, where we use an ActiveRecord style to retrieve data from MongoDB.

I have models like User and Category, which all have a companion object that uses the trait:

class MongoModel[T <: IdentifiableModel with CaseClass] extends ModelCompanion[T, ObjectId]

ModelCompanion is a Salat class which provide common MongoDB crud operations. This permits to retrieve data like this:

User.profile(userId)

I never had any experience with this ActiveRecord query style. But I know Rails people are using it. And I think I saw it on Play documentation (version 1.2?) to deal with JPA.

For now it works fine, but I want to be able to run integration tests on my MongoDB. I can run an "embedded" MongoDB with a library. The big deal is that my host/port configuration are actually kind of hardcoded on the MongoModel class which is extended by all the model companions.

I want to be able to specify a different host/port when I run integration tests (or any other "profile" I could create in the future).


I understand well dependency injection, using Spring for many years in Java, and the drawbacks of all this static stuff in my application. I saw that there is now a scala-friendly way to configure a Spring application, but I'm not sure using Spring is appropriate in Scala.

I have read some stuff about the Cake pattern and it seems to do what I want, being some kind of typesafe, compile-time-checked spring context. Should I definitely go to the Cake pattern, or is there any other elegant alternative in Scala? Can I keep using an ActiveRecord style or is it a total anti-pattern for testability?

Thanks

Sebastien Lorber
  • 89,644
  • 67
  • 288
  • 419

2 Answers2

2

No any static references - using Cake pattern you got 2 different classes for 2 namespaces/environments, each overriding "host/port" resource on its own. Create a trait containing your resources, inherit it 2 times (by providing actual information about host/port, depending on environment) and add to appropriate companion objects (for prod and for test). Inside MongoModel add self type that is your new trait, and refactor all host/port references in MongoModel, to use that self type (your new trait).

idonnie
  • 1,703
  • 12
  • 11
  • Not sure to understand. Can you provide a simple code sample where I can still do "User.profile(userId)" while this can lead to different databases?. Perhaps by "No any static references" you mean I should forget about this query style. But after you write "add to appropriate companion objects". When you talk about such companion objets, you mean the companion objects that hold the "context" and do the wiring between components, or you mean the models companions like User/Category that I have in my application? – Sebastien Lorber Jan 08 '13 at 09:28
  • Companion objects are a `object real {}` and `object test {}`, from your previous question. They will each contain a class, in which host/port gets overriden. Mainly I looked at an example of Cake pattern from your previous question. Here is a reference, for consistency: https://gist.github.com/2127745 – idonnie Jan 08 '13 at 10:18
1

I'd definitely go with the Cake Pattern.

You can read the following article with show an example of how to use the Cake Pattern in a Play2 application: http://julien.richard-foy.fr/blog/2011/11/26/dependency-injection-in-scala-with-play-2-it-s-free/

ndeverge
  • 21,378
  • 4
  • 56
  • 85
  • Thanks. I'm not sure to understand the package object part of your link. So in the end without this object I guess it doesn't work right? And requests are routed to the controllers declared in this package object? Then if I have many controllers, I should declare a new variable for each controller? With the appropriate name used by the router? – Sebastien Lorber Jan 08 '13 at 09:45
  • The [package object](https://github.com/julienrf/di-with-play2/blob/master/app/bootstrap.scala) is mandatory since it is used to instantiate the objects (that's what's Spring do when you provide an xml file). So yes, you'll have to declare new variable for each controller (if the controller needs DI). – ndeverge Jan 08 '13 at 09:56
  • Thanks. This part seems like a little trick to be able to use a different context according to the play configuration – Sebastien Lorber Jan 08 '13 at 10:49