ucl.cr
LibUCL bindings for Crystal
LibUCL is a universal configuration language.
This shard is a Crystal wrapper around LibUCL. It lets you:
- load UCL/JSON configuration into native Crystal values,
- dump Crystal objects back to UCL, JSON, YAML or MsgPack,
- validate data against a UCL/JSON schema.
It was heavily inspired by ucl (Ruby).
Requirements
This shard links against the native libucl library, which must be installed on your system before you can compile or run anything.
The library is vendored as a git submodule (ext/libucl) and built from source. You'll need the usual C build toolchain (build-essential automake autoconf libtool on Debian/Ubuntu, or the equivalent on macOS).
# Fetch the vendored libucl source
git submodule update --init --recursive
# Build and install it into /usr/local/lib
mise libucl:build
mise libucl:install
These tasks are defined in
mise.toml. If you don't use mise, run the equivalent commands by hand:cd ext/libucl && ./autogen.sh && ./configure && make, then copysrc/.libs/libucl.*into/usr/local/lib.
Installation
Add the dependency to your shard.yml:
dependencies:
ucl:
github: jbox-web/ucl.cr
Then run shards install.
Usage
require "ucl"
Load
Parse a UCL (or JSON) string into native Crystal values:
UCL.load("foo = bar")
# => {"foo" => "bar"}
Load directly from a file (libucl resolves file variables and relative includes, unlike UCL.load(File.read(path))):
UCL.load_file("config.conf")
Repeated keys always decode to arrays (scalars and objects):
UCL.load("s { a = 1 }\ns { b = 2 }")
# => {"s" => [{"a" => 1}, {"b" => 2}]}
For typed, cast-free access, use load_any — a JSON::Any-style wrapper:
cfg = UCL.load_any(%(server { port = 8080, hosts = ["a", "b"] }))
cfg["server"]["port"].as_i # => 8080
cfg["server"]["hosts"].as_a.map(&.as_s) # => ["a", "b"]
cfg["server"]["missing"]? # => nil
Dump
Serialize a Crystal object. The default emitter is config (UCL). Pick a format with the typed UCL::Emitter enum (preferred) or the legacy string form — json, json_compact, yaml, msgpack:
UCL.dump({"foo" => "bar"})
# => "foo = \"bar\";\n"
UCL.dump({"foo" => "bar"}, UCL::Emitter::Json)
# => "{\n \"foo\": \"bar\"\n}"
UCL.dump({"foo" => "bar"}, "json") # legacy string form still works
Hash keys must be strings; supported value types are String, Bool, Int, Float, Time::Span, Nil, Array, Hash and NamedTuple. Anything else raises UCL::Error::TypeError. A NamedTuple encodes like the equivalent Hash, with its Symbol keys stringified:
UCL.dump({port: 8080, host: "localhost"})
# => "port = 8080;\nhost = \"localhost\";\n"
Each of those types also carries a #to_ucl instance method mirroring Crystal's #to_json / #to_yaml, so you can dump inline. UCL::Value and UCL::Any expose it too:
{"foo" => "bar"}.to_ucl # => "foo = \"bar\";\n"
{"foo" => "bar"}.to_ucl(UCL::Emitter::Json) # => "{\n \"foo\": \"bar\"\n}"
Hash#to_ucl checks its key type at compile time: a non-String key (e.g. Hash(Int32, String)) is a compile error, not a runtime TypeError. Values are not checked recursively — a nested unsupported value still raises UCL::Error::TypeError at runtime.
Validate
Check data against a UCL/JSON schema. validate raises UCL::Error::SchemaError on mismatch; valid? returns a boolean instead:
schema = File.read("schema.json")
data = File.read("data.json")
UCL.validate(schema, data) # => true, or raises UCL::Error::SchemaError
UCL.valid?(schema, data) # => true / false
Development
This project uses mise to manage the Crystal version and common tasks (see mise.toml):
mise dev:deps # shards install
mise dev:spec # run the specs
mise dev:format # crystal tool format src/
mise dev:ameba # static analysis
The specs require the native libucl to be installed (see Requirements); dev:spec sets LD_LIBRARY_PATH=/usr/local/lib for you.
Contributing
- Fork it (https://github.com/jbox-web/ucl.cr/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
License
This shard is released under the MIT License.
ucl.cr
- 1
- 0
- 0
- 0
- 1
- about 22 hours ago
- June 20, 2020
MIT License
Sun, 26 Jul 2026 00:11:41 GMT