Every now and then it's useful if your application tweaks itself to the environment its running in; be it development, testing or production.
For example, during development its great for your web application to serve up 500 error pages with stack traces, session dumps and other detailed system information. But in production, so not to scare users, you're gonna want to turn that off and serve up a plain Internal Application Error page instead.
Other reasons may include environment specific database connections, email accounts and so on.
IocEnv is a small utility library for Fantom that scans system information to determine what environment your application is running in. It looks at environment variables, program arguments and even offers a manual override option.
IocEnv is built on top of IoC and makes the IocEnv class available as an IoC service. It also integrates with IocConfig to give you a handful of injectable config values. All of these may be freely used by your IoC application.
Usage - Service Injection
IocEnv uses IoC for Dependency Injection (DI) so we can inject the service in the usual manner:
using afIoc::Inject using afIocEnv::IocEnv class MyService { @Inject IocEnv iocEnv ... Void wotever() { if (iocEnv.isDev) { ...// dev only stuff} } }
The IocEnv class holds the environment setting and has handy utility methods for some common use cases.
Usage - Config Injection
IocEnv also makes available some IocConfig values, allowing you to inject Bool's straight into your service:
using afIoc::Inject using afIocConfig::Config class MyService { @Config { id="afIocEnv.isDev" } @Inject Bool isDev ... Void wotever() { if (isDev) { ...// dev only stuff} } }
Configuring the Environment
The environment
may be obtained in a number of ways. In order of precedence, it is inferred by the following:
- Manual Override
You may manually set the environment when you create an IocEnv instance.
- Program Arguments
IocEnv checks the program arguments that Fantom was launched with. It looks for an option labelled
-env
or-environment
and assumes the environment is the parameter following. Example:fan afBedSheet myWebApp 8080 -env test
- Environment Variables
IocEnv looks for an environment variable called
env
orenvironment
The ordering means the environment variables are checked first but may be overridden by program arguments. Subsequently, program arguments may be overridden by setting the environment manually.
The convention for prefixing program arguments with a hyphen is taken from the @Opt facet and AbstractMain. If no environment setting is found, it defaults to PRODUCTION
as this is often the lowest common denominator. It also means it's easy to deploy your application to production, which is good, as prod deployments can be tricky enough!
To ensure apps always run in development mode on your local machine, set a new environment variable called ENV
to the value dev
or development
.
Manual Override
Sometimes you want to override the environment setting programmatically - which is common in testing. You can do this by manually creating the IocEnv service in a test specific AppModule, passing in whatever environment you want.
@Override IocEnv overrideIocEnv() { return IocEnv.fromStr("Testing") }
As we can't change or un-make the existing IocEnv service, we create a new service instance and contribute it as a service override. IoC then uses the override everywhere in place of the original.
Complete Example
This example has been successfully tested with:
- Fantom 1.0.68
- Ioc 3.0.2
- IocEnv 1.1.0
- IocConfig 1.1.0
1). Create a text file called Example.fan
:
using afIoc using afIocEnv using afIocConfig const class Example { @Inject const IocEnv iocEnv// --> Inject IocEnv service@Config { id="afIocEnv.isProd" }// --> Inject Config valuesconst Bool isProd new make(|This| in) { in(this) } Void printEnv() { echo("The environment is '${iocEnv.env}'") if (isProd) { echo("I'm in Production!") } else { echo("I'm in Development!!") } } }// ---- Standard afIoc Support Classes ----class Main { Void main() { registry := RegistryBuilder() { addModulesFromPod("afIocEnv") addService(Example#) }.build() example := (Example) registry.rootScope.serviceByType(Example#) example.printEnv() } }
2). Run Example.fan
as a Fantom script from the command line:
C:\> fan Example.fan -env PRODUCTION [info] [afIocEnv] Setting from environment variable 'env' : development [info] [afIocEnv] Overriding from cmd line argument '-env' : PRODUCTION The environment is 'PRODUCTION' I'm in Production!
Epilogue
We have seen it is useful and important for an application to distinguish between the environments it runs in. We've looked at how the IocEnv library is environmentally aware and how your application can use it to make decisions dependent on the environment it runs in.
Have fun!
Edits
- 31 Jul 2016 - Updated examples to use IoC 3.0 and IocEnv 1.1.
- 3 Dec 2013 - Original article.