Get started / Ruby
docuconf for Ruby
You keep your Anyway::Config classes, and anyway_config keeps loading YAML, credentials and the environment. docuconf adds describe, secret and file inputs, checks everything when the config loads, and exports the contract with a rake task.
- Builds on
- anyway_config 2
- Package
- docuconf-anyway (RubyGems)
- Requires
- Ruby 3.2
- Status
- v0.1 alpha, not yet released · conformance 134 of 134
- Source
- docuconf-ruby · the orders example
Not published yet
main branch. After the first release it will be gem "docuconf-anyway".The code below is the orders service, the example every SDK ships: six variables, one of them a secret, checked the same way in every language. Compare it across languages.
1.Install
The gem is not on RubyGems yet. Take it from the main branch (Ruby 3.2+, anyway_config 2.6+); in Rails the Railtie loads automatically.
bundle install# Until the first release, from the main branch:
gem "docuconf-anyway", git: "https://github.com/Docuconf/docuconf-ruby", branch: "main"2.Declare your configuration
An Anyway::Config class with include Docuconf::Anyway. attr_config and required are anyway_config's own; describe :attr, "description", **constraints is docuconf's, with secret: true for a secret. The YARD comment above a describe becomes the details.
# frozen_string_literal: true
require "docuconf/anyway"
class OrdersConfig < Anyway::Config
include Docuconf::Anyway
# Read PORT, DATABASE_URL, ... with no ORDERS_ prefix.
env_prefix ""
attr_config :database_url,
port: 8080, log_level: "info", allowed_origins: ["http://localhost:3000"],
request_timeout: "30s", worker_count: 4
required :database_url
describe :port, "HTTP listen port", min: 1, max: 65_535
describe :log_level, "Minimum log level", values: %w[debug info warn error]
describe :database_url, "Postgres connection string for orders", type: :url, schemes: %w[postgres],
max_length: 2048, secret: true # never printed, and the contract marks it secret
describe :allowed_origins, "CORS origins allowed to call the API", min_items: 1
# How long the server works on one request before it gives up.
#
# Raise it when clients upload large order batches. Keep it below the load
# balancer's idle timeout, or the client sees a reset rather than a +504+.
describe :request_timeout, "Time allowed to handle one request", min: "1s", max: "5m"
describe :worker_count, "Background workers processing orders", min: 1, max: 64
end3.Load it at boot
load! loads and validates the class, and on a problem prints every one and exits 1. Instantiating the class raises Docuconf::Anyway::ValidationError, a subclass of anyway_config's own ValidationError, instead.
# Loading the config validates it: every problem is reported at once, each
# with a stable code, and the secret's value is never printed. On failure
# load! prints the problems and exits 1.
CONFIG = OrdersConfig.load!4.Run it, and see an error
Run the example with a valid environment:
DATABASE_URL=postgres://orders:pw@localhost:5432/orders bundle exec ruby server.rbWith PORT=0 and no DATABASE_URL, the service refuses to start, lists every problem with its error code and exits 1. The secret's value, and even its scheme, is left out.
$ PORT=0 bundle exec ruby server.rb
docuconf: 2 configuration problems:
- DATABASE_URL [missing_required]: required, and not set: set DATABASE_URL in the environment (a secret cannot come from a file)
- PORT [out_of_range]: 0 is below min 15.Test your configuration
A Minitest test. anyway_config's own with_env helper sets variables for one block and restores ENV afterwards. RSpec works the same way.
# frozen_string_literal: true
require "minitest/autorun"
require "anyway/testing"
require_relative "../../orders/config/orders_config"
class OrdersConfigTest < Minitest::Test
# with_env sets these variables for the block and restores ENV afterwards.
include Anyway::Testing::Helpers
def test_defaults
with_env("DATABASE_URL" => "postgres://orders@db/orders") do
config = OrdersConfig.new
assert_equal 8080, config.port
assert_equal 4, config.worker_count
end
end
def test_rejects_bad_values
with_env("PORT" => "70000", "DATABASE_URL" => nil) do
error = assert_raises(Docuconf::Anyway::ValidationError) { OrdersConfig.new }
assert_includes error.message, "PORT [out_of_range]"
assert_includes error.message, "DATABASE_URL [missing_required]"
end
end
endbundle exec ruby test/orders_config_test.rb6.Export the contract
Export loads the class without instantiating it, so it needs no environment. In Rails, bin/rails docuconf:export OUT=contract.cue does the same, taking the name from the app.
bundle exec docuconf export --name orders-api --package orders --out contract.cue config/orders_config.rb// Code generated by docuconf. DO NOT EDIT.
package orders
import "docuconf.dev/contract"
contract.#Contract & {
apiVersion: "docuconf.dev/v1alpha1"
kind: "ConfigContract"
metadata: {
name: "orders-api"
generator: {
language: "ruby"
sdk: "docuconf-anyway"
version: "0.1.0"
}
}
vars: {
ALLOWED_ORIGINS: {
type: "list"
description: "CORS origins allowed to call the API"
configKey: "orders.allowed_origins"
items: "string"
encoding: "csv"
separator: ","
minItems: 1
default: ["http://localhost:3000"]
}
DATABASE_URL: {
type: "url"
description: "Postgres connection string for orders"
required: true
secret: true
configKey: "orders.database_url"
schemes: ["postgres"]
maxLength: 2048
}
LOG_LEVEL: {
type: "enum"
description: "Minimum log level"
configKey: "orders.log_level"
values: ["debug", "info", "warn", "error"]
default: "info"
}
PORT: {
type: "int"
description: "HTTP listen port"
configKey: "orders.port"
min: 1
max: 65535
default: 8080
}
REQUEST_TIMEOUT: {
type: "duration"
description: "Time allowed to handle one request"
details: "How long the server works on one request before it gives up.\n\nRaise it when clients upload large order batches. Keep it below the load\nbalancer's idle timeout, or the client sees a reset rather than a `504`."
configKey: "orders.request_timeout"
encoding: "iso8601"
min: "1s"
max: "5m"
default: "30s"
}
WORKER_COUNT: {
type: "int"
description: "Background workers processing orders"
configKey: "orders.worker_count"
min: 1
max: 64
default: 4
}
}
}The platform then checks each environment's values against contract.cue before it deploys, with docuconf vet, the Helm chart or CUE. See How it works.
7.Rails
Put the class in config/configs/, as anyway_config suggests. The Railtie adds bin/rails docuconf:export and bin/rails docuconf:check, and reads config/<name>.yml per RAILS_ENV into the contract as profiles. Values from Rails credentials are not part of the platform contract: mark them with exclude :attr. See the README.
Next
- The docuconf-ruby README: every type, file input and option.
- The orders example, with its smoke test.
- The same service in every language, side by side.
- What every SDK supports, and this one's known gaps: Rails credentials must be excluded explicitly. TOML needs the tomlrb gem.
Every snippet on this page is compiled or run against docuconf-ruby's main branch in CI. Facts checked 2026-10-08.