verse-schema 1.1.0 → 1.2.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 +4 -4
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +7 -5
- data/README.md +58 -13
- data/lib/tasks/readme_doc_extractor.rb +1 -1
- data/lib/verse/schema/base.rb +1 -1
- data/lib/verse/schema/coalescer.rb +1 -1
- data/lib/verse/schema/collection.rb +22 -5
- data/lib/verse/schema/dictionary.rb +1 -2
- data/lib/verse/schema/field.rb +3 -3
- data/lib/verse/schema/json.rb +160 -0
- data/lib/verse/schema/scalar.rb +27 -4
- data/lib/verse/schema/struct.rb +39 -40
- data/lib/verse/schema/version.rb +1 -1
- data/lib/verse/schema.rb +1 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac21990f8abf487af0be8d14959c40595c2327cdbbda87a4bc15d53cfcf87a36
|
4
|
+
data.tar.gz: 102efefd85e3181bda88e337672a810c54bfafe4c623ae825972b7e82e6bc689
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e464f3fcbaa0d77b69d81f7001c80ecb7efba9298476aab84f16390bbda8481c1f37c3c7a5d80e11d9de31f7064c67e9db1448b545d4b927079d331ea939b3d
|
7
|
+
data.tar.gz: 89cfcaa93e6813a5ffab8834e49002ebf3791a42a64a5457444a0fa4ba941412583ea1925e5ba7c607e7bfcd3bd16628437f9c954b6710f6296f400b9b0d3be7
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-3.4.3
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
@@ -7,7 +7,7 @@ gemspec
|
|
7
7
|
|
8
8
|
# Development dependencies
|
9
9
|
group :development do
|
10
|
-
gem "prism", "~>
|
10
|
+
gem "prism", "~> 1.5.2", require: false
|
11
11
|
end
|
12
12
|
|
13
13
|
gem "rake", "~> 13.0"
|
@@ -18,6 +18,6 @@ gem "relaxed-rubocop"
|
|
18
18
|
gem "rubocop", "~> 1.21"
|
19
19
|
gem "simplecov"
|
20
20
|
|
21
|
-
gem "ruby-prof"
|
21
|
+
gem "ruby-prof", "~> 1.7.2"
|
22
22
|
|
23
23
|
gem "rspec", "~> 3.0"
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
verse-schema (1.
|
4
|
+
verse-schema (1.2.0)
|
5
5
|
attr_chainable (~> 1.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -9,6 +9,7 @@ GEM
|
|
9
9
|
specs:
|
10
10
|
ast (2.4.2)
|
11
11
|
attr_chainable (1.0.0)
|
12
|
+
base64 (0.3.0)
|
12
13
|
bootsnap (1.18.3)
|
13
14
|
msgpack (~> 1.2)
|
14
15
|
coderay (1.1.3)
|
@@ -22,7 +23,7 @@ GEM
|
|
22
23
|
parser (3.3.0.5)
|
23
24
|
ast (~> 2.4.1)
|
24
25
|
racc
|
25
|
-
prism (
|
26
|
+
prism (1.5.2)
|
26
27
|
pry (0.14.2)
|
27
28
|
coderay (~> 1.1)
|
28
29
|
method_source (~> 1.0)
|
@@ -58,7 +59,8 @@ GEM
|
|
58
59
|
unicode-display_width (>= 2.4.0, < 3.0)
|
59
60
|
rubocop-ast (1.31.2)
|
60
61
|
parser (>= 3.3.0.4)
|
61
|
-
ruby-prof (1.7.
|
62
|
+
ruby-prof (1.7.2)
|
63
|
+
base64
|
62
64
|
ruby-progressbar (1.13.0)
|
63
65
|
simplecov (0.22.0)
|
64
66
|
docile (~> 1.1)
|
@@ -73,13 +75,13 @@ PLATFORMS
|
|
73
75
|
|
74
76
|
DEPENDENCIES
|
75
77
|
bootsnap (~> 1.16)
|
76
|
-
prism (~>
|
78
|
+
prism (~> 1.5.2)
|
77
79
|
pry
|
78
80
|
rake (~> 13.0)
|
79
81
|
relaxed-rubocop
|
80
82
|
rspec (~> 3.0)
|
81
83
|
rubocop (~> 1.21)
|
82
|
-
ruby-prof
|
84
|
+
ruby-prof (~> 1.7.2)
|
83
85
|
simplecov
|
84
86
|
verse-schema!
|
85
87
|
|
data/README.md
CHANGED
@@ -123,6 +123,11 @@ These examples are extracted directly from the gem's specs, ensuring they are ac
|
|
123
123
|
- [Polymorphic Schema](#polymorphic-schema)
|
124
124
|
|
125
125
|
|
126
|
+
- [JSON Schema Generation](#json-schema-generation)
|
127
|
+
|
128
|
+
- [Generating JSON Schema](#generating-json-schema)
|
129
|
+
|
130
|
+
|
126
131
|
|
127
132
|
|
128
133
|
## 1. Basic Usage
|
@@ -952,7 +957,7 @@ it "demonstrates reusable rules defined with Verse::Schema.rule" do
|
|
952
957
|
is_positive = Verse::Schema.rule("must be positive") { |value| value > 0 }
|
953
958
|
|
954
959
|
# Define another reusable rule
|
955
|
-
is_even = Verse::Schema.rule("must be even"
|
960
|
+
is_even = Verse::Schema.rule("must be even", &:even?)
|
956
961
|
|
957
962
|
# Create a schema that uses the reusable rules
|
958
963
|
schema = Verse::Schema.define do
|
@@ -1666,7 +1671,6 @@ it "demonstrates a polymorphic schema" do
|
|
1666
1671
|
# 3. Define a builder schema. The best way to do this is to use the
|
1667
1672
|
# scalar type:
|
1668
1673
|
builder_schema = Verse::Schema.scalar(Hash).transform do |input, error_builder|
|
1669
|
-
|
1670
1674
|
type = input[:type]
|
1671
1675
|
|
1672
1676
|
if type.respond_to?(:to_sym)
|
@@ -1677,14 +1681,14 @@ it "demonstrates a polymorphic schema" do
|
|
1677
1681
|
end
|
1678
1682
|
|
1679
1683
|
schema = case type
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1683
|
-
|
1684
|
-
|
1685
|
-
|
1686
|
-
|
1687
|
-
|
1684
|
+
when :facebook
|
1685
|
+
facebook_schema
|
1686
|
+
when :google
|
1687
|
+
google_schema
|
1688
|
+
else
|
1689
|
+
error_builder.add(:type, "invalid type")
|
1690
|
+
stop
|
1691
|
+
end
|
1688
1692
|
|
1689
1693
|
# Validate the input against the selected schema
|
1690
1694
|
result = schema.validate(input, error_builder:)
|
@@ -1753,9 +1757,9 @@ it "demonstrates a polymorphic schema" do
|
|
1753
1757
|
expect(invalid_result.success?).to be false
|
1754
1758
|
# The errors are collected
|
1755
1759
|
expect(invalid_result.errors).to eq({
|
1756
|
-
|
1757
|
-
|
1758
|
-
|
1760
|
+
"events.0.url": ["is required"],
|
1761
|
+
"events.1.location": ["is required"],
|
1762
|
+
"events.2.type": ["invalid type"]
|
1759
1763
|
})
|
1760
1764
|
end
|
1761
1765
|
|
@@ -1763,6 +1767,47 @@ end
|
|
1763
1767
|
|
1764
1768
|
|
1765
1769
|
|
1770
|
+
## JSON Schema Generation
|
1771
|
+
|
1772
|
+
|
1773
|
+
### Generating JSON Schema
|
1774
|
+
|
1775
|
+
|
1776
|
+
```ruby
|
1777
|
+
it "converts a simple schema to a valid JSON schema" do
|
1778
|
+
schema = Verse::Schema.define do
|
1779
|
+
field(:name, String).meta(description: "The name of the user")
|
1780
|
+
field(:age, Integer)
|
1781
|
+
end
|
1782
|
+
|
1783
|
+
json_schema = Verse::Schema::Json.from(schema)
|
1784
|
+
puts JSON.pretty_generate(json_schema)
|
1785
|
+
|
1786
|
+
# The output of the `to_json` method will be a valid JSON schema hash:
|
1787
|
+
#
|
1788
|
+
# {
|
1789
|
+
# "type": "object",
|
1790
|
+
# "properties": {
|
1791
|
+
# "name": {
|
1792
|
+
# "type": "string",
|
1793
|
+
# "description": "The name of the user"
|
1794
|
+
# },
|
1795
|
+
# "age": {
|
1796
|
+
# "type": "integer"
|
1797
|
+
# }
|
1798
|
+
# },
|
1799
|
+
# "required": [
|
1800
|
+
# "name",
|
1801
|
+
# "age"
|
1802
|
+
# ],
|
1803
|
+
# "additionalProperties": false
|
1804
|
+
# }
|
1805
|
+
end
|
1806
|
+
|
1807
|
+
```
|
1808
|
+
|
1809
|
+
|
1810
|
+
|
1766
1811
|
|
1767
1812
|
## License
|
1768
1813
|
|
@@ -109,7 +109,7 @@ class ReadmeDocExtractor
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
|
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)
|
data/lib/verse/schema/base.rb
CHANGED
@@ -57,10 +57,28 @@ 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
|
-
#
|
61
|
-
#
|
62
|
-
|
63
|
-
|
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
|
+
# Use standard Ruby `<=` for comparison
|
66
|
+
child_type <= scalar_value
|
67
|
+
rescue TypeError
|
68
|
+
# Handle cases where <= is not defined between types
|
69
|
+
false
|
70
|
+
end
|
71
|
+
else
|
72
|
+
# Use the existing `<=` operator defined on schema types (Scalar, Struct, etc.)
|
73
|
+
# This assumes the `<=` operator correctly handles class inheritance (e.g., Integer <= Object)
|
74
|
+
# and schema type compatibility.
|
75
|
+
begin
|
76
|
+
child_type <= parent_type
|
77
|
+
rescue TypeError
|
78
|
+
# Handle cases where <= is not defined between types
|
79
|
+
false
|
80
|
+
end
|
81
|
+
end
|
64
82
|
end
|
65
83
|
end
|
66
84
|
end
|
@@ -170,7 +188,6 @@ module Verse
|
|
170
188
|
|
171
189
|
Result.new(output, error_builder.errors)
|
172
190
|
end
|
173
|
-
|
174
191
|
end
|
175
192
|
end
|
176
193
|
end
|
@@ -14,7 +14,7 @@ module Verse
|
|
14
14
|
def initialize(values:, post_processors: nil)
|
15
15
|
super(post_processors:)
|
16
16
|
|
17
|
-
@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
|
data/lib/verse/schema/field.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
272
|
+
c <= p
|
273
273
|
elsif c.is_a?(Class) && p.is_a?(Class)
|
274
274
|
c <= p
|
275
275
|
else
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Verse
|
4
|
+
module Schema
|
5
|
+
module Json
|
6
|
+
# rubocop:disable Lint/HashCompareByIdentity
|
7
|
+
# @param schema [Verse::Schema::Base] The schema to convert to JSON schema
|
8
|
+
# @return [Hash] The JSON schema
|
9
|
+
def self.from(schema)
|
10
|
+
definitions = {}
|
11
|
+
# directly build the root schema, don't use a ref.
|
12
|
+
output = _build_schema(schema, registry: {}, definitions: definitions)
|
13
|
+
|
14
|
+
if definitions.any?
|
15
|
+
output[:"$defs"] = definitions
|
16
|
+
end
|
17
|
+
|
18
|
+
output
|
19
|
+
end
|
20
|
+
|
21
|
+
def self._from_schema(schema, registry:, definitions:)
|
22
|
+
return { "$ref": registry[schema.object_id] } if registry.key?(schema.object_id)
|
23
|
+
|
24
|
+
if schema.is_a?(Verse::Schema::Struct)
|
25
|
+
# Register the schema to handle recursion
|
26
|
+
# and give it a name.
|
27
|
+
# The name is based on the class name, or the object_id if the class is anonymous.
|
28
|
+
name = :"Schema#{schema.object_id}"
|
29
|
+
ref = "#/$defs/#{name}"
|
30
|
+
|
31
|
+
registry[schema.object_id] = ref
|
32
|
+
|
33
|
+
# if it's the root schema, don't create a definition, just build it.
|
34
|
+
built_schema = _build_schema(schema, registry:, definitions:)
|
35
|
+
|
36
|
+
definitions[name] = built_schema
|
37
|
+
|
38
|
+
return { "$ref": ref }
|
39
|
+
end
|
40
|
+
|
41
|
+
_build_schema(schema, registry:, definitions:)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self._build_schema(schema, registry:, definitions:)
|
45
|
+
case schema
|
46
|
+
when Verse::Schema::Struct
|
47
|
+
properties = schema.fields.each_with_object({}) do |field_obj, obj|
|
48
|
+
next if field_obj.type.is_a?(Verse::Schema::Selector)
|
49
|
+
|
50
|
+
obj[field_obj.name] = begin
|
51
|
+
output = _from_schema(field_obj.type, registry:, definitions:)
|
52
|
+
desc = field_obj.opts.dig(:meta, :description)
|
53
|
+
|
54
|
+
output[:description] = desc if desc
|
55
|
+
|
56
|
+
default = field_obj.opts[:default]
|
57
|
+
|
58
|
+
if default && !default.is_a?(Proc)
|
59
|
+
output[:default] = default
|
60
|
+
end
|
61
|
+
|
62
|
+
output
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
required_fields = schema.fields.select(&:required?).map(&:name)
|
67
|
+
|
68
|
+
json = {
|
69
|
+
type: "object",
|
70
|
+
properties: properties
|
71
|
+
}
|
72
|
+
json[:required] = required_fields if required_fields.any?
|
73
|
+
json[:additionalProperties] = schema.extra_fields?
|
74
|
+
|
75
|
+
# Handle selectors
|
76
|
+
schema.fields.each do |field_obj|
|
77
|
+
next unless field_obj.type.is_a?(Verse::Schema::Selector)
|
78
|
+
|
79
|
+
discriminator = field_obj.opts[:over]
|
80
|
+
json[:properties][field_obj.name] = { type: "object" }
|
81
|
+
|
82
|
+
selector_keys = field_obj.type.values.keys
|
83
|
+
if !selector_keys.include?(:__else__)
|
84
|
+
json[:properties][discriminator][:enum] = selector_keys.map(&:to_s)
|
85
|
+
end
|
86
|
+
|
87
|
+
json[:allOf] = field_obj.type.values.map do |key, sub_schema|
|
88
|
+
next if key == :__else__
|
89
|
+
|
90
|
+
{
|
91
|
+
if: {
|
92
|
+
properties: { discriminator.to_sym => { const: key.to_s } }
|
93
|
+
},
|
94
|
+
then: {
|
95
|
+
properties: {
|
96
|
+
field_obj.name => _from_schema(sub_schema, registry:, definitions:)
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
end.compact
|
101
|
+
end
|
102
|
+
|
103
|
+
json
|
104
|
+
when Verse::Schema::Collection
|
105
|
+
items = if schema.values.length > 1
|
106
|
+
{ anyOf: schema.values.map { |v| _from_schema(v, registry:, definitions:) } }
|
107
|
+
else
|
108
|
+
_from_schema(schema.values.first, registry:, definitions:)
|
109
|
+
end
|
110
|
+
|
111
|
+
{
|
112
|
+
type: "array",
|
113
|
+
items: items
|
114
|
+
}
|
115
|
+
when Verse::Schema::Dictionary
|
116
|
+
additional_properties = if schema.values.length > 1
|
117
|
+
{ anyOf: schema.values.map { |v| _from_schema(v, registry:, definitions:) } }
|
118
|
+
else
|
119
|
+
_from_schema(schema.values.first, registry:, definitions:)
|
120
|
+
end
|
121
|
+
{
|
122
|
+
type: "object",
|
123
|
+
additionalProperties: additional_properties
|
124
|
+
}
|
125
|
+
when Verse::Schema::Scalar
|
126
|
+
{
|
127
|
+
anyOf: schema.values.map { |v| _from_schema(v, registry:, definitions:) }
|
128
|
+
}
|
129
|
+
when Verse::Schema::Selector
|
130
|
+
# This should not be reached directly for a valid schema with `over`
|
131
|
+
raise "Selector schema must be used within a Struct with `over` option."
|
132
|
+
when String.singleton_class, Symbol.singleton_class
|
133
|
+
{ type: "string" }
|
134
|
+
when Integer.singleton_class
|
135
|
+
{ type: "integer" }
|
136
|
+
when Float.singleton_class, Numeric.singleton_class
|
137
|
+
{ type: "number" }
|
138
|
+
when TrueClass.singleton_class, FalseClass.singleton_class
|
139
|
+
{ type: "boolean" }
|
140
|
+
when Time.singleton_class
|
141
|
+
{ type: "string", format: "date-time" }
|
142
|
+
when NilClass.singleton_class, nil
|
143
|
+
{ type: "null" }
|
144
|
+
when Array
|
145
|
+
case schema.length
|
146
|
+
when 0
|
147
|
+
{ type: "null" }
|
148
|
+
when 1
|
149
|
+
_from_schema(schema.first, registry:, definitions:)
|
150
|
+
else
|
151
|
+
{ anyOf: schema.map { |v| _from_schema(v, registry:, definitions:) } }
|
152
|
+
end
|
153
|
+
else
|
154
|
+
raise "Unknown type #{schema.inspect}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Lint/HashCompareByIdentity
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/lib/verse/schema/scalar.rb
CHANGED
@@ -65,16 +65,40 @@ module Verse
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def <=(other)
|
68
|
-
|
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
|
+
# Use standard Ruby `<=` for comparison.
|
82
|
+
# This works for Class <= Class (e.g., Integer <= Integer, Integer <= Numeric)
|
83
|
+
# and potentially for SchemaType <= SchemaType if defined.
|
84
|
+
wrapped_type <= other
|
85
|
+
rescue TypeError
|
86
|
+
# Handle cases where <= is not defined between wrapped_type and other
|
87
|
+
false
|
88
|
+
end
|
69
89
|
end
|
70
90
|
|
71
91
|
def <(other)
|
72
|
-
other != self &&
|
92
|
+
other != self && self <= other
|
73
93
|
end
|
74
94
|
|
75
95
|
# rubocop:disable Style/InverseMethods
|
76
96
|
def >(other)
|
77
|
-
!self
|
97
|
+
!(self <= other)
|
98
|
+
end
|
99
|
+
|
100
|
+
def >=(other)
|
101
|
+
other <= self
|
78
102
|
end
|
79
103
|
# rubocop:enable Style/InverseMethods
|
80
104
|
|
@@ -149,7 +173,6 @@ module Verse
|
|
149
173
|
|
150
174
|
Result.new(coalesced_value, error_builder.errors)
|
151
175
|
end
|
152
|
-
|
153
176
|
end
|
154
177
|
end
|
155
178
|
end
|
data/lib/verse/schema/struct.rb
CHANGED
@@ -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
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
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
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
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
|
data/lib/verse/schema/version.rb
CHANGED
data/lib/verse/schema.rb
CHANGED
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.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yacine Petitprez
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: attr_chainable
|
@@ -33,6 +33,7 @@ files:
|
|
33
33
|
- ".rspec"
|
34
34
|
- ".rubocop-https---relaxed-ruby-style-rubocop-yml"
|
35
35
|
- ".rubocop.yml"
|
36
|
+
- ".ruby-version"
|
36
37
|
- CHANGELOG.md
|
37
38
|
- Gemfile
|
38
39
|
- Gemfile.lock
|
@@ -51,6 +52,7 @@ files:
|
|
51
52
|
- lib/verse/schema/field.rb
|
52
53
|
- lib/verse/schema/field/ext.rb
|
53
54
|
- lib/verse/schema/invalid_schema_error.rb
|
55
|
+
- lib/verse/schema/json.rb
|
54
56
|
- lib/verse/schema/optionable.rb
|
55
57
|
- lib/verse/schema/post_processor.rb
|
56
58
|
- lib/verse/schema/result.rb
|
@@ -78,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
80
|
- !ruby/object:Gem::Version
|
79
81
|
version: '0'
|
80
82
|
requirements: []
|
81
|
-
rubygems_version: 3.6.
|
83
|
+
rubygems_version: 3.6.7
|
82
84
|
specification_version: 4
|
83
85
|
summary: Consume input schema and output coalesced version of them
|
84
86
|
test_files: []
|