verse-schema 1.1.0 → 1.1.1

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: 63618ff4cf11b43ec35e8bb88adaffdf8d15ac3a49a461427fbdc3cd8908299a
4
- data.tar.gz: 2ab22e1cd477e2b4f0e4cd070f50e2fd56e89b1c323795f4ca3cbfa1944e271d
3
+ metadata.gz: 6608dcff00a7bef2d8313866053ac7d37d82933d9b597052a222f0bcad693f91
4
+ data.tar.gz: dfcecf7d576b599b3c39d552cfbbf082ceda4707f0ec532d94d4a0db3dbfa620
5
5
  SHA512:
6
- metadata.gz: d7c5cb3e34919f47de38726ea146aa1d3d3ffe77b26d999c18da1d5e441ca06fcba4d8827c42d3b515ea7c7abd73177e7627352e1c2714ef82c0e2e196b9d01a
7
- data.tar.gz: 4b60874afb6bcf4b7721396b9fe799b38b1b21fe4fb31c9b8fd467b835ded25ff72f8df8a2ff7bb4cddd1dca322937aa8ad1f879bcc13e7d9e012cf027cc4f63
6
+ metadata.gz: d3bc6f97bf7b683d4e45ee578af5a1cd57375d56f54161a93ed3b725c2f783cbfedb2583fa9dc6470e68243bdaa3cda93f703dc82df4a63bd912d386695933bc
7
+ data.tar.gz: 49c7ae54ea24d5a4e7a8304e1361f70feb81f68a4993beddc153fabd1c88fa1d63e5eb5bd5bbcb41eea83bd863f7188bcfc73cdf31d98c9a0b08b32d749c0ecc
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- verse-schema (1.1.0)
4
+ verse-schema (1.1.1)
5
5
  attr_chainable (~> 1.0)
6
6
 
7
7
  GEM
@@ -109,7 +109,7 @@ class ReadmeDocExtractor
109
109
  end
110
110
  end
111
111
 
112
- # Extract code from an example
112
+ # Extract code from an example
113
113
  def extract_code_from_example(example)
114
114
  # Get the example block directly using instance_variable_get
115
115
  example_block = example.instance_variable_get(:@example_block)
@@ -14,7 +14,7 @@ module Verse
14
14
 
15
15
  # Initialize a new schema.
16
16
  def initialize(post_processors: nil)
17
- @post_processors = post_processors
17
+ @post_processors = post_processors
18
18
  end
19
19
 
20
20
  def rule(fields = nil, message = "rule failed", &block)
@@ -10,7 +10,7 @@ module Verse
10
10
  DEFAULT_MAPPER = lambda do |type|
11
11
  if type.is_a?(Base)
12
12
  proc do |value, _opts, locals:, strict:|
13
- type.validate(value, locals:, strict:)
13
+ type.validate(value, locals:, strict:)
14
14
  end
15
15
  elsif type.is_a?(Class)
16
16
  proc do |value|
@@ -57,10 +57,30 @@ module Verse
57
57
  @values.all? do |child_type|
58
58
  # ...is a subtype (`<=`) of *at least one* type allowed by the parent collection.
59
59
  parent_schema.values.any? do |parent_type|
60
- # Use the existing `<=` operator defined on schema types (Scalar, Struct, etc.)
61
- # This assumes the `<=` operator correctly handles class inheritance (e.g., Integer <= Object)
62
- # and schema type compatibility.
63
- child_type <= parent_type
60
+ # Special handling for when child_type is a primitive class (like Integer)
61
+ # and parent_type is a Scalar
62
+ if !child_type.is_a?(Base) && parent_type.is_a?(Scalar)
63
+ # Check if the primitive class is compatible with any of the Scalar's values
64
+ parent_type.values.any? do |scalar_value|
65
+ begin
66
+ # Use standard Ruby `<=` for comparison
67
+ child_type <= scalar_value
68
+ rescue TypeError
69
+ # Handle cases where <= is not defined between types
70
+ false
71
+ end
72
+ end
73
+ else
74
+ # Use the existing `<=` operator defined on schema types (Scalar, Struct, etc.)
75
+ # This assumes the `<=` operator correctly handles class inheritance (e.g., Integer <= Object)
76
+ # and schema type compatibility.
77
+ begin
78
+ child_type <= parent_type
79
+ rescue TypeError
80
+ # Handle cases where <= is not defined between types
81
+ false
82
+ end
83
+ end
64
84
  end
