ucl
Version, currently master branch1 version
- master branchlatestJul 26, 2026
github.com/jbox-web/ucl.cr
LibUCL bindings for Crystal, easy ;)
Installation
# Add this to your shard.yml
dependencies:
ucl:
github: jbox-web/ucl.cr
branch: mastermaster is a branch, not a release, so this tracks it rather than pinning a version.
Then run:
shards installshard.yml
- Crystal
>= 1.20.0- License
- MIT
- Author
- Nicolas Rodriguez
Dependencies
Development Dependencies
- ameba~> 1.6.0github: crystal-ameba/amebadev
README
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.
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This branch
- Branch
master- Seen
- Jul 26, 2026
- Crystal
>= 1.20.0- Indexed
- yes
Dependents
No indexed shard depends on this one yet.
Repository
github.com/jbox-web/ucl.cr
Metadata
- Created
- Aug 12, 2026
- Updated
- Aug 14, 2026
- Synced
- Aug 14, 2026
- Versions
- 1