specific config by environment in Scala

2019-01-31 23:44发布

What is a good way to set up a project in Scala which uses different configuration depending on environments.

I need to specifically have different databases for development, test and production environment (similar to what is done in Rails)

4条回答
戒情不戒烟
2楼-- · 2019-01-31 23:48

I wasn't happy with how Daniel Cukiers solution did not allow defaults and overrides, so I changed it around to make full use of those.

The only configuration you have to do is set a ENVIRONMENT variable on the system (defaults to 'dev' if none is set)

(Java solution, compatible with Scala):

import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;

public class MyCompanyConfig {
    public final static Config base = ConfigFactory.load().getConfig("mycompany");
    public final static String environment = System.getenv("ENVIRONMENT") == null ? "dev" : System.getenv("ENVIRONMENT");

    /**
     * Returns a subtree of the base configuration with environment settings applied.
     *
     * @param setting The subtree to return config for.
     * @return A config with base in given setting, with environment modifications applied.
     */
    public static Config load(String setting) {

        Config config = base.getConfig(setting);

        if (config.hasPath(environment)) {
            return config.getConfig(environment).withFallback(config);
        }

        return config;
    }
}

This allows a single reference.conf in a library looking like this:

mycompany.module1 {
    setting1 : "adefaultvalue"
    url : "localhost"

    test {
        // will be used where ENVIRONMENT="test"
        url : "test.mycompany.com"
    }

    prod {
        // will be used where ENVIRONMENT="prod"
        setting1 : "changethedefault"
        url : "www.mycompany.com"
    }
}

Usage:

Config conf = MyCompanyConfig.load("module1")
查看更多
女痞
3楼-- · 2019-01-31 23:50

Another strategy I'm using consists of using includes. I usually store my DEV settings in the default application.conf file then I create a new conf file for other environments and include the default one.

Let's say my DEV conf application.conf looks like this:

myapp {
    server-address = "localhost"
    server-port = 9000

    some-other-setting = "cool !"
}

Then for the PROD, I could have another file called prod.conf:

include "application"

# override default (DEV) settings
myapp {
    server-address = ${PROD_SERVER_HOSTNAME}
    server-port = ${PROD_SERVER_PORT}
}

Note that I override only the settings that change in the PROD environment (some-other-setting is thus the same as in DEV).

The config bootstrap code doesn't test anything

...
val conf = ConfigFactory.load()
...

To switch from the DEV to the PROD conf, simply pass a system property with the name of the config file to load:

java -Dconfig.resource=prod.conf ...

In DEV, no need to pass it since application.conf will be loaded by default.

So here we're using Typesafe Config's default loading mechanism to achieve this.

I've created a simple project to demonstrate this technique. Feel free to clone and experiment.

查看更多
爱情/是我丢掉的垃圾
4楼-- · 2019-02-01 00:09

Use typesafe Config. Create a Config object like this:

import com.typesafe.config._

object Config {
  val env = if (System.getenv("SCALA_ENV") == null) "development" else System.getenv("SCALA_ENV")

  val conf = ConfigFactory.load()
  def apply() = conf.getConfig(env)
}

Then create the application.conf file in src/main/resources folder:

development {
  your_app {
    databaseUrl = "jdbc:mysql://localhost:3306/dev_db"
    databaseUser = "xxxx"
    databasePassword = "xxxx"
  }
}
test {
  your_app {
    databaseUrl = "jdbc:mysql://localhost:3306/test_db"
    databaseUser = "xxxxx"
    databasePassword = "xxxx"
  }
}

Now from anywhere in your application, you can access configuration:

Config().getString("your_app.databaseUrl")

If you have your environment set up (e.g. export SCALA_ENV=test) when you run your application, it will consider the right configuration section. The default is development

查看更多
再贱就再见
5楼-- · 2019-02-01 00:12

Here is a solution that is in Scala, allows for overrides, and doesn't rely on an external library.

object Config {

  var test: Map[String, String] = {
    Map(
      "libsvmData" -> new java.io.File("./src/test/resources/sample_libsvm_data.txt").getCanonicalPath,
      "somethingElse" -> "hi"
    )
  }

  var production: Map[String, String] = {
    Map(
      "libsvmData" -> "s3a://my-cool-bucket/fun-data/libsvm.txt",
      "somethingElse" -> "whatever"
    )
  }

  var environment = sys.env.getOrElse("PROJECT_ENV", "production")

  def get(key: String): String = {
    if (environment == "test") {
      test(key)
    } else {
      production(key)
    }
  }

}

If PROJECT_ENV is set to test, Config.get("somethingElse") will return "hi". Otherwise, it will return "whatever".

Running PROJECT_ENV=test sbt test will get old fast, so you can have SBT set the environment variable when the test suite is run.

fork in Test := true
envVars in Test := Map("PROJECT_ENV" -> "test")

Here is how to override the existing config.

Config.test = Config.test ++ Map("somethingElse" -> "give me clean air")

Here's a link to the full blog post I wrote on this topic.

查看更多
登录 后发表回答