questionersam
questionersam

Reputation: 1125

Scala trait mixins for an object/ typesafe Config

This is an OO design Q. Im using typesafe Config in my App. The Config interface is very useful, however there are a couple of fields in my applications; config file that are mandatory. What I wanted to do was create a subInterface of Config and add these 2 top-level methods . Something like this

trait AppConfig extends Config{
    def param1:String 
    def param2:String 
}

However creating a real instance of AppConfig given an instance of Config doesnt seem feasible.( I dont want to create wrapper object and duplicate all the methods on the Config interface) . Ideally , Im looking for something that would achieve something close to this

val conf:Config = //get config object from somewhere
return conf with AppConfig { overrider def param1 = {"blah"} }

I do understand the last line is not valid . But Im looking for a pattern/construct with an equivalent functionality.

Upvotes: 4

Views: 1795

Answers (3)

Régis Jean-Gilles
Régis Jean-Gilles

Reputation: 32729

What you are looking for is basically what some call a "dynamic mixin". This is not supported out of the box by scala. Someone developed a compiler plugin to support this: http://www.artima.com/weblogs/viewpost.jsp?thread=275588

However it's a bit old and the project does not seem active anymore.

A better alternative would be to implement this feature using scala macros (requires scala 2.10, which has no stable release yet).

All of this is probably overkill in your case though. Until some well tested library is available, I would advise to just create the proxy by hand (however dull that may look):

trait ConfigProxy extends Config {
  def impl: Config
  // Forward to the inner config
  def root: ConfigObject = impl.root
  def origin: ConfigOrigin = impl.origin
  //... and so on
}

val conf:Config = //get config object from somewhere
val myConf: AppConfig = new AppConfig with ConfigProxy { 
  val impl = conf
  val param1:String = "foo"
  val param2:String = "bar"
}

Upvotes: 1

sourcedelica
sourcedelica

Reputation: 24047

How about using a combination of Dynamic and Reflection. Dynamic to handle your convenience methods and reflection to handle the methods on config.

Here's a stab:

class ConfigDynamic(val config: Config) extends Dynamic {
  def selectDynamic(name: String)= {
    name match {
      case "field1" => 
        config.getString("field1")
      case x @ _ =>
        // overly simplified here
        val meth = configClass.getDeclaredMethod(x)
        meth.invoke(config)
    }
  }
}

Upvotes: 1

Noah
Noah

Reputation: 13959

We've been using Configrity for things like this. Here's an example:

We keep our compiled defaults that we use for unit test/etc in objects

object DefaultConfigs {
  val defaultConfig = Configuration(
    "param1" -> "blah"
  )

  val otherConfig = Configuration(
    "param2" -> "blah"
  )

  val appConfig = defaultConfig.include(otherConfig)
}

And then at run time we can include them or not

val appConfig = Configuration.load(pathToConfig) include DefaultConfigs.appConfig

Upvotes: 2

Related Questions