philiprehberger-config_kit
Layered configuration with YAML, ENV, and defaults
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem "philiprehberger-config_kit"
Or install directly:
gem install philiprehberger-config_kit
Usage
require "philiprehberger/config_kit"
config = Philiprehberger::ConfigKit.define do
string :app_name, default: "my-app"
integer :port, default: 3000
boolean :debug, default: false
end
config[:app_name] # => "my-app"
config[:port] # => 3000
config[:debug] # => false
With YAML file
# config.yml
app_name: "production-app"
port: 8080
config = Philiprehberger::ConfigKit.define(yaml: "config.yml") do
string :app_name, default: "my-app"
integer :port, default: 3000
boolean :debug, default: false
end
config[:app_name] # => "production-app" (from YAML)
config[:port] # => 8080 (from YAML)
config[:debug] # => false (from default)
With ENV overrides
config = Philiprehberger::ConfigKit.define(yaml: "config.yml") do
string :app_name, default: "my-app", env: "APP_NAME"
integer :port, default: 3000, env: "PORT"
boolean :debug, default: false, env: "DEBUG"
end
# ENV["PORT"] = "9090" would override both YAML and default
config[:port] # => 9090
Nested configuration
Dot-notation keys map to nested YAML structures and uppercase ENV variables with underscores:
config = Philiprehberger::ConfigKit.define(yaml: "config.yml") do
string "database.host", default: "localhost"
integer "database.port", default: 5432
end
# Reads from YAML: database: { host: "db.example.com" }
# Or from ENV: DATABASE_HOST=db.example.com
config["database.host"] # => "db.example.com"
config.to_h # => { "database" => { "host" => "...", "port" => 5432 } }
Array and hash types
config = Philiprehberger::ConfigKit.define do
array :tags, of: :string # ENV: splits by comma — "ruby,web,api"
array :ports, of: :integer # ENV: "3000,4000,5000" => [3000, 4000, 5000]
hash_type :redis # ENV: collects REDIS_HOST, REDIS_PORT into hash
end
config[:tags] # => ["ruby", "web", "api"]
config[:ports] # => [3000, 4000, 5000]
config[:redis] # => { "host" => "localhost", "port" => "6379" }
Nested access
Use #dig to walk nested hash and array values with standard Ruby dig semantics. Returns nil if any intermediate key is missing.
config = Philiprehberger::ConfigKit.define(yaml: "config.yml") do
hash_type :database
end
# Given YAML: database: { host: "localhost", port: 5432 }
config.dig(:database, "host") # => "localhost"
config.dig(:database, "missing") # => nil
config.dig(:nonexistent, :deep) # => nil
Required keys
config = Philiprehberger::ConfigKit.define do
required :api_key, type: :string, env: "API_KEY"
required :port, type: :integer, env: "PORT"
end
# Raises ConfigKit::MissingKeyError if API_KEY or PORT is not set
Resolution order
Values are resolved in this order (highest priority first):
- ENV variables (auto-generated or explicit
env:key) - YAML file (if
yaml:path is provided and the key exists) - Defaults (from the schema definition)
API
| Method | Description |
|---|---|
Philiprehberger::ConfigKit.define(yaml:, env:, &block) |
Create a new config store |
config.get(key) / config[key] |
Get a config value |
config.dig(*keys) |
Walk nested hash/array values; returns nil if any key is missing |
config.to_h |
Export all values as a nested hash |
config.keys |
List all defined keys |
config.key?(key) |
Check if a key is defined |
Schema DSL
| Method | Type | Cast behavior |
|---|---|---|
string(name, default:, env:) |
:string |
.to_s |
integer(name, default:, env:) |
:integer |
Integer(value) |
float(name, default:, env:) |
:float |
Float(value) |
boolean(name, default:, env:) |
:boolean |
true/"true"/"1"/"yes" are truthy |
array(name, of:, default:, env:) |
:array |
Splits ENV by comma, coerces each element |
hash_type(name, default:, env:) |
:hash |
Collects KEY_* ENV vars into a hash |
required(name, type:, env:) |
varies | Raises MissingKeyError if no value found |
Development
bundle install
bundle exec rspec
bundle exec rubocop
Support
If you find this project useful: