philiprehberger-ini_parser
INI file parser and writer with section support and type coercion
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem "philiprehberger-ini_parser"
Or install directly:
gem install philiprehberger-ini_parser
Usage
require "philiprehberger/ini_parser"
config = Philiprehberger::IniParser.parse(<<~INI)
name = MyApp
[database]
host = localhost
port = 5432
ssl = true
INI
config["name"] # => "MyApp"
config["database"]["port"] # => 5432
config["database"]["ssl"] # => true
Loading from a File
config = Philiprehberger::IniParser.load("config.ini")
Serializing to INI
hash = {
"name" => "MyApp",
"database" => { "host" => "localhost", "port" => 5432 }
}
ini_string = Philiprehberger::IniParser.dump(hash)
Philiprehberger::IniParser.save(hash, "output.ini")
Inline Comments
config = Philiprehberger::IniParser.parse(<<~INI)
host = localhost ; the server host
port = 8080 # default port
INI
config["host"] # => "localhost"
config["port"] # => 8080
Multiline Values
config = Philiprehberger::IniParser.parse("description = this is a\\\n long value", coerce_types: false)
config["description"] # => "this is a long value"
Escape Sequences
config = Philiprehberger::IniParser.parse('msg = hello\nworld', coerce_types: false)
config["msg"] # => "hello\nworld"
Variable Interpolation
Expand ${VAR} references in values after parsing. Variables resolve first from parsed INI values (using section.key paths), then fall back to environment variables. Unresolved variables remain as-is.
config = Philiprehberger::IniParser.parse(<<~INI, interpolate: true)
[app]
name = MyApp
[logging]
prefix = ${app.name}-log
INI
config["logging"]["prefix"] # => "MyApp-log"
Include Directives
Process @include path/to/other.ini lines to load and merge referenced files. Circular includes raise an error.
# main.ini:
# @include database.ini
# name = MyApp
config = Philiprehberger::IniParser.parse(File.read("main.ini"), includes: true)
Detailed Validation
Return an array of error hashes with line numbers instead of a simple boolean:
errors = Philiprehberger::IniParser.validate("key = value\nnot valid ini\n[section]")
# => [{line: 2, message: "invalid line: not valid ini"}]
Environment Export
Convert a parsed INI hash to flat KEY=VALUE format suitable for environment variables. Section keys become SECTION_KEY=value (uppercased with underscore separator).
config = Philiprehberger::IniParser.parse(<<~INI)
[database]
host = localhost
port = 5432
INI
env = Philiprehberger::IniParser.to_env(config)
# => "DATABASE_HOST=localhost\nDATABASE_PORT=5432"
Disabling Type Coercion
config = Philiprehberger::IniParser.parse(ini_string, coerce_types: false)
config["database"]["port"] # => "5432" (remains a string)
Merging Configurations
base = Philiprehberger::IniParser.load("defaults.ini")
local = Philiprehberger::IniParser.load("local.ini")
merged = Philiprehberger::IniParser.merge(base, local)
Comparing Configurations
a = Philiprehberger::IniParser.load("old.ini")
b = Philiprehberger::IniParser.load("new.ini")
diff = Philiprehberger::IniParser.diff(a, b)
diff[:added] # => {"section" => {"new_key" => "value"}}
diff[:removed] # => {"section" => {"old_key" => "value"}}
diff[:changed] # => {"section" => {"key" => {from: "old", to: "new"}}}
Dot-Path Access
Retrieve or set nested values using dot-separated paths:
config = Philiprehberger::IniParser.load("config.ini")
Philiprehberger::IniParser.get(config, "database.host") # => "localhost"
Philiprehberger::IniParser.get(config, "database.missing", default: 3306) # => 3306
Philiprehberger::IniParser.set(config, "database.port", 5433)
Validation
Philiprehberger::IniParser.valid?("[section]\nkey = value") # => true
Philiprehberger::IniParser.valid?("not valid ini") # => false
Flatten / Unflatten
config = Philiprehberger::IniParser.load("config.ini")
flat = Philiprehberger::IniParser.flatten(config)
# => {"name" => "MyApp", "database.host" => "localhost", "database.port" => 5432}
nested = Philiprehberger::IniParser.unflatten(flat)
# => {"name" => "MyApp", "database" => {"host" => "localhost", "port" => 5432}}
Deleting by Path
Philiprehberger::IniParser.delete(config, "database.host")
# => "localhost" (removed from config)
Listing Sections
sections = Philiprehberger::IniParser.sections("config.ini")
# => ["database", "logging", "cache"]
API
| Method | Description |
|---|---|
IniParser.parse(string, coerce_types: true, interpolate: false, includes: false) |
Parse an INI string into a Hash |
IniParser.load(path, coerce_types: true, interpolate: false, includes: false) |
Parse an INI file into a Hash |
IniParser.dump(hash) |
Serialize a Hash to an INI string |
IniParser.save(hash, path) |
Write a Hash to an INI file |
IniParser.merge(base, override) |
Deep merge two INI configurations |
IniParser.diff(a, b) |
Compare two parsed hashes and return added, removed, and changed keys |
IniParser.valid?(string) |
Check if an INI string is syntactically valid |
IniParser.validate(string) |
Return array of { line:, message: } hashes for each syntax error |
IniParser.to_env(hash) |
Convert parsed hash to flat SECTION_KEY=value environment format |
IniParser.get(hash, path, default: nil) |
Retrieve a value using a dot-separated path |
IniParser.set(hash, path, value) |
Set a value using a dot-separated path |
IniParser.flatten(hash) |
Convert nested sections to flat dot-separated keys |
IniParser.unflatten(hash) |
Convert dot-separated keys back to nested sections |
IniParser.delete(hash, path) |
Delete a value by dot-separated path, returns deleted value |
IniParser.sections(string_or_path) |
Extract section names without fully parsing values |
Development
bundle install
bundle exec rspec
bundle exec rubocop
Support
If you find this project useful: