shale 0.4.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +449 -40
  5. data/exe/shaleb +30 -6
  6. data/lib/shale/adapter/json.rb +3 -3
  7. data/lib/shale/adapter/nokogiri/document.rb +97 -0
  8. data/lib/shale/adapter/nokogiri/node.rb +100 -0
  9. data/lib/shale/adapter/nokogiri.rb +17 -156
  10. data/lib/shale/adapter/ox/document.rb +90 -0
  11. data/lib/shale/adapter/ox/node.rb +97 -0
  12. data/lib/shale/adapter/ox.rb +14 -138
  13. data/lib/shale/adapter/rexml/document.rb +98 -0
  14. data/lib/shale/adapter/rexml/node.rb +99 -0
  15. data/lib/shale/adapter/rexml.rb +14 -154
  16. data/lib/shale/adapter/toml_rb.rb +34 -0
  17. data/lib/shale/error.rb +57 -2
  18. data/lib/shale/mapper.rb +61 -9
  19. data/lib/shale/mapping/descriptor/dict.rb +12 -1
  20. data/lib/shale/mapping/descriptor/xml.rb +12 -2
  21. data/lib/shale/mapping/dict.rb +26 -2
  22. data/lib/shale/mapping/xml.rb +52 -6
  23. data/lib/shale/schema/{json_compiler → compiler}/boolean.rb +1 -1
  24. data/lib/shale/schema/{json_compiler/object.rb → compiler/complex.rb} +11 -8
  25. data/lib/shale/schema/{json_compiler → compiler}/date.rb +1 -1
  26. data/lib/shale/schema/{json_compiler → compiler}/float.rb +1 -1
  27. data/lib/shale/schema/{json_compiler → compiler}/integer.rb +1 -1
  28. data/lib/shale/schema/{json_compiler → compiler}/property.rb +6 -6
  29. data/lib/shale/schema/{json_compiler → compiler}/string.rb +1 -1
  30. data/lib/shale/schema/{json_compiler → compiler}/time.rb +1 -1
  31. data/lib/shale/schema/compiler/value.rb +21 -0
  32. data/lib/shale/schema/compiler/xml_complex.rb +50 -0
  33. data/lib/shale/schema/compiler/xml_property.rb +73 -0
  34. data/lib/shale/schema/json_compiler.rb +32 -34
  35. data/lib/shale/schema/json_generator.rb +4 -6
  36. data/lib/shale/schema/xml_compiler.rb +919 -0
  37. data/lib/shale/schema/xml_generator/import.rb +2 -2
  38. data/lib/shale/schema/xml_generator.rb +11 -13
  39. data/lib/shale/schema.rb +16 -0
  40. data/lib/shale/type/complex.rb +763 -0
  41. data/lib/shale/type/value.rb +38 -9
  42. data/lib/shale/utils.rb +42 -7
  43. data/lib/shale/version.rb +1 -1
  44. data/lib/shale.rb +22 -19
  45. data/shale.gemspec +3 -3
  46. metadata +26 -17
  47. data/lib/shale/schema/json_compiler/utils.rb +0 -52
  48. data/lib/shale/schema/json_compiler/value.rb +0 -13
  49. data/lib/shale/type/composite.rb +0 -331
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26877841702bfce542b42206f519ffd1b03e2f0d3db1e70a42e37836ff0f3bff
4
- data.tar.gz: 23fddba7256bc38b5d64309cf56833f47fd636ed0e9f6ec61f2a076764dad2bb
3
+ metadata.gz: 5f3c860cf358f78476f8f22a86006694c3d9fd6cb554c945d7364bbf8bb70815
4
+ data.tar.gz: 5eba158f49fdcfb3012f2e4dcf804fdc4c8fe1e0c37e8e3dbb37972aa469c8bb
5
5
  SHA512:
6
- metadata.gz: e98bd0bbb20fbbaf4a8bdbfb5e2f83ba5d7bae78ad66a8597a1fe28c0c13d9799bc3ade785105ea7261147f55590ff530e85c31da459fe8c9171ee5b68f02977
7
- data.tar.gz: 54be0efe36f4d330c551f1bd8994a36b360ecec3696aafe12382408a0656e50a181b91ff54ec9f39758908d15fcf51eafc4810540cd1aeb00bda7b552382bc80
6
+ metadata.gz: b002a6837137fa024788e50b2b408ebd7d80f1fd73c7bf1182a2d0b0ab784a07cbda08147af703c4ded76efcb086958049c3d82573e5f885d1b1745f9312eb0c
7
+ data.tar.gz: e66ba2657a540ad6e9cebd98cf4a61a9ae4b31c0fc6ba1e8085493cc727eda48e2cb87515d399a2524cac8bb4d92932f57dd548afa2ba1d8fb5b97bb5c8ef1a7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,37 @@
1
+ ## [0.7.0] - [2022-08-09]
2
+
3
+ ### Added
4
+ - `only: []` and `except: []` options that allow to controll what attributes are rendered/parsed
5
+ - `render_nil: true` option that allows to render nil values
6
+ - Allow to pass a context object to extractor/generator methods
7
+
8
+ ### Changed
9
+ - Pass whole document to methods for JSON/YAML/TOML so its behavior is consistent with XML mapping
10
+ - Convert splat arguments to keyword arguments
11
+ - RSpec: enable random spec execution and warnings
12
+
13
+ ## [0.6.0] - 2022-07-05
14
+
15
+ ### Added
16
+ - Support for TOML
17
+ - Support for CDATA nodes in XML documents
18
+ - Support for using custom models
19
+
20
+ ### Fixed
21
+ - Allow to map XML content using methods
22
+ - Prevent adding default mapping after mapping block was declared
23
+
24
+ ## [0.5.0] - 2022-06-28
25
+
26
+ ### Added
27
+ - Allow to generate Shale model from XML Schema
28
+
29
+ ### Changed
30
+ - Shale doesn't defaults to REXML anymore - XML adapter needs to be set explicitly
31
+ - Rename "JSONSchemaError" to "SchemaError"
32
+ - Rename "Composite" type to "Complex"
33
+ - Drop support for Ruby 2.6
34
+
1
35
  ## [0.4.0] - 2022-05-30
2
36
 
3
37
  ### Added
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2021 TODO: Write your name
3
+ Copyright (c) 2021 Kamil Giszczak
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,21 +1,23 @@
1
1
  # Shale
2
2
 
3
- Shale is a Ruby object mapper and serializer for JSON, YAML and XML.
4
- It allows you to parse JSON, YAML and XML data and convert it into Ruby data structures,
5
- as well as serialize data structures into JSON, YAML or XML.
3
+ Shale is a Ruby object mapper and serializer for JSON, YAML, TOML and XML.
4
+ It allows you to parse JSON, YAML, TOML and XML data and convert it into Ruby data structures,
5
+ as well as serialize data structures into JSON, YAML, TOML or XML.
6
+
7
+ Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
6
8
 
7
9
  ## Features
8
10
 
9
- * Convert JSON, YAML and XML to Ruby data model
10
- * Convert Ruby data model to JSON, YAML and XML
11
+ * Convert JSON, YAML, TOML and XML to Ruby data model
12
+ * Convert Ruby data model to JSON, YAML, TOML and XML
11
13
  * Generate JSON and XML Schema from Ruby models
12
- * Compile JSON Schema into Ruby models
13
- * Out of the box support for JSON, YAML, Nokogiri, REXML and Ox parsers
14
+ * Compile JSON and XML Schema into Ruby models
15
+ * Out of the box support for JSON, YAML, Tomlib, toml-rb, Nokogiri, REXML and Ox parsers
14
16
  * Support for custom adapters
15
17
 
16
18
  ## Installation
17
19
 
18
- Shale supports Ruby (MRI) 2.6+
20
+ Shale supports Ruby (MRI) 2.7+
19
21
 
20
22
  Add this line to your application's Gemfile:
21
23
 
