shale 1.0.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/README.md +263 -69
  4. data/lib/shale/adapter/json.rb +10 -7
  5. data/lib/shale/adapter/nokogiri.rb +1 -1
  6. data/lib/shale/adapter/ox.rb +2 -1
  7. data/lib/shale/adapter/toml_rb.rb +5 -3
  8. data/lib/shale/adapter/tomlib.rb +36 -0
  9. data/lib/shale/error.rb +26 -1
  10. data/lib/shale/mapper.rb +36 -30
  11. data/lib/shale/mapping/descriptor/dict.rb +10 -1
  12. data/lib/shale/mapping/dict.rb +4 -3
  13. data/lib/shale/mapping/dict_base.rb +29 -2
  14. data/lib/shale/schema/json_generator/base.rb +9 -3
  15. data/lib/shale/schema/json_generator/boolean.rb +2 -1
  16. data/lib/shale/schema/json_generator/collection.rb +18 -2
  17. data/lib/shale/schema/json_generator/date.rb +3 -1
  18. data/lib/shale/schema/json_generator/float.rb +7 -1
  19. data/lib/shale/schema/json_generator/integer.rb +7 -1
  20. data/lib/shale/schema/json_generator/object.rb +12 -2
  21. data/lib/shale/schema/json_generator/string.rb +6 -1
  22. data/lib/shale/schema/json_generator/time.rb +3 -1
  23. data/lib/shale/schema/json_generator/value.rb +2 -1
  24. data/lib/shale/schema/json_generator.rb +7 -3
  25. data/lib/shale/schema/xml_compiler.rb +12 -12
  26. data/lib/shale/type/boolean.rb +2 -0
  27. data/lib/shale/type/complex.rb +43 -18
  28. data/lib/shale/type/date.rb +2 -0
  29. data/lib/shale/type/float.rb +2 -0
  30. data/lib/shale/type/integer.rb +2 -0
  31. data/lib/shale/type/string.rb +2 -0
  32. data/lib/shale/type/time.rb +2 -0
  33. data/lib/shale/type.rb +56 -0
  34. data/lib/shale/utils.rb +1 -1
  35. data/lib/shale/version.rb +1 -1
  36. data/lib/shale.rb +18 -20
  37. data/shale.gemspec +3 -1
  38. metadata +21 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e3b4d27ab7e8cc9b4873dfa60b52bdbafe50c2885085c4ec77ac6887351aab1
4
- data.tar.gz: 6ce71a10ebf53633930b6d5a92ebb5f736aab44c47d6910a25238209192a4b92
3
+ metadata.gz: 81b22181e1c63b2757fdd137f09a185caf894de6d43c108119c18c9260b48afd
4
+ data.tar.gz: 065bc2d6f8a6eec466b52574ed38846dc0b4e0315ba9b83fc0d24ed75066ef07
5
5
  SHA512:
6
- metadata.gz: 23c3b182c72c0e1d5102eb667541caccfb56e836e636bd083027425a60a12ed39ffce1ab3540adc0659c6b1bcbcf5255d192bbdc2d39bfc10c762ee4bdaaaa1e
7
- data.tar.gz: ef7481e6076cc8f426c1dd0fb002d984594cbf3d59743a31450e8989048ffdd6cdc8fc735e90b7529c1935d7251c6751e70f7c3734d5c3f7df4b8124cc4b8256
6
+ metadata.gz: 9b106ac74372bc089093ee8c12238b48505cff4d318967e2760ef2842e9a783125227177fd5f2cb47b27872adc7cbbf0686226934132b5c8f346c71b92159c8a
7
+ data.tar.gz: dcb0088d6f700f1654313a72156e9d1e4cd0c1b75b99a82cea17e12af96ced1407e358b1d0cc52e7e3f0a1c46cb19d702b3a49628a0da67e521a20906e006a6c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## [1.2.0] - 2024-10-31
2
+
3
+ ### Added
4
+ - Allow to pass adapter specific options
5
+ - Allow to pass `additional_properties` option to JSON Schema generator
6
+ - Allow to pass `description` option to JSON Schema generator
7
+ - Support for symbol type aliases in attribute mappings
8
+
9
+ ## [1.1.0] - 2024-02-17
10
+
11
+ ### Added
12
+ - [bkjohnson] Add support for JSON Schema validation keywords (#29)
13
+ - Add support for Ruby 3.3
14
+
15
+ ### Changed
16
+ - Drop support for Ruby 2.6 and Ruby 2.7
17
+
18
+ ### Fixed
19
+ - Fix Ox adapter incorrectly handling documents with XML declaration
20
+
1
21
  ## [1.0.0] - 2023-07-15
2
22
 
3
23
  ### Added
data/README.md CHANGED
@@ -17,7 +17,7 @@ Documentation with interactive examples is available at [Shale website](https://
17
17
 
18
18
  ## Installation
19
19
 
20
- Shale supports Ruby (MRI) 2.6+
20
+ Shale supports Ruby (MRI) 3.0+
21
21
 
22
22
  Add this line to your application's Gemfile:
23
23
 
@@ -65,6 +65,7 @@ $ gem install shale
65
65
  * [Using methods to extract and generate data](#using-methods-to-extract-and-generate-data)
66
66
  * [Delegating fields to child attributes](#delegating-fields-to-child-attributes)
67
67
  * [Additional options](#additional-options)
68
+ * [Overriding attribute methods](#overriding-attribute-methods)
68
69
  * [Using custom models](#using-custom-models)
69
70
  * [Supported types](#supported-types)
70
71
  * [Writing your own type](#writing-your-own-type)
@@ -82,17 +83,17 @@ $ gem install shale
82
83
  require 'shale'
83
84
 
84
85
  class Address < Shale::Mapper
85
- attribute :city, Shale::Type::String
86
- attribute :street, Shale::Type::String
87
- attribute :zip, Shale::Type::String
86
+ attribute :city, :string
87
+ attribute :street, :string
88
+ attribute :zip, :string
88
89
  end
89
90
 
90
91
  class Person < Shale::Mapper
91
- attribute :first_name, Shale::Type::String
92
- attribute :last_name, Shale::Type::String
93
- attribute :age, Shale::Type::Integer
94
- attribute :married, Shale::Type::Boolean, default: -> { false }
95
- attribute :hobbies, Shale::Type::String, collection: true
92
+ attribute :first_name, :string
93
+ attribute :last_name, :string
94
+ attribute :age, :integer
95
+ attribute :married, :boolean, default: -> { false }
96
+ attribute :hobbies, :string, collection: true
96
97
  attribute :address, Address
97
98
  end
98
99
  ```
@@ -208,8 +209,8 @@ person.to_yaml
208
209
  ### Converting TOML to object
209
210
 
210
211
  To use TOML with Shale you have to set adapter you want to use.
211
- Out of the box Shale suports [Tomlib](https://github.com/kgiszczak/tomlib).
212
- It also comes with adapter for [toml-rb](https://github.com/emancu/toml-rb) if you prefer that.
212
+ It comes with adapters for [Tomlib](https://github.com/kgiszczak/tomlib) and
213
+ [toml-rb](https://github.com/emancu/toml-rb).
213
214
  For details see [Adapters](#adapters) section.
214
215
 
215
216
  To set it, first make sure Tomlib gem is installed:
@@ -221,8 +222,8 @@ $ gem install tomlib
221
222
  then setup adapter:
222
223
 
223
224
  ```ruby
224
- require 'tomlib'
225
- Shale.toml_adapter = Tomlib
225
+ require 'sahle/adapter/tomlib'
226
+ Shale.toml_adapter = Shale::Adapter::Tomlib
226
227
 
227
228
  # Alternatively if you'd like to use toml-rb, use:
228
229
  require 'shale/adapter/toml_rb'
@@ -353,6 +354,25 @@ person.to_xml
353
354
 
354
355
  ### Converting CSV to object
355
356
 
357
+ To use CSV with Shale you have to set adapter.
358
+ Shale comes with adapter for [csv](https://github.com/ruby/csv).
359
+ For details see [Adapters](#adapters) section.
360
+
361
+ To set it, first make sure CSV gem is installed:
362
+
363
+ ```
364
+ $ gem install csv
365
+ ```
366
+
367
+ then setup adapter:
368
+
369
+ ```ruby
370
+ require 'shale/adapter/csv'
371
+ Shale.csv_adapter = Shale::Adapter::CSV
372
+ ```
373
+
374
+ Now you can use CSV with Shale.
375
+
356
376
  CSV represents a flat data structure, so you can't map properties to complex types directly,
357
377
  but you can use methods to map properties to complex types
358
378
  (see [Using methods to extract and generate data](#using-methods-to-extract-and-generate-data)
@@ -423,8 +443,8 @@ By default keys are named the same as attributes. To use custom keys use:
423
443
 
424
444
  ```ruby
425
445
  class Person < Shale::Mapper
426
- attribute :first_name, Shale::Type::String
427
- attribute :last_name, Shale::Type::String
446
+ attribute :first_name, :string
447
+ attribute :last_name, :string
428
448
 
429
449
  json do
430
450
  map 'firstName', to: :first_name
@@ -437,8 +457,8 @@ end
437
457
 
438
458
  ```ruby
439
459
  class Person < Shale::Mapper
440
- attribute :first_name, Shale::Type::String
441
- attribute :last_name, Shale::Type::String
460
+ attribute :first_name, :string
461
+ attribute :last_name, :string
442
462
 
443
463
  yaml do
444
464
  map 'firstName', to: :first_name
@@ -451,8 +471,8 @@ end
451
471
 
452
472
  ```ruby
453
473
  class Person < Shale::Mapper
454
- attribute :first_name, Shale::Type::String
455
- attribute :last_name, Shale::Type::String
474
+ attribute :first_name, :string
475
+ attribute :last_name, :string
456
476
 
457
477
  toml do
458
478
  map 'firstName', to: :first_name
@@ -469,8 +489,8 @@ to `:first_name` attribute and the second column to `:last_name`.
469
489
 
470
490
  ```ruby
471
491
  class Person < Shale::Mapper
472
- attribute :first_name, Shale::Type::String
473
- attribute :last_name, Shale::Type::String
492
+ attribute :first_name, :string
493
+ attribute :last_name, :string
474
494
 
475
495
  csv do
476
496
  map 'firstName', to: :first_name
@@ -483,8 +503,8 @@ end
483
503
 
484
504
  ```ruby
485
505
  class Person < Shale::Mapper
486
- attribute :first_name, Shale::Type::String
487
- attribute :last_name, Shale::Type::String
506
+ attribute :first_name, :string
507
+ attribute :last_name, :string
488
508
 
489
509
  hsh do
490
510
  map 'firstName', to: :first_name
@@ -499,9 +519,9 @@ XML is more complicated format than JSON or YAML. To map elements, attributes an
499
519
 
500
520
  ```ruby
501
521
  class Address < Shale::Mapper
502
- attribute :street, Shale::Type::String
503
- attribute :city, Shale::Type::String
504
- attribute :zip, Shale::Type::String
522
+ attribute :street, :string
523
+ attribute :city, :string
524
+ attribute :zip, :string
505
525
 
506
526
  xml do
507
527
  map_content to: :street
@@ -511,10 +531,10 @@ class Address < Shale::Mapper
511
531
  end
512
532
 
513
533
  class Person < Shale::Mapper
514
- attribute :first_name, Shale::Type::String
515
- attribute :last_name, Shale::Type::String
516
- attribute :age, Shale::Type::Integer
517
- attribute :hobbies, Shale::Type::String, collection: true
534
+ attribute :first_name, :string
535
+ attribute :last_name, :string
536
+ attribute :age, :integer
537
+ attribute :hobbies, :string, collection: true
518
538
  attribute :address, Address
519
539
 
520
540
  xml do
@@ -553,7 +573,7 @@ You can use `cdata: true` option on `map_element` and `map_content` to handle CD
553
573
 
554
574
  ```ruby
555
575
  class Address < Shale::Mapper
556
- attribute :content, Shale::Type::String
576
+ attribute :content, :string
557
577
 
558
578
  xml do
559
579
  map_content to: :content, cdata: true
@@ -561,7 +581,7 @@ class Address < Shale::Mapper
561
581
  end
562
582
 
563
583
  class Person < Shale::Mapper
564
- attribute :first_name, Shale::Type::String
584
+ attribute :first_name, :string
565
585
  attribute :address, Address
566
586
 
567
587
  xml do
@@ -587,9 +607,9 @@ To map namespaced elements and attributes use `namespace` and `prefix` propertie
587
607
 
588
608
  ```ruby
589
609
  class Person < Shale::Mapper
590
- attribute :first_name, Shale::Type::String
591
- attribute :last_name, Shale::Type::String
592
- attribute :age, Shale::Type::Integer
610
+ attribute :first_name, :string
611
+ attribute :last_name, :string
612
+ attribute :age, :integer
593
613
 
594
614
  xml do
595
615
  root 'person'
@@ -614,11 +634,11 @@ explicitly declare it on `map_attribute`).
614
634
 
615
635
  ```ruby
616
636
  class Person < Shale::Mapper
617
- attribute :first_name, Shale::Type::String
618
- attribute :middle_name, Shale::Type::String
619
- attribute :last_name, Shale::Type::String
620
- attribute :age, Shale::Type::Integer
621
- attribute :hobby, Shale::Type::String
637
+ attribute :first_name, :string
638
+ attribute :middle_name, :string
639
+ attribute :last_name, :string
640
+ attribute :age, :integer
641
+ attribute :hobby, :string
622
642
 
623
643
  xml do
624
644
  root 'person'
@@ -654,9 +674,9 @@ For CSV the default is to render `nil` elements.
654
674
 
655
675
  ```ruby
656
676
  class Person < Shale::Mapper
657
- attribute :first_name, Shale::Type::String
658
- attribute :last_name, Shale::Type::String
659
- attribute :age, Shale::Type::Integer
677
+ attribute :first_name, :string
678
+ attribute :last_name, :string
679
+ attribute :age, :integer
660
680
 
661
681
  json do
662
682
  map 'first_name', to: :first_name, render_nil: true
@@ -704,9 +724,9 @@ class Base < Shale::Mapper
704
724
  end
705
725
 
706
726
  class Person < Base
707
- attribute :first_name, Shale::Type::String
708
- attribute :last_name, Shale::Type::String
709
- attribute :age, Shale::Type::Integer
727
+ attribute :first_name, :string
728
+ attribute :last_name, :string
729
+ attribute :age, :integer
710
730
 
711
731
  json do
712
732
  # override default from Base class
@@ -723,8 +743,8 @@ end
723
743
 
724
744
  ```ruby
725
745
  class Person < Base
726
- attribute :first_name, Shale::Type::String
727
- attribute :last_name, Shale::Type::String
746
+ attribute :first_name, :string
747
+ attribute :last_name, :string
728
748
 
729
749
  json do
730
750
  render_nil false
@@ -743,9 +763,9 @@ you can use methods to do so:
743
763
 
744
764
  ```ruby
745
765
  class Person < Shale::Mapper
746
- attribute :hobbies, Shale::Type::String, collection: true
747
- attribute :street, Shale::Type::String
748
- attribute :city, Shale::Type::String
766
+ attribute :hobbies, :string, collection: true
767
+ attribute :street, :string
768
+ attribute :city, :string
749
769
 
750
770
  json do
751
771
  map 'hobbies', using: { from: :hobbies_from_json, to: :hobbies_to_json }
@@ -834,7 +854,7 @@ You can also pass a `context` object that will be available in extractor/generat
834
854
 
835
855
  ```ruby
836
856
  class Person < Shale::Mapper
837
- attribute :password, Shale::Type::String
857
+ attribute :password, :string
838
858
 
839
859
  json do
840
860
  map 'password', using: { from: :password_from_json, to: :password_to_json }
@@ -864,7 +884,7 @@ If you want to work on multiple elements at a time you can group them using `gro
864
884
 
865
885
  ```ruby
866
886
  class Person < Shale::Mapper
867
- attribute :name, Shale::Type::String
887
+ attribute :name, :string
868
888
 
869
889
  json do
870
890
  group from: :name_from_json, to: :name_to_json do
@@ -915,12 +935,12 @@ To delegate fields to child complex types you can use `receiver: :child` declara
915
935
 
916
936
  ```ruby
917
937
  class Address < Shale::Mapper
918
- attribute :city, Shale::Type::String
919
- attribute :street, Shale::Type::String
938
+ attribute :city, :string
939
+ attribute :street, :string
920
940
  end
921
941
 
922
942
  class Person < Shale::Mapper
923
- attribute :name, Shale::Type::String
943
+ attribute :name, :string
924
944
  attribute :address, Address
925
945
 
926
946
  json do
@@ -1040,8 +1060,8 @@ names and shouldn't be included in the returned collection. It also accepts all
1040
1060
 
1041
1061
  ```ruby
1042
1062
  class Person
1043
- attribute :first_name, Shale::Type::String
1044
- attribute :last_name, Shale::Type::String
1063
+ attribute :first_name, :string
1064
+ attribute :last_name, :string
1045
1065
  end
1046
1066
 
1047
1067
  people = Person.from_csv(<<~DATA, headers: true, col_sep: '|')
@@ -1066,6 +1086,63 @@ Person.to_csv(people, headers: true, col_sep: '|')
1066
1086
  # James|Sixpack
1067
1087
  ```
1068
1088
 
1089
+ Most adapters accept options specific to them. Eg. if you want to be able to work
1090
+ with NaN values in JSON:
1091
+
1092
+ ```ruby
1093
+ class Person
1094
+ attribute :age, :float
1095
+ end
1096
+
1097
+ person = Person.from_json('{"age": NaN}', allow_nan: true)
1098
+
1099
+ # =>
1100
+ #
1101
+ # #<Person:0x0000000113d7a488 @age=Float::NAN>
1102
+
1103
+ Person.to_json(person, allow_nan: true)
1104
+
1105
+ # =>
1106
+ #
1107
+ # {
1108
+ # "age": NaN
1109
+ # }
1110
+ ```
1111
+
1112
+ ### Overriding attribute methods
1113
+
1114
+ It's possible to override an attribute method to change its output:
1115
+
1116
+ ```ruby
1117
+ class Person < Shale::Mapper
1118
+ attribute :gender, :string
1119
+
1120
+ def gender
1121
+ if super == 'm'
1122
+ 'male'
1123
+ else
1124
+ 'female'
1125
+ end
1126
+ end
1127
+ end
1128
+
1129
+ puts Person.from_json('{"gender":"m"}')
1130
+
1131
+ # =>
1132
+ #
1133
+ # #<Person:0x00007f9bc3086d60
1134
+ # @gender="male">
1135
+ ```
1136
+
1137
+ Be conscious that the original attribute value will be lost after its transformation though:
1138
+
1139
+ ```ruby
1140
+ puts User.from_json('{"gender":"m"}').to_json
1141
+ # => {"gender":"male"}
1142
+ ```
1143
+
1144
+ It'll no longer return gender `m`.
1145
+
1069
1146
  ### Using custom models
1070
1147
 
1071
1148
  By default Shale combines mapper and model into one class. If you want to use your own classes
@@ -1083,15 +1160,15 @@ end
1083
1160
  class AddressMapper < Shale::Mapper
1084
1161
  model Address
1085
1162
 
1086
- attribute :street, Shale::Type::String
1087
- attribute :city, Shale::Type::String
1163
+ attribute :street, :string
1164
+ attribute :city, :string
1088
1165
  end
1089
1166
 
1090
1167
  class PersonMapper < Shale::Mapper
1091
1168
  model Person
1092
1169
 
1093
- attribute :first_name, Shale::Type::String
1094
- attribute :last_name, Shale::Type::String
1170
+ attribute :first_name, :string
1171
+ attribute :last_name, :string
1095
1172
  attribute :address, AddressMapper
1096
1173
  end
1097
1174
 
@@ -1131,12 +1208,21 @@ PersonMapper.to_json(person, pretty: true)
1131
1208
 
1132
1209
  Shale supports these types out of the box:
1133
1210
 
1134
- - `Shale::Type::Boolean`
1135
- - `Shale::Type::Date`
1136
- - `Shale::Type::Float`
1137
- - `Shale::Type::Integer`
1138
- - `Shale::Type::String`
1139
- - `Shale::Type::Time`
1211
+ - `:boolean` (`Shale::Type::Boolean`)
1212
+ - `:date` (`Shale::Type::Date`)
1213
+ - `:float` (`Shale::Type::Float`)
1214
+ - `:integer` (`Shale::Type::Integer`)
1215
+ - `:string` (`Shale::Type::String`)
1216
+ - `:time` (`Shale::Type::Time`)
1217
+
1218
+ The symbol type alias and the type class are interchangeable:
1219
+
1220
+ ```ruby
1221
+ class Person < Shale::Mapper
1222
+ attribute :age, Shale::Type::Integer
1223
+ # attribute :age, :integer
1224
+ end
1225
+ ```
1140
1226
 
1141
1227
  ### Writing your own type
1142
1228
 
@@ -1152,10 +1238,35 @@ class MyIntegerType < Shale::Type::Value
1152
1238
  end
1153
1239
  ```
1154
1240
 
1241
+ Then you can use it in your model:
1242
+
1243
+ ```ruby
1244
+ class Person < Shale::Mapper
1245
+ attribute :age, MyIntegerType
1246
+ end
1247
+ ```
1248
+
1249
+ You can also register your own type with a symbol alias if you
1250
+ intend to use it often.
1251
+
1252
+ ```ruby
1253
+ require 'shale/type'
1254
+
1255
+ Shale::Type.register(:my_integer, MyIntegerType)
1256
+ ```
1257
+
1258
+ Then you can use it like this:
1259
+
1260
+ ```ruby
1261
+ class Person < Shale::Mapper
1262
+ attribute :age, :my_integer
1263
+ end
1264
+ ```
1265
+
1155
1266
  ### Adapters
1156
1267
 
1157
1268
  Shale uses adapters for parsing and generating documents.
1158
- By default Ruby's standard JSON, YAML, CSV parsers are used for handling JSON YAML, CSV documents.
1269
+ By default Ruby's standard JSON and YAML parsers are used for handling JSON and YAML documents.
1159
1270
 
1160
1271
  You can change it by providing your own adapter. For JSON, YAML, TOML and CSV adapter must
1161
1272
  implement `.load` and `.dump` class methods.
@@ -1183,6 +1294,14 @@ require 'shale/adapter/toml_rb'
1183
1294
  Shale.toml_adapter = Shale::Adapter::TomlRB
1184
1295
  ```
1185
1296
 
1297
+ To handle CSV documents you have to set CSV adapter. Shale provides adapter for `csv` parser:
1298
+
1299
+ ```ruby
1300
+ require 'shale'
1301
+ require 'shale/adapter/csv'
1302
+ Shale.csv_adapter = Shale::Adapter::CSV
1303
+ ```
1304
+
1186
1305
  To handle XML documents you have to explicitly set XML adapter.
1187
1306
  Shale provides adapters for most popular Ruby XML parsers:
1188
1307
 
@@ -1288,6 +1407,81 @@ end
1288
1407
  Shale::Schema::JSONGenerator.register_json_type(MyEmailType, MyEmailJSONType)
1289
1408
  ```
1290
1409
 
1410
+ To add validation keywords to the schema, you can use a custom model and do this:
1411
+
1412
+ ```ruby
1413
+ require 'shale/schema'
1414
+
1415
+ class PersonMapper < Shale::Mapper
1416
+ model Person
1417
+
1418
+ attribute :first_name, :string
1419
+ attribute :last_name, :string
1420
+ attribute :address, :string
1421
+ attribute :age, :integer
1422
+
1423
+ json do
1424
+ properties max_properties: 5, additional_properties: false
1425
+
1426
+ map "first_name", to: :first_name, schema: { required: true }
1427
+ map "last_name", to: :last_name, schema: { required: true }
1428
+ map "address", to: :address, schema: { max_length: 128, description: "Street, home number, city and country" }
1429
+ map "age", to: :age, schema: { minimum: 1, maximum: 150, description: "Person age" }
1430
+ end
1431
+ end
1432
+
1433
+ Shale::Schema.to_json(
1434
+ PersonMapper,
1435
+ pretty: true
1436
+ )
1437
+
1438
+ # =>
1439
+ #
1440
+ # {
1441
+ # "$schema": "https://json-schema.org/draft/2020-12/schema",
1442
+ # "description": "My description",
1443
+ # "$ref": "#/$defs/Person",
1444
+ # "$defs": {
1445
+ # "Person": {
1446
+ # "type": "object",
1447
+ # "properties": {
1448
+ # "first_name": {
1449
+ # "type": "string"
1450
+ # },
1451
+ # "last_name": {
1452
+ # "type": "string"
1453
+ # },
1454
+ # "address": {
1455
+ # "type": [
1456
+ # "string",
1457
+ # "null"
1458
+ # ],
1459
+ # "maxLength": 128,
1460
+ # "description": "Street, home number, city and country"
1461
+ # },
1462
+ # "age": {
1463
+ # "type": [
1464
+ # "integer",
1465
+ # "null"
1466
+ # ],
1467
+ # "minimum": 1,
1468
+ # "maximum": 150,
1469
+ # "description": "Person age"
1470
+ # }
1471
+ # },
1472
+ # "required": [
1473
+ # "first_name",
1474
+ # "last_name"
1475
+ # ],
1476
+ # "maxProperties": 5,
1477
+ # "additionalProperties": false
1478
+ # }
1479
+ # }
1480
+ # }
1481
+ ```
1482
+
1483
+ Validation keywords are supported for all types, only the global `enum` and `const` types are not supported.
1484
+
1291
1485
  ### Compiling JSON Schema into Shale model
1292
1486
 
1293
1487
  :warning: Only **[Draft 2020-12](https://json-schema.org/draft/2020-12/schema)** JSON Schema is supported
@@ -11,27 +11,30 @@ module Shale
11
11
  # Parse JSON into Hash
12
12
  #
13
13
  # @param [String] json JSON document
14
+ # @param [Hash] options
14
15
  #
15
16
  # @return [Hash]
16
17
  #
17
18
  # @api private
18
- def self.load(json)
19
- ::JSON.parse(json)
19
+ def self.load(json, **options)
20
+ ::JSON.parse(json, **options)
20
21
  end
21
22
 
22
23
  # Serialize Hash into JSON
23
24
  #
24
25
  # @param [Hash] obj Hash object
25
- # @param [true, false] pretty
26
+ # @param [Hash] options
26
27
  #
27
28
  # @return [String]
28
29
  #
29
30
  # @api private
30
- def self.dump(obj, pretty: false)
31
- if pretty
32
- ::JSON.pretty_generate(obj)
31
+ def self.dump(obj, **options)
32
+ json_options = options.except(:pretty)
33
+
34
+ if options[:pretty]
35
+ ::JSON.pretty_generate(obj, **json_options)
33
36
  else
34
- ::JSON.generate(obj)
37
+ ::JSON.generate(obj, **json_options)
35
38
  end
36
39
  end
37
40
  end
@@ -61,7 +61,7 @@ module Shale
61
61
  result = doc.to_xml(save_with: save_with)
62
62
 
63
63
  unless pretty
64
- result = result.sub(/\n/, '')
64
+ result = result.sub("\n", '')
65
65
  end
66
66
 
67
67
  result
@@ -22,7 +22,8 @@ module Shale
22
22
  #
23
23
  # @api private
24
24
  def self.load(xml)
25
- Node.new(::Ox.parse(xml))
25
+ element = ::Ox.parse(xml)
26
+ Node.new(element.respond_to?(:root) ? element.root : element)
26
27
  rescue ::Ox::ParseError => e
27
28
  raise ParseError, "Document is invalid: #{e.message}"
28
29
  end
@@ -4,29 +4,31 @@ require 'toml-rb'
4
4
 
5
5
  module Shale
6
6
  module Adapter
7
- # TOML adapter
7
+ # TomlRB adapter
8
8
  #
9
9
  # @api public
10
10
  class TomlRB
11
11
  # Parse TOML into Hash
12
12
  #
13
13
  # @param [String] toml TOML document
14
+ # @param [Hash] options
14
15
  #
15
16
  # @return [Hash]
16
17
  #
17
18
  # @api private
18
- def self.load(toml)
19
+ def self.load(toml, **_options)
19
20
  ::TomlRB.parse(toml)
20
21
  end
21
22
 
22
23
  # Serialize Hash into TOML
23
24
  #
24
25
  # @param [Hash] obj Hash object
26
+ # @param [Hash] options
25
27
  #
26
28
  # @return [String]
27
29
  #
28
30
  # @api private
29
- def self.dump(obj)
31
+ def self.dump(obj, **_options)
30
32
  ::TomlRB.dump(obj)
31
33
  end
32
34
  end