65
85
  end
66
86
  end
@@ -170,7 +190,6 @@ module Verse
170
190
 
171
191
  Result.new(output, error_builder.errors)
172
192
  end
173
-
174
193
  end
175
194
  end
176
195
  end
@@ -14,7 +14,7 @@ module Verse
14
14
  def initialize(values:, post_processors: nil)
15
15
  super(post_processors:)
16
16
 
17
- @values = values
17
+ @values = values
18
18
  end
19
19
 
20
20
  def validate(input, error_builder: nil, locals: {}, strict: false)
@@ -155,7 +155,6 @@ module Verse
155
155
 
156
156
  Result.new(output, error_builder.errors)
157
157
  end
158
-
159
158
  end
160
159
  end
161
160
  end
@@ -8,7 +8,7 @@ module Verse
8
8
  module Schema
9
9
  # A field in a schema
10
10
  class Field
11
- attr_reader :opts, :post_processors, :name, :type
11
+ attr_reader :opts, :post_processors, :name
12
12
 
13
13
  def initialize(name, type, opts, post_processors: nil, &block)
14
14
  @name = name
@@ -53,7 +53,7 @@ module Verse
53
53
  of_arg = @opts[:of] # For array and dictionary
54
54
  of_arg = [of_arg] unless of_arg.nil? || of_arg.is_a?(Array)
55
55
 
56
- if type == Hash || type == Object
56
+ if [Hash, Object].include?(type)
57
57
  type = Schema.dictionary(*of_arg) if of_arg # dictionary
58
58
  elsif type == Array
59
59
  type = Schema.array(*of_arg) if of_arg
@@ -269,7 +269,7 @@ module Verse
269
269
  elsif c.is_a?(Class) && p.is_a?(Verse::Schema::Scalar)
270
270
  p.values.any? { |p_val| c <= p_val }
271
271
  elsif c.is_a?(Verse::Schema::Base) && p.is_a?(Verse::Schema::Base)
272
- c <= p
272
+ c <= p
273
273
  elsif c.is_a?(Class) && p.is_a?(Class)
274
274
  c <= p
275
275
  else
@@ -64,19 +64,45 @@ module Verse
64
64
  end
65
65
  end
66
66
 
67
- def <=(other)
68
- other == self || inherit?(other)
67
+ def <=(other)
68
+ # 1. Identical check: Is it the exact same object?
69
+ return true if other == self
70
+
71
+ # 2. Check if inheriting from another Scalar:
72
+ # Use the existing inherit? method which correctly handles Scalar-to-Scalar inheritance.
73
+ # (inherit? implicitly checks `other.is_a?(Scalar)`)
74
+ return true if inherit?(other)
75
+
76
+ # 3. NEW: Check compatibility with non-Scalar types:
77
+ # If 'other' is not a Scalar, check if any type *wrapped* by this Scalar
78
+ # is a subtype of 'other'. This handles `Scalar<Integer> <= Integer`.
79
+ # We rely on the `<=` operator of the wrapped types themselves.
80
+ @values.any? do |wrapped_type|
81
+ begin
82
+ # Use standard Ruby `<=` for comparison.
83
+ # This works for Class <= Class (e.g., Integer <= Integer, Integer <= Numeric)
84
+ # and potentially for SchemaType <= SchemaType if defined.
85
+ wrapped_type <= other
86
+ rescue TypeError
87
+ # Handle cases where <= is not defined between wrapped_type and other
88
+ false
69
89
  end
90
+ end
91
+ end
70
92
 
71
- def <(other)
72
- other != self && inherit?(other)
73
- end
93
+ def <(other)
94
+ other != self && self <= other
95
+ end
74
96
 
