Senko

Senko is a fast JSON Schema validator for Ruby. It compiles schemas into a reusable representation, uses generated Ruby code for simple boolean validation, and falls back to a full interpreter for detailed errors and advanced JSON Schema semantics.

Installation

Add this line to your application's Gemfile:

gem 'senko'

And then execute:

bundle install

Or install it yourself as:

gem install senko

Senko includes a native extension for validation hot paths. If the extension cannot be loaded, validation continues through the pure Ruby fallback.

Usage

Compile a schema once and reuse it:

require 'senko'

schema = Senko.compile(
  'type' => 'object',
  'required' => ['name'],
  'properties' => {
    'name' => { 'type' => 'string', 'minLength' => 1 }
  },
  'additionalProperties' => false
)

schema.valid?({ 'name' => 'senko' })
# => true

schema.valid?({ 'name' => '' })
# => false

Senko.compile accepts either a schema hash or schema keywords directly.

Senko.compile({ 'type' => 'string' })
Senko.compile('type' => 'string')

Validation

schema.valid?(data)          # boolean-only validation
schema.validate(data)        # returns Senko::Result
schema.validate!(data)       # returns data or raises Senko::ValidationError
schema.valid_json?(json)     # parses and validates a JSON string
schema.validate_json(json)   # parses JSON and returns Senko::Result

One-shot helpers are also available:

Senko.valid?(schema_hash, data)
Senko.validate(schema_hash, data)
Senko.valid_json?(schema_hash, '[1, 2, 3]')
Senko.validate_json(schema_hash, '[1, 2, 3]')

Compile from a JSON file:

schema = Senko.compile_file('schema.json')

Errors and Output

validate returns a Senko::Result.

result = schema.validate({ 'name' => '' })

result.valid?
# => false

result.errors.map(&:to_h)
# => [
#      {
#        'keywordLocation' => '/properties/name/minLength',
#        'instanceLocation' => '/name',
#        'error' => 'string length must be >= 1'
#      }
#    ]

Result objects can be rendered in JSON Schema output-style shapes:

result.to_basic
result.to_detailed
result.to_verbose

Options

Options can be passed to compile or to the one-shot helpers.

schema = Senko.compile(
  schema_hash,
  format: :assertion,
  fail_fast: true,
  validate_meta_schema: true,
  codegen: :auto
)

Common options:

  • format: :annotation records format annotations without failing validation.
  • format: :assertion makes supported formats validation errors.
  • fail_fast: true stops detailed validation after the first error.
  • validate_meta_schema: true checks schema shape before compilation.
  • codegen: false disables the generated boolean fast path.
  • messages: { type: 'custom message' } customizes built-in error messages.
  • schemas: { uri => schema_hash } registers external schemas for $ref.

Custom Formats and Keywords

Register process-wide extensions:

Senko.register_format('starts-with-x') do |value|
  value.start_with?('x-')
end

Senko.register_keyword('even') do |data, enabled|
  !enabled || data.even?
end

Senko.clear_registry!

Or pass extensions to a single compiled schema:

schema = Senko.compile(
  { 'format' => 'starts-with-x' },
  format: :assertion,
  custom_formats: {
    'starts-with-x' => ->(value) { value.start_with?('x-') }
  }
)

Drafts and OpenAPI

Senko detects $schema when present and defaults to Draft 2020-12 otherwise. It supports compatibility paths for Draft 2019-09, Draft 7, Draft 6, and Draft 4.

Legacy and OpenAPI forms are normalized where possible:

  • definitions
  • tuple items and additionalItems
  • dependencies
  • legacy id
  • Draft 4 boolean exclusive numeric bounds
  • OpenAPI 3.0 nullable: true

OpenAPI-style oneOf and anyOf schemas with a required const discriminator property, or an explicit discriminator.mapping, are compiled into direct discriminator dispatch.

Performance

valid? uses generated Ruby code when the schema is supported by the code generator. Schemas that require full JSON Schema semantics use the interpreter. validate always uses the interpreter so detailed errors and annotations remain available.

Run the local performance gate:

bundle exec rake benchmark:check

The benchmark compares Senko against json_schemer across codegen, interpreter, $ref, oneOf, and unevaluatedProperties scenarios.

Development

After checking out the repo, install dependencies:

bundle install

Gemfile.lock is intentionally ignored for this gem.

Useful commands:

bundle exec rake native:compile
bundle exec rake test
bundle exec rake suite:install
bundle exec rake suite:optional
bundle exec rake benchmark:check
bundle exec rake ci

rake ci compiles the native extension, runs the unit tests, runs the optional JSON Schema Test Suite, checks performance, and builds the gem.

Contributing

Bug reports and pull requests are welcome. Before opening a pull request, run:

bundle exec rake ci

License

The gem is available as open source under the terms of the MIT License.