jsonschema_rs 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 612136d54e48cbe70a384939de4ee760c240ebf37ffbb40924d8eb1bccf4e382
4
+ data.tar.gz: a3b9968753b319ef31129311c9598be508dc331eca081386eb8de8f645a40cf1
5
+ SHA512:
6
+ metadata.gz: 93e07f2476489fcef0315b06b187895449435b072a269ce9cf29f302a481ba5a6e5ac4cf6bd75f79002f3aa5409d28283e76e6739bb7ce7578db7b2dd002e1dd
7
+ data.tar.gz: 032c5b841a42b9e3ee5e4a3a4c03826074cabfcc4d24d3b9d71c3e85eed7b0c417c352e7b95f5d0e6a9ac87c888df0520961f45298095c17df26117f0b6cbd5c
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ## 0.42.0 - 2026-02-15
6
+
7
+ - Initial public release
8
+
data/Cargo.toml ADDED
@@ -0,0 +1,24 @@
1
+ [package]
2
+ name = "jsonschema-rb"
3
+ version = "0.42.0"
4
+ edition = "2021"
5
+ authors = ["Dmitry Dygalo <dmitry@dygalo.dev>"]
6
+ license = "MIT"
7
+ description = "A high-performance JSON Schema validator for Ruby"
8
+ readme = "README.md"
9
+ repository = "https://github.com/Stranger6667/jsonschema"
10
+ publish = false
11
+
12
+ [lib]
13
+ crate-type = ["cdylib"]
14
+
15
+ [dependencies]
16
+ jsonschema = { version = "0.42.0", default-features = false, features = ["arbitrary-precision", "resolve-http", "resolve-file", "tls-ring"] }
17
+ magnus = { version = "0.8", features = ["rb-sys"] }
18
+ rb-sys = "0.9"
19
+ serde = { workspace = true }
20
+ serde_json = { workspace = true, features = ["arbitrary_precision"] }
21
+ referencing = { version = "*", path = "../jsonschema-referencing" }
22
+
23
+ [lints]
24
+ workspace = true
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020-2026 Dmitry Dygalo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/MIGRATION.md ADDED
@@ -0,0 +1,87 @@
1
+ # Migrating from json_schemer
2
+
3
+ ## Quick Reference
4
+
5
+ | json_schemer | jsonschema |
6
+ |---|---|
7
+ | `JSONSchemer.schema(s)` | `JSONSchema.validator_for(s)` |
8
+ | `JSONSchemer.schema(s, meta_schema: 'draft7')` | `JSONSchema::Draft7Validator.new(s)` |
9
+ | `schemer.valid?(d)` | `validator.valid?(d)` |
10
+ | `schemer.validate(d)` | `validator.each_error(d)` |
11
+ | `schemer.validate!(d)` | `validator.validate!(d)` |
12
+ | `JSONSchemer.valid?(s, d)` | `JSONSchema.valid?(s, d)` |
13
+ | `JSONSchemer.valid_schema?(s)` | `JSONSchema::Meta.valid?(s)` |
14
+ | `error["data_pointer"]` | `error.instance_path_pointer` |
15
+ | `error["schema_pointer"]` | `error.schema_path_pointer` |
16
+ | `error["type"]` | `error.kind.name` |
17
+ | `error["error"]` | `error.message` |
18
+ | `ref_resolver: proc` | `retriever: proc` |
19
+ | `format: true` | `validate_formats: true` |
20
+
21
+ Draft-specific validators are also available: `JSONSchema::Draft7Validator.new(schema)`
22
+
23
+ ## What Stays the Same
24
+
25
+ - JSON Schema documents work as-is
26
+ - `valid?` and `validate!` method names
27
+ - Custom format validators via `formats:` with the same proc syntax
28
+ - One-off validation: `JSONSchema.valid?(schema, data)`
29
+
30
+ ## Error Objects
31
+
32
+ json_schemer returns hashes, jsonschema returns `ValidationError` objects:
33
+
34
+ ```ruby
35
+ # json_schemer
36
+ error["data_pointer"] # => "/foo/bar"
37
+ error["schema_pointer"] # => "/properties/foo/minimum"
38
+ error["type"] # => "minimum"
39
+ error["error"] # => "value is less than 10"
40
+
41
+ # jsonschema
42
+ error.instance_path # => ["foo", "bar"]
43
+ error.instance_path_pointer # => "/foo/bar" (same format as data_pointer)
44
+ error.schema_path # => ["properties", "foo", "minimum"]
45
+ error.schema_path_pointer # => "/properties/foo/minimum"
46
+ error.kind.name # => "minimum"
47
+ error.message # => "value is less than 10"
48
+
49
+ # Need a hash? Use to_h on error kind
50
+ error.kind.to_h # => { "name" => "minimum", "value" => { "limit" => 10 } }
51
+ ```
52
+
53
+ ## Reference Resolution
54
+
55
+ ```ruby
56
+ # json_schemer
57
+ JSONSchemer.schema(schema, ref_resolver: refs.to_proc)
58
+
59
+ # jsonschema — retriever
60
+ JSONSchema.validator_for(schema, retriever: ->(uri) { fetch_schema(uri) })
61
+
62
+ # jsonschema — registry
63
+ registry = JSONSchema::Registry.new([["http://example.com/s", sub_schema]])
64
+ JSONSchema.validator_for(schema, registry: registry)
65
+ ```
66
+
67
+ ## What You Gain
68
+
69
+ - **Structured output** — `evaluate` API with flag, list, and hierarchical output formats
70
+ - **Custom keywords** — extend JSON Schema with domain-specific validation rules
71
+ - **Error masking** — hide sensitive data in error messages with `mask:`
72
+ - **Regex engine configuration** — choose between fancy-regex (default) and linear-time regex
73
+ - **Email validation options** — fine-grained control over email format validation
74
+
75
+ ## Not Supported
76
+
77
+ - `insert_property_defaults` — jsonschema is a validator, not a data transformer
78
+ - OpenAPI document parsing — use a dedicated OpenAPI library
79
+
80
+ ## Migration Checklist
81
+
82
+ - [ ] Replace `JSONSchemer.schema` with `JSONSchema.validator_for`
83
+ - [ ] Replace `validate` (error iteration) with `each_error`
84
+ - [ ] Replace `ref_resolver:` with `retriever:` or use `Registry`
85
+ - [ ] Replace `format: true` with `validate_formats: true`
86
+ - [ ] Replace `meta_schema: 'draft7'` with `JSONSchema::Draft7Validator.new(s)` or `draft: :draft7` on one-off functions
87
+ - [ ] Update error handling from hash access to `ValidationError` attributes
data/README.md ADDED
@@ -0,0 +1,489 @@
1
+ # jsonschema_rs
2
+
3
+ [![Build](https://img.shields.io/github/actions/workflow/status/Stranger6667/jsonschema/ci.yml?branch=master&style=flat-square)](https://github.com/Stranger6667/jsonschema/actions)
4
+ [![Version](https://img.shields.io/gem/v/jsonschema_rs.svg?style=flat-square)](https://rubygems.org/gems/jsonschema_rs)
5
+ [![Ruby versions](https://img.shields.io/badge/ruby-3.2%20%7C%203.4%20%7C%204.0-blue?style=flat-square)](https://rubygems.org/gems/jsonschema_rs)
6
+ [<img alt="Supported Dialects" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fsupported_versions.json&style=flat-square">](https://bowtie.report/#/implementations/rust-jsonschema)
7
+
8
+ A high-performance JSON Schema validator for Ruby.
9
+
10
+ ```ruby
11
+ require 'jsonschema_rs'
12
+
13
+ schema = { "maxLength" => 5 }
14
+ instance = "foo"
15
+
16
+ # One-off validation
17
+ JSONSchema.valid?(schema, instance) # => true
18
+
19
+ begin
20
+ JSONSchema.validate!(schema, "incorrect")
21
+ rescue JSONSchema::ValidationError => e
22
+ puts e.message # => "\"incorrect\" is longer than 5 characters"
23
+ end
24
+
25
+ # Build & reuse (faster)
26
+ validator = JSONSchema.validator_for(schema)
27
+
28
+ # Iterate over errors
29
+ validator.each_error(instance) do |error|
30
+ puts "Error: #{error.message}"
31
+ puts "Location: #{error.instance_path}"
32
+ end
33
+
34
+ # Boolean result
35
+ validator.valid?(instance) # => true
36
+
37
+ # Structured output (JSON Schema Output v1)
38
+ evaluation = validator.evaluate(instance)
39
+ evaluation.annotations.each do |ann|
40
+ puts "Annotation at #{ann[:schemaLocation]}: #{ann[:annotations]}"
41
+ end
42
+ ```
43
+
44
+ > **Migrating from `json_schemer`?** See the [migration guide](MIGRATION.md).
45
+
46
+ ## Highlights
47
+
48
+ - 📚 Full support for popular JSON Schema drafts
49
+ - 🌐 Remote reference fetching (network/file)
50
+ - 🔧 Custom keywords and format validators
51
+ - ✨ Meta-schema validation for schema documents
52
+ - ♦️ Supports Ruby 3.2, 3.4 and 4.0
53
+
54
+ ### Supported drafts
55
+
56
+ The following drafts are supported:
57
+
58
+ - [![Draft 2020-12](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft2020-12.json)](https://bowtie.report/#/implementations/rust-jsonschema)
59
+ - [![Draft 2019-09](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft2019-09.json)](https://bowtie.report/#/implementations/rust-jsonschema)
60
+ - [![Draft 7](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft7.json)](https://bowtie.report/#/implementations/rust-jsonschema)
61
+ - [![Draft 6](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft6.json)](https://bowtie.report/#/implementations/rust-jsonschema)
62
+ - [![Draft 4](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft4.json)](https://bowtie.report/#/implementations/rust-jsonschema)
63
+
64
+ You can check the current status on the [Bowtie Report](https://bowtie.report/#/implementations/rust-jsonschema).
65
+
66
+ ## Installation
67
+
68
+ Add to your Gemfile:
69
+
70
+ ```
71
+ gem 'jsonschema_rs'
72
+ ```
73
+
74
+ Pre-built native gems are available for:
75
+
76
+ - **Linux**: `x86_64`, `aarch64` (glibc and musl)
77
+ - **macOS**: `x86_64`, `arm64`
78
+ - **Windows**: `x64` (mingw-ucrt)
79
+
80
+ If no pre-built gem is available for your platform, it will be compiled from source during installation. You'll need:
81
+ - Ruby 3.2+
82
+ - Rust toolchain ([rustup](https://rustup.rs/))
83
+
84
+ ## Usage
85
+
86
+ ### Reusable validators
87
+
88
+ For validating multiple instances against the same schema, create a reusable validator.
89
+ `validator_for` automatically detects the draft version from the `$schema` keyword in the schema:
90
+
91
+ ```ruby
92
+ validator = JSONSchema.validator_for({
93
+ "type" => "object",
94
+ "properties" => {
95
+ "name" => { "type" => "string" },
96
+ "age" => { "type" => "integer", "minimum" => 0 }
97
+ },
98
+ "required" => ["name"]
99
+ })
100
+
101
+ validator.valid?({ "name" => "Alice", "age" => 30 }) # => true
102
+ validator.valid?({ "age" => 30 }) # => false
103
+ ```
104
+
105
+ You can use draft-specific validators for different JSON Schema versions:
106
+
107
+ ```ruby
108
+ validator = JSONSchema::Draft7Validator.new(schema)
109
+
110
+ # Available: Draft4Validator, Draft6Validator, Draft7Validator,
111
+ # Draft201909Validator, Draft202012Validator
112
+ ```
113
+
114
+ ### Custom format validators
115
+
116
+ ```ruby
117
+ phone_format = ->(value) { value.match?(/^\+?[1-9]\d{1,14}$/) }
118
+
119
+ validator = JSONSchema.validator_for(
120
+ { "type" => "string", "format" => "phone" },
121
+ validate_formats: true,
122
+ formats: { "phone" => phone_format }
123
+ )
124
+ ```
125
+
126
+ ### Custom keyword validators
127
+
128
+ ```ruby
129
+ class EvenValidator
130
+ def initialize(parent_schema, value, schema_path)
131
+ @enabled = value
132
+ end
133
+
134
+ def validate(instance)
135
+ return unless @enabled && instance.is_a?(Integer)
136
+ raise "#{instance} is not even" if instance.odd?
137
+ end
138
+ end
139
+
140
+ validator = JSONSchema.validator_for(
141
+ { "type" => "integer", "even" => true },
142
+ keywords: { "even" => EvenValidator }
143
+ )
144
+ ```
145
+
146
+ Each custom keyword class must implement:
147
+ - `initialize(parent_schema, value, schema_path)` - called during schema compilation
148
+ - `validate(instance)` - raise on failure, return normally on success
149
+
150
+ ### Structured evaluation output
151
+
152
+ When you need more than a boolean result, use the `evaluate` API to access the JSON Schema Output v1 formats:
153
+
154
+ ```ruby
155
+ schema = {
156
+ "type" => "object",
157
+ "properties" => {
158
+ "name" => { "type" => "string" },
159
+ "age" => { "type" => "integer" }
160
+ },
161
+ "required" => ["name"]
162
+ }
163
+ validator = JSONSchema.validator_for(schema)
164
+
165
+ evaluation = validator.evaluate({ "age" => "not_an_integer" })
166
+
167
+ evaluation.valid? # => false
168
+
169
+ # Flag output (simplest)
170
+ evaluation.flag
171
+ # => { "valid" => false }
172
+
173
+ # List output (flat)
174
+ evaluation.list
175
+ # => { "valid" => false, "details" => [
176
+ # { "valid" => false, "evaluationPath" => "", "instanceLocation" => "", "schemaLocation" => "" },
177
+ # { "valid" => false, "evaluationPath" => "/required", "instanceLocation" => "", "schemaLocation" => "/required",
178
+ # "errors" => { "required" => "\"name\" is a required property" } },
179
+ # ...
180
+ # ] }
181
+
182
+ # Hierarchical output (nested tree following schema structure)
183
+ evaluation.hierarchical
184
+ # => { "valid" => false, "evaluationPath" => "", "instanceLocation" => "", "schemaLocation" => "",
185
+ # "details" => [ ... ] }
186
+
187
+ # Collected errors across all nodes
188
+ evaluation.errors
189
+ # => [{ "schemaLocation" => "/required", "instanceLocation" => "",
190
+ # "absoluteKeywordLocation" => nil, "error" => "\"name\" is a required property" }, ...]
191
+
192
+ # Collected annotations
193
+ evaluation.annotations
194
+ # => [{ "schemaLocation" => "/properties", "instanceLocation" => "",
195
+ # "absoluteKeywordLocation" => nil, "annotations" => ["age"] }]
196
+ ```
197
+
198
+ ## Meta-Schema Validation
199
+
200
+ Validate that a JSON Schema document is itself valid:
201
+
202
+ ```ruby
203
+ JSONSchema::Meta.valid?({ "type" => "string" }) # => true
204
+ JSONSchema::Meta.valid?({ "type" => "invalid_type" }) # => false
205
+
206
+ begin
207
+ JSONSchema::Meta.validate!({ "type" => 123 })
208
+ rescue JSONSchema::ValidationError => e
209
+ e.message # => "123 is not valid under any of the schemas listed in the 'anyOf' keyword"
210
+ end
211
+ ```
212
+
213
+ ## External References
214
+
215
+ By default, `jsonschema` resolves HTTP references and file references from the local file system. You can implement a custom retriever to handle external references:
216
+
217
+ ```ruby
218
+ schemas = {
219
+ "https://example.com/person.json" => {
220
+ "type" => "object",
221
+ "properties" => {
222
+ "name" => { "type" => "string" },
223
+ "age" => { "type" => "integer" }
224
+ },
225
+ "required" => ["name", "age"]
226
+ }
227
+ }
228
+
229
+ retriever = ->(uri) { schemas[uri] }
230
+
231
+ schema = { "$ref" => "https://example.com/person.json" }
232
+ validator = JSONSchema.validator_for(schema, retriever: retriever)
233
+
234
+ validator.valid?({ "name" => "Alice", "age" => 30 }) # => true
235
+ validator.valid?({ "name" => "Bob" }) # => false (missing "age")
236
+ ```
237
+
238
+ ## Schema Registry
239
+
240
+ For applications that frequently use the same schemas, create a registry to store and reference them:
241
+
242
+ ```ruby
243
+ registry = JSONSchema::Registry.new([
244
+ ["https://example.com/address.json", {
245
+ "type" => "object",
246
+ "properties" => {
247
+ "street" => { "type" => "string" },
248
+ "city" => { "type" => "string" }
249
+ }
250
+ }],
251
+ ["https://example.com/person.json", {
252
+ "type" => "object",
253
+ "properties" => {
254
+ "name" => { "type" => "string" },
255
+ "address" => { "$ref" => "https://example.com/address.json" }
256
+ }
257
+ }]
258
+ ])
259
+
260
+ validator = JSONSchema.validator_for(
261
+ { "$ref" => "https://example.com/person.json" },
262
+ registry: registry
263
+ )
264
+
265
+ validator.valid?({
266
+ "name" => "John",
267
+ "address" => { "street" => "Main St", "city" => "Boston" }
268
+ }) # => true
269
+ ```
270
+
271
+ The registry also accepts `draft:` and `retriever:` options:
272
+
273
+ ```ruby
274
+ registry = JSONSchema::Registry.new(
275
+ [["https://example.com/person.json", schemas["https://example.com/person.json"]]],
276
+ draft: :draft7,
277
+ retriever: retriever
278
+ )
279
+ ```
280
+
281
+ ## Regular Expression Configuration
282
+
283
+ When validating schemas with regex patterns (in `pattern` or `patternProperties`), you can configure the underlying regex engine:
284
+
285
+ ```ruby
286
+ # Default fancy-regex engine with backtracking limits
287
+ # (supports lookaround and backreferences but needs protection against DoS)
288
+ validator = JSONSchema.validator_for(
289
+ { "type" => "string", "pattern" => "^(a+)+$" },
290
+ pattern_options: JSONSchema::FancyRegexOptions.new(backtrack_limit: 10_000)
291
+ )
292
+
293
+ # Standard regex engine for guaranteed linear-time matching
294
+ # (prevents regex DoS attacks but supports fewer features)
295
+ validator = JSONSchema.validator_for(
296
+ { "type" => "string", "pattern" => "^a+$" },
297
+ pattern_options: JSONSchema::RegexOptions.new
298
+ )
299
+
300
+ # Both engines support memory usage configuration
301
+ validator = JSONSchema.validator_for(
302
+ { "type" => "string", "pattern" => "^a+$" },
303
+ pattern_options: JSONSchema::RegexOptions.new(
304
+ size_limit: 1024 * 1024, # Maximum compiled pattern size
305
+ dfa_size_limit: 10240 # Maximum DFA cache size
306
+ )
307
+ )
308
+ ```
309
+
310
+ The available options:
311
+
312
+ - `FancyRegexOptions`: Default engine with lookaround and backreferences support
313
+
314
+ - `backtrack_limit`: Maximum backtracking steps
315
+ - `size_limit`: Maximum compiled regex size in bytes
316
+ - `dfa_size_limit`: Maximum DFA cache size in bytes
317
+
318
+ - `RegexOptions`: Safer engine with linear-time guarantee
319
+
320
+ - `size_limit`: Maximum compiled regex size in bytes
321
+ - `dfa_size_limit`: Maximum DFA cache size in bytes
322
+
323
+ This configuration is crucial when working with untrusted schemas where attackers might craft malicious regex patterns.
324
+
325
+ ## Email Format Configuration
326
+
327
+ When validating email addresses using `{"format": "email"}`, you can customize the validation behavior:
328
+
329
+ ```ruby
330
+ # Require a top-level domain (reject "user@localhost")
331
+ validator = JSONSchema.validator_for(
332
+ { "format" => "email", "type" => "string" },
333
+ validate_formats: true,
334
+ email_options: JSONSchema::EmailOptions.new(require_tld: true)
335
+ )
336
+ validator.valid?("user@localhost") # => false
337
+ validator.valid?("user@example.com") # => true
338
+
339
+ # Disallow IP address literals and display names
340
+ validator = JSONSchema.validator_for(
341
+ { "format" => "email", "type" => "string" },
342
+ validate_formats: true,
343
+ email_options: JSONSchema::EmailOptions.new(
344
+ allow_domain_literal: false, # Reject "user@[127.0.0.1]"
345
+ allow_display_text: false # Reject "Name <user@example.com>"
346
+ )
347
+ )
348
+
349
+ # Require minimum domain segments
350
+ validator = JSONSchema.validator_for(
351
+ { "format" => "email", "type" => "string" },
352
+ validate_formats: true,
353
+ email_options: JSONSchema::EmailOptions.new(minimum_sub_domains: 3) # e.g., user@sub.example.com
354
+ )
355
+ ```
356
+
357
+ Available options:
358
+
359
+ - `require_tld`: Require a top-level domain (e.g., reject "user@localhost")
360
+ - `allow_domain_literal`: Allow IP address literals like "user@[127.0.0.1]" (default: true)
361
+ - `allow_display_text`: Allow display names like "Name <user@example.com>" (default: true)
362
+ - `minimum_sub_domains`: Minimum number of domain segments required
363
+
364
+ ## Error Handling
365
+
366
+ `jsonschema` provides detailed validation errors through the `ValidationError` class:
367
+
368
+ ```ruby
369
+ schema = { "type" => "string", "maxLength" => 5 }
370
+
371
+ begin
372
+ JSONSchema.validate!(schema, "too long")
373
+ rescue JSONSchema::ValidationError => error
374
+ # Basic error information
375
+ error.message # => '"too long" is longer than 5 characters'
376
+ error.verbose_message # => Full context with schema path and instance
377
+ error.instance_path # => Location in the instance that failed
378
+ error.schema_path # => Location in the schema that failed
379
+
380
+ # Detailed error information via `kind`
381
+ error.kind.name # => "maxLength"
382
+ error.kind.value # => { "limit" => 5 }
383
+ error.kind.to_h # => { "name" => "maxLength", "value" => { "limit" => 5 } }
384
+ end
385
+ ```
386
+
387
+ ### Error Kind Properties
388
+
389
+ Each error has a `kind` property with convenient accessors:
390
+
391
+ ```ruby
392
+ JSONSchema.each_error({ "minimum" => 5 }, 3).each do |error|
393
+ error.kind.name # => "minimum"
394
+ error.kind.value # => { "limit" => 5 }
395
+ error.kind.to_h # => { "name" => "minimum", "value" => { "limit" => 5 } }
396
+ error.kind.to_s # => "minimum"
397
+ end
398
+ ```
399
+
400
+ ### Error Message Masking
401
+
402
+ When working with sensitive data, you can mask instance values in error messages:
403
+
404
+ ```ruby
405
+ schema = {
406
+ "type" => "object",
407
+ "properties" => {
408
+ "password" => { "type" => "string", "minLength" => 8 },
409
+ "api_key" => { "type" => "string", "pattern" => "^[A-Z0-9]{32}$" }
410
+ }
411
+ }
412
+
413
+ validator = JSONSchema.validator_for(schema, mask: "[REDACTED]")
414
+
415
+ begin
416
+ validator.validate!({ "password" => "123", "api_key" => "secret_key_123" })
417
+ rescue JSONSchema::ValidationError => exc
418
+ puts exc.message
419
+ # => '[REDACTED] does not match "^[A-Z0-9]{32}$"'
420
+ puts exc.verbose_message
421
+ # => '[REDACTED] does not match "^[A-Z0-9]{32}$"\n\nFailed validating...\nOn instance["api_key"]:\n [REDACTED]'
422
+ end
423
+ ```
424
+
425
+ ### Exception Classes
426
+
427
+ - **`JSONSchema::ValidationError`** - raised on validation failure
428
+ - `message`, `verbose_message`, `instance_path`, `schema_path`, `evaluation_path`, `kind`, `instance`
429
+ - JSON Pointer helpers: `instance_path_pointer`, `schema_path_pointer`, `evaluation_path_pointer`
430
+ - **`JSONSchema::ReferencingError`** - raised when `$ref` cannot be resolved
431
+
432
+ ## Options Reference
433
+
434
+ One-off validation methods (`valid?`, `validate!`, `each_error`, `evaluate`) accept these keyword arguments:
435
+
436
+ ```ruby
437
+ JSONSchema.valid?(schema, instance,
438
+ draft: :draft7, # Specific draft version (symbol)
439
+ validate_formats: true, # Enable format validation (default: false)
440
+ ignore_unknown_formats: true, # Don't error on unknown formats (default: true)
441
+ base_uri: "https://example.com", # Base URI for reference resolution
442
+ mask: "[REDACTED]", # Mask sensitive data in error messages
443
+ retriever: ->(uri) { ... }, # Custom schema retriever for $ref
444
+ formats: { "name" => proc }, # Custom format validators
445
+ keywords: { "name" => Klass }, # Custom keyword validators
446
+ registry: registry, # Pre-registered schemas
447
+ pattern_options: opts, # RegexOptions or FancyRegexOptions
448
+ email_options: opts, # EmailOptions
449
+ http_options: opts # HttpOptions
450
+ )
451
+ ```
452
+
453
+ `evaluate` accepts the same options except `mask` (currently unsupported for evaluation output).
454
+
455
+ `validator_for` accepts the same options except `draft:` — use draft-specific validators (`Draft7Validator.new`, etc.) to pin a draft version.
456
+
457
+ Valid draft symbols: `:draft4`, `:draft6`, `:draft7`, `:draft201909`, `:draft202012`.
458
+
459
+ ## Performance
460
+
461
+ `jsonschema` is designed for high performance, outperforming other Ruby JSON Schema validators in most scenarios:
462
+
463
+ - **26-141x** faster than `json_schemer` for complex schemas and large instances
464
+ - **174-535x** faster than `json-schema` where supported
465
+ - **7-125x** faster than `rj_schema` (RapidJSON/C++)
466
+
467
+ For detailed benchmarks, see our [full performance comparison](https://github.com/Stranger6667/jsonschema/blob/master/crates/jsonschema-rb/BENCHMARKS.md).
468
+
469
+ **Tips:** Reuse validators. Use `valid?` for boolean checks (short-circuits on first error).
470
+
471
+ ## Acknowledgements
472
+
473
+ This library draws API design inspiration from the Python [`jsonschema`](https://github.com/python-jsonschema/jsonschema) package. We're grateful to the Python `jsonschema` maintainers and contributors for their pioneering work in JSON Schema validation.
474
+
475
+ ## Support
476
+
477
+ If you have questions, need help, or want to suggest improvements, please use [GitHub Discussions](https://github.com/Stranger6667/jsonschema/discussions).
478
+
479
+ ## Sponsorship
480
+
481
+ If you find `jsonschema` useful, please consider [sponsoring its development](https://github.com/sponsors/Stranger6667).
482
+
483
+ ## Contributing
484
+
485
+ See [CONTRIBUTING.md](https://github.com/Stranger6667/jsonschema/blob/master/CONTRIBUTING.md) for details.
486
+
487
+ ## License
488
+
489
+ Licensed under [MIT License](https://github.com/Stranger6667/jsonschema/blob/master/LICENSE).