Configuration
About configuration
There are times when you need to avoid hard coding value in application because you need to change it without having to rebuild application binary. For example, you need to be able to store database credential in configuration rather than hard coding, so you can change it easily.
Fano Framework provides IAppConfiguration interface for that purpose.
IAppConfiguration
IAppConfiguration interface provides several methods,
getString()accepts name and returns string value.getInt()accepts name returns integer value.getBool()accepts name returns boolean value.getFloat()accepts name returns double value.has()test if name is exists in configuration.
Built-in IAppConfiguration implementation
Fano Framework provides TJsonFileConfig, TIniFileConfig, TEnvConfig class which loads configuration data from JSON, INI file and environment variables respectively.
Fano Framework also provides TCompositeConfig and TNullConfig class. First one is IAppConfiguration implementation with capability to combine twoIAppConfiguration instance and latter is null class implements IAppConfiguration interface.
Load config from JSON
var config : IAppConfiguration;
...
config := TJsonFileConfig.create(
getCurrentDir() + '/config/config.json'
);
Load config from INI
var config : IAppConfiguration;
...
config := TIniFileConfig.create(
getCurrentDir() + '/config/config.ini',
'fano'
);
Last parameter is name of default section to use. Read INI file configuration section in this document for more information.
Load configuration from environment variables
var config : IAppConfiguration;
...
config := TEnvConfig.create();
Combine multiple configurations as one
TCompositeConfig allows you to use multiple configurations. In following setup,
if configuration not found in environment variables, then it will try to find it in
config.json.
var config : IAppConfiguration;
...
config := TCompositeConfig.create(
TEnvConfig.create(),
TJsonFileConfig.create(
getCurrentDir() + '/config/config.json'
)
);
Register config instance to dependency container
To be able to use TJsonFileConfig, TIniFileConfig, TEnvConfig, TCompositeConfig and TNullConfig class with dependency container, Fano Framework provides TJsonFileConfigFactory, TIniFileConfigFactory, TEnvConfigFactory, TCompositeConfigFactory and TNullConfigFactory class which enables you to register above classes in container.
Register JSON config
container.add(
'config',
TJsonFileConfigFactory.create(
getCurrentDir() + '/config/config.json'
)
);
Register INI config
container.add(
'config',
TIniFileConfigFactory.create(
getCurrentDir() + '/config/config.ini'
)
);
Register environment variable config
container.add(
'config',
TEnvConfigFactory.create()
);
Register composite configurations
container.add(
'config',
TCompositeConfigFactory.create(
TNullConfigFactory.create(),
TJsonFileConfigFactory.create(getCurrentDir() + '/config/config.json')
)
);
Retrieve configuration instance from dependency container
To get configuration instance
var config : IAppConfiguration;
...
config := container.get('config') as IAppConfiguration;
or with array-like syntax
config := container['config'] as IAppConfiguration;
Read configuration data
Suppose you have JSON file with content as follows
{
"appName" : "MyApp",
"baseUrl" : "http://myapp.fano",
"session" : {
"name" : "fano_sess",
"dir" : "storages/sessions/"
},
"cookie" : {
"domain" : "myapp.fano",
"maxAge" : 3600
}
}
To get baseUrl value,
var baseUrl : string;
...
baseUrl := config.getString('baseUrl');
To get dir value,
var sessionDir : integer;
...
sessionDir := config.getString('session.dir');
To get maxAge value,
var cookieMaxAge : integer;
...
cookieMaxAge := config.getInt('cookie.maxAge');
getString(), getInt(), getBool() and getFloat() methods accept second parameter
if you want to use different value when key does not exist.
baseUrl := config.getString('baseUrl', 'https://example.com');
If key baseUrl is not found, it returns second parameter value.
INI file configuration
TIniFileConfig is thin wrapper of Free Pascal TIniFile. TIniFile cannot read data from INI file that has no section. Your INI file must contain at least one section which serve as default section. The last parameter of TIniFileConfig’s constructor expect name of default section. If you use TIniFileConfigFactory, by default it uses fano as default section if not specified. You can specify default section by calling setDefaultSection() method as shown in following code.
container.add(
'config',
TIniFileConfigFactory.create(
getCurrentDir() + '/config/config.ini'
).setDefaultSection('hello')
);
Consider reading cookie.maxAge configuration in code example above. It will read maxAge from cookie section.
[cookie]
maxAge=3600
However, because nested section are not allowed in INI file, you can only read one section. For example,
nestedData := config.getInt('fano.data.nested');
will read data from
[fano]
data.nested=test
Setting up application configuration with Fano CLI
When creating new project, you can use --config parameter to setup application configuration.
Read Setup application configuration when creating project for more information.
You can also manually setting application configuration by overriding buildAppConfig() method of TBasicAppServiceProvider or TDaemonAppServiceProvider class as shown in following code sample.
TAppServiceProvider = class(TDaemonAppServiceProvider)
protected
function buildAppConfig(
const container : IDependencyContainer
) : IAppConfiguration; override;
...
end;
...
function TAppServiceProvider.buildAppConfig(
const container : IDependencyContainer
) : IAppConfiguration;
begin
container.add(
'config',
TIniFileConfigFactory.create(
getCurrentDir() + '/config/config.ini'
)
);
result := container['config'] as IAppConfiguration;
end;