validrb 0.5.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb818549e6bca37a89242aee2e72b9f74c32cbafa3db4bb6cbaad060b93fc426
4
- data.tar.gz: 0d7debd6603cbf1afb0bf664e1a3252de6c270bd2844ea8f2cd1406b198e4008
3
+ metadata.gz: 501a47fc614649d04bede576eba6ee6d235906c47a41968b8f2458fee449a1f5
4
+ data.tar.gz: 3f2ae0e43d9508efd7e88ee5a08a84c52d1bc6af13ff2406eb2826e63789287f
5
5
  SHA512:
6
- metadata.gz: 33da9e6eefcc7c7b4ab78d8785c7b12b0216f8f0677b4d7120ebec5056ba1b6a42030afa3c13618c03d4e22e47538da085bc0783db7973698dcec6d2b44fea20
7
- data.tar.gz: 2845fbd1a754e91509692d671d5ff050e738cc8799625d34028acc77cf47a041861f80556b23cefb05206c58b56350c62643d540b2ad6e51d5cc2644fea1ce16
6
+ metadata.gz: edc2ba6b67124989d7266c71fed51589042fdaae23d486972b98c1dce86c013843a60472d383ace2f096a37b10ad1daa6a1666fbde9d6891cc192832074c54a4
7
+ data.tar.gz: 90cd6baccf04eef4775416c96ce38fcc705ff8e43a3812bd218a6c85afdc03c0c53565bbba92098ef36e515eb2d9cccb151d3c251272a065f213cd1c1d93560a
data/CHANGELOG.md CHANGED
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.6.0] - 2024-01-31
9
+
10
+ ### Added
11
+ - **Inline nested schemas** - Define nested schemas directly in field blocks
12
+ ```ruby
13
+ field :address, :object do
14
+ field :street, :string
15
+ field :city, :string
16
+ end
17
+ ```
18
+ - **Array of schemas shorthand** - Pass Schema instances directly to `of:` option
19
+ ```ruby
20
+ field :items, :array, of: ItemSchema # No wrapper needed
21
+ ```
22
+ - **OpenAPI convenience methods** on Generator:
23
+ - `request_body(schema)` - Generate request body structure
24
+ - `query_params(schema)` - Convert schema to query parameters
25
+ - `path_params(*names)` - Generate path parameters
26
+ - `response_schema(schema)` - Generate response with schema
27
+ - `response(description)` - Generate simple response
28
+ - `error_response` - Generate standard error response structure
29
+
30
+ ### Changed
31
+ - `parse` and `safe_parse` now accept both hash and kwargs syntax:
32
+ ```ruby
33
+ schema.safe_parse({ name: "John" }) # Hash syntax
34
+ schema.safe_parse(name: "John") # Kwargs syntax (new)
35
+ ```
36
+
8
37
  ## [0.5.0] - 2024-01-15
9
38
 
10
39
  ### Added
@@ -92,6 +121,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
92
121
  - Requires Ruby >= 3.0
93
122
  - Inspired by Pydantic (Python) and Zod (TypeScript)
94
123
 
124
+ [0.6.0]: https://github.com/validrb/validrb/compare/v0.5.0...v0.6.0
95
125
  [0.5.0]: https://github.com/validrb/validrb/compare/v0.4.0...v0.5.0
96
126
  [0.4.0]: https://github.com/validrb/validrb/compare/v0.3.0...v0.4.0
97
127
  [0.3.0]: https://github.com/validrb/validrb/compare/v0.2.0...v0.3.0
data/README.md CHANGED
@@ -128,6 +128,45 @@ schema = Validrb.schema do
128
128
  end
129
129
  ```
130
130
 
131
+ ### Inline Nested Schemas (v0.6.0+)
132
+
133
+ Define nested schemas directly without creating separate schema objects:
134
+
135
+ ```ruby
136
+ schema = Validrb.schema do
137
+ field :name, :string
138
+
139
+ # Inline object schema
140
+ field :address, :object do
141
+ field :street, :string
142
+ field :city, :string
143
+ field :zip, :string, format: /\A\d{5}\z/
144
+ end
145
+
146
+ # Inline array item schema
147
+ field :items, :array do
148
+ field :product_id, :integer
149
+ field :quantity, :integer, min: 1
150
+ end
151
+ end
152
+ ```
153
+
154
+ ### Array of Schemas Shorthand (v0.6.0+)
155
+
156
+ Pass schema instances directly to `of:`:
157
+
158
+ ```ruby
159
+ ItemSchema = Validrb.schema do
160
+ field :id, :integer
161
+ field :name, :string
162
+ end
163
+
164
+ schema = Validrb.schema do
165
+ # Pass schema directly - no wrapper needed
166
+ field :items, :array, of: ItemSchema
167
+ end
168
+ ```
169
+
131
170
  ## Constraints
132
171
 
133
172
  ```ruby
