This is one of my early attempts at implementing a Scala Cake Pattern:
trait dbConfig {
val m: Model = ???
}
trait testDB extends dbConfig {
override val m = new Model(Database.forURL("jdbc:h2:mem:testdb", driver = "org.h2.Driver"))
m.cleanDB
}
trait productionDB extends dbConfig {
override val m = new Model(Database.forURL("jdbc:postgresql:silly:productionDB", driver = "org.postgresql.Driver"))
}
trait SillySystem extends HttpService with dbConfig {
....
// System logic
....
}
This will allow me to use my service like this while testing:
class TestService extends SillySystem with testDB {
.....
}
And like this for production:
class ProductionService extends SillySystem with productionDB {
.....
}
This works, but am I doing it correctly?
It could be helpful to make DbConfig abstract and use def since one can override a def with a val or lazy val, but not the other way round.
SillySystem is not a DbConfig, so use dependency injection instead of inheritance.
trait DbConfig {
def m: Model // abstract
}
trait TestDB extends DbConfig {
// you can override def with val
val m = new Model(Database.forURL("jdbc:h2:mem:testdb", driver = "org.h2.Driver"))
m.cleanDB
}
trait ProductionDB extends DbConfig {
val m = new Model(Database.forURL("jdbc:postgresql:silly:productionDB", driver = "org.postgresql.Driver"))
}
trait SillySystem extends HttpService {
this: DbConfig => // self-type. SillySystem is not a DbConfig, but it can use methods of DbConfig.
....
// System logic
....
}
val testService = new SillySystem with TestDB
val productionService = new SillySystem with ProductionDB
val wrongService1 = new SillySystem // error: trait SillySystem is abstract; cannot be instantiated
val wrongService2 = new SillySystem with DbConfig // error: object creation impossible, since method m in trait DbConfig of type => Model is not defined
val correctService = new SillySystem with DbConfig { val m = new Model(...) } // correct
Related
I have typeclass:
trait ProcessorTo[T]{
def process(s: String): T
}
and its implementation
class DefaultProcessor extends ProcessorTo[String]{
def process(s: String): String = s
}
trait DefaultProcessorSupport{
implicit val p: Processor[String] = new DefaultProcessor
}
To make it available for using I created
object ApplicationContext
extends DefaultProcessorSupport
with //Some other typeclasses
But now I have to add a processor which performs some DataBase - read. The DB URL etc are placed in condifguration file that is available only a runtime. For now I did the following.
class DbProcessor extends ProcessorTo[Int]{
private var config: Config = _
def start(config: Config) = //set the configuration, open connections etc
//Other implementation
}
object ApplicationContext{
implicit val p: ProcessorTo[Int] = new DbProcessor
def configure(config: Config) = p.asInstanceOf[DbProcessor].start(config)
}
It works for me, but I'm not sure about this technique. Looks strange for me a little bit. Is it a bad practice? If so, what would be a good solution?
I am a bit confused by the requirements as DbProcessor is missing the process implementation(???) and trait ProcessorTo[T] is missing start method which is defined in DbProcessor. So, I will assume the following while answering: the type class has both process and start methods
Define a type class:
trait ProcessorTo[T]{
def start(config: Config): Unit
def process(s: String): T
}
Provide implementations for the type class in the companion objects:
object ProcessorTo {
implicit object DbProcessor extends ProcessorTo[Int] {
override def start(config: Config): Unit = ???
override def process(s: String): Int = ???
}
implicit object DefaultProcessor extends ProcessorTo[String] {
override def start(config: Config): Unit = ???
override def process(s: String): String = s
}
}
and use it in your ApplicationContext as follows:
object ApplicationContext {
def configure[T](config: Config)(implicit ev: ProcessorTo[T]) = ev.start(config)
}
This is a nice blog post about Type Classes: http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html
I don't really see why you need start. If your implicit DbProcessor has a dependency, why not make it an explicit dependency via constructor? I mean something like this:
class DbConfig(val settings: Map[String, Object]) {}
class DbProcessor(config: DbConfig) extends ProcessorTo[Int] {
// here goes actual configuration of the processor using config
private val mappings: Map[String, Int] = config.settings("DbProcessor").asInstanceOf[Map[String, Int]]
override def process(s: String): Int = mappings.getOrElse(s, -1)
}
object ApplicationContext {
// first create config then pass it explicitly
val config = new DbConfig(Map[String, Object]("DbProcessor" -> Map("1" -> 123)))
implicit val p: ProcessorTo[Int] = new DbProcessor(config)
}
Or if you like Cake pattern, you can do something like this:
trait DbConfig {
def getMappings(): Map[String, Int]
}
class DbProcessor(config: DbConfig) extends ProcessorTo[Int] {
// here goes actual configuration of the processor using config
private val mappings: Map[String, Int] = config.getMappings()
override def process(s: String): Int = mappings.getOrElse(s, -1)
}
trait DbProcessorSupport {
self: DbConfig =>
implicit val dbProcessor: ProcessorTo[Int] = new DbProcessor(self)
}
object ApplicationContext extends DbConfig with DbProcessorSupport {
override def getMappings(): Map[String, Int] = Map("1" -> 123)
}
So the only thing you do in your ApplicationContext is providing actual implementation of the DbConfig trait.
To work with OAuth2, Slick, Akka-HTTP in scala, I'm using the code from cdiniz https://github.com/cdiniz/slick-akka-http-oauth2.
I tried to create a DAO: UserDao and respective API: UserApi to fetch all records(in JSON) from database after successful verification of OAuth2.
BaseDao Code:
trait BaseDao[T,A] {
...
def getAll(): Future[Seq[A]]
}
class BaseDaoImpl[T <: BaseTable[A], A <: BaseEntity]()(implicit val tableQ: TableQuery[T], implicit val db: JdbcProfile#Backend#Database,implicit val profile: JdbcProfile) extends BaseDao[T,A] with Profile with DbModule {
import profile.api._
...
override def getAll(): Future[Seq[A]] = {
db.run(tableQ.result)
}
}
UserDao Code:
object UserService {
val modules = new ConfigurationServiceImpl with ActorServiceImpl with PersistenceServiceImpl
import modules._
class UserDao extends BaseDaoImpl[UserTable,UserEntity1] {}
}
UserApi Code:
//using io.circe for json encoder
class UserApi(val modules: Configuration with PersistenceService, val db: DatabaseService)(implicit executionContext: ExecutionContext) extends BaseApi {
override val oauth2DataHandler = modules.oauth2DataHandler
val userService = new UserDao
val testApi = pathPrefix("auth") {
path("users") {
pathEndOrSingleSlash {
get {
authenticateOAuth2Async[AuthInfo[OauthAccount]]("realm", oauth2Authenticator) {
auth => complete(userService.getAll().map(_.asJson))
}
}
}
}
}
}
Supporting Codes:
trait Profile {
val profile: JdbcProfile
}
trait DbModule extends Profile{
val db: JdbcProfile#Backend#Database
}
trait PersistenceService {
val accountsDao: AccountsDao
val oAuthAuthorizationCodesDao: OAuthAuthorizationCodesDao
val oauthClientsDao: OAuthClientsDao
val oauthAccessTokensDao: OAuthAccessTokensDao
val oauth2DataHandler : DataHandler[OauthAccount]
}
trait PersistenceServiceImpl extends PersistenceService with DbModule{
this: Configuration =>
private val dbConfig : DatabaseConfig[JdbcProfile] = DatabaseConfig.forConfig[JdbcProfile]("mysqldb")
override implicit val profile: JdbcProfile = dbConfig.driver
override implicit val db: JdbcProfile#Backend#Database = dbConfig.db
override val accountsDao = new AccountsDaoImpl
override val oAuthAuthorizationCodesDao = new OAuthAuthorizationCodesDaoImpl
override val oauthClientsDao = new OAuthClientsDaoImpl(this)
override val oauthAccessTokensDao = new OAuthAccessTokensDaoImpl(this)
override val oauth2DataHandler = new OAuth2DataHandler(this)
}
Here, The code runs without any issue but doesn't get any records as the response though there are records.
What is the problem in this code that I cannot look into? Any suggestion would be appreciable.
I have an issue with Slick configuration in my Play application (Play 2.4.3).
I read documentation article but want to move dbConfig from controller to specified trait and mixin this trait to repository class.
There are several files in project: ClientRepository (class), BaseClientRepository (trait) and BaseDbRepository (trait).
trait BaseDbRepository {
val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
import dbConfig.driver.api._
def withConnection[T](f: => DBIOAction[T, NoStream, Nothing]) = {
dbConfig.db.run(f)
}
}
trait BaseClientRepository {
def getById(id: Int): Future[Client]
def getByLocation(location: String): Future[Seq[Client]]
}
class ClientRepository extends BaseDbRepository with BaseClientRepository {
def getById: Future[Client] = withConnection {
...
}
def getByLocation: Future[Seq[Client]] = withConnection {
...
}
}
This works great with my Client controller:
class Client extends Controller {
def getById(id: Int) = ???
}
But when I try to use DI with Guice:
class Client #Inject()(clientRepository: BaseClientRepository) extends Controller {
def getById(id: Int) = Action.async {
// I try to use client repository here
}
}
It failes with the following exception
CreationException: Unable to create injector, see the following errors:
1) An exception was caught and reported. Message: There is no started application
at com.google.inject.util.Modules$OverrideModule.configure(Modules.java:177)
I tried to move this definition val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current) into Global.scala and it just works, but as I now Global.scala is deprecated now.
So, where is the best place for it?
Update: I use injection module for DI configuration:
class InjectionModule extends AbstractModule {
def configure() {
bind(classOf[BaseClientRepository]).toInstance(new ClientRepository)
}
}
dbConfig should be a lazy val or a function in this case.
That works for me:
private lazy val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
Guice failed to inject an implementation of BaseClientRepository, the annotation #ImplementedBy can help.
#ImplementedBy(classOf[ClientRepository])
trait BaseClientRepository {
def getById(id: Int): Future[Client]
def getByLocation(location: String): Future[Seq[Client]]
}
I have a trait and a class that extends the trait. I can use the methods from the trait as follows:
trait A {
def a = ""
}
class B(s: String) extends A {
def b = a
}
However, when I use the trait's method in the constructor like this:
trait A {
def a = ""
}
class B(s: String) extends A {
def this() = this(a)
}
then the following error appears:
error: not found: value a
Is there some way to define default parameters for the construction of classes in the trait?
EDIT: To clarify the purpose: There is the akka-testkit:
class TestKit(_system: ActorSystem) extends { implicit val system = _system }
And each test looks like this:
class B(_system: ActorSystem) extends TestKit(_system) with A with ... {
def this() = this(actorSystem)
...
}
because I want to create common creation of the ActorSystem in A:
trait A {
val conf = ...
def actorSystem = ActorSystem("MySpec", conf)
...
}
It's a little bit tricky because of Scala initialization order. The simplest solution I found is to define a companion object for your class B with apply as factory method:
trait A {
def a = "aaaa"
}
class B(s: String) {
println(s)
}
object B extends A {
def apply() = new B(a)
def apply(s: String) = new B(s)
}
Most of the examples of the Cake Pattern I've come across appear to consider dependencies as singleton type services; where there is only one instance of each type in the final assembly of components. Is it possible to write a configuration that has more than one instance of a particular type, perhaps configured in different ways, when using the Cake Pattern for dependency injection?
Consider the following components. Generic HTTP service:
trait HttpService { def get(query:String):String }
trait HttpServiceComponent {
val httpService:HttpService
class HttpServiceImpl(address:String) extends HttpService {
def get(query:String):String = ...
}
}
Trade & Company services, that each depend on an HttpService, which may be different instances:
trait TradeService { def lastTrade(symbol:String):String }
trait TradeServiceComponent {
this:HttpServiceComponent => // Depends on HttpService
val tradeService:TradeService
class TradeServiceImpl extends TradeService {
def lastTrade(symbol:String):String =
httpService.get("symbol=" + symbol)
}
}
trait CompanyService { def getCompanySymbols(exchange:String):String }
trait CompanyServiceComponent {
this:HttpServiceComponent => // Depends on different HttpService instance
val companyService:CompanyService
class CompanyServiceImpl extends CompanyService {
def getCompanySymbols(exchange:String):String =
httpService.get("exchange=" + exchange)
}
}
Main app component that depends on Trade & Company services:
trait App { def run(exchange:String):Unit }
trait AppComponent {
this:CompanyServiceComponent with TradeServiceComponent =>
val app:App
class AppImpl extends App {
def run(exchange:String) =
companyService.getCompanySymbols(exchange).split(",").foreach(sym => {
val lastTrade = tradeService.lastTrade(sym)
printf("Last trade for %s: %s".format(sym, lastTrade))
})
}
}
Is it possible to wire up the App so that its TradeService uses a HttpService that points to one address, and its CompanySerivce uses a different HttpService instance pointing to another address?
As you can see from the answers (notably Daniel's, but also your own), it is possible, but it doesn't look elegant. The difficulty appears because when you use the Cake pattern, you mix all required traits into one object (using "with" keyword), and you cannot mix a trait more than once into one instance. That is how mixins work, and the Cake is based on them.
The fact you can force Cake to handle non-singleton dependencies doesn't mean you should do it. I would advise you to simply use plain-old constructor in such cases, that is where self-type annotation doesn't fit well:
trait HttpService { ... }
/* HttpServiceImpl has become a top-level class now,
* as the Cake pattern adds no more value here.
* In addition, trait HttpServiceComponent gets deleted */
class HttpServiceImpl(address:String) extends HttpService {
...
}
trait TradeService { def lastTrade(symbol:String):String }
trait TradeServiceComponent {
// The dependency on HttpService is no longer declared as self-type
val tradeService:TradeService
// It is declared as a constructor parameter now
class TradeServiceImpl(httpService: HttpService) extends TradeService {
def lastTrade(symbol:String):String =
httpService.get("symbol=" + symbol)
}
}
trait CompanyService { def getCompanySymbols(exchange:String):String }
trait CompanyServiceComponent {
// Again, self-type annotation deleted
val companyService:CompanyService
// Again, the dependency is declared as a constructor parameter
class CompanyServiceImpl(httpService: HttpService) extends CompanyService {
def getCompanySymbols(exchange:String):String =
httpService.get("exchange=" + exchange)
}
}
The App and AppComponent traits stay in their original form. Now you can use the all components in the following way:
object App {
def main(args:Array[String]):Unit = {
val appAssembly = new AppComponent
with TradeServiceComponent
with CompanyServiceComponent {
// Note, that HttpServiceComponent it neither needed nor mixed-in now
val tradeService = new TradeServiceImpl(
new HttpServiceImpl("http://trades-r-us.com"))
val companyService = new CompanyServiceImpl(
new HttpServiceImpl("http://exchange-services.com"))
val app = new AppImpl
}
appAssembly.app.run(args(0))
}
}
Also, you may want do double-check if the Cake pattern is really best suited for your needs, as it is actually a complex pattern and dependency injection is only one part of it. If you use it only for DI, I would advise you to use a simpler solution. I've blogged about that here.
Since each "client" may need a different implementation, you could just parameterize the service.
trait HttpService { def get(query:String):String }
trait HttpServiceComponent {
def httpService(name: String):HttpService
class HttpServiceImpl(address:String) extends HttpService {
def get(query:String):String = ...
}
}
To be used like this:
trait TradeService { def lastTrade(symbol:String):String }
trait TradeServiceComponent {
this:HttpServiceComponent => // Depends on HttpService
val tradeService:TradeService
class TradeServiceImpl extends TradeService {
def lastTrade(symbol:String):String =
httpService("TradeService").get("symbol=" + symbol)
}
}
The final mix would then do something like this:
trait AppComponent {
this:CompanyServiceComponent with TradeServiceComponent =>
val httpServices = Map( "TradeService" -> new HttpServiceImpl("http://trades-r-us.com"),
"CompanyService" -> new HttpServiceImpl("http://exchange-services.com"))
def httpService(name: String) = httpServices(name)
This compiles and runs as expected, but it leaves a lot to be desired:
object App {
def main(args:Array[String]):Unit = {
val tradeServiceAssembly = new TradeServiceComponent with HttpServiceComponent {
val httpService = new HttpServiceImpl("http://trades-r-us.com")
val tradeService = new TradeServiceImpl
}
val companyServiceAssembly = new CompanyServiceComponent with HttpServiceComponent {
val httpService = new HttpServiceImpl("http://exchange-services.com")
val companyService = new CompanyServiceImpl
}
val appAssembly = new AppComponent
with TradeServiceComponent
with CompanyServiceComponent
with HttpServiceComponent {
lazy val httpService = error("Required for compilation but not used")
val tradeService = tradeServiceAssembly.tradeService
val companyService = companyServiceAssembly.companyService
val app = new AppImpl
}
appAssembly.app.run(args(0))
}
}