json_model_rb 0.1.5 → 0.1.7

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: ffcf41bdf354c5c3d979ca928d7d7c7c774c3c5506a8c6d96997309a2a9df6b1
4
- data.tar.gz: 9f1f13bfde527fc6a4d1cac1a91ad9e85a34ccfd2fbcc8780e824128a11b18ce
3
+ metadata.gz: 91d9055f02d8e7422d6915d44620adbd1668ca5fcff577fe2cf0964412699153
4
+ data.tar.gz: 447bfbd423aa8e176faf3844079e48d3a1e01f5e367778ca48b8e0f659f51928
5
5
  SHA512:
6
- metadata.gz: 107a70cadd0f6b9b373b71383f5200ab986c31570f27b9685488c3c37af4abe97347a76718c1ca1da1053dd573ca92e04679f3622f5554672bee99c0f7b61098
7
- data.tar.gz: 0cf9afcc30d3344ed5371b1125c59f99132ac3f62c7d00d96c2257e01e8c847d60f4a76d3428b1dcc7f4a09b97bf251843f0814643a462b591ee185f602df175
6
+ metadata.gz: 7b99e2ddcbde5c4adec3138796292ff564ad19342ba32c7513bba90c7beac1b8522b6d1c62f7cf987840b08eadca5a2d3206ab892ba9cc36fa6760b71490f6bc
7
+ data.tar.gz: 87247c327a4bbf77e171c7bf89389f616109dbc8a6e0b37d6ceb177bf2a3d89cd5def0c585d614d8f10ce4b91da589af3a7f042e005bca1ae60d3b3ba2b83bad
data/README.md CHANGED
@@ -0,0 +1,686 @@
1
+ # JSON Model
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/json_model.svg)](https://badge.fury.io/rb/json_model)
4
+ [![Ruby](https://github.com/gillesbergerp/json_model_rb/actions/workflows/ci.yml/badge.svg)](https://github.com/gillesbergerp/json_model_rb/actions/workflows/ci.yml)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ A Ruby DSL for building JSON Schema definitions with a clean, declarative syntax. Define your schemas in Ruby and generate complete, standards-compliant JSON Schema documents.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'json_model'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```bash
20
+ bundle install
21
+ ```
22
+
23
+ Or install it yourself as:
24
+
25
+ ```bash
26
+ gem install json_model
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```ruby
32
+ require 'json_model'
33
+
34
+ class User
35
+ include JsonModel::Schema
36
+
37
+ schema_id "https://example.com/schemas/user.json"
38
+ title "User"
39
+ description "A registered user in the system"
40
+
41
+ property :name, type: String
42
+ property :email, type: String, format: :email
43
+ property :age, type: Integer, minimum: 0, maximum: 120, optional: true
44
+ end
45
+
46
+ # Generate the JSON Schema
47
+ puts JSON.pretty_generate(User.as_schema)
48
+ ```
49
+
50
+ **Output:**
51
+ ```json
52
+ {
53
+ "$id": "https://example.com/schemas/user.json",
54
+ "additionalProperties": false,
55
+ "title": "User",
56
+ "description": "A registered user in the system",
57
+ "properties": {
58
+ "age": {
59
+ "type": "integer",
60
+ "minimum": 0,
61
+ "maximum": 120
62
+ },
63
+ "email": {
64
+ "type": "string",
65
+ "format": "email"
66
+ },
67
+ "name": {
68
+ "type": "string"
69
+ }
70
+ },
71
+ "required": [
72
+ {
73
+ "json_class": "Symbol",
74
+ "s": "email"
75
+ },
76
+ {
77
+ "json_class": "Symbol",
78
+ "s": "name"
79
+ }
80
+ ],
81
+ "type": "object"
82
+ }
83
+ ```
84
+
85
+ ## Schema Metadata
86
+
87
+ You can set top-level schema metadata properties directly in your schema class:
88
+
89
+ ```ruby
90
+ class Product
91
+ include JsonModel::Schema
92
+
93
+ # Schema metadata
94
+ schema_id "https://api.example.com/schemas/product.json"
95
+ schema_version :draft_2020_12
96
+ title "Product"
97
+ description "A product available in the catalog"
98
+
99
+ # Properties
100
+ property :id, type: String
101
+ property :name, type: String
102
+ property :price, type: Float, minimum: 0
103
+ property :available, type: T::Boolean, default: true, optional: true
104
+ end
105
+ ```
106
+
107
+ ### Available Metadata Keywords
108
+
109
+ - **`schema_id`** - Sets the `$id` (unique URI identifier for the schema)
110
+ - **`schema_version`** - Sets the `$schema` (JSON Schema version)
111
+ - **`title`** - Human-readable title for the schema
112
+ - **`description`** - Detailed explanation of the schema's purpose
113
+ - **`additional_properties`** - Whether additional properties are allowed (default: `false`)
114
+
115
+ ## Data Types
116
+
117
+ ### String Type
118
+
119
+ ```ruby
120
+ class StringExample
121
+ include JsonModel::Schema
122
+
123
+ # Basic string
124
+ property :simple_string, type: String
125
+
126
+ # String with length constraints
127
+ property :username, type: String,
128
+ min_length: 3,
129
+ max_length: 20
130
+
131
+ # String with pattern (regex)
132
+ property :product_code, type: String, pattern: /\A[A-Z]{3}-\d{4}\z/
133
+
134
+ # String with format
135
+ property :email, type: String, format: :email
136
+ property :uri, type: String, format: :uri
137
+ property :hostname, type: String, format: :hostname
138
+ property :ipv4, type: String, format: :ipv4
139
+ property :ipv6, type: String, format: :ipv6
140
+ property :uuid, type: String, format: :uuid
141
+ property :date, type: String, format: :date
142
+ property :time, type: String, format: :time
143
+ property :datetime, type: String, format: :date_time
144
+ property :duration, type: String, format: :duration
145
+
146
+ # String with enum
147
+ property :status, type: String, enum: ["draft", "published", "archived"]
148
+
149
+ # String with const
150
+ property :api_version, type: String, const: "v1"
151
+
152
+ # Optional string
153
+ property :nickname, type: String, optional: true
154
+ end
155
+
156
+ # Generate the JSON Schema
157
+ puts JSON.pretty_generate(StringExample.as_schema)
158
+ ```
159
+
160
+ **Output:**
161
+ ```json
162
+ {
163
+ "additionalProperties": false,
164
+ "properties": {
165
+ "date": {
166
+ "type": "string",
167
+ "format": "date"
168
+ },
169
+ "datetime": {
170
+ "type": "string",
171
+ "format": "date-time"
172
+ },
173
+ "duration": {
174
+ "type": "string",
175
+ "format": "duration"
176
+ },
177
+ "email": {
178
+ "type": "string",
179
+ "format": "email"
180
+ },
181
+ "hostname": {
182
+ "type": "string",
183
+ "format": "hostname"
184
+ },
185
+ "ipv4": {
186
+ "type": "string",
187
+ "format": "ipv4"
188
+ },
189
+ "ipv6": {
190
+ "type": "string",
191
+ "format": "ipv6"
192
+ },
193
+ "product_code": {
194
+ "type": "string",
195
+ "pattern": "\\A[A-Z]{3}-\\d{4}\\z"
196
+ },
197
+ "simple_string": {
198
+ "type": "string"
199
+ },
200
+ "time": {
201
+ "type": "string",
202
+ "format": "time"
203
+ },
204
+ "uri": {
205
+ "type": "string",
206
+ "format": "uri"
207
+ },
208
+ "username": {
209
+ "type": "string",
210
+ "minLength": 3,
211
+ "maxLength": 20
212
+ },
213
+ "uuid": {
214
+ "type": "string",
215
+ "format": "uuid"
216
+ }
217
+ },
218
+ "required": [
219
+ "date",
220
+ "datetime",
221
+ "duration",
222
+ "email",
223
+ "hostname",
224
+ "ipv4",
225
+ "ipv6",
226
+ "product_code",
227
+ "simple_string",
228
+ "time",
229
+ "uri",
230
+ "username",
231
+ "uuid"
232
+ ],
233
+ "type": "object"
234
+ }
235
+ ```
236
+
237
+ ### Number and Integer Types
238
+
239
+ ```ruby
240
+ class NumericExample
241
+ include JsonModel::Schema
242
+
243
+ # Integer
244
+ property :count, type: Integer
245
+
246
+ # Integer with range
247
+ property :port, type: Integer, minimum: 1024, maximum: 65535
248
+
249
+ # Integer with exclusive bounds
250
+ property :positive_int, type: Integer, exclusive_minimum: 0
251
+
252
+ # Number (float/double)
253
+ property :price, type: Float, minimum: 0
254
+
255
+ # Number with multiple_of
256
+ property :quantity, type: Integer, multiple_of: 10
257
+
258
+ # Number with precision
259
+ property :temperature, type: Float, minimum: -273.15, maximum: 1000.0
260
+
261
+ # Optional number
262
+ property :discount, type: Float, optional: true
263
+ end
264
+
265
+ # Generate the JSON Schema
266
+ puts JSON.pretty_generate(NumericExample.as_schema)
267
+ ```
268
+
269
+ **Output:**
270
+ ```json
271
+ {
272
+ "additionalProperties": false,
273
+ "properties": {
274
+ "count": {
275
+ "type": "integer"
276
+ },
277
+ "discount": {
278
+ "type": "number"
279
+ },
280
+ "port": {
281
+ "type": "integer",
282
+ "minimum": 1024,
283
+ "maximum": 65535
284
+ },
285
+ "positive_int": {
286
+ "type": "integer",
287
+ "exclusiveMinimum": 0
288
+ },
289
+ "price": {
290
+ "type": "number",
291
+ "minimum": 0
292
+ },
293
+ "quantity": {
294
+ "type": "integer",
295
+ "multipleOf": 10
296
+ },
297
+ "temperature": {
298
+ "type": "number",
299
+ "minimum": -273.15,
300
+ "maximum": 1000.0
301
+ }
302
+ },
303
+ "required": [
304
+ "count",
305
+ "port",
306
+ "positive_int",
307
+ "price",
308
+ "quantity",
309
+ "temperature"
310
+ ],
311
+ "type": "object"
312
+ }
313
+ ```
314
+
315
+ ### Boolean Type
316
+
317
+ ```ruby
318
+ class BooleanExample
319
+ include JsonModel::Schema
320
+
321
+ property :is_active, type: T::Boolean
322
+ property :has_agreed, type: T::Boolean, default: false
323
+ property :enabled, type: T::Boolean, optional: true
324
+ end
325
+
326
+ # Generate the JSON Schema
327
+ puts JSON.pretty_generate(BooleanExample.as_schema)
328
+ ```
329
+
330
+ **Output:**
331
+ ```json
332
+ {
333
+ "additionalProperties": false,
334
+ "properties": {
335
+ "enabled": {
336
+ "type": "boolean"
337
+ },
338
+ "has_agreed": {
339
+ "type": "boolean",
340
+ "default": false
341
+ },
342
+ "is_active": {
343
+ "type": "boolean"
344
+ }
345
+ },
346
+ "required": [
347
+ "has_agreed",
348
+ "is_active"
349
+ ],
350
+ "type": "object"
351
+ }
352
+ ```
353
+
354
+ ### Array Type
355
+
356
+ ```ruby
357
+ class ArrayExample
358
+ include JsonModel::Schema
359
+
360
+ # Simple array
361
+ property :tags, type: T::Array[String]
362
+
363
+ # Array with constraints
364
+ property :numbers, type: T::Array[Integer], min_items: 1, max_items: 10, unique_items: true
365
+ end
366
+
367
+ # Generate the JSON Schema
368
+ puts JSON.pretty_generate(ArrayExample.as_schema)
369
+ ```
370
+
371
+ **Output:**
372
+ ```json
373
+ {
374
+ "additionalProperties": false,
375
+ "properties": {
376
+ "numbers": {
377
+ "type": "array",
378
+ "items": {
379
+ "type": "integer"
380
+ },
381
+ "minItems": 1,
382
+ "maxItems": 10,
383
+ "uniqueItems": true
384
+ },
385
+ "tags": {
386
+ "type": "array",
387
+ "items": {
388
+ "type": "string"
389
+ }
390
+ }
391
+ },
392
+ "required": [
393
+ "numbers",
394
+ "tags"
395
+ ],
396
+ "type": "object"
397
+ }
398
+ ```
399
+
400
+ ## Schema Composition
401
+
402
+ JSON Model supports powerful schema composition using `T::AllOf`, `T::AnyOf`, and `T::OneOf`:
403
+
404
+ ### AllOf - Must Match All Schemas
405
+
406
+ Use `T::AllOf` when a value must validate against all provided schemas (intersection/combining schemas):
407
+
408
+ ```ruby
409
+ class PersonBase
410
+ include JsonModel::Schema
411
+
412
+ property :name, type: String
413
+ property :age, type: Integer, minimum: 0, optional: true
414
+ end
415
+
416
+ class EmployeeDetails
417
+ include JsonModel::Schema
418
+
419
+ property :employee_id, type: String, pattern: /\AE-\d{4}\z/
420
+ property :department, type: String
421
+ property :salary, type: Float, minimum: 0, optional: true
422
+ end
423
+
424
+ class Employee
425
+ include JsonModel::Schema
426
+
427
+ title "Employee"
428
+ description "Combines person and employee-specific properties"
429
+
430
+ property :employee, type: T::AllOf[PersonBase, EmployeeDetails]
431
+ end
432
+
433
+ # Generate the JSON Schema
434
+ puts JSON.pretty_generate(Employee.as_schema)
435
+ ```
436
+
437
+ **Output:**
438
+ ```json
439
+ {
440
+ "additionalProperties": false,
441
+ "title": "Employee",
442
+ "description": "Combines person and employee-specific properties",
443
+ "properties": {
444
+ "employee": {
445
+ "allOf": [
446
+ {
447
+ "additionalProperties": false,
448
+ "properties": {
449
+ "age": {
450
+ "type": "integer",
451
+ "minimum": 0
452
+ },
453
+ "name": {
454
+ "type": "string"
455
+ }
456
+ },
457
+ "required": [
458
+ "name"
459
+ ],
460
+ "type": "object"
461
+ },
462
+ {
463
+ "additionalProperties": false,
464
+ "properties": {
465
+ "department": {
466
+ "type": "string"
467
+ },
468
+ "employee_id": {
469
+ "type": "string",
470
+ "pattern": "\\AE-\\d{4}\\z"
471
+ },
472
+ "salary": {
473
+ "type": "number",
474
+ "minimum": 0
475
+ }
476
+ },
477
+ "required": [
478
+ "department",
479
+ "employee_id"
480
+ ],
481
+ "type": "object"
482
+ }
483
+ ]
484
+ }
485
+ },
486
+ "required": [
487
+ "employee"
488
+ ],
489
+ "type": "object"
490
+ }
491
+ ```
492
+
493
+ ### AnyOf - Must Match At Least One Schema
494
+
495
+ Use `T::AnyOf` when a value must validate against one or more schemas (union/alternatives):
496
+
497
+ ```ruby
498
+ class EmailContact
499
+ include JsonModel::Schema
500
+
501
+ property :email, type: String, format: :email
502
+ end
503
+
504
+ class PhoneContact
505
+ include JsonModel::Schema
506
+
507
+ property :phone, type: String, pattern: /\A\+?[1-9]\\d{1,14}\z/
508
+ end
509
+
510
+ class AddressContact
511
+ include JsonModel::Schema
512
+
513
+ property :street, type: String
514
+ property :city, type: String
515
+ end
516
+
517
+ class Contact
518
+ include JsonModel::Schema
519
+
520
+ title "Contact Method"
521
+ description "Must provide at least one contact method"
522
+
523
+ property :contact, type: T::AnyOf[EmailContact, PhoneContact, AddressContact]
524
+ end
525
+
526
+ # Generate the JSON Schema
527
+ puts JSON.pretty_generate(Contact.as_schema)
528
+ ```
529
+
530
+ **Output:**
531
+ ```json
532
+ {
533
+ "additionalProperties": false,
534
+ "title": "Contact Method",
535
+ "description": "Must provide at least one contact method",
536
+ "properties": {
537
+ "contact": {
538
+ "anyOf": [
539
+ {
540
+ "additionalProperties": false,
541
+ "properties": {
542
+ "email": {
543
+ "type": "string",
544
+ "format": "email"
545
+ }
546
+ },
547
+ "required": [
548
+ "email"
549
+ ],
550
+ "type": "object"
551
+ },
552
+ {
553
+ "additionalProperties": false,
554
+ "properties": {
555
+ "phone": {
556
+ "type": "string",
557
+ "pattern": "\\A\\+?[1-9]\\\\d{1,14}\\z"
558
+ }
559
+ },
560
+ "required": [
561
+ "phone"
562
+ ],
563
+ "type": "object"
564
+ },
565
+ {
566
+ "additionalProperties": false,
567
+ "properties": {
568
+ "city": {
569
+ "type": "string"
570
+ },
571
+ "street": {
572
+ "type": "string"
573
+ }
574
+ },
575
+ "required": [
576
+ "city",
577
+ "street"
578
+ ],
579
+ "type": "object"
580
+ }
581
+ ]
582
+ }
583
+ },
584
+ "required": [
585
+ "contact"
586
+ ],
587
+ "type": "object"
588
+ }
589
+ ```
590
+
591
+ ### OneOf - Must Match Exactly One Schema
592
+
593
+ Use `T::OneOf` when a value must validate against exactly one schema (exclusive alternatives):
594
+
595
+ ```ruby
596
+ class CreditCardPayment
597
+ include JsonModel::Schema
598
+
599
+ property :payment_type, type: String, const: "credit_card"
600
+ property :card_number, type: String, pattern: /\A\d{16}\z/
601
+ property :cvv, type: String, pattern: /\A\d{3,4}\z/
602
+ property :expiry, type: String, pattern: /\A\d{2}\/\d{2}\z/
603
+ end
604
+
605
+ class PayPalPayment
606
+ include JsonModel::Schema
607
+
608
+ property :payment_type, type: String, const: "paypal"
609
+ property :paypal_email, type: String, format: :email
610
+ end
611
+
612
+ class BankTransferPayment
613
+ include JsonModel::Schema
614
+
615
+ property :payment_type, type: String, const: "bank_transfer"
616
+ property :iban, type: String, pattern: "^[A-Z]{2}\\d{2}[A-Z0-9]+$"
617
+ property :swift, type: String, optional: true
618
+ end
619
+
620
+ class PaymentMethod
621
+ include JsonModel::Schema
622
+
623
+ title "Payment Method"
624
+ description "Must specify exactly one payment method"
625
+
626
+ property :payment, type: T::OneOf[CreditCardPayment, PayPalPayment, BankTransferPayment]
627
+ end
628
+
629
+ # Generate the JSON Schema
630
+ puts JSON.pretty_generate(PaymentMethod.as_schema)
631
+ ```
632
+
633
+ **Output:**
634
+ ```json
635
+ {
636
+ "additionalProperties": false,
637
+ "title": "Payment Method",
638
+ "description": "Must specify exactly one payment method",
639
+ "properties": {
640
+ "payment": {
641
+ "oneOf": [
642
+ {
643
+ "additionalProperties": false,
644
+ "type": "object"
645
+ },
646
+ {
647
+ "additionalProperties": false,
648
+ "type": "object"
649
+ },
650
+ {
651
+ "additionalProperties": false,
652
+ "type": "object"
653
+ }
654
+ ]
655
+ }
656
+ },
657
+ "required": [
658
+ "payment"
659
+ ],
660
+ "type": "object"
661
+ }
662
+ ```
663
+
664
+ ## Use Cases
665
+
666
+ - **API Documentation**: Generate JSON schemas for API request/response validation
667
+ - **Configuration Files**: Define and validate application configuration schemas
668
+ - **Data Validation**: Validate incoming data against defined schemas
669
+ - **Code Generation**: Use schemas to generate code in other languages
670
+ - **OpenAPI/Swagger**: Generate OpenAPI schema definitions for your APIs
671
+ - **Form Generation**: Generate forms from schema definitions
672
+
673
+ ## Resources
674
+
675
+ - [JSON Schema Specification](https://json-schema.org/)
676
+ - [Understanding JSON Schema](https://json-schema.org/understanding-json-schema/)
677
+ - [JSON Schema Validator](https://www.jsonschemavalidator.net/)
678
+ - [Draft 7 Reference](https://json-schema.org/draft-07/json-schema-release-notes.html)
679
+
680
+ ## License
681
+
682
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
683
+
684
+ ## Credits
685
+
686
+ Developed and maintained by [gillesbergerp](https://github.com/gillesbergerp).
@@ -46,9 +46,13 @@ module JsonModel
46
46
  end
47
47
 
48
48
  # @param [::Object] json
49
- # @return [::Object]
49
+ # @return [::Object, nil]
50
50
  def cast(json)
51
- json.map { |item| @type.cast(item) }
51
+ if json.is_a?(Enumerable)
52
+ json.map { |item| @type.cast(item) }
53
+ else
54
+ raise(Errors::TypeError, "Expected an array, got #{json.class} (#{json.inspect})")
55
+ end
52
56
  end
53
57
 
54
58
  private
@@ -11,7 +11,7 @@ module JsonModel
11
11
  end
12
12
 
13
13
  # @param [::Object] json
14
- # @return [::Object]
14
+ # @return [::Object, nil]
15
15
  def cast(json)
16
16
  if json.nil?
17
17
  return nil
@@ -11,7 +11,7 @@ module JsonModel
11
11
  end
12
12
 
13
13
  # @param [::Object] json
14
- # @return [::Object]
14
+ # @return [::Object, nil]
15
15
  def cast(json)
16
16
  if json.nil?
17
17
  return nil
@@ -11,7 +11,7 @@ module JsonModel
11
11
  end
12
12
 
13
13
  # @param [::Object] json
14
- # @return [::Object]
14
+ # @return [::Object, nil]
15
15
  def cast(json)
16
16
  if json.nil?
17
17
  return nil
@@ -25,7 +25,7 @@ module JsonModel
25
25
  end
26
26
 
27
27
  # @param [::Object] json
28
- # @return [::Object]
28
+ # @return [::Object, nil]
29
29
  def cast(json)
30
30
  raise(NotImplementedError)
31
31
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JsonModel
4
+ class TypeSpec
5
+ class Const < TypeSpec
6
+ # @param [String] value
7
+ def initialize(value)
8
+ super()
9
+ @value = value
10
+
11
+ if value.blank?
12
+ raise(ArgumentError, 'Const type spec requires a non-empty value')
13
+ end
14
+ end
15
+
16
+ # @param [Hash] _options
17
+ # @return [Hash]
18
+ def as_schema(**_options)
19
+ {
20
+ const: @value,
21
+ }.compact
22
+ end
23
+
24
+ # @param [Symbol] name
25
+ # @param [ActiveModel::Validations] klass
26
+ def register_validations(name, klass)
27
+ super
28
+
29
+ klass.validates(name, inclusion: { in: @value })
30
+ end
31
+ end
32
+ end
33
+ end
@@ -21,7 +21,7 @@ module JsonModel
21
21
  end
22
22
 
23
23
  # @param [::Object] json
24
- # @return [::Object]
24
+ # @return [::Object, nil]
25
25
  def cast(json)
26
26
  @type.new(**json)
27
27
  end
@@ -76,7 +76,6 @@ module JsonModel
76
76
  relative_json_pointer: ->(v) { v.match?(%r{\A(?:0|[1-9][0-9]*)(?:#|(?:/(?:[^~]|~[01])*))*\z}) },
77
77
  }.freeze
78
78
 
79
-
80
79
  # @param [Integer, nil] min_length
81
80
  # @param [Integer, nil] max_length
82
81
  # @param [Regexp, nil] pattern
@@ -18,14 +18,14 @@ module JsonModel
18
18
  end
19
19
 
20
20
  # @param [::Object] json
21
- # @return [::Object]
21
+ # @return [::Object, nil]
22
22
  def cast(json)
23
23
  if json.nil?
24
24
  nil
25
25
  elsif @types.any? { |type| json.is_a?(type) }
26
26
  json
27
27
  else
28
- raise(Errors::TypeError, "Expected #{@type}, got #{json.class} (#{json.inspect})")
28
+ raise(Errors::TypeError, "Expected one of #{@type.join('/')}, got #{json.class} (#{json.inspect})")
29
29
  end
30
30
  end
31
31
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative('type_spec/array')
4
4
  require_relative('type_spec/composition')
5
+ require_relative('type_spec/const')
5
6
  require_relative('type_spec/enum')
6
7
  require_relative('type_spec/object')
7
8
  require_relative('type_spec/primitive')
@@ -24,7 +25,7 @@ module JsonModel
24
25
  end
25
26
 
26
27
  # @param [::Object] json
27
- # @return [::Object]
28
+ # @return [::Object, nil]
28
29
  def cast(json)
29
30
  json
30
31
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module T
4
+ class Const
5
+ # @param [String] value
6
+ def initialize(value)
7
+ @value = value
8
+ end
9
+
10
+ # @return [JsonModel::TypeSpec::Const]
11
+ def to_type_spec(**options)
12
+ JsonModel::TypeSpec::Const.new(*@value, **options)
13
+ end
14
+
15
+ class << self
16
+ # @param [Array] args
17
+ # @return [Const]
18
+ def [](*args)
19
+ Const.new(*args)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -5,4 +5,5 @@ require_relative('types/any_of')
5
5
  require_relative('types/boolean')
6
6
  require_relative('types/one_of')
7
7
  require_relative('types/array')
8
+ require_relative('types/const')
8
9
  require_relative('types/enum')
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JsonModel
4
- VERSION = '0.1.5'
4
+ VERSION = '0.1.7'
5
5
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require('spec_helper')
4
+
5
+ RSpec.describe(JsonModel::TypeSpec::Const) do
6
+ describe('#as_schema') do
7
+ it('returns a const schema') do
8
+ expect(described_class.new('a').as_schema)
9
+ .to(
10
+ eq(
11
+ {
12
+ const: 'a'
13
+ },
14
+ ),
15
+ )
16
+ end
17
+ end
18
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json_model_rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Gillesberger
@@ -121,6 +121,7 @@ files:
121
121
  - lib/json_model/type_spec/composition/all_of.rb
122
122
  - lib/json_model/type_spec/composition/any_of.rb
123
123
  - lib/json_model/type_spec/composition/one_of.rb
124
+ - lib/json_model/type_spec/const.rb
124
125
  - lib/json_model/type_spec/enum.rb
125
126
  - lib/json_model/type_spec/object.rb
126
127
  - lib/json_model/type_spec/primitive.rb
@@ -135,6 +136,7 @@ files:
135
136
  - lib/json_model/types/any_of.rb
136
137
  - lib/json_model/types/array.rb
137
138
  - lib/json_model/types/boolean.rb
139
+ - lib/json_model/types/const.rb
138
140
  - lib/json_model/types/enum.rb
139
141
  - lib/json_model/types/one_of.rb
140
142
  - lib/json_model/version.rb
@@ -152,6 +154,7 @@ files:
152
154
  - spec/type_spec/composition/any_of_spec.rb
153
155
  - spec/type_spec/composition/one_of_spec.rb
154
156
  - spec/type_spec/composition_spec.rb
157
+ - spec/type_spec/const_spec.rb
155
158
  - spec/type_spec/enum_spec.rb
156
159
  - spec/type_spec/primitive/boolean_spec.rb
157
160
  - spec/type_spec/primitive/integer_spec.rb