ask-schema 0.1.0 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f5a9c41e5de3b5e4c3bdaad787059efa89197dc88048cb24b4f545a34adfd060
4
- data.tar.gz: 2f3dc5d00239bf5364ffd0894feb138a060cca73359310aee1b576ac2f6a77bb
3
+ metadata.gz: 5f2ef95da38b7caac722010a37b4b1ac4432518cc038bfab88578d4f5398eb2f
4
+ data.tar.gz: 8c9d76ec1d43750ae4d6d17886762894683dac987614ec07b445a9ffaf6c627c
5
5
  SHA512:
6
- metadata.gz: e05cfa19bd6d8160fb98c14b46a39dd5cc69970dacd1e571cb813c27b1f73454d73ce7bf74dfaa7e14ad1399ec0fd3f93bfe193aaf4e0939f6ccae59beeb0636
7
- data.tar.gz: bc91c29e8bcea693ed199cccea34a6581f4289b2ddf1420634fc1b0b32e7828b591a7bc1addc28cd028a99fbd888b2e5ebf5e6a97a432e7985ac88b67e16ee4f
6
+ metadata.gz: 9b461dc547ad617f95e7d2de6069eca9cd714dfa3761cdf71535090eec069e2892a56164e704888306ae570117cb3d942789b9f316ff9e1b22a3ae8a60f33347
7
+ data.tar.gz: d6235cc63c273f88ed8552d3e40968a9b325aa9f84b03dd0990aa44e18255e619318b8883163c26d108bab4686b8ff823c07acc67cde72ff962ec238f16431de
data/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ ## [0.1.1] - 2026-06-25
2
+
3
+ ### Changed
4
+ - Robustness suite (11 tests): empty schemas, edge cases, schema isolation. Infrastructure: rubocop, overcommit, bin/setup, CI matrix, gemspec test.
5
+ # Changelog
6
+
7
+ ## [0.1.0] - 2026-06-21
8
+
9
+ ### Added
10
+
11
+ - Initial release of `ask-schema` — a zero-dependency Ruby DSL for building JSON Schema documents.
12
+ - Ported from `ruby_llm-schema` v0.4.0 with namespace repackage (`RubyLLM::Schema` → `Ask::Schema`).
13
+
14
+ ### Features
15
+
16
+ - **Block-based DSL** — `Ask::Schema.create { string(:name); integer(:age) }` for inline schema definitions
17
+ - **Class-based DSL** — `class Product < Ask::Schema; string :name; end` for reusable schema classes
18
+ - **Primitive types** — `string`, `number`, `integer`, `boolean`, `null` with full constraint support
19
+ - **Complex types** — `object`, `array`, `any_of`, `one_of`, `enum`, `const`
20
+ - **Composition** — `define`/`reference` for named sub-schemas with `$defs` and `$ref`
21
+ - **Conditionals** — `given`/`then`/`else` for conditional validation, `dependent` for property dependencies
22
+ - **Validation** — Circular reference detection via DFS topological sort
23
+ - **JSON output** — `to_json_schema` (hash) and `to_json` (pretty-printed JSON string)
24
+ - **Strict mode** — `strict`, `additionalProperties` controls with sensible defaults
25
+ - **Modifiers** — `description`, `default`, `minimum`, `maximum`, `pattern`, `format`, `min_length`, `max_length`, `enum`, `multiple_of`, `min_items`, `max_items`
26
+ - **Optional fields** — `required: false` and `optional { ... }` helper for nullable properties
27
+ - **Dependencies** — Zero runtime dependencies. stdlib only (`json`).
28
+ - **Ruby 3.2+** — Uses anonymous block forwarding, endless methods, and modern Ruby idioms.
29
+
30
+ ### Ported modules
31
+
32
+ | Module | Source | Lines |
33
+ |---|---|---|
34
+ | `Ask::Schema` | `lib/ask/schema.rb` | 99 |
35
+ | DSL assembly | `lib/ask/schema/dsl.rb` | 19 |
36
+ | Schema builders | `lib/ask/schema/dsl/schema_builders.rb` | 186 |
37
+ | Primitive types | `lib/ask/schema/dsl/primitive_types.rb` | 29 |
38
+ | Complex types | `lib/ask/schema/dsl/complex_types.rb` | 32 |
39
+ | Conditionals | `lib/ask/schema/dsl/conditionals.rb` | 169 |
40
+ | Utilities | `lib/ask/schema/dsl/utilities.rb` | 62 |
41
+ | JSON output | `lib/ask/schema/json_output.rb` | 37 |
42
+ | Validator | `lib/ask/schema/validator.rb` | 81 |
43
+ | Errors | `lib/ask/schema/errors.rb` | 30 |
44
+ | Helpers | `lib/ask/schema/helpers.rb` | 12 |
data/README.md CHANGED
@@ -1,11 +1,17 @@
1
1
  # ask-schema