75
- # rubocop:disable Style/InverseMethods
76
- def >(other)
77
- !self.<=(other)
78
- end
79
- # rubocop:enable Style/InverseMethods
97
+ # rubocop:disable Style/InverseMethods
98
+ def >(other)
99
+ !(self <= other)
100
+ end
101
+
102
+ def >=(other)
103
+ other <= self
104
+ end
105
+ # rubocop:enable Style/InverseMethods
80
106
 
81
107
  # Aggregation of two schemas.
82
108
  def +(other)
@@ -149,7 +175,6 @@ module Verse
149
175
 
150
176
  Result.new(coalesced_value, error_builder.errors)
151
177
  end
152
-
153
178
  end
154
179
  end
155
180
  end
@@ -221,49 +221,49 @@ module Verse
221
221
  fields << :extra_fields if extra_fields?
222
222
 
223
223
  # Special case for empty schema (yeah, I know, it happens in my production code...)
224
- if fields.empty?
225
- @dataclass = Class.new do
226
- def self.from_raw(*)=new
227
- def self.schema = dataclass_schema
228
-
229
- class_eval(&block) if block
230
- end
231
- else
232
- @dataclass = ::Struct.new(*fields, keyword_init: true) do
233
- # Redefine new method
234
- define_singleton_method(:from_raw, &method(:new))
235
-
236
- define_singleton_method(:new) do |*args, **kwargs|
237
- # Use the schema to generate the hash for our record
238
- if args.size > 1
239
- raise ArgumentError, "You cannot pass more than one argument"
240
- end
241
-
242
- if args.size == 1
243
- if kwargs.any?
244
- raise ArgumentError, "You cannot pass both a hash and keyword arguments"
245
- end
246
-
247
- kwargs = args.first
248
- end
249
-
250
- dataclass_schema.new(kwargs)
251
- end
252
-
253
- define_singleton_method(:schema){ dataclass_schema }
254
-
255
- class_eval(&block) if block
256
- end
257
- end
224
+ @dataclass = if fields.empty?
225
+ Class.new do
226
+ def self.from_raw(*)=new
227
+ def self.schema = dataclass_schema
228
+
229
+ class_eval(&block) if block
230
+ end
231
+ else
232
+ ::Struct.new(*fields, keyword_init: true) do
233
+ # Redefine new method
234
+ define_singleton_method(:from_raw, &method(:new))
235
+
236
+ define_singleton_method(:new) do |*args, **kwargs|
237
+ # Use the schema to generate the hash for our record
238
+ if args.size > 1
239
+ raise ArgumentError, "You cannot pass more than one argument"
240
+ end
241
+
242
+ if args.size == 1
243
+ if kwargs.any?
244
+ raise ArgumentError, "You cannot pass both a hash and keyword arguments"
245
+ end
246
+
247
+ kwargs = args.first
248
+ end
249
+
250
+ dataclass_schema.new(kwargs)
251
+ end
252
+
253
+ define_singleton_method(:schema){ dataclass_schema }
254
+
255
+ class_eval(&block) if block
256
+ end
257
+ end
258
258
  end
259
259
 
260
260
  def inspect
261
261
  fields_string = @fields.map do |field|
262
- if field.type.is_a?(Array)
263
- type_str = field.type.map(&:inspect).join("|")
264
- else
265
- type_str = field.type.inspect
266
- end
262
+ type_str = if field.type.is_a?(Array)
263
+ field.type.map(&:inspect).join("|")
264
+ else
265
+ field.type.inspect
266
+ end
267
267
 
268
268
  optional_marker = field.optional? ? "?" : ""
269
269
  "#{field.name}#{optional_marker}: #{type_str}"
@@ -320,7 +320,6 @@ module Verse
320
320
 
321
321
  Result.new(output, error_builder.errors)
322
322
  end
323
-
324
323
  end
325
324
  end
326
325
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Verse
4
4
  module Schema
5
- VERSION = "1.1.0"
5
+ VERSION = "1.1.1"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: verse-schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yacine Petitprez
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-23 00:00:00.000000000 Z
10
+ date: 2025-04-25 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: attr_chainable