Skip to content

v1.1.0

Latest

Choose a tag to compare

@github-actions github-actions released this 11 Aug 10:49
a57ec4f

Schematist now covers the whole Draft 2020-12 vocabulary. 1.0 emitted Draft 2020-12 documents but couldn't express every schema the spec allows. This closes the five places where it fell short.

A schema does not have to be an object

A type with a name declares a property. Without a name it declares what the schema itself is.

class Tags < Schematist::Schema
  array of: :string, unique: true       # the whole schema is an array
end

class Id < Schematist::Schema
  one_of do                             # the whole schema is a choice
    string
    integer
  end
end

class Person < Schematist::Schema
  raw({ "$ref" => "https://example.com/person.json" })
end

It works inside define too, so a definition can be a reusable string, a shared enum, an array, or a union, not just an object:

define :status do
  string enum: %w[draft sent paid]
end

Objects also take min_properties, max_properties, unevaluated_properties and unevaluated_items at the root, alongside additional_properties. Closes #59 and #61.

Conditionals hold any schema

if, then and else each hold any schema in Draft 2020-12. A branch was stuck with a required list plus nine validation keys, so it couldn't ask for a nested object, an array, a reference, or any composition. A branch is backed by a schema class now, so the whole DSL works inside one:

given kind: "business" do
  requires :vat_id

  object :tax_details do
    string :vat_number
  end

  otherwise do
    validates :vat_id, type: :string
  end
end

given still matches on property values, and takes a schema outright when the condition is something else:

given({ required: %w[tax_id] }) { requires :summary }

requires and validates stay as shorthands. A dependency that only lists fields still emits dependentRequired; one that describes a schema upgrades to dependentSchemas. Closes #60.

Open ended tuples

A tuple is exactly its prefix unless you give it somewhere to put the rest. That covers both of the usual Draft 2020-12 array shapes:

tuple :event, of: :string do              # prefixItems, then a typed tail
  string
  integer
end

tuple :pair, unevaluated_items: false do  # closed after the prefix
  string
  string
end

unevaluated_items: is on the composition builders too, alongside unevaluated_properties:. Closes #62.

Wider keywords

enum and const belong to the validation vocabulary and apply to any type, so enum works on boolean and null now. format isn't type restricted either, so integer :id, format: "int64" works. Closes #63.

const on null stays out on purpose. {"type":"null"} and {"type":"null","const":null} accept and reject the same things, so it buys you nothing, and raw covers it if you want the exact bytes.

Fixed

  • Cycle detection only walked a definition's properties, so a $ref buried in a union was invisible. It walks the whole thing now, which matters more now that definitions can be unions.
  • An object with no block and no of: schema raised wrong number of arguments (given 0, expected 1..3). It says what's wrong now.

Also

ruby_llm-schema 1.0.0 is on RubyGems. It depends on Schematist and aliases RubyLLM::Schema, so the old constant keeps resolving while you migrate. It warns on load, and it's the last release of that name.