2
2
 
3
- A compact Ruby DSL for building standards-compliant JSON Schema documents. Zero dependencies.
3
+ [![Gem Version](https://badge.fury.io/rb/ask-schema.svg)](https://badge.fury.io/rb/ask-schema)
4
+
5
+ A compact Ruby DSL for building standards-compliant JSON Schema documents. Zero dependencies. ask-schema powers tool parameter schemas in ask-tools.
6
+
7
+ ## Installation
4
8
 
5
9
  ```ruby
6
10
  gem "ask-schema"
7
11
  ```
8
12
 
13
+ ## Quick Start
14
+
9
15
  ```ruby
10
16
  require "ask-schema"
11
17
 
@@ -15,221 +21,45 @@ schema = Ask::Schema.create do
15
21
  boolean :active, required: false
16
22
  end
17
23
 
18
- schema.new("user", description: "A user profile").to_json
19
- # => {
20
- # "name": "user",
21
- # "description": "A user profile",
22
- # "schema": {
23
- # "type": "object",
24
- # "properties": {
25
- # "name": { "type": "string", "description": "Full name" },
26
- # "age": { "type": "integer", "description": "Age in years", "minimum": 0 },
27
- # "active": { "type": "boolean" }
28
- # },
29
- # "required": ["name", "age"],
30
- # "additionalProperties": false,
31
- # "strict": true
32
- # }
33
- # }
34
- ```
35
-
36
- ## Quick Start
37
-
38
- ### Block-based DSL
39
-
40
- ```ruby
41
- schema = Ask::Schema.create do
42
- string :name, description: "The user's name"
43
- integer :age, description: "Age in years"
44
- boolean :active, required: false
45
- end
46
-
47
- instance = schema.new("user_profile", description: "A user profile")
24
+ instance = schema.new("user", description: "A user profile")
48
25
  instance.to_json_schema
49
- # => { name: "user_profile", description: "A user profile", schema: { ... } }
50
- ```
51
-
52
- ### Class-based DSL
53
-
54
- ```ruby
55
- class Address < Ask::Schema
56
- string :street
57
- string :city
58
- string :zip
59
- string :country, required: false
60
- end
61
-
62
- class User < Ask::Schema
63
- string :name, description: "Full name"
64
- string :email, format: "email"
65
- integer :age
66
- object :address, of: Address
67
- end
68
-
69
- User.new("user").to_json_schema
70
- ```
71
-
72
- ## Primitive Types
73
-
74
- Each primitive type supports standard JSON Schema constraints.
75
-
76
- ### String
77
-
78
- ```ruby
79
- string :username,
80
- description: "Username",
81
- enum: %w[admin user guest],
82
- min_length: 3,
83
- max_length: 50,
84
- pattern: "^[a-zA-Z0-9_]+$",
85
- format: "email"
86
- ```
87
-
88
- ### Number
89
-
90
- ```ruby
91
- number :price,
92
- description: "Price in USD",
93
- minimum: 0,
94
- maximum: 999999.99,
95
- multiple_of: 0.01
96
- ```
97
-
98
- ### Integer
99
-
100
- ```ruby
101
- integer :age,
102
- minimum: 0,
103
- maximum: 150
104
- ```
105
-
106
- ### Boolean
107
-
108
- ```ruby
109
- boolean :active, description: "Is the user active?"
110
- ```
111
-
112
- ### Null
113
-
114
- ```ruby
115
- null :deleted_at, description: "When the record was deleted"
116
- ```
117
-
118
- ## Complex Types
119
-
120
- ### Object
121
-
122
- ```ruby
123
- # Inline object
124
- object :address do
125
- string :street
126
- string :city
127
- string :zip
128
- end
129
-
130
- # Reference to a defined schema
131
- define(:address) do
132
- string :street
133
- string :city
134
- end
135
- object :billing, of: :address
136
-
137
- # Reference to a Schema class
138
- object :shipping, of: Address
139
- ```
140
-
141
- ### Array
142
-
143
- ```ruby
144
- # Array of primitive type
145
- array :tags, of: :string, description: "List of tags"
26
+ # => { name: "user", description: "A user profile", schema: { type: "object", ... } }
146
27
 
147
- # Array with min/max items
148
- array :prices, of: :number, min_items: 1, max_items: 100
149
-
150
- # Array with complex items (block)
151
- array :contacts do
152
- object do
153
- string :name
154
- string :email
155
- end
156
- end
157
-
158
- # Array with any_of items
159
- array :identifiers do
160
- any_of do
161
- string
162
- integer
163
- end
164
- end
165
- ```
166
-
167
- ### any_of / one_of
168
-
169
- ```ruby
170
- any_of :contact do
171
- string description: "Phone number"
172
- object do
173
- string :email
174
- end
175
- end
176
-
177
- one_of :payment_method do
178
- string :credit_card
179
- string :paypal
180
- end
28
+ instance.to_json # pretty-printed JSON string
181
29
  ```
182
30
 
183
- ### Optional (nullable)
31
+ `Ask::Schema.create` returns a schema class. Instantiate it with `.new("name")` and call `to_json_schema` (a Hash with `:name`, `:description`, `:schema` keys) or `to_json` (pretty-printed JSON). Class-based usage works the same way: `class Product < Ask::Schema` with the same DSL, instantiated with `Product.new("product")`.
184
32
 
185
- ```ruby
186
- optional :nickname do
187
- string
188
- end
189
- # Produces: anyOf: [{ type: "string" }, { type: "null" }]
190
- ```
33
+ ## Types and keywords
191
34
 
192
- ## Named Definitions and References
35
+ | Category | DSL methods |
36
+ |---|---|
37
+ | Primitives | `string`, `number`, `integer`, `boolean`, `null` |
38
+ | Complex | `object`, `array`, `any_of`, `one_of`, `optional` (nullable via `anyOf` + `null`) |
39
+ | Named sub-schemas | `define(:address) { ... }`, referenced with `object :billing, of: :address` |
193
40
 
194
- Use `define` to create reusable named sub-schemas and `reference` (or `of:`) to reference them:
41
+ Keywords: `description:`, `required:` (default `true`), `enum:`, `minimum:` / `maximum:` / `multiple_of:`, `min_length:` / `max_length:` / `pattern:` / `format:`, `min_items:` / `max_items:`.
195
42
 
196
43
  ```ruby
197
- class User < Ask::Schema
44
+ schema = Ask::Schema.create do
45
+ string :username, description: "Username", enum: %w[admin user guest], min_length: 3
46
+ number :price, minimum: 0, maximum: 999_999.99
47
+ array :tags, of: :string, min_items: 1
198
48
  define(:address) do
199
49
  string :street
200
50
  string :city
201
- string :zip
202
51
  end
203
-
204
- string :name
205
- object :home_address, of: :address
206
- object :work_address, of: :address
52
+ object :billing, of: :address
53
+ optional :nickname do
54
+ string
55
+ end
207
56
  end
208
57
  ```
209
58
 
210
- Output includes proper `$defs` and `$ref`:
211
-
212
- ```json
213
- {
214
- "type": "object",
215
- "properties": {
216
- "name": { "type": "string" },
217
- "home_address": { "$ref": "#/$defs/address" },
218
- "work_address": { "$ref": "#/$defs/address" }
219
- },
220
- "$defs": {
221
- "address": {
222
- "type": "object",
223
- "properties": { "street": { "type": "string" }, ... }
224
- }
225
- }
226
- }
227
- ```
59
+ Named definitions are emitted under `$defs` and referenced with `$ref`.
228
60
 
229
61
  ## Conditionals
230
62
 
231
- ### If/Then/Else
232
-
233
63
  ```ruby
234
64
  schema = Ask::Schema.create do
235
65
  integer :age
@@ -237,98 +67,34 @@ schema = Ask::Schema.create do
237
67
 
238
68
  given(age: 18, country: "US") do
239
69
  requires :license_number
240
- validates :license_number, type: :string, pattern: /^[A-Z]{2}\d{6}$/
241
70
  otherwise do
242
71
  requires :country_name
243
72
  end
244
73
  end
245
- end
246
- ```
247
-
248
- ### Dependent Required
249
74
 
250
- ```ruby
251
- dependent :shipping_address do
252
- requires :name, :street, :city
75
+ dependent :shipping_address do
76
+ requires :name, :street, :city
77
+ end
253
78
  end
254
79
  ```
255
80
 
256
- ### Coercion rules
257
-
258
- | Ruby value | JSON Schema |
259
- |---|---|
260
- | `18` (scalar) | `{ const: 18 }` |
261
- | `["admin", "user"]` (Array) | `{ enum: ["admin", "user"] }` |
262
- | `/^[A-Z]+$/` (Regexp) | `{ pattern: "^[A-Z]+$" }` |
263
- | `{ minimum: 0 }` (Hash) | Passed through as-is |
81
+ Values in `given` are coerced automatically: scalars become `const`, arrays become `enum`, Regexps become `pattern`. `requires` marks fields as required, `validates` adds per-field constraints.
264
82
 
265
83
  ## Validation
266
84
 
267
85
  ```ruby
268
- schema = Ask::Schema.create { string :name }
269
- schema.valid? # => true
270
- schema.validate! # => nil (or raises Ask::Schema::ValidationError)
271
-
272
- # Circular reference detection
273
86
  schema = Ask::Schema.create do
274
87
  define(:a) { object :b, of: :b }
275
88
  define(:b) { object :a, of: :a }
276
89
  end
277
- schema.valid? # => false
278
- schema.validate! # => raises Ask::Schema::ValidationError
279
- ```
280
-
281
- ## Output Formats
282
90
 
283
- ```ruby
284
- instance.to_json_schema
285
- # => Hash with :name, :description, :schema keys
286
-
287
- instance.to_json
288
- # => Pretty-printed JSON string
91
+ schema.valid? # => false (circular reference)
92
+ schema.validate! # raises Ask::Schema::ValidationError
289
93
  ```
290
94
 
291
- ## Configuration
95
+ ## Full documentation
292
96
 
293
- ```ruby
294
- class StrictSchema < Ask::Schema
295
- string :name
296
- strict true # defaults to true
297
- additional_properties false # defaults to false
298
- end
299
- ```
300
-
301
- ## Integration with ask-tools
302
-
303
- `ask-schema` powers tool parameter schemas in `ask-tools`:
304
-
305
- ```ruby
306
- class WeatherTool < Ask::Tool
307
- description "Get weather for a location"
308
-
309
- params do
310
- string :location, description: "City name"
311
- string :unit, enum: %w[celsius fahrenheit]
312
- end
313
-
314
- def execute(location:, unit: "celsius")
315
- # ...
316
- end
317
- end
318
- ```
319
-
320
- Under the hood, `Ask::Schema.create` is used to build the JSON Schema for tool parameters.
321
-
322
- ## Error Types
323
-
324
- | Error | When |
325
- |---|---|
326
- | `Ask::Schema::InvalidArrayTypeError` | Invalid type for array `:of` |
327
- | `Ask::Schema::InvalidObjectTypeError` | Invalid type for object `:of` |
328
- | `Ask::Schema::ValidationError` | Schema validation fails (e.g., circular refs) |
329
- | `Ask::Schema::InvalidSchemaTypeError` | Unknown schema type specified |
330
- | `Ask::Schema::InvalidSchemaError` | Schema definition is invalid |
331
- | `Ask::Schema::LimitExceededError` | Maximum limits exceeded |
97
+ The full ask-rb documentation lives at https://ask-rb.github.io/ask-docs. [ask-schema in depth](https://ask-rb.github.io/ask-docs/core/schema) covers the complete DSL. API reference: https://ask-rb.github.io/ask-docs/reference/api.
332
98
 
333
99
  ## Development
334
100
 
@@ -337,14 +103,6 @@ bundle install
337
103
  bundle exec rake test
338
104
  ```
339
105
 
340
- ## Status
341
-
342
- **Phase 3** of the ask-rb ecosystem migration. This gem replaces `ruby_llm-schema`
343
- in the ask-rb stack. It should be built after `ask-core` and `ask-llm-providers`
344
- are stable.
345
-
346
- Current state: v0.1.0 — initial port complete with full feature parity.
347
-
348
106
  ## License
349
107
 
350
108
  MIT
@@ -13,6 +13,10 @@ module Ask
13
13
  # @param options [Hash] Additional options (of:, reference:)
14
14
  # @param block [Proc] Inline property definitions
15
15
  def object(name, description: nil, required: true, requires: nil, **options, &block)
16
+ if options[:of].nil? && block.nil?
17
+ raise ArgumentError,
18
+ "object :#{name} needs a block (inline properties) or of: (a named definition)"
19
+ end
16
20
  add_property(name, object_schema(description: description, **options, &block), required: required, requires: requires)
17
21
  end
18
22
 
@@ -24,6 +28,9 @@ module Ask
24
28
  # @param options [Hash] Additional options (of:, min_items:, max_items:)
25
29
  # @param block [Proc] Block for complex item definitions
26
30
  def array(name, description: nil, required: true, requires: nil, **options, &block)
31
+ if options[:of].nil? && block.nil?
32
+ raise ArgumentError, "array :#{name} needs of: :<item type> (e.g. of: :string) or a block"
33
+ end
27
34
  add_property(name, array_schema(description: description, **options, &block), required: required, requires: requires)
28
35
  end
29
36
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ask
4
4
  class Schema
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.2"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ask-schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto
@@ -59,6 +59,7 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
+ - CHANGELOG.md
62
63
  - LICENSE
63
64
  - README.md
64
65
  - lib/ask-schema.rb
@@ -76,7 +77,10 @@ files:
76
77
  homepage: https://github.com/ask-rb/ask-schema
77
78
  licenses:
78
79
  - MIT
79
- metadata: {}
80
+ metadata:
81
+ homepage_uri: https://github.com/ask-rb/ask-schema
82
+ source_code_uri: https://github.com/ask-rb/ask-schema
83
+ changelog_uri: https://github.com/ask-rb/ask-schema/blob/master/CHANGELOG.md
80
84
  rdoc_options: []
81
85
  require_paths:
82
86
  - lib
@@ -91,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
95
  - !ruby/object:Gem::Version
92
96
  version: '0'
93
97
  requirements: []
94
- rubygems_version: 4.0.3
98
+ rubygems_version: 4.0.18
95
99
  specification_version: 4
96
100
  summary: JSON Schema DSL for Ruby
97
101
  test_files: []