json_model_rb 0.1.14 → 0.1.16
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/README.md +33 -35
- data/lib/json_model/config.rb +2 -0
- data/lib/json_model/properties.rb +3 -4
- data/lib/json_model/schema_meta.rb +11 -2
- data/lib/json_model/type_spec/castable.rb +34 -0
- data/lib/json_model/type_spec/primitive/string.rb +2 -3
- data/lib/json_model/type_spec.rb +26 -19
- data/lib/json_model/types/all_of.rb +8 -5
- data/lib/json_model/types/any_of.rb +8 -5
- data/lib/json_model/types/array.rb +8 -5
- data/lib/json_model/types/boolean.rb +1 -1
- data/lib/json_model/types/const.rb +2 -2
- data/lib/json_model/types/enum.rb +2 -2
- data/lib/json_model/types/integer.rb +23 -0
- data/lib/json_model/types/null.rb +7 -0
- data/lib/json_model/types/number.rb +23 -0
- data/lib/json_model/types/one_of.rb +8 -5
- data/lib/json_model/types/string.rb +23 -0
- data/lib/json_model/types.rb +6 -2
- data/lib/json_model/version.rb +1 -1
- data/spec/examples/file_system_spec.rb +7 -14
- data/spec/examples/user_spec.rb +12 -6
- data/spec/schema_meta_spec.rb +22 -2
- data/spec/schema_spec.rb +2 -9
- data/spec/type_spec/castable_spec.rb +19 -0
- data/spec/type_spec/composition_spec.rb +0 -1
- metadata +8 -3
- data/spec/examples/component_spec.rb +0 -57
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5c3f4ee507240e543a97f2ef35f1ec1bc998530547c4f841164c86baaf06c88
|
|
4
|
+
data.tar.gz: 456f3afae37e3a2f5d040f20dabc7c86a3ae534c60ca40fefae2d6b24155e200
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6404570b5ae3113f6f067da85b595fe066f866f226a87b381909593da31046f427ed59ccb08f7174986106b58dc47487bca9eeee1b7b4eb1312bb7f6102b6712
|
|
7
|
+
data.tar.gz: 65a200948801a4238e3780ab24953b7e36a1404c1a4012a21af6e117b2cf24b2609aeb87eaaa36f8fa7d0d8917c9a97e9843dcb3e5976689c99152bc7e855ef1
|
data/README.md
CHANGED
|
@@ -99,7 +99,7 @@ class Product
|
|
|
99
99
|
# Properties
|
|
100
100
|
property :id, type: String
|
|
101
101
|
property :name, type: String
|
|
102
|
-
property :price, type: Float
|
|
102
|
+
property :price, type: T::Float[minimum: 0]
|
|
103
103
|
property :available, type: T::Boolean, default: true, optional: true
|
|
104
104
|
end
|
|
105
105
|
```
|
|
@@ -124,30 +124,28 @@ class StringExample
|
|
|
124
124
|
property :simple_string, type: String
|
|
125
125
|
|
|
126
126
|
# String with length constraints
|
|
127
|
-
property :username, type: String,
|
|
128
|
-
min_length: 3,
|
|
129
|
-
max_length: 20
|
|
127
|
+
property :username, type: T::String[min_length: 3, max_length: 20]
|
|
130
128
|
|
|
131
129
|
# String with pattern (regex)
|
|
132
|
-
property :product_code, type: String
|
|
130
|
+
property :product_code, type: T::String[pattern: /\A[A-Z]{3}-\d{4}\z/]
|
|
133
131
|
|
|
134
132
|
# String with format
|
|
135
|
-
property :email, type: String
|
|
136
|
-
property :uri, type: String
|
|
137
|
-
property :hostname, type: String
|
|
138
|
-
property :ipv4, type: String
|
|
139
|
-
property :ipv6, type: String
|
|
140
|
-
property :uuid, type: String
|
|
141
|
-
property :date, type: String
|
|
142
|
-
property :time, type: String
|
|
143
|
-
property :datetime, type: String
|
|
144
|
-
property :duration, type: String
|
|
133
|
+
property :email, type: T::String[format: :email]
|
|
134
|
+
property :uri, type: T::String[format: :uri]
|
|
135
|
+
property :hostname, type: T::String[format: :hostname]
|
|
136
|
+
property :ipv4, type: T::String[format: :ipv4]
|
|
137
|
+
property :ipv6, type: T::String[format: :ipv6]
|
|
138
|
+
property :uuid, type: T::String[format: :uuid]
|
|
139
|
+
property :date, type: T::String[format: :date]
|
|
140
|
+
property :time, type: T::String[format: :time]
|
|
141
|
+
property :datetime, type: T::String[format: :date_time]
|
|
142
|
+
property :duration, type: T::String[format: :duration]
|
|
145
143
|
|
|
146
144
|
# String with enum
|
|
147
|
-
property :status,
|
|
145
|
+
property :status, T::Enum["draft", "published", "archived"]
|
|
148
146
|
|
|
149
147
|
# String with const
|
|
150
|
-
property :api_version,
|
|
148
|
+
property :api_version, T::Const["v1"]
|
|
151
149
|
|
|
152
150
|
# Optional string
|
|
153
151
|
property :nickname, type: String, optional: true
|
|
@@ -244,19 +242,19 @@ class NumericExample
|
|
|
244
242
|
property :count, type: Integer
|
|
245
243
|
|
|
246
244
|
# Integer with range
|
|
247
|
-
property :port, type: Integer
|
|
245
|
+
property :port, type: T::Integer[minimum: 1024, maximum: 65535]
|
|
248
246
|
|
|
249
247
|
# Integer with exclusive bounds
|
|
250
|
-
property :positive_int, type: Integer
|
|
248
|
+
property :positive_int, type: T::Integer[exclusive_minimum: 0]
|
|
251
249
|
|
|
252
250
|
# Number (float/double)
|
|
253
|
-
property :price, type:
|
|
251
|
+
property :price, type: T::Number[minimum: 0]
|
|
254
252
|
|
|
255
253
|
# Number with multiple_of
|
|
256
|
-
property :quantity, type: Integer
|
|
254
|
+
property :quantity, type: T::Integer[multiple_of: 10]
|
|
257
255
|
|
|
258
256
|
# Number with precision
|
|
259
|
-
property :temperature, type:
|
|
257
|
+
property :temperature, type: T::Number[minimum: -273.15, maximum: 1000.0]
|
|
260
258
|
|
|
261
259
|
# Optional number
|
|
262
260
|
property :discount, type: Float, optional: true
|
|
@@ -361,7 +359,7 @@ class ArrayExample
|
|
|
361
359
|
property :tags, type: T::Array[String]
|
|
362
360
|
|
|
363
361
|
# Array with constraints
|
|
364
|
-
property :numbers, type: T::Array[Integer
|
|
362
|
+
property :numbers, type: T::Array[Integer, min_items: 1, max_items: 10, unique_items: true]
|
|
365
363
|
end
|
|
366
364
|
|
|
367
365
|
# Generate the JSON Schema
|
|
@@ -410,15 +408,15 @@ class PersonBase
|
|
|
410
408
|
include JsonModel::Schema
|
|
411
409
|
|
|
412
410
|
property :name, type: String
|
|
413
|
-
property :age, type: Integer
|
|
411
|
+
property :age, type: T::Integer[minimum: 0], optional: true
|
|
414
412
|
end
|
|
415
413
|
|
|
416
414
|
class EmployeeDetails
|
|
417
415
|
include JsonModel::Schema
|
|
418
416
|
|
|
419
|
-
property :employee_id, type: String
|
|
417
|
+
property :employee_id, type: T::String[pattern: /\AE-\d{4}\z/]
|
|
420
418
|
property :department, type: String
|
|
421
|
-
property :salary, type:
|
|
419
|
+
property :salary, type: T::Number[minimum: 0], optional: true
|
|
422
420
|
end
|
|
423
421
|
|
|
424
422
|
class Employee
|
|
@@ -504,7 +502,7 @@ end
|
|
|
504
502
|
class PhoneContact
|
|
505
503
|
include JsonModel::Schema
|
|
506
504
|
|
|
507
|
-
property :phone, type: String
|
|
505
|
+
property :phone, type: T::String[pattern: /\A\+?[1-9]\\d{1,14}\z/]
|
|
508
506
|
end
|
|
509
507
|
|
|
510
508
|
class AddressContact
|
|
@@ -596,24 +594,24 @@ Use `T::OneOf` when a value must validate against exactly one schema (exclusive
|
|
|
596
594
|
class CreditCardPayment
|
|
597
595
|
include JsonModel::Schema
|
|
598
596
|
|
|
599
|
-
property :payment_type,
|
|
600
|
-
property :card_number, type: String
|
|
601
|
-
property :cvv, type: String
|
|
602
|
-
property :expiry, type: String
|
|
597
|
+
property :payment_type, T::Const["credit_card"]
|
|
598
|
+
property :card_number, type: T::String[pattern: /\A\d{16}\z/]
|
|
599
|
+
property :cvv, type: T::String[pattern: /\A\d{3,4}\z/]
|
|
600
|
+
property :expiry, type: T::String[pattern: /\A\d{2}\/\d{2}\z/]
|
|
603
601
|
end
|
|
604
602
|
|
|
605
603
|
class PayPalPayment
|
|
606
604
|
include JsonModel::Schema
|
|
607
605
|
|
|
608
|
-
property :payment_type,
|
|
609
|
-
property :paypal_email, type: String
|
|
606
|
+
property :payment_type, T::Const["paypal"]
|
|
607
|
+
property :paypal_email, type: T::String[format: :email]
|
|
610
608
|
end
|
|
611
609
|
|
|
612
610
|
class BankTransferPayment
|
|
613
611
|
include JsonModel::Schema
|
|
614
612
|
|
|
615
|
-
property :payment_type, type:
|
|
616
|
-
property :iban, type: String
|
|
613
|
+
property :payment_type, type: T::Const["bank_transfer"]
|
|
614
|
+
property :iban, type: T::String[pattern: "^[A-Z]{2}\\d{2}[A-Z0-9]+$"]
|
|
617
615
|
property :swift, type: String, optional: true
|
|
618
616
|
end
|
|
619
617
|
|
data/lib/json_model/config.rb
CHANGED
|
@@ -37,10 +37,9 @@ module JsonModel
|
|
|
37
37
|
# @param [Object, Class] type
|
|
38
38
|
# @param [Hash] options
|
|
39
39
|
def property(name, type:, **options)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
add_property(name, type: resolved_type, **
|
|
43
|
-
descendants.each { |subclass| subclass.add_property(name, type: resolved_type, **property_options) }
|
|
40
|
+
resolved_type = TypeSpec.resolve(type)
|
|
41
|
+
add_property(name, type: resolved_type, **options)
|
|
42
|
+
descendants.each { |subclass| subclass.add_property(name, type: resolved_type, **options) }
|
|
44
43
|
end
|
|
45
44
|
|
|
46
45
|
protected
|
|
@@ -17,12 +17,11 @@ module JsonModel
|
|
|
17
17
|
def self.inherited(subclass)
|
|
18
18
|
super
|
|
19
19
|
subclass.schema_id(JsonModel.config.schema_id_naming_strategy.call(subclass))
|
|
20
|
-
subclass.additional_properties(false)
|
|
21
20
|
subclass.meta_attributes[:$ref] = schema_id
|
|
22
21
|
end
|
|
23
22
|
|
|
24
23
|
schema_id(JsonModel.config.schema_id_naming_strategy.call(self))
|
|
25
|
-
|
|
24
|
+
schema_version(JsonModel.config.schema_version)
|
|
26
25
|
end
|
|
27
26
|
|
|
28
27
|
class_methods do
|
|
@@ -71,6 +70,16 @@ module JsonModel
|
|
|
71
70
|
end
|
|
72
71
|
end
|
|
73
72
|
|
|
73
|
+
# @param [Boolean, nil] value
|
|
74
|
+
# @return [Boolean]
|
|
75
|
+
def unevaluated_properties(value = nil)
|
|
76
|
+
if value.nil?
|
|
77
|
+
meta_attributes[:unevaluatedProperties] || false
|
|
78
|
+
else
|
|
79
|
+
meta_attributes[:unevaluatedProperties] = value
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
74
83
|
# @param [Symbol, nil] version
|
|
75
84
|
# @return [Boolean]
|
|
76
85
|
def schema_version(version = nil)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JsonModel
|
|
4
|
+
class TypeSpec
|
|
5
|
+
class Castable < TypeSpec
|
|
6
|
+
# @param [String] format
|
|
7
|
+
# @param [Proc] cast_block
|
|
8
|
+
def initialize(format:, &cast_block)
|
|
9
|
+
super()
|
|
10
|
+
@format = format
|
|
11
|
+
@cast_block = cast_block
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @param [Hash] _options
|
|
15
|
+
# @return [Hash]
|
|
16
|
+
def as_schema(**_options)
|
|
17
|
+
{
|
|
18
|
+
type: 'string',
|
|
19
|
+
format: @format,
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @param [::Object] json
|
|
24
|
+
# @return [::Object, nil]
|
|
25
|
+
def cast(json)
|
|
26
|
+
if json.nil?
|
|
27
|
+
nil
|
|
28
|
+
else
|
|
29
|
+
@cast_block.call(json)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -62,7 +62,7 @@ module JsonModel
|
|
|
62
62
|
end
|
|
63
63
|
},
|
|
64
64
|
uuid: lambda { |v|
|
|
65
|
-
|
|
65
|
+
v.match?(/\A[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\z/i)
|
|
66
66
|
},
|
|
67
67
|
regex: lambda { |v|
|
|
68
68
|
begin
|
|
@@ -79,7 +79,7 @@ module JsonModel
|
|
|
79
79
|
# @param [Integer, nil] min_length
|
|
80
80
|
# @param [Integer, nil] max_length
|
|
81
81
|
# @param [Regexp, nil] pattern
|
|
82
|
-
# @param [
|
|
82
|
+
# @param [Symbol, nil] format
|
|
83
83
|
def initialize(min_length: nil, max_length: nil, pattern: nil, format: nil)
|
|
84
84
|
super(types: [::String], schema_type: 'string')
|
|
85
85
|
|
|
@@ -115,7 +115,6 @@ module JsonModel
|
|
|
115
115
|
end
|
|
116
116
|
if @format
|
|
117
117
|
if JSON_SCHEMA_FORMATS.key?(@format)
|
|
118
|
-
|
|
119
118
|
register_format_validation(klass, name)
|
|
120
119
|
else
|
|
121
120
|
raise(ArgumentError, "Invalid format: #{@format}")
|
data/lib/json_model/type_spec.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative('type_spec/array')
|
|
4
|
+
require_relative('type_spec/castable')
|
|
4
5
|
require_relative('type_spec/composition')
|
|
5
6
|
require_relative('type_spec/const')
|
|
6
7
|
require_relative('type_spec/enum')
|
|
@@ -9,6 +10,20 @@ require_relative('type_spec/primitive')
|
|
|
9
10
|
|
|
10
11
|
module JsonModel
|
|
11
12
|
class TypeSpec
|
|
13
|
+
TYPE_MAP = {
|
|
14
|
+
Date => Castable.new(format: 'date') { |v| ::DateTime.iso8601(v) },
|
|
15
|
+
DateTime => Castable.new(format: 'date-time') { |v| ::DateTime.iso8601(v) },
|
|
16
|
+
FalseClass => Primitive::Boolean.new,
|
|
17
|
+
Float => Primitive::Number.new,
|
|
18
|
+
Integer => Primitive::Integer.new,
|
|
19
|
+
NilClass => Primitive::Null.new,
|
|
20
|
+
Regexp => Castable.new(format: 'regex') { |v| Regexp.new(v) },
|
|
21
|
+
String => Primitive::String.new,
|
|
22
|
+
Time => Castable.new(format: 'time') { |v| ::Time.iso8601(v) },
|
|
23
|
+
TrueClass => Primitive::Boolean.new,
|
|
24
|
+
URI => Castable.new(format: 'uri') { |v| URI.parse(v) },
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
12
27
|
# @param [Hash] options
|
|
13
28
|
# @return [Hash]
|
|
14
29
|
def as_schema(**options)
|
|
@@ -32,39 +47,31 @@ module JsonModel
|
|
|
32
47
|
|
|
33
48
|
class << self
|
|
34
49
|
# @param [Object, Class] type
|
|
35
|
-
# @param [Hash] options
|
|
36
50
|
# @return [TypeSpec]
|
|
37
|
-
def resolve(type
|
|
51
|
+
def resolve(type)
|
|
38
52
|
case type
|
|
39
53
|
when TypeSpec
|
|
40
54
|
type
|
|
41
55
|
when Class
|
|
42
|
-
resolve_type_from_class(type
|
|
43
|
-
when T::AllOf, T::AnyOf, T::Boolean, T::OneOf, T::Array, T::Enum, T::Const
|
|
44
|
-
type.to_type_spec(**options)
|
|
56
|
+
resolve_type_from_class(type)
|
|
45
57
|
else
|
|
46
|
-
|
|
58
|
+
if type.respond_to?(:to_type_spec)
|
|
59
|
+
type.to_type_spec
|
|
60
|
+
else
|
|
61
|
+
raise(ArgumentError, "Unsupported type: #{type}")
|
|
62
|
+
end
|
|
47
63
|
end
|
|
48
64
|
end
|
|
49
65
|
|
|
50
66
|
private
|
|
51
67
|
|
|
52
68
|
# @param [Object, Class] type
|
|
53
|
-
# @param [Hash] options
|
|
54
69
|
# @return [TypeSpec]
|
|
55
|
-
def resolve_type_from_class(type
|
|
56
|
-
if type
|
|
57
|
-
|
|
58
|
-
elsif type == Integer
|
|
59
|
-
Primitive::Integer.new(**options)
|
|
60
|
-
elsif type == Float
|
|
61
|
-
Primitive::Number.new(**options)
|
|
62
|
-
elsif [TrueClass, FalseClass].include?(type)
|
|
63
|
-
Primitive::Boolean.new(**options)
|
|
64
|
-
elsif type == NilClass
|
|
65
|
-
Primitive::Null.new(**options)
|
|
70
|
+
def resolve_type_from_class(type)
|
|
71
|
+
if TYPE_MAP.key?(type)
|
|
72
|
+
TYPE_MAP[type]
|
|
66
73
|
elsif type < Schema
|
|
67
|
-
TypeSpec::Object.new(type
|
|
74
|
+
TypeSpec::Object.new(type)
|
|
68
75
|
else
|
|
69
76
|
raise(ArgumentError, "Unsupported type: #{type}")
|
|
70
77
|
end
|
|
@@ -3,23 +3,26 @@
|
|
|
3
3
|
module T
|
|
4
4
|
class AllOf
|
|
5
5
|
# @param [Array<Class>] types
|
|
6
|
-
|
|
6
|
+
# @param [Hash] options
|
|
7
|
+
def initialize(*types, **options)
|
|
7
8
|
@types = types
|
|
9
|
+
@options = options
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
# @return [JsonModel::TypeSpec::Composition::AllOf]
|
|
11
|
-
def to_type_spec
|
|
13
|
+
def to_type_spec
|
|
12
14
|
JsonModel::TypeSpec::Composition::AllOf.new(
|
|
13
15
|
*@types.map { |type| JsonModel::TypeSpec.resolve(type) },
|
|
14
|
-
|
|
16
|
+
**@options,
|
|
15
17
|
)
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
class << self
|
|
19
21
|
# @param [Array] types
|
|
22
|
+
# @param [Hash] options
|
|
20
23
|
# @return [AllOf]
|
|
21
|
-
def [](*types)
|
|
22
|
-
AllOf.new(*types)
|
|
24
|
+
def [](*types, **options)
|
|
25
|
+
AllOf.new(*types, **options)
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
end
|
|
@@ -3,23 +3,26 @@
|
|
|
3
3
|
module T
|
|
4
4
|
class AnyOf
|
|
5
5
|
# @param [Array<Class>] types
|
|
6
|
-
|
|
6
|
+
# @param [Hash] options
|
|
7
|
+
def initialize(*types, **options)
|
|
7
8
|
@types = types
|
|
9
|
+
@options = options
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
# @return [JsonModel::TypeSpec::Composition::AnyOf]
|
|
11
|
-
def to_type_spec
|
|
13
|
+
def to_type_spec
|
|
12
14
|
JsonModel::TypeSpec::Composition::AnyOf.new(
|
|
13
15
|
*@types.map { |type| JsonModel::TypeSpec.resolve(type) },
|
|
14
|
-
|
|
16
|
+
**@options,
|
|
15
17
|
)
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
class << self
|
|
19
21
|
# @param [Array] types
|
|
22
|
+
# @param [Hash] options
|
|
20
23
|
# @return [AnyOf]
|
|
21
|
-
def [](*types)
|
|
22
|
-
AnyOf.new(*types)
|
|
24
|
+
def [](*types, **options)
|
|
25
|
+
AnyOf.new(*types, **options)
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
end
|
|
@@ -3,23 +3,26 @@
|
|
|
3
3
|
module T
|
|
4
4
|
class Array
|
|
5
5
|
# @param [Class] type
|
|
6
|
-
|
|
6
|
+
# @param [Hash] options
|
|
7
|
+
def initialize(type, **options)
|
|
7
8
|
@type = type
|
|
9
|
+
@options = options
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
# @return [JsonModel::TypeSpec::Array]
|
|
11
|
-
def to_type_spec
|
|
13
|
+
def to_type_spec
|
|
12
14
|
JsonModel::TypeSpec::Array.new(
|
|
13
15
|
JsonModel::TypeSpec.resolve(@type),
|
|
14
|
-
|
|
16
|
+
**@options,
|
|
15
17
|
)
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
class << self
|
|
19
21
|
# @param [Class] type
|
|
22
|
+
# @param [Hash] options
|
|
20
23
|
# @return [Array]
|
|
21
|
-
def [](type)
|
|
22
|
-
Array.new(type)
|
|
24
|
+
def [](type, **options)
|
|
25
|
+
Array.new(type, **options)
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module T
|
|
4
|
+
class Integer
|
|
5
|
+
# @param [Hash] options
|
|
6
|
+
def initialize(**options)
|
|
7
|
+
@options = options
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# @return [JsonModel::TypeSpec::Composition::Primitive::Integer]
|
|
11
|
+
def to_type_spec
|
|
12
|
+
JsonModel::TypeSpec::Primitive::Integer.new(**@options)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
# @param [Hash] options
|
|
17
|
+
# @return [T::Integer]
|
|
18
|
+
def [](**options)
|
|
19
|
+
Integer.new(**options)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module T
|
|
4
|
+
class Number
|
|
5
|
+
# @param [Hash] options
|
|
6
|
+
def initialize(**options)
|
|
7
|
+
@options = options
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# @return [JsonModel::TypeSpec::Composition::Primitive::Number]
|
|
11
|
+
def to_type_spec
|
|
12
|
+
JsonModel::TypeSpec::Primitive::Number.new(**@options)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
# @param [Hash] options
|
|
17
|
+
# @return [Number]
|
|
18
|
+
def [](**options)
|
|
19
|
+
Number.new(**options)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -3,23 +3,26 @@
|
|
|
3
3
|
module T
|
|
4
4
|
class OneOf
|
|
5
5
|
# @param [Array<Class>] types
|
|
6
|
-
|
|
6
|
+
# @param [Hash] options
|
|
7
|
+
def initialize(*types, **options)
|
|
7
8
|
@types = types
|
|
9
|
+
@options = options
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
# @return [JsonModel::TypeSpec::Composition::OneOf]
|
|
11
|
-
def to_type_spec
|
|
13
|
+
def to_type_spec
|
|
12
14
|
JsonModel::TypeSpec::Composition::OneOf.new(
|
|
13
15
|
*@types.map { |type| JsonModel::TypeSpec.resolve(type) },
|
|
14
|
-
|
|
16
|
+
**@options,
|
|
15
17
|
)
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
class << self
|
|
19
21
|
# @param [Array] types
|
|
22
|
+
# @param [Hash] options
|
|
20
23
|
# @return [OneOf]
|
|
21
|
-
def [](*types)
|
|
22
|
-
OneOf.new(*types)
|
|
24
|
+
def [](*types, **options)
|
|
25
|
+
OneOf.new(*types, **options)
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module T
|
|
4
|
+
class String
|
|
5
|
+
# @param [Hash] options
|
|
6
|
+
def initialize(**options)
|
|
7
|
+
@options = options
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# @return [JsonModel::TypeSpec::Composition::Primitive::String]
|
|
11
|
+
def to_type_spec
|
|
12
|
+
JsonModel::TypeSpec::Primitive::String.new(**@options)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
# @param [Hash] options
|
|
17
|
+
# @return [T::String]
|
|
18
|
+
def [](**options)
|
|
19
|
+
String.new(**options)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/json_model/types.rb
CHANGED
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative('types/all_of')
|
|
4
4
|
require_relative('types/any_of')
|
|
5
|
-
require_relative('types/boolean')
|
|
6
|
-
require_relative('types/one_of')
|
|
7
5
|
require_relative('types/array')
|
|
6
|
+
require_relative('types/boolean')
|
|
8
7
|
require_relative('types/const')
|
|
9
8
|
require_relative('types/enum')
|
|
9
|
+
require_relative('types/integer')
|
|
10
|
+
require_relative('types/null')
|
|
11
|
+
require_relative('types/number')
|
|
12
|
+
require_relative('types/one_of')
|
|
13
|
+
require_relative('types/string')
|
data/lib/json_model/version.rb
CHANGED
|
@@ -10,7 +10,7 @@ RSpec.describe('File system schema') do
|
|
|
10
10
|
include(JsonModel::Schema)
|
|
11
11
|
|
|
12
12
|
property(:type, type: T::Const['disk'])
|
|
13
|
-
property(:device, type: String
|
|
13
|
+
property(:device, type: T::String[pattern: %r{\A/dev/[^/]+(/[^/]+)*\z}])
|
|
14
14
|
end,
|
|
15
15
|
)
|
|
16
16
|
|
|
@@ -22,8 +22,7 @@ RSpec.describe('File system schema') do
|
|
|
22
22
|
property(:type, type: T::Enum['diskUUID', 'diskuuid'])
|
|
23
23
|
property(
|
|
24
24
|
:label,
|
|
25
|
-
type: String,
|
|
26
|
-
pattern: /\A[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\z/,
|
|
25
|
+
type: T::String[pattern: /\A[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\z/],
|
|
27
26
|
)
|
|
28
27
|
end,
|
|
29
28
|
)
|
|
@@ -34,8 +33,8 @@ RSpec.describe('File system schema') do
|
|
|
34
33
|
include(JsonModel::Schema)
|
|
35
34
|
|
|
36
35
|
property(:type, type: T::Const['nfs'])
|
|
37
|
-
property(:remote_path, type: String
|
|
38
|
-
property(:server, type: String
|
|
36
|
+
property(:remote_path, type: T::String[pattern: %r{\A(/[^/]+)+\z}], as: :remotePath)
|
|
37
|
+
property(:server, type: T::String[format: :ipv4])
|
|
39
38
|
end,
|
|
40
39
|
)
|
|
41
40
|
|
|
@@ -45,7 +44,7 @@ RSpec.describe('File system schema') do
|
|
|
45
44
|
include(JsonModel::Schema)
|
|
46
45
|
|
|
47
46
|
property(:type, type: T::Const['tmpfs'])
|
|
48
|
-
property(:size_in_mb, type: Integer
|
|
47
|
+
property(:size_in_mb, type: T::Integer[minimum: 16, maximum: 512], as: :sizeInMB)
|
|
49
48
|
end,
|
|
50
49
|
)
|
|
51
50
|
|
|
@@ -57,12 +56,11 @@ RSpec.describe('File system schema') do
|
|
|
57
56
|
description('JSON Schema for an fstab entry')
|
|
58
57
|
property(
|
|
59
58
|
:storage,
|
|
60
|
-
type: T::OneOf[DiskDevice, DiskUuid, Nfs, Tmpfs],
|
|
59
|
+
type: T::OneOf[DiskDevice, DiskUuid, Nfs, Tmpfs, discriminator: :type],
|
|
61
60
|
ref_mode: JsonModel::RefMode::LOCAL,
|
|
62
|
-
discriminator: :type,
|
|
63
61
|
)
|
|
64
62
|
property(:fstype, type: T::Enum['ext3', 'ext4', 'btrfs'], optional: true)
|
|
65
|
-
property(:options, type: T::Array[String
|
|
63
|
+
property(:options, type: T::Array[String, min_items: 1, unique_items: true], optional: true)
|
|
66
64
|
property(:readonly, type: T::Boolean, optional: true)
|
|
67
65
|
end,
|
|
68
66
|
)
|
|
@@ -100,7 +98,6 @@ RSpec.describe('File system schema') do
|
|
|
100
98
|
type: 'boolean',
|
|
101
99
|
},
|
|
102
100
|
},
|
|
103
|
-
additionalProperties: false,
|
|
104
101
|
'$defs': {
|
|
105
102
|
DiskDevice: {
|
|
106
103
|
properties: {
|
|
@@ -113,7 +110,6 @@ RSpec.describe('File system schema') do
|
|
|
113
110
|
},
|
|
114
111
|
},
|
|
115
112
|
required: %i(device type),
|
|
116
|
-
additionalProperties: false,
|
|
117
113
|
type: 'object',
|
|
118
114
|
},
|
|
119
115
|
DiskUuid: {
|
|
@@ -125,7 +121,6 @@ RSpec.describe('File system schema') do
|
|
|
125
121
|
},
|
|
126
122
|
},
|
|
127
123
|
required: %i(label type),
|
|
128
|
-
additionalProperties: false,
|
|
129
124
|
type: 'object',
|
|
130
125
|
},
|
|
131
126
|
Nfs: {
|
|
@@ -141,7 +136,6 @@ RSpec.describe('File system schema') do
|
|
|
141
136
|
},
|
|
142
137
|
},
|
|
143
138
|
required: %i(remotePath server type),
|
|
144
|
-
additionalProperties: false,
|
|
145
139
|
type: 'object',
|
|
146
140
|
},
|
|
147
141
|
Tmpfs: {
|
|
@@ -150,7 +144,6 @@ RSpec.describe('File system schema') do
|
|
|
150
144
|
sizeInMB: { type: 'integer', minimum: 16, maximum: 512 },
|
|
151
145
|
},
|
|
152
146
|
required: %i(sizeInMB type),
|
|
153
|
-
additionalProperties: false,
|
|
154
147
|
type: 'object',
|
|
155
148
|
},
|
|
156
149
|
},
|
data/spec/examples/user_spec.rb
CHANGED
|
@@ -14,7 +14,7 @@ RSpec.describe('User schema') do
|
|
|
14
14
|
property(:street, type: String)
|
|
15
15
|
property(:city, type: String)
|
|
16
16
|
property(:state, type: String, optional: true)
|
|
17
|
-
property(:postal_code, type: String
|
|
17
|
+
property(:postal_code, type: T::String[pattern: /\A\d{5}(-\d{4})?\z/], optional: true)
|
|
18
18
|
property(:country, type: String, default: 'USA')
|
|
19
19
|
end
|
|
20
20
|
|
|
@@ -28,11 +28,12 @@ RSpec.describe('User schema') do
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
property(:name, type: String)
|
|
31
|
-
property(:email, type: String
|
|
32
|
-
property(:age, type: Integer
|
|
31
|
+
property(:email, type: T::String[format: :email])
|
|
32
|
+
property(:age, type: T::Integer[minimum: 0, maximum: 120], optional: true)
|
|
33
33
|
property(:active, type: T::Boolean, default: true, optional: true)
|
|
34
34
|
property(:addresses, type: T::Array[Address], ref_mode: JsonModel::RefMode::LOCAL)
|
|
35
35
|
property(:tags, type: T::Array[String], optional: true)
|
|
36
|
+
property(:birthday, type: Date, optional: true)
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
stub_const('User', user_class)
|
|
@@ -54,8 +55,8 @@ RSpec.describe('User schema') do
|
|
|
54
55
|
items: { '$ref': '#/$defs/Address' },
|
|
55
56
|
},
|
|
56
57
|
tags: { type: 'array', items: { type: 'string' } },
|
|
58
|
+
birthday: { type: 'string', format: 'date' },
|
|
57
59
|
},
|
|
58
|
-
additionalProperties: false,
|
|
59
60
|
required: %i(addresses email name),
|
|
60
61
|
'$defs': {
|
|
61
62
|
Address: {
|
|
@@ -68,7 +69,6 @@ RSpec.describe('User schema') do
|
|
|
68
69
|
street: { type: 'string' },
|
|
69
70
|
},
|
|
70
71
|
required: %i(city country street),
|
|
71
|
-
additionalProperties: false,
|
|
72
72
|
},
|
|
73
73
|
},
|
|
74
74
|
},
|
|
@@ -77,7 +77,12 @@ RSpec.describe('User schema') do
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
it('can instantiate a model') do
|
|
80
|
-
user = User.new(
|
|
80
|
+
user = User.new(
|
|
81
|
+
name: 'Foo',
|
|
82
|
+
email: 'foo@example.com',
|
|
83
|
+
addresses: [{ street: '123 Main St', city: 'Anytown' }],
|
|
84
|
+
birthday: '2000-01-01',
|
|
85
|
+
)
|
|
81
86
|
|
|
82
87
|
expect(user.name).to(eq('Foo'))
|
|
83
88
|
expect(user.email).to(eq('foo@example.com'))
|
|
@@ -88,5 +93,6 @@ RSpec.describe('User schema') do
|
|
|
88
93
|
expect(user.addresses.first.postal_code).to(be_nil)
|
|
89
94
|
expect(user.addresses.first.state).to(be_nil)
|
|
90
95
|
expect(user.addresses.first.city).to(eq('Anytown'))
|
|
96
|
+
expect(user.birthday).to(eq(Date.new(2000, 1, 1)))
|
|
91
97
|
end
|
|
92
98
|
end
|
data/spec/schema_meta_spec.rb
CHANGED
|
@@ -103,9 +103,9 @@ RSpec.describe(JsonModel::SchemaMeta) do
|
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
-
it('is
|
|
106
|
+
it('is missing by default') do
|
|
107
107
|
expect(klass.meta_attributes)
|
|
108
|
-
.to(eq({
|
|
108
|
+
.to(eq({}))
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
it('can be changed') do
|
|
@@ -115,4 +115,24 @@ RSpec.describe(JsonModel::SchemaMeta) do
|
|
|
115
115
|
.to(eq({ additionalProperties: true }))
|
|
116
116
|
end
|
|
117
117
|
end
|
|
118
|
+
|
|
119
|
+
describe('.additional_properties') do
|
|
120
|
+
let(:klass) do
|
|
121
|
+
Class.new do
|
|
122
|
+
include(JsonModel::SchemaMeta)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it('is missing by default') do
|
|
127
|
+
expect(klass.meta_attributes)
|
|
128
|
+
.to(eq({}))
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it('can be changed') do
|
|
132
|
+
klass.unevaluated_properties(true)
|
|
133
|
+
|
|
134
|
+
expect(klass.meta_attributes)
|
|
135
|
+
.to(eq({ unevaluatedProperties: true }))
|
|
136
|
+
end
|
|
137
|
+
end
|
|
118
138
|
end
|
data/spec/schema_spec.rb
CHANGED
|
@@ -44,7 +44,7 @@ RSpec.describe(JsonModel::Schema) do
|
|
|
44
44
|
Class.new do
|
|
45
45
|
include(JsonModel::Schema)
|
|
46
46
|
|
|
47
|
-
property(:foo, type: String
|
|
47
|
+
property(:foo, type: T::String[min_length: 3])
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
|
|
@@ -100,7 +100,7 @@ RSpec.describe(JsonModel::Schema) do
|
|
|
100
100
|
|
|
101
101
|
it('returns an empty schema') do
|
|
102
102
|
expect(klass.as_schema)
|
|
103
|
-
.to(eq({ type: 'object'
|
|
103
|
+
.to(eq({ type: 'object' }))
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
it('includes the schema id') do
|
|
@@ -112,7 +112,6 @@ RSpec.describe(JsonModel::Schema) do
|
|
|
112
112
|
{
|
|
113
113
|
'$id': 'https://example.com/schemas/example.json',
|
|
114
114
|
type: 'object',
|
|
115
|
-
additionalProperties: false,
|
|
116
115
|
},
|
|
117
116
|
),
|
|
118
117
|
)
|
|
@@ -145,7 +144,6 @@ RSpec.describe(JsonModel::Schema) do
|
|
|
145
144
|
foo: { type: 'string' },
|
|
146
145
|
},
|
|
147
146
|
required: %i(bam baz foo),
|
|
148
|
-
additionalProperties: false,
|
|
149
147
|
},
|
|
150
148
|
),
|
|
151
149
|
)
|
|
@@ -206,16 +204,13 @@ RSpec.describe(JsonModel::Schema) do
|
|
|
206
204
|
type: 'object',
|
|
207
205
|
properties: { foo: { type: 'string' } },
|
|
208
206
|
required: %i(foo),
|
|
209
|
-
additionalProperties: false,
|
|
210
207
|
},
|
|
211
208
|
},
|
|
212
|
-
additionalProperties: false,
|
|
213
209
|
'$defs': {
|
|
214
210
|
Bar: {
|
|
215
211
|
type: 'object',
|
|
216
212
|
properties: { bar: { type: 'string' } },
|
|
217
213
|
required: %i(bar),
|
|
218
|
-
additionalProperties: false,
|
|
219
214
|
},
|
|
220
215
|
},
|
|
221
216
|
required: %i(bam bar foo),
|
|
@@ -250,7 +245,6 @@ RSpec.describe(JsonModel::Schema) do
|
|
|
250
245
|
'$id': 'https://example.com/schemas/child.json',
|
|
251
246
|
'$ref': 'https://example.com/schemas/example.json',
|
|
252
247
|
type: 'object',
|
|
253
|
-
additionalProperties: false,
|
|
254
248
|
properties: { baz: { type: 'string' } },
|
|
255
249
|
required: %i(baz),
|
|
256
250
|
},
|
|
@@ -265,7 +259,6 @@ RSpec.describe(JsonModel::Schema) do
|
|
|
265
259
|
'$ref': 'https://example.com/schemas/example.json',
|
|
266
260
|
title: 'SecondChild',
|
|
267
261
|
type: 'object',
|
|
268
|
-
additionalProperties: false,
|
|
269
262
|
properties: { bar: { type: 'string' } },
|
|
270
263
|
required: %i(bar),
|
|
271
264
|
},
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require('spec_helper')
|
|
4
|
+
|
|
5
|
+
RSpec.describe(JsonModel::TypeSpec::Castable) do
|
|
6
|
+
describe('#as_schema') do
|
|
7
|
+
it('returns a string schema') do
|
|
8
|
+
expect(described_class.new(format: 'date') { |v| v }.as_schema)
|
|
9
|
+
.to(eq({ type: 'string', format: 'date' }))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe('#cast') do
|
|
14
|
+
it('casts a string') do
|
|
15
|
+
expect(described_class.new(format: 'date') { |v| Date.parse(v) }.cast('2020-01-01'))
|
|
16
|
+
.to(eq(Date.new(2020, 1, 1)))
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: json_model_rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.16
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Paul Gillesberger
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2026-01-05 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rake
|
|
@@ -117,6 +117,7 @@ files:
|
|
|
117
117
|
- lib/json_model/schema_meta.rb
|
|
118
118
|
- lib/json_model/type_spec.rb
|
|
119
119
|
- lib/json_model/type_spec/array.rb
|
|
120
|
+
- lib/json_model/type_spec/castable.rb
|
|
120
121
|
- lib/json_model/type_spec/composition.rb
|
|
121
122
|
- lib/json_model/type_spec/composition/all_of.rb
|
|
122
123
|
- lib/json_model/type_spec/composition/any_of.rb
|
|
@@ -138,10 +139,13 @@ files:
|
|
|
138
139
|
- lib/json_model/types/boolean.rb
|
|
139
140
|
- lib/json_model/types/const.rb
|
|
140
141
|
- lib/json_model/types/enum.rb
|
|
142
|
+
- lib/json_model/types/integer.rb
|
|
143
|
+
- lib/json_model/types/null.rb
|
|
144
|
+
- lib/json_model/types/number.rb
|
|
141
145
|
- lib/json_model/types/one_of.rb
|
|
146
|
+
- lib/json_model/types/string.rb
|
|
142
147
|
- lib/json_model/version.rb
|
|
143
148
|
- spec/config_spec.rb
|
|
144
|
-
- spec/examples/component_spec.rb
|
|
145
149
|
- spec/examples/file_system_spec.rb
|
|
146
150
|
- spec/examples/user_spec.rb
|
|
147
151
|
- spec/json_model_spec.rb
|
|
@@ -151,6 +155,7 @@ files:
|
|
|
151
155
|
- spec/schema_spec.rb
|
|
152
156
|
- spec/spec_helper.rb
|
|
153
157
|
- spec/type_spec/array_spec.rb
|
|
158
|
+
- spec/type_spec/castable_spec.rb
|
|
154
159
|
- spec/type_spec/composition/all_of_spec.rb
|
|
155
160
|
- spec/type_spec/composition/any_of_spec.rb
|
|
156
161
|
- spec/type_spec/composition/one_of_spec.rb
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require('spec_helper')
|
|
4
|
-
|
|
5
|
-
RSpec.describe('User schema') do
|
|
6
|
-
it('todo') do
|
|
7
|
-
stub_const(
|
|
8
|
-
'BaseComponent',
|
|
9
|
-
Class.new do
|
|
10
|
-
include(JsonModel::Schema)
|
|
11
|
-
|
|
12
|
-
property(:type, type: String)
|
|
13
|
-
property(:styles, type: T::Array[String], optional: true)
|
|
14
|
-
end,
|
|
15
|
-
)
|
|
16
|
-
stub_const(
|
|
17
|
-
'TextComponent',
|
|
18
|
-
Class.new(BaseComponent) do
|
|
19
|
-
include(JsonModel::Schema)
|
|
20
|
-
|
|
21
|
-
property(:type, type: T::Enum['text'])
|
|
22
|
-
property(:value, type: String)
|
|
23
|
-
end,
|
|
24
|
-
)
|
|
25
|
-
stub_const(
|
|
26
|
-
'NumberComponent',
|
|
27
|
-
Class.new(BaseComponent) do
|
|
28
|
-
include(JsonModel::Schema)
|
|
29
|
-
|
|
30
|
-
property(:type, type: T::Const['number'])
|
|
31
|
-
property(:value, type: Integer)
|
|
32
|
-
end,
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
all_component_types = ObjectSpace
|
|
36
|
-
.each_object(Class)
|
|
37
|
-
.select { |klass| klass.ancestors.include?(BaseComponent) && klass != BaseComponent }
|
|
38
|
-
stub_const(
|
|
39
|
-
'GridComponent',
|
|
40
|
-
Class.new(BaseComponent) do
|
|
41
|
-
include(JsonModel::Schema)
|
|
42
|
-
|
|
43
|
-
property(:type, type: T::Const['grid'])
|
|
44
|
-
property(:components, type: T::Array[T::OneOf[*all_component_types]], optional: true, ref_mode: JsonModel::RefMode::EXTERNAL)
|
|
45
|
-
end,
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
instance = GridComponent.new(
|
|
50
|
-
type: 'grid',
|
|
51
|
-
components: [{ type: 'text', value: 'foo' }, { type: 'number', value: 1 }],
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
expect(instance.components.first).to(be_instance_of(TextComponent))
|
|
55
|
-
expect(instance.components.last).to(be_instance_of(NumberComponent))
|
|
56
|
-
end
|
|
57
|
-
end
|