data/lib/validrb/field.rb CHANGED
@@ -137,7 +137,13 @@ module Validrb
137
137
  def extract_type_options(type, options)
138
138
  case type
139
139
  when :array
140
- { of: options[:of] }.compact
140
+ of_option = options[:of]
141
+ # If of: is a Schema, wrap it in an Object type
142
+ if of_option.is_a?(Schema)
143
+ { of: Types::Object.new(schema: of_option) }
144
+ else
145
+ { of: of_option }.compact
146
+ end
141
147
  when :object, :hash
142
148
  { schema: options[:schema] }.compact
143
149
  when :discriminated_union
@@ -82,8 +82,125 @@ module Validrb
82
82
  YAML.dump(generate(info: info, **options))
83
83
  end
84
84
 
85
+ # ============================================================
86
+ # Convenience Methods
87
+ # ============================================================
88
+
89
+ # Generate a request body structure for a schema
90
+ # @param schema [Validrb::Schema] The schema to use
91
+ # @param required [Boolean] Whether the request body is required (default: true)
92
+ # @param content_type [String] The content type (default: "application/json")
93
+ # @return [Hash] OpenAPI request body object
94
+ def request_body(schema, required: true, content_type: "application/json")
95
+ {
96
+ "required" => required,
97
+ "content" => {
98
+ content_type => {
99
+ "schema" => schema_to_openapi(schema)
100
+ }
101
+ }
102
+ }
103
+ end
104
+
105
+ # Generate query parameters from a schema
106
+ # @param schema [Validrb::Schema] The schema to convert to parameters
107
+ # @return [Array<Hash>] Array of OpenAPI parameter objects
108
+ def query_params(schema)
109
+ schema.fields.map do |name, field|
110
+ {
111
+ "name" => name.to_s,
112
+ "in" => "query",
113
+ "required" => field.required? && !field.has_default? && !field.conditional?,
114
+ "schema" => field_to_openapi(field)
115
+ }
116
+ end
117
+ end
118
+
119
+ # Generate path parameters from field names
120
+ # @param names [Array<Symbol, String>] Parameter names
121
+ # @param types [Hash] Optional type overrides { name: :integer }
122
+ # @return [Array<Hash>] Array of OpenAPI parameter objects
123
+ def path_params(*names, types: {})
124
+ names.map do |name|
125
+ type = types[name.to_sym] || :string
126
+ {
127
+ "name" => name.to_s,
128
+ "in" => "path",
129
+ "required" => true,
130
+ "schema" => primitive_type_schema(type)
131
+ }
132
+ end
133
+ end
134
+
135
+ # Generate a response schema structure
136
+ # @param schema [Validrb::Schema] The schema for the response
137
+ # @param description [String] Response description
138
+ # @param content_type [String] The content type (default: "application/json")
139
+ # @return [Hash] OpenAPI response object
140
+ def response_schema(schema, description: "Successful response", content_type: "application/json")
141
+ {
142
+ "description" => description,
143
+ "content" => {
144
+ content_type => {
145
+ "schema" => schema_to_openapi(schema)
146
+ }
147
+ }
148
+ }
149
+ end
150
+
151
+ # Generate a simple response without body
152
+ # @param description [String] Response description
153
+ # @return [Hash] OpenAPI response object
154
+ def response(description)
155
+ { "description" => description }
156
+ end
157
+
158
+ # Generate error response structure
159
+ # @param description [String] Error description
160
+ # @return [Hash] OpenAPI response object with standard error schema
161
+ def error_response(description: "Validation error")
162
+ {
163
+ "description" => description,
164
+ "content" => {
165
+ "application/json" => {
166
+ "schema" => {
167
+ "type" => "object",
168
+ "properties" => {
169
+ "error" => { "type" => "string" },
170
+ "details" => {
171
+ "type" => "array",
172
+ "items" => {
173
+ "type" => "object",
174
+ "properties" => {
175
+ "path" => { "type" => "string" },
176
+ "message" => { "type" => "string" }
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+ }
183
+ }
184
+ }
185
+ end
186
+
85
187
  private
86
188
 
189
+ def primitive_type_schema(type)
190
+ case type.to_sym
191
+ when :string
192
+ { "type" => "string" }
193
+ when :integer
194
+ { "type" => "integer" }
195
+ when :float, :number
196
+ { "type" => "number" }
197
+ when :boolean
198
+ { "type" => "boolean" }
199
+ else
200
+ { "type" => "string" }
201
+ end
202
+ end
203
+
87
204
  def normalize_info(info)
88
205
  info = info.transform_keys(&:to_s)
89
206
  {
@@ -21,11 +21,13 @@ module Validrb
21
21
  end
22
22
 
23
23
  # Parse data and raise ValidationError on failure
24
- # @param data [Hash] The data to validate
24
+ # @param data [Hash] The data to validate (can be passed as positional arg or kwargs)
25
25
  # @param path_prefix [Array] Path prefix for error messages
26
26
  # @param context [Context, Hash, nil] Optional validation context
27
- def parse(data, path_prefix: [], context: nil)
28
- result = safe_parse(data, path_prefix: path_prefix, context: context)
27
+ def parse(data = nil, path_prefix: [], context: nil, **data_kwargs)
28
+ # Support both parse({ name: 'John' }) and parse(name: 'John')
29
+ actual_data = data.nil? ? data_kwargs : data
30
+ result = safe_parse(actual_data, path_prefix: path_prefix, context: context)
29
31
 
30
32
  raise ValidationError, result.errors if result.failure?
31
33
 
@@ -33,11 +35,13 @@ module Validrb
33
35
  end
34
36
 
35
37
  # Parse data and return Result (Success or Failure)
36
- # @param data [Hash] The data to validate
38
+ # @param data [Hash] The data to validate (can be passed as positional arg or kwargs)
37
39
  # @param path_prefix [Array] Path prefix for error messages
38
40
  # @param context [Context, Hash, nil] Optional validation context
39
- def safe_parse(data, path_prefix: [], context: nil)
40
- normalized = normalize_input(data)
41
+ def safe_parse(data = nil, path_prefix: [], context: nil, **data_kwargs)
42
+ # Support both safe_parse({ name: 'John' }) and safe_parse(name: 'John')
43
+ actual_data = data.nil? ? data_kwargs : data
44
+ normalized = normalize_input(actual_data)
41
45
  ctx = normalize_context(context)
42
46
  errors = []
43
47
  result_data = {}
@@ -279,19 +283,45 @@ module Validrb
279
283
  @schema = schema
280
284
  end
281
285
 
282
- def field(name, type, **options)
286
+ # Define a field with optional inline schema block
287
+ # @example Basic field
288
+ # field :name, :string
289
+ # @example Object with inline schema
290
+ # field :address, :object do
291
+ # field :street, :string
292
+ # field :city, :string
293
+ # end
294
+ # @example Array with inline item schema
295
+ # field :items, :array do
296
+ # field :product_id, :integer
297
+ # field :quantity, :integer
298
+ # end
299
+ def field(name, type, **options, &block)
300
+ if block_given?
301
+ # Create inline nested schema from block
302
+ inline_schema = Schema.new(&block)
303
+
304
+ case type
305
+ when :object, :hash
306
+ options[:schema] = inline_schema
307
+ when :array
308
+ # For arrays, the block defines the item schema
309
+ options[:of] = inline_schema
310
+ end
311
+ end
312
+
283
313
  field = Field.new(name, type, **options)
284
314
  @schema.add_field(field)
285
315
  end
286
316
 
287
317
  # Shorthand for optional field
288
- def optional(name, type, **options)
289
- field(name, type, **options.merge(optional: true))
318
+ def optional(name, type, **options, &block)
319
+ field(name, type, **options.merge(optional: true), &block)
290
320
  end
291
321
 
292
322
  # Shorthand for required field (explicit)
293
- def required(name, type, **options)
294
- field(name, type, **options.merge(optional: false))
323
+ def required(name, type, **options, &block)
324
+ field(name, type, **options.merge(optional: false), &block)
295
325
  end
296
326
 
297
327
  # Add a custom validator block
@@ -67,6 +67,9 @@ module Validrb
67
67
  @item_type
68
68
  when Class
69
69
  @item_type.new
70
+ when ::Validrb::Schema
71
+ # Schema instance - wrap in Object type
72
+ Types::Object.new(schema: @item_type)
70
73
  else
71
74
  raise ArgumentError, "Invalid item type: #{@item_type.inspect}"
72
75
  end
@@ -80,6 +83,8 @@ module Validrb
80
83
  @item_type.type_name
81
84
  when Class
82
85
  @item_type.name.split("::").last.downcase
86
+ when ::Validrb::Schema
87
+ "object"
83
88
  else
84
89
  "unknown"
85
90
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Validrb
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.0"
5
5
  end
data/validrb.gemspec CHANGED
@@ -1,43 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "lib/validrb/version"
3
+ require_relative 'lib/validrb/version'
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = "validrb"
6
+ spec.name = 'validrb'
7
7
  spec.version = Validrb::VERSION
8
- spec.authors = ["Validrb Contributors"]
9
- spec.email = ["validrb@example.com"]
8
+ spec.authors = ['Elysium Arc']
9
+ spec.email = ['imam6mouad@gmail.com']
10
10
 
11
- spec.summary = "A Ruby schema validation library with type coercion"
11
+ spec.summary = 'A Ruby schema validation library with type coercion'
12
12
  spec.description = <<~DESC
13
13
  Validrb is a powerful Ruby schema validation library inspired by Pydantic and Zod.
14
14
  It provides type coercion, rich constraints, schema composition, union types,
15
15
  discriminated unions, custom validators, JSON Schema generation, and serialization.
16
- Zero runtime dependencies, pure Ruby.
17
16
  DESC
18
- spec.homepage = "https://github.com/validrb/validrb"
19
- spec.license = "MIT"
20
- spec.required_ruby_version = ">= 3.0.0"
17
+ spec.homepage = 'http://github.com/Elysium-Arc/Validrb'
18
+ spec.license = 'MIT'
19
+ spec.required_ruby_version = '>= 3.0.0'
21
20
 
22
21
  spec.metadata = {
23
- "homepage_uri" => spec.homepage,
24
- "source_code_uri" => spec.homepage,
25
- "changelog_uri" => "#{spec.homepage}/blob/main/CHANGELOG.md",
26
- "documentation_uri" => spec.homepage,
27
- "bug_tracker_uri" => "#{spec.homepage}/issues",
28
- "rubygems_mfa_required" => "true"
22
+ 'homepage_uri' => spec.homepage,
23
+ 'source_code_uri' => spec.homepage,
24
+ 'changelog_uri' => "#{spec.homepage}/blob/main/CHANGELOG.md",
25
+ 'documentation_uri' => spec.homepage,
26
+ 'bug_tracker_uri' => "#{spec.homepage}/issues",
27
+ 'rubygems_mfa_required' => 'true'
29
28
  }
30
29
 
31
30
  spec.files = Dir.chdir(__dir__) do
32
31
  `git ls-files -z`.split("\x0").reject do |f|
33
32
  (File.expand_path(f) == __FILE__) ||
34
33
  f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile]) ||
35
- f == "demo.rb"
34
+ f == 'demo.rb'
36
35
  end
37
36
  end
38
- spec.bindir = "exe"
37
+ spec.bindir = 'exe'
39
38
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
40
- spec.require_paths = ["lib"]
39
+ spec.require_paths = ['lib']
41
40
 
42
- # Zero runtime dependencies - pure Ruby
41
+ # bigdecimal is used for Decimal type - required explicitly for Ruby 3.4+
42
+ spec.add_dependency 'bigdecimal'
43
43
  end
metadata CHANGED
@@ -1,22 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: validrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
- - Validrb Contributors
7
+ - Elysium Arc
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
11
  date: 2026-01-31 00:00:00.000000000 Z
12
- dependencies: []
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bigdecimal
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: |
14
28
  Validrb is a powerful Ruby schema validation library inspired by Pydantic and Zod.
15
29
  It provides type coercion, rich constraints, schema composition, union types,
16
30
  discriminated unions, custom validators, JSON Schema generation, and serialization.
17
- Zero runtime dependencies, pure Ruby.
18
31
  email:
19
- - validrb@example.com
32
+ - imam6mouad@gmail.com
20
33
  executables: []
21
34
  extensions: []
22
35
  extra_rdoc_files: []
@@ -59,15 +72,15 @@ files:
59
72
  - lib/validrb/types/union.rb
60
73
  - lib/validrb/version.rb
61
74
  - validrb.gemspec
62
- homepage: https://github.com/validrb/validrb
75
+ homepage: http://github.com/Elysium-Arc/Validrb
63
76
  licenses:
64
77
  - MIT
65
78
  metadata:
66
- homepage_uri: https://github.com/validrb/validrb
67
- source_code_uri: https://github.com/validrb/validrb
68
- changelog_uri: https://github.com/validrb/validrb/blob/main/CHANGELOG.md
69
- documentation_uri: https://github.com/validrb/validrb
70
- bug_tracker_uri: https://github.com/validrb/validrb/issues
79
+ homepage_uri: http://github.com/Elysium-Arc/Validrb
80
+ source_code_uri: http://github.com/Elysium-Arc/Validrb
81
+ changelog_uri: http://github.com/Elysium-Arc/Validrb/blob/main/CHANGELOG.md
82
+ documentation_uri: http://github.com/Elysium-Arc/Validrb
83
+ bug_tracker_uri: http://github.com/Elysium-Arc/Validrb/issues
71
84
  rubygems_mfa_required: 'true'
72
85
  post_install_message:
73
86
  rdoc_options: []