@@ -43,28 +45,32 @@ $ gem install shale
43
45
  * [Converting object to JSON](#converting-object-to-json)
44
46
  * [Converting YAML to object](#converting-yaml-to-object)
45
47
  * [Converting object to YAML](#converting-object-to-yaml)
48
+ * [Converting TOML to object](#converting-toml-to-object)
49
+ * [Converting object to TOML](#converting-object-to-toml)
46
50
  * [Converting Hash to object](#converting-hash-to-object)
47
51
  * [Converting object to Hash](#converting-object-to-hash)
48
52
  * [Converting XML to object](#converting-xml-to-object)
49
53
  * [Converting object to XML](#converting-object-to-xml)
50
54
  * [Mapping JSON keys to object attributes](#mapping-json-keys-to-object-attributes)
51
55
  * [Mapping YAML keys to object attributes](#mapping-yaml-keys-to-object-attributes)
56
+ * [Mapping TOML keys to object attributes](#mapping-toml-keys-to-object-attributes)
52
57
  * [Mapping Hash keys to object attributes](#mapping-hash-keys-to-object-attributes)
53
58
  * [Mapping XML elements and attributes to object attributes](#mapping-xml-elements-and-attributes-to-object-attributes)
54
59
  * [Using XML namespaces](#using-xml-namespaces)
60
+ * [Rendering nil values](#rendering-nil-values)
55
61
  * [Using methods to extract and generate data](#using-methods-to-extract-and-generate-data)
56
- * [Pretty printing and XML declaration](#pretty-printing-and-xml-declaration)
62
+ * [Additional options](#additional-options)
63
+ * [Using custom models](#using-custom-models)
57
64
  * [Supported types](#supported-types)
58
65
  * [Writing your own type](#writing-your-own-type)
59
66
  * [Adapters](#adapters)
60
67
  * [Generating JSON Schema](#generating-json-schema)
61
68
  * [Compiling JSON Schema into Shale model](#compiling-json-schema-into-shale-model)
62
69
  * [Generating XML Schema](#generating-xml-schema)
70
+ * [Compiling XML Schema into Shale model](#compiling-xml-schema-into-shale-model)
63
71
 
64
72
  ## Usage
65
73
 
66
- Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
67
-
68
74
  ### Simple use case
69
75
 
70
76
  ```ruby
@@ -194,6 +200,65 @@ person.to_yaml
194
200
  # zip: E1 6AN
195
201
  ```
196
202
 
203
+ ### Converting TOML to object
204
+
205
+ To use TOML with Shale you have to set adapter you want to use.
206
+ Out of the box Shale suports [Tomlib](https://github.com/kgiszczak/tomlib).
207
+ It also comes with adapter for [toml-rb](https://github.com/emancu/toml-rb) if you prefer that.
208
+ For details see [Adapters](#adapters) section.
209
+
210
+ To set it, first make sure Tomlib gem is installed:
211
+
212
+ ```
213
+ $ gem install tomlib
214
+ ```
215
+
216
+ then setup adapter:
217
+
218
+ ```ruby
219
+ require 'tomlib'
220
+ Shale.toml_adapter = Tomlib
221
+
222
+ # Alternatively if you'd like to use toml-rb, use:
223
+ require 'shale/adapter/toml_rb'
224
+ Shale.toml_adapter = Shale::Adapter::TomlRB
225
+ ```
226
+
227
+ Now you can use TOML with Shale:
228
+
229
+ ```ruby
230
+ person = Person.from_toml(<<~DATA)
231
+ first_name = "John"
232
+ last_name = "Doe"
233
+ age = 50
234
+ married = false
235
+ hobbies = ["Singing", "Dancing"]
236
+ [address]
237
+ city = "London"
238
+ street = "Oxford Street"
239
+ zip = "E1 6AN"
240
+ DATA
241
+ ```
242
+
243
+ ### Converting object to TOML
244
+
245
+ ```ruby
246
+ person.to_toml
247
+
248
+ # =>
249
+ #
250
+ # first_name = "John"
251
+ # last_name = "Doe"
252
+ # age = 50
253
+ # married = false
254
+ # hobbies = [ "Singing", "Dancing" ]
255
+ #
256
+ # [address]
257
+ # city = "London"
258
+ # street = "Oxford Street"
259
+ # zip = "E1 6AN"
260
+ ```
261
+
197
262
  ### Converting Hash to object
198
263
 
199
264
  ```ruby
@@ -230,6 +295,17 @@ person.to_hash
230
295
 
231
296
  ### Converting XML to object
232
297
 
298
+ To use XML with Shale you have to set adapter you want to use.
299
+ Shale comes with adapters for REXML, Nokogiri and OX parsers.
300
+ For details see [Adapters](#adapters) section.
301
+
302
+ ```ruby
303
+ require 'shale/adapter/rexml'
304
+ Shale.xml_adapter = Shale::Adapter::REXML
305
+ ```
306
+
307
+ Now you can use XML with Shale:
308
+
233
309
  ```ruby
234
310
  person = Person.from_xml(<<~DATA)
235
311
  <person>
@@ -274,6 +350,8 @@ person.to_xml
274
350
 
275
351
  By default keys are named the same as attributes. To use custom keys use:
276
352
 
353
+ :warning: **Declaring custom mapping removes default mapping for given format!**
354
+
277
355
  ```ruby
278
356
  class Person < Shale::Mapper
279
357
  attribute :first_name, Shale::Type::String
@@ -300,6 +378,20 @@ class Person < Shale::Mapper
300
378
  end
301
379
  ```
302
380
 
381
+ ### Mapping TOML keys to object attributes
382
+
383
+ ```ruby
384
+ class Person < Shale::Mapper
385
+ attribute :first_name, Shale::Type::String
386
+ attribute :last_name, Shale::Type::String
387
+
388
+ toml do
389
+ map 'firstName', to: :first_name
390
+ map 'lastName', to: :last_name
391
+ end
392
+ end
393
+ ```
394
+
303
395
  ### Mapping Hash keys to object attributes
304
396
 
305
397
  ```ruby
@@ -370,6 +462,37 @@ DATA
370
462
  - `map_attribute` - map element's attribute to attribute
371
463
  - `map_content` - map first text node to attribute
372
464
 
465
+ You can use `cdata: true` option on `map_element` and `map_content` to handle CDATA nodes:
466
+
467
+ ```ruby
468
+ class Address < Shale::Mapper
469
+ attribute :content, Shale::Type::String
470
+
471
+ xml do
472
+ map_content to: :content, cdata: true
473
+ end
474
+ end
475
+
476
+ class Person < Shale::Mapper
477
+ attribute :first_name, Shale::Type::String
478
+ attribute :address, Address
479
+
480
+ xml do
481
+ root 'Person'
482
+
483
+ map_element 'FirstName', to: :first_name, cdata: true
484
+ map_element 'Address', to: :address
485
+ end
486
+ end
487
+
488
+ person = Person.from_xml(<<~DATA)
489
+ <Person>
490
+ <FirstName><![CDATA[John]]></FirstName>
491
+ <Address><![CDATA[Oxford Street]]></Address>
492
+ </person>
493
+ DATA
494
+ ```
495
+
373
496
  ### Using XML namespaces
374
497
 
375
498
  To map namespaced elements and attributes use `namespace` and `prefix` properties on
@@ -436,6 +559,52 @@ person = Person.from_xml(<<~DATA)
436
559
  DATA
437
560
  ```
438
561
 
562
+ ### Rendering nil values
563
+
564
+ By default elements with `nil` value are not rendered. You can change this behavior
565
+ by using `render_nil: true` on a mapping.
566
+
567
+ ```ruby
568
+ class Person < Shale::Mapper
569
+ attribute :first_name, Shale::Type::String
570
+ attribute :last_name, Shale::Type::String
571
+ attribute :age, Shale::Type::Integer
572
+
573
+ json do
574
+ map 'first_name', to: :first_name, render_nil: true
575
+ map 'last_name', to: :last_name, render_nil: false
576
+ map 'age', to: :age, render_nil: true
577
+ end
578
+
579
+ xml do
580
+ root 'person'
581
+
582
+ map_element 'first_name', to: :first_name, render_nil: true
583
+ map_element 'last_name', to: :last_name, render_nil: false
584
+ map_attribute 'age', to: :age, render_nil: true
585
+ end
586
+ end
587
+
588
+ person = Person.new(first_name: nil, last_name: nil, age: nil)
589
+
590
+ puts person.to_json(pretty: true)
591
+
592
+ # =>
593
+ #
594
+ # {
595
+ # "first_name": null,
596
+ # "age": "null"
597
+ # }
598
+
599
+ puts person.to_xml(pretty: true)
600
+
601
+ # =>
602
+ #
603
+ # <person age="">
604
+ # <first_name/>
605
+ # </person>
606
+ ```
607
+
439
608
  ### Using methods to extract and generate data
440
609
 
441
610
  If you need full controll over extracting and generating data from/to document,
@@ -459,42 +628,42 @@ class Person < Shale::Mapper
459
628
  map_element 'Address', using: { from: :address_from_xml, to: :address_to_xml }
460
629
  end
461
630
 
462
- def hobbies_from_json(value)
463
- self.hobbies = value.split(',').map(&:strip)
631
+ def hobbies_from_json(model, value)
632
+ model.hobbies = value.split(',').map(&:strip)
464
633
  end
465
634
 
466
- def hobbies_to_json
467
- hobbies.join(', ')
635
+ def hobbies_to_json(model, doc)
636
+ doc['hobbies'] = model.hobbies.join(', ')
468
637
  end
469
638
 
470
- def address_from_json(value)
471
- self.street = value['street']
472
- self.city = value['city']
639
+ def address_from_json(model, value)
640
+ model.street = value['street']
641
+ model.city = value['city']
473
642
  end
474
643
 
475
- def address_to_json
476
- { 'street' => street, 'city' => city }
644
+ def address_to_json(model, doc)
645
+ doc['address'] = { 'street' => model.street, 'city' => model.city }
477
646
  end
478
647
 
479
- def hobbies_from_xml(value)
480
- self.hobbies = value.split(',').map(&:strip)
648
+ def hobbies_from_xml(model, value)
649
+ model.hobbies = value.split(',').map(&:strip)
481
650
  end
482
651
 
483
- def hobbies_to_xml(element, doc)
484
- doc.add_attribute(element, 'hobbies', hobbies.join(', '))
652
+ def hobbies_to_xml(model, element, doc)
653
+ doc.add_attribute(element, 'hobbies', model.hobbies.join(', '))
485
654
  end
486
655
 
487
- def address_from_xml(node)
488
- self.street = node.children.find { |e| e.name == 'Street' }.text
489
- self.city = node.children.find { |e| e.name == 'City' }.text
656
+ def address_from_xml(model, node)
657
+ model.street = node.children.find { |e| e.name == 'Street' }.text
658
+ model.city = node.children.find { |e| e.name == 'City' }.text
490
659
  end
491
660
 
492
- def address_to_xml(parent, doc)
661
+ def address_to_xml(model, parent, doc)
493
662
  street_element = doc.create_element('Street')
494
- doc.add_text(street_element, street.to_s)
663
+ doc.add_text(street_element, model.street.to_s)
495
664
 
496
665
  city_element = doc.create_element('City')
497
- doc.add_text(city_element, city.to_s)
666
+ doc.add_text(city_element, model.city.to_s)
498
667
 
499
668
  address_element = doc.create_element('Address')
500
669
  doc.add_element(address_element, street_element)
@@ -519,7 +688,7 @@ person = Person.from_xml(<<~DATA)
519
688
  <Street>Oxford Street</Street>
520
689
  <City>London</City>
521
690
  </Address>
522
- </person>
691
+ </Person>
523
692
  DATA
524
693
 
525
694
  # =>
@@ -530,12 +699,100 @@ DATA
530
699
  # @city="London">
531
700
  ```
532
701
 
533
- ### Pretty printing and XML declaration
702
+ You can also pass a `context` object that will be available in extractor/generator methods:
534
703
 
535
- If you need formatted output you can pass `:pretty` parameter to `#to_json` and `#to_xml`
704
+ ```ruby
705
+ class Person < Shale::Mapper
706
+ attribute :password, Shale::Type::String
707
+
708
+ json do
709
+ map 'password', using: { from: :password_from_json, to: :password_to_json }
710
+ end
711
+
712
+ def password_from_json(model, value, context)
713
+ if context.admin?
714
+ model.password = value
715
+ else
716
+ model.password = '*****'
717
+ end
718
+ end
719
+
720
+ def password_to_json(model, doc, context)
721
+ if context.admin?
722
+ doc['password'] = model.password
723
+ else
724
+ doc['password'] = '*****'
725
+ end
726
+ end
727
+ end
728
+
729
+ Person.new(password: 'secret').to_json(context: current_user)
730
+ ```
731
+
732
+ ### Additional options
733
+
734
+ You can control which attributes to render and parse by
735
+ using `only: []` and `except: []` parameters.
536
736
 
537
737
  ```ruby
538
- person.to_json(:pretty)
738
+ # e.g. if you have this model graph:
739
+ person = Person.new(
740
+ first_name: 'John'
741
+ last_name: 'Doe',
742
+ address: Address.new(city: 'London', street: 'Oxford Street')
743
+ )
744
+
745
+ # if you want to render only `first_name` and `address.city` do:
746
+ person.to_json(only: [:first_name, address: [:city]], pretty: true)
747
+
748
+ # =>
749
+ #
750
+ # {
751
+ # "first_name": "John",
752
+ # "address": {
753
+ # "city": "London"
754
+ # }
755
+ # }
756
+
757
+ # and if you don't need an address you can do:
758
+ person.to_json(except: [:address], pretty: true)
759
+
760
+ # =>
761
+ #
762
+ # {
763
+ # "first_name": "John",
764
+ # "last_name": "Doe"
765
+ # }
766
+ ```
767
+
768
+ It works the same for parsing:
769
+
770
+ ```ruby
771
+ # e.g. if you want to parse only `address.city` do:
772
+ Person.from_json(doc, only: [address: [:city]])
773
+
774
+ # =>
775
+ #
776
+ # #<Person:0x0000000113d7a488
777
+ # @first_name=nil,
778
+ # @last_name=nil,
779
+ # @address=#<Address:0x0000000113d7a140 @street=nil, @city="London">>
780
+
781
+ # and if you don't need an `address`:
782
+ Person.from_json(doc, except: [:address])
783
+
784
+ # =>
785
+ #
786
+ # #<Person:0x0000000113d7a488
787
+ # @first_name="John",
788
+ # @last_name="Doe",
789
+ # @address=nil>
790
+ ```
791
+
792
+ If you need formatted output you can pass `pretty: true` parameter to `#to_json` and `#to_xml`
793
+
794
+ ```ruby
795
+ person.to_json(pretty: true)
539
796
 
540
797
  # =>
541
798
  #
@@ -547,10 +804,10 @@ person.to_json(:pretty)
547
804
  # }
548
805
  ```
549
806
 
550
- You can also add an XML declaration by passing `:declaration` to `#to_xml`
807
+ You can also add an XML declaration by passing `declaration: true` to `#to_xml`
551
808
 
552
809
  ```ruby
553
- person.to_xml(:pretty, :declaration)
810
+ person.to_xml(pretty: true, declaration: true)
554
811
 
555
812
  # =>
556
813
  #
@@ -560,6 +817,67 @@ person.to_xml(:pretty, :declaration)
560
817
  # </Person>
561
818
  ```
562
819
 
820
+ ### Using custom models
821
+
822
+ By default Shale combines mapper and model into one class. If you want to use your own classes
823
+ as models you can do it by using `model` directive on the mapper:
824
+
825
+ ```ruby
826
+ class Address
827
+ attr_accessor :street, :city
828
+ end
829
+
830
+ class Person
831
+ attr_accessor :first_name, :last_name, :address
832
+ end
833
+
834
+ class AddressMapper < Shale::Mapper
835
+ model Address
836
+
837
+ attribute :street, Shale::Type::String
838
+ attribute :city, Shale::Type::String
839
+ end
840
+
841
+ class PersonMapper < Shale::Mapper
842
+ model Person
843
+
844
+ attribute :first_name, Shale::Type::String
845
+ attribute :last_name, Shale::Type::String
846
+ attribute :address, AddressMapper
847
+ end
848
+
849
+ person = PersonMapper.from_json(<<~DATA)
850
+ {
851
+ "first_name": "John",
852
+ "last_name": "Doe",
853
+ "address": {
854
+ "street": "Oxford Street",
855
+ "city": "London"
856
+ }
857
+ }
858
+ DATA
859
+
860
+ # =>
861
+ #
862
+ # #<Person:0x0000000113d7a488
863
+ # @first_name="John",
864
+ # @last_name="Doe",
865
+ # @address=#<Address:0x0000000113d7a140 @street="Oxford Street", @city="London">>
866
+
867
+ PersonMapper.to_json(person, pretty: true)
868
+
869
+ # =>
870
+ #
871
+ # {
872
+ # "first_name": "John",
873
+ # "last_name": "Doe",
874
+ # "address": {
875
+ # "street": "Oxford Street",
876
+ # "city": "London"
877
+ # }
878
+ # }
879
+ ```
880
+
563
881
  ### Supported types
564
882
 
565
883
  Shale supports these types out of the box:
@@ -588,10 +906,9 @@ end
588
906
  ### Adapters
589
907
 
590
908
  Shale uses adapters for parsing and generating documents.
591
- By default Ruby's standard JSON parser is used for handling JSON documents, YAML for YAML and
592
- REXML for XML.
909
+ By default Ruby's standard JSON, YAML parsers are used for handling JSON and YAML documents.
593
910
 
594
- You can change it by providing your own adapter. For JSON and YAML, adapter must implement
911
+ You can change it by providing your own adapter. For JSON, YAML and TOML, adapter must implement
595
912
  `.load` and `.dump` class methods.
596
913
 
597
914
  ```ruby
@@ -602,6 +919,22 @@ Shale.json_adapter = MultiJson
602
919
  Shale.yaml_adapter = MyYamlAdapter
603
920
  ```
604
921
 
922
+ To handle TOML documents you have to set TOML adapter. Out of the box `Tomlib` is supported.
923
+ Shale also provides adapter for `toml-rb` parser:
924
+
925
+ ```ruby
926
+ require 'shale'
927
+
928
+ # if you want to use Tomlib
929
+ require 'tomlib'
930
+ Shale.toml_adapter = Tomlib
931
+
932
+ # if you want to use toml-rb
933
+ require 'shale/adapter/toml_rb'
934
+ Shale.toml_adapter = Shale::Adapter::TomlRB
935
+ ```
936
+
937
+ To handle XML documents you have to explicitly set XML adapter.
605
938
  Shale provides adapters for most popular Ruby XML parsers:
606
939
 
607
940
  :warning: **Ox doesn't support XML namespaces**
@@ -609,7 +942,7 @@ Shale provides adapters for most popular Ruby XML parsers:
609
942
  ```ruby
610
943
  require 'shale'
611
944
 
612
- # REXML is used by default:
945
+ # if you want to use REXML:
613
946
 
614
947
  require 'shale/adapter/rexml'
615
948
  Shale.xml_adapter = Shale::Adapter::REXML
@@ -842,6 +1175,82 @@ end
842
1175
  Shale::Schema::XMLGenerator.register_xml_type(MyEmailType, 'myEmailXMLType')
843
1176
  ```
844
1177
 
1178
+ ### Compiling XML Schema into Shale model
1179
+
1180
+ To generate Shale data model from XML Schema use:
1181
+
1182
+ ```ruby
1183
+ require 'shale/schema'
1184
+
1185
+ schema = <<~SCHEMA
1186
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1187
+ <xs:element name="Person" type="Person" />
1188
+
1189
+ <xs:complexType name="Person">
1190
+ <xs:sequence>
1191
+ <xs:element name="FirstName" type="xs:string" />
1192
+ <xs:element name="LastName" type="xs:string" />
1193
+ <xs:element name="Address" type="Address" />
1194
+ </xs:sequence>
1195
+ </xs:complexType>
1196
+
1197
+ <xs:complexType name="Address">
1198
+ <xs:sequence>
1199
+ <xs:element name="Street" type="xs:string" />
1200
+ <xs:element name="City" type="xs:string" />
1201
+ </xs:sequence>
1202
+ </xs:complexType>
1203
+ </xs:schema>
1204
+ SCHEMA
1205
+
1206
+ Shale::Schema.from_xml([schema])
1207
+
1208
+ # =>
1209
+ #
1210
+ # {
1211
+ # "address" => "
1212
+ # require 'shale'
1213
+ #
1214
+ # class Address < Shale::Mapper
1215
+ # attribute :street, Shale::Type::String
1216
+ # attribute :city, Shale::Type::String
1217
+ #
1218
+ # xml do
1219
+ # root 'Address'
1220
+ #
1221
+ # map_element 'Street', to: :street
1222
+ # map_element 'City', to: :city
1223
+ # end
1224
+ # end
1225
+ # ",
1226
+ # "person" => "
1227
+ # require 'shale'
1228
+ #
1229
+ # require_relative 'address'
1230
+ #
1231
+ # class Person < Shale::Mapper
1232
+ # attribute :first_name, Shale::Type::String
1233
+ # attribute :last_name, Shale::Type::String
1234
+ # attribute :address, Address
1235
+ #
1236
+ # xml do
1237
+ # root 'Person'
1238
+ #
1239
+ # map_element 'FirstName', to: :first_name
1240
+ # map_element 'LastName', to: :last_name
1241
+ # map_element 'Address', to: :address
1242
+ # end
1243
+ # end
1244
+ # "
1245
+ # }
1246
+ ```
1247
+
1248
+ You can also use a command line tool to do it:
1249
+
1250
+ ```
1251
+ $ shaleb -c -f xml -i schema.xml
1252
+ ```
1253
+
845
1254
  ## Contributing
846
1255
 
847
1256
  Bug reports and pull requests are welcome on GitHub at https://github.com/kgiszczak/shale.