shale 1.0.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +263 -69
- data/lib/shale/adapter/json.rb +10 -7
- data/lib/shale/adapter/nokogiri.rb +1 -1
- data/lib/shale/adapter/ox.rb +2 -1
- data/lib/shale/adapter/toml_rb.rb +5 -3
- data/lib/shale/adapter/tomlib.rb +36 -0
- data/lib/shale/error.rb +26 -1
- data/lib/shale/mapper.rb +36 -30
- data/lib/shale/mapping/descriptor/dict.rb +10 -1
- data/lib/shale/mapping/dict.rb +4 -3
- data/lib/shale/mapping/dict_base.rb +29 -2
- data/lib/shale/schema/json_generator/base.rb +9 -3
- data/lib/shale/schema/json_generator/boolean.rb +2 -1
- data/lib/shale/schema/json_generator/collection.rb +18 -2
- data/lib/shale/schema/json_generator/date.rb +3 -1
- data/lib/shale/schema/json_generator/float.rb +7 -1
- data/lib/shale/schema/json_generator/integer.rb +7 -1
- data/lib/shale/schema/json_generator/object.rb +12 -2
- data/lib/shale/schema/json_generator/string.rb +6 -1
- data/lib/shale/schema/json_generator/time.rb +3 -1
- data/lib/shale/schema/json_generator/value.rb +2 -1
- data/lib/shale/schema/json_generator.rb +7 -3
- data/lib/shale/schema/xml_compiler.rb +12 -12
- data/lib/shale/type/boolean.rb +2 -0
- data/lib/shale/type/complex.rb +43 -18
- data/lib/shale/type/date.rb +2 -0
- data/lib/shale/type/float.rb +2 -0
- data/lib/shale/type/integer.rb +2 -0
- data/lib/shale/type/string.rb +2 -0
- data/lib/shale/type/time.rb +2 -0
- data/lib/shale/type.rb +56 -0
- data/lib/shale/utils.rb +1 -1
- data/lib/shale/version.rb +1 -1
- data/lib/shale.rb +18 -20
- data/shale.gemspec +3 -1
- metadata +21 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81b22181e1c63b2757fdd137f09a185caf894de6d43c108119c18c9260b48afd
|
4
|
+
data.tar.gz: 065bc2d6f8a6eec466b52574ed38846dc0b4e0315ba9b83fc0d24ed75066ef07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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)
|
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,
|
86
|
-
attribute :street,
|
87
|
-
attribute :zip,
|
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,
|
92
|
-
attribute :last_name,
|
93
|
-
attribute :age,
|
94
|
-
attribute :married,
|
95
|
-
attribute :hobbies,
|
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
|
-
|
212
|
-
|
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,
|
427
|
-
attribute :last_name,
|
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,
|
441
|
-
attribute :last_name,
|
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,
|
455
|
-
attribute :last_name,
|
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,
|
473
|
-
attribute :last_name,
|
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,
|
487
|
-
attribute :last_name,
|
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,
|
503
|
-
attribute :city,
|
504
|
-
attribute :zip,
|
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,
|
515
|
-
attribute :last_name,
|
516
|
-
attribute :age,
|
517
|
-
attribute :hobbies,
|
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,
|
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,
|
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,
|
591
|
-
attribute :last_name,
|
592
|
-
attribute :age,
|
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,
|
618
|
-
attribute :middle_name,
|
619
|
-
attribute :last_name,
|
620
|
-
attribute :age,
|
621
|
-
attribute :hobby,
|
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,
|
658
|
-
attribute :last_name,
|
659
|
-
attribute :age,
|
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,
|
708
|
-
attribute :last_name,
|
709
|
-
attribute :age,
|
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,
|
727
|
-
attribute :last_name,
|
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,
|
747
|
-
attribute :street,
|
748
|
-
attribute :city,
|
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,
|
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,
|
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,
|
919
|
-
attribute :street,
|
938
|
+
attribute :city, :string
|
939
|
+
attribute :street, :string
|
920
940
|
end
|
921
941
|
|
922
942
|
class Person < Shale::Mapper
|
923
|
-
attribute :name,
|
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,
|
1044
|
-
attribute :last_name,
|
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,
|
1087
|
-
attribute :city,
|
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,
|
1094
|
-
attribute :last_name,
|
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
|
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
|
data/lib/shale/adapter/json.rb
CHANGED
@@ -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 [
|
26
|
+
# @param [Hash] options
|
26
27
|
#
|
27
28
|
# @return [String]
|
28
29
|
#
|
29
30
|
# @api private
|
30
|
-
def self.dump(obj,
|
31
|
-
|
32
|
-
|
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
|
data/lib/shale/adapter/ox.rb
CHANGED
@@ -22,7 +22,8 @@ module Shale
|
|
22
22
|
#
|
23
23
|
# @api private
|
24
24
|
def self.load(xml)
|
25
|
-
|
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
|
-
#
|
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
|