Cookbook
Ready-to-use recipes for common configuration patterns.
Recipe 1: Database Configuration
Load database settings with environment override for production.
config.json
{
"database": {
"host": "localhost",
"port": 5432,
"name": "myapp_dev",
"pool_size": 5
}
}
Eiffel Code
class
DATABASE_SETTINGS
create
make
feature
host: STRING
port: INTEGER
name: STRING
pool_size: INTEGER
make (a_config: SIMPLE_CONFIG)
do
-- Environment variables override config in production
host := a_config.string_value_or_env_or_default (
"database.host", "DB_HOST", "localhost")
port := a_config.integer_value_or_env (
"database.port", "DB_PORT", 5432)
name := a_config.string_value_or_env_or_default (
"database.name", "DB_NAME", "myapp")
pool_size := a_config.integer_value_or_default (
"database.pool_size", 10)
end
connection_string: STRING
do
Result := "host=" + host + " port=" + port.out +
" dbname=" + name
end
end
Recipe 2: Multi-Environment Config
Base config with environment-specific overrides.
config.json (base)
{
"app": {
"name": "MyApp",
"debug": false,
"log_level": "info"
},
"server": {
"port": 8080,
"host": "0.0.0.0"
}
}
config.development.json
{
"app": {
"debug": true,
"log_level": "debug"
}
}
config.production.json
{
"server": {
"port": 80
}
}
Eiffel Code
local
config: SIMPLE_CONFIG
env: STRING
do
-- Load base config
create config.make_with_file ("config.json")
-- Get current environment
env := ((create {SIMPLE_ENV}).get ("APP_ENV")).to_string_8
if env = Void then
env := "development"
end
-- Merge environment-specific config
config.merge_file ("config." + env + ".json")
-- Now config has merged values
print ("Debug: " + config.boolean_value ("app.debug").out)
end
Recipe 3: Feature Flags
Toggle features on/off without code changes.
config.json
{
"features": {
"new_ui": false,
"beta_api": false,
"metrics": true,
"cache": true
}
}
Eiffel Code
class
FEATURE_FLAGS
create
make
feature
config: SIMPLE_CONFIG
make (a_config: SIMPLE_CONFIG)
do
config := a_config
end
is_enabled (a_feature: STRING): BOOLEAN
do
Result := config.boolean_value_or_default (
"features." + a_feature, False)
end
with_feature (a_feature: STRING; a_action: PROCEDURE)
-- Execute action only if feature is enabled
do
if is_enabled (a_feature) then
a_action.call
end
end
end
-- Usage
flags.with_feature ("new_ui", agent render_new_ui)
flags.with_feature ("metrics", agent collect_metrics)
Recipe 4: Service Registry
Configure multiple service endpoints.
config.json
{
"services": {
"auth": {
"url": "https://auth.example.com",
"timeout": 30,
"retries": 3
},
"storage": {
"url": "https://storage.example.com",
"timeout": 60,
"retries": 5
},
"email": {
"url": "https://email.example.com",
"timeout": 10,
"retries": 2
}
}
}
Eiffel Code
class
SERVICE_REGISTRY
create
make
feature
config: SIMPLE_CONFIG
make (a_config: SIMPLE_CONFIG)
do
config := a_config
end
service_url (a_service: STRING): STRING
do
Result := config.string_value_or_default (
"services." + a_service + ".url", "")
end
service_timeout (a_service: STRING): INTEGER
do
Result := config.integer_value_or_default (
"services." + a_service + ".timeout", 30)
end
service_retries (a_service: STRING): INTEGER
do
Result := config.integer_value_or_default (
"services." + a_service + ".retries", 3)
end
end
-- Usage
registry.service_url ("auth") -- "https://auth.example.com"
registry.service_timeout ("storage") -- 60
Recipe 5: User Preferences with Save
Save user preferences to a config file.
Eiffel Code
class
USER_PREFERENCES
create
make
feature
config: SIMPLE_CONFIG
file_path: STRING
make (a_path: STRING)
do
file_path := a_path
create config.make_with_file (a_path)
end
theme: STRING
do
Result := config.string_value_or_default ("theme", "light")
end
set_theme (a_theme: STRING)
do
config.set_string ("theme", a_theme)
end
font_size: INTEGER
do
Result := config.integer_value_or_default ("font_size", 14)
end
set_font_size (a_size: INTEGER)
do
config.set_integer ("font_size", a_size)
end
recent_files: ARRAYED_LIST [STRING]
do
Result := config.string_list ("recent_files")
end
save
do
if config.is_modified then
config.save
end
end
is_dirty: BOOLEAN
do
Result := config.is_modified
end
end
Recipe 6: Logging Configuration
Configure logging levels and handlers.
config.json
{
"logging": {
"level": "info",
"handlers": {
"console": {
"enabled": true,
"format": "[%level%] %message%"
},
"file": {
"enabled": true,
"path": "/var/log/myapp.log",
"max_size_mb": 10,
"rotate_count": 5
}
}
}
}
Eiffel Code
class
LOGGING_CONFIG
create
make
feature
level: STRING
console_enabled: BOOLEAN
console_format: STRING
file_enabled: BOOLEAN
file_path: STRING
max_size_mb: INTEGER
rotate_count: INTEGER
make (a_config: SIMPLE_CONFIG)
do
level := a_config.string_value_or_default ("logging.level", "info")
-- Console handler
console_enabled := a_config.boolean_value_or_default (
"logging.handlers.console.enabled", True)
console_format := a_config.string_value_or_default (
"logging.handlers.console.format", "[%level%] %message%")
-- File handler
file_enabled := a_config.boolean_value_or_default (
"logging.handlers.file.enabled", False)
file_path := a_config.string_value_or_default (
"logging.handlers.file.path", "app.log")
max_size_mb := a_config.integer_value_or_default (
"logging.handlers.file.max_size_mb", 10)
rotate_count := a_config.integer_value_or_default (
"logging.handlers.file.rotate_count", 5)
end
end
Recipe 7: Validation on Load
Validate required configuration at startup.
Eiffel Code
class
CONFIG_VALIDATOR
feature
validate (a_config: SIMPLE_CONFIG): ARRAYED_LIST [STRING]
-- Return list of validation errors (empty if valid)
local
errors: ARRAYED_LIST [STRING]
do
create errors.make (0)
-- Required keys
if not a_config.has_key ("database.host") then
errors.extend ("Missing required: database.host")
end
-- Type validation
if a_config.has_key ("server.port") then
if a_config.integer_value ("server.port") <= 0 then
errors.extend ("server.port must be positive")
end
end
-- Range validation
if a_config.integer_value ("pool_size") > 100 then
errors.extend ("pool_size cannot exceed 100")
end
Result := errors
end
validate_or_die (a_config: SIMPLE_CONFIG)
-- Validate and exit if errors
local
errors: ARRAYED_LIST [STRING]
do
errors := validate (a_config)
if not errors.is_empty then
print ("Configuration errors:%N")
across errors as e loop
print (" - " + e + "%N")
end
(create {EXCEPTIONS}).die (1)
end
end
end
Recipe 8: Hot Reload Configuration
Reload configuration without restarting application.
Eiffel Code
class
HOT_RELOAD_CONFIG
create
make
feature
config: SIMPLE_CONFIG
file_path: STRING
on_reload: detachable PROCEDURE [SIMPLE_CONFIG]
make (a_path: STRING)
do
file_path := a_path
create config.make_with_file (a_path)
end
set_on_reload (a_handler: PROCEDURE [SIMPLE_CONFIG])
-- Set callback for reload events
do
on_reload := a_handler
end
reload
-- Reload configuration from disk
do
config.load
-- Notify listeners
if attached on_reload as handler then
handler.call ([config])
end
end
check_and_reload
-- Check if file changed and reload if needed
local
l_file: SIMPLE_FILE
do
create l_file.make (file_path)
if l_file.modified_timestamp > last_check then
reload
last_check := l_file.modified_timestamp
end
end
last_check: INTEGER_64
end
-- Usage
hot_config.set_on_reload (agent handle_config_change)
-- In main loop or timer:
hot_config.check_and_reload