shale 0.5.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/LICENSE.txt +1 -1
- data/README.md +357 -34
- data/lib/shale/adapter/json.rb +3 -3
- data/lib/shale/adapter/nokogiri/document.rb +10 -0
- data/lib/shale/adapter/nokogiri/node.rb +1 -1
- data/lib/shale/adapter/nokogiri.rb +6 -5
- data/lib/shale/adapter/ox/document.rb +10 -0
- data/lib/shale/adapter/ox/node.rb +10 -1
- data/lib/shale/adapter/ox.rb +5 -4
- data/lib/shale/adapter/rexml/document.rb +11 -1
- data/lib/shale/adapter/rexml.rb +5 -4
- data/lib/shale/adapter/toml_rb.rb +34 -0
- data/lib/shale/error.rb +27 -5
- data/lib/shale/mapper.rb +59 -7
- data/lib/shale/mapping/descriptor/dict.rb +12 -1
- data/lib/shale/mapping/descriptor/xml.rb +12 -2
- data/lib/shale/mapping/dict.rb +26 -2
- data/lib/shale/mapping/xml.rb +52 -6
- data/lib/shale/schema/json_generator.rb +1 -3
- data/lib/shale/schema/xml_compiler.rb +1 -1
- data/lib/shale/schema/xml_generator/import.rb +2 -2
- data/lib/shale/schema/xml_generator.rb +4 -6
- data/lib/shale/type/complex.rb +498 -84
- data/lib/shale/type/date.rb +2 -2
- data/lib/shale/type/time.rb +2 -2
- data/lib/shale/type/value.rb +38 -9
- data/lib/shale/version.rb +1 -1
- data/lib/shale.rb +14 -0
- data/shale.gemspec +2 -2
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 468fc4f3fa1ccd796958748eea653aaa9aec0dbf223c2e5486ddba00350d79fc
|
4
|
+
data.tar.gz: 4b78746e97bc6fd080b8a6566be2534c3a1c05017acacabd3935f7e2ea81b097
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 776a30c22e95f8c85e150b64ec7df09e89aeb4e86fb2f6f0fcec67a438b13b73926c3d5419b8194f457a2f09fc8eb64bf491e8b1ea5c5d6957e1e1b5cfab9549
|
7
|
+
data.tar.gz: 0d236bde4f3b5aaf9b3ea5a5b8380b3861b24f5d0b310430329c68d010c368890dedbd7f6fad8649b71cd443362c8c6272ebb9c7a234998fa546ecb99e728f44
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
## [0.7.1] - [2022-08-12]
|
2
|
+
|
3
|
+
### Fixed
|
4
|
+
- Fix broken handling of Date and Time types
|
5
|
+
|
6
|
+
## [0.7.0] - [2022-08-09]
|
7
|
+
|
8
|
+
### Added
|
9
|
+
- `only: []` and `except: []` options that allow to controll what attributes are rendered/parsed
|
10
|
+
- `render_nil: true` option that allows to render nil values
|
11
|
+
- Allow to pass a context object to extractor/generator methods
|
12
|
+
|
13
|
+
### Changed
|
14
|
+
- Pass whole document to methods for JSON/YAML/TOML so its behavior is consistent with XML mapping
|
15
|
+
- Convert splat arguments to keyword arguments
|
16
|
+
- RSpec: enable random spec execution and warnings
|
17
|
+
|
18
|
+
## [0.6.0] - 2022-07-05
|
19
|
+
|
20
|
+
### Added
|
21
|
+
- Support for TOML
|
22
|
+
- Support for CDATA nodes in XML documents
|
23
|
+
- Support for using custom models
|
24
|
+
|
25
|
+
### Fixed
|
26
|
+
- Allow to map XML content using methods
|
27
|
+
- Prevent adding default mapping after mapping block was declared
|
28
|
+
|
1
29
|
## [0.5.0] - 2022-06-28
|
2
30
|
|
3
31
|
### Added
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,18 +1,18 @@
|
|
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
6
|
|
7
7
|
Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
|
8
8
|
|
9
9
|
## Features
|
10
10
|
|
11
|
-
* Convert JSON, YAML and XML to Ruby data model
|
12
|
-
* 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
|
13
13
|
* Generate JSON and XML Schema from Ruby models
|
14
14
|
* Compile JSON and XML Schema into Ruby models
|
15
|
-
* Out of the box support for JSON, YAML, Nokogiri, REXML and Ox parsers
|
15
|
+
* Out of the box support for JSON, YAML, Tomlib, toml-rb, Nokogiri, REXML and Ox parsers
|
16
16
|
* Support for custom adapters
|
17
17
|
|
18
18
|
## Installation
|
@@ -45,17 +45,22 @@ $ gem install shale
|
|
45
45
|
* [Converting object to JSON](#converting-object-to-json)
|
46
46
|
* [Converting YAML to object](#converting-yaml-to-object)
|
47
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)
|
48
50
|
* [Converting Hash to object](#converting-hash-to-object)
|
49
51
|
* [Converting object to Hash](#converting-object-to-hash)
|
50
52
|
* [Converting XML to object](#converting-xml-to-object)
|
51
53
|
* [Converting object to XML](#converting-object-to-xml)
|
52
54
|
* [Mapping JSON keys to object attributes](#mapping-json-keys-to-object-attributes)
|
53
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)
|
54
57
|
* [Mapping Hash keys to object attributes](#mapping-hash-keys-to-object-attributes)
|
55
58
|
* [Mapping XML elements and attributes to object attributes](#mapping-xml-elements-and-attributes-to-object-attributes)
|
56
59
|
* [Using XML namespaces](#using-xml-namespaces)
|
60
|
+
* [Rendering nil values](#rendering-nil-values)
|
57
61
|
* [Using methods to extract and generate data](#using-methods-to-extract-and-generate-data)
|
58
|
-
* [
|
62
|
+
* [Additional options](#additional-options)
|
63
|
+
* [Using custom models](#using-custom-models)
|
59
64
|
* [Supported types](#supported-types)
|
60
65
|
* [Writing your own type](#writing-your-own-type)
|
61
66
|
* [Adapters](#adapters)
|
@@ -195,6 +200,65 @@ person.to_yaml
|
|
195
200
|
# zip: E1 6AN
|
196
201
|
```
|
197
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
|
+
|
198
262
|
### Converting Hash to object
|
199
263
|
|
200
264
|
```ruby
|
@@ -240,6 +304,8 @@ require 'shale/adapter/rexml'
|
|
240
304
|
Shale.xml_adapter = Shale::Adapter::REXML
|
241
305
|
```
|
242
306
|
|
307
|
+
Now you can use XML with Shale:
|
308
|
+
|
243
309
|
```ruby
|
244
310
|
person = Person.from_xml(<<~DATA)
|
245
311
|
<person>
|
@@ -284,6 +350,8 @@ person.to_xml
|
|
284
350
|
|
285
351
|
By default keys are named the same as attributes. To use custom keys use:
|
286
352
|
|
353
|
+
:warning: **Declaring custom mapping removes default mapping for given format!**
|
354
|
+
|
287
355
|
```ruby
|
288
356
|
class Person < Shale::Mapper
|
289
357
|
attribute :first_name, Shale::Type::String
|
@@ -310,6 +378,20 @@ class Person < Shale::Mapper
|
|
310
378
|
end
|
311
379
|
```
|
312
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
|
+
|
313
395
|
### Mapping Hash keys to object attributes
|
314
396
|
|
315
397
|
```ruby
|
@@ -380,6 +462,37 @@ DATA
|
|
380
462
|
- `map_attribute` - map element's attribute to attribute
|
381
463
|
- `map_content` - map first text node to attribute
|
382
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
|
+
|
383
496
|
### Using XML namespaces
|
384
497
|
|
385
498
|
To map namespaced elements and attributes use `namespace` and `prefix` properties on
|
@@ -446,6 +559,52 @@ person = Person.from_xml(<<~DATA)
|
|
446
559
|
DATA
|
447
560
|
```
|
448
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
|
+
|
449
608
|
### Using methods to extract and generate data
|
450
609
|
|
451
610
|
If you need full controll over extracting and generating data from/to document,
|
@@ -469,42 +628,42 @@ class Person < Shale::Mapper
|
|
469
628
|
map_element 'Address', using: { from: :address_from_xml, to: :address_to_xml }
|
470
629
|
end
|
471
630
|
|
472
|
-
def hobbies_from_json(value)
|
473
|
-
|
631
|
+
def hobbies_from_json(model, value)
|
632
|
+
model.hobbies = value.split(',').map(&:strip)
|
474
633
|
end
|
475
634
|
|
476
|
-
def hobbies_to_json
|
477
|
-
hobbies.join(', ')
|
635
|
+
def hobbies_to_json(model, doc)
|
636
|
+
doc['hobbies'] = model.hobbies.join(', ')
|
478
637
|
end
|
479
638
|
|
480
|
-
def address_from_json(value)
|
481
|
-
|
482
|
-
|
639
|
+
def address_from_json(model, value)
|
640
|
+
model.street = value['street']
|
641
|
+
model.city = value['city']
|
483
642
|
end
|
484
643
|
|
485
|
-
def address_to_json
|
486
|
-
{ 'street' => street, 'city' => city }
|
644
|
+
def address_to_json(model, doc)
|
645
|
+
doc['address'] = { 'street' => model.street, 'city' => model.city }
|
487
646
|
end
|
488
647
|
|
489
|
-
def hobbies_from_xml(value)
|
490
|
-
|
648
|
+
def hobbies_from_xml(model, value)
|
649
|
+
model.hobbies = value.split(',').map(&:strip)
|
491
650
|
end
|
492
651
|
|
493
|
-
def hobbies_to_xml(element, doc)
|
494
|
-
doc.add_attribute(element, 'hobbies', hobbies.join(', '))
|
652
|
+
def hobbies_to_xml(model, element, doc)
|
653
|
+
doc.add_attribute(element, 'hobbies', model.hobbies.join(', '))
|
495
654
|
end
|
496
655
|
|
497
|
-
def address_from_xml(node)
|
498
|
-
|
499
|
-
|
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
|
500
659
|
end
|
501
660
|
|
502
|
-
def address_to_xml(parent, doc)
|
661
|
+
def address_to_xml(model, parent, doc)
|
503
662
|
street_element = doc.create_element('Street')
|
504
|
-
doc.add_text(street_element, street.to_s)
|
663
|
+
doc.add_text(street_element, model.street.to_s)
|
505
664
|
|
506
665
|
city_element = doc.create_element('City')
|
507
|
-
doc.add_text(city_element, city.to_s)
|
666
|
+
doc.add_text(city_element, model.city.to_s)
|
508
667
|
|
509
668
|
address_element = doc.create_element('Address')
|
510
669
|
doc.add_element(address_element, street_element)
|
@@ -529,7 +688,7 @@ person = Person.from_xml(<<~DATA)
|
|
529
688
|
<Street>Oxford Street</Street>
|
530
689
|
<City>London</City>
|
531
690
|
</Address>
|
532
|
-
</
|
691
|
+
</Person>
|
533
692
|
DATA
|
534
693
|
|
535
694
|
# =>
|
@@ -540,12 +699,100 @@ DATA
|
|
540
699
|
# @city="London">
|
541
700
|
```
|
542
701
|
|
543
|
-
|
702
|
+
You can also pass a `context` object that will be available in extractor/generator methods:
|
703
|
+
|
704
|
+
```ruby
|
705
|
+
class Person < Shale::Mapper
|
706
|
+
attribute :password, Shale::Type::String
|
544
707
|
|
545
|
-
|
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.
|
546
736
|
|
547
737
|
```ruby
|
548
|
-
|
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)
|
549
796
|
|
550
797
|
# =>
|
551
798
|
#
|
@@ -557,10 +804,10 @@ person.to_json(:pretty)
|
|
557
804
|
# }
|
558
805
|
```
|
559
806
|
|
560
|
-
You can also add an XML declaration by passing
|
807
|
+
You can also add an XML declaration by passing `declaration: true` to `#to_xml`
|
561
808
|
|
562
809
|
```ruby
|
563
|
-
person.to_xml(:
|
810
|
+
person.to_xml(pretty: true, declaration: true)
|
564
811
|
|
565
812
|
# =>
|
566
813
|
#
|
@@ -570,6 +817,67 @@ person.to_xml(:pretty, :declaration)
|
|
570
817
|
# </Person>
|
571
818
|
```
|
572
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
|
+
|
573
881
|
### Supported types
|
574
882
|
|
575
883
|
Shale supports these types out of the box:
|
@@ -598,9 +906,9 @@ end
|
|
598
906
|
### Adapters
|
599
907
|
|
600
908
|
Shale uses adapters for parsing and generating documents.
|
601
|
-
By default Ruby's standard JSON
|
909
|
+
By default Ruby's standard JSON, YAML parsers are used for handling JSON and YAML documents.
|
602
910
|
|
603
|
-
You can change it by providing your own adapter. For JSON and
|
911
|
+
You can change it by providing your own adapter. For JSON, YAML and TOML, adapter must implement
|
604
912
|
`.load` and `.dump` class methods.
|
605
913
|
|
606
914
|
```ruby
|
@@ -611,6 +919,21 @@ Shale.json_adapter = MultiJson
|
|
611
919
|
Shale.yaml_adapter = MyYamlAdapter
|
612
920
|
```
|
613
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
|
+
|
614
937
|
To handle XML documents you have to explicitly set XML adapter.
|
615
938
|
Shale provides adapters for most popular Ruby XML parsers:
|
616
939
|
|
data/lib/shale/adapter/json.rb
CHANGED
@@ -22,13 +22,13 @@ module Shale
|
|
22
22
|
# Serialize Hash into JSON
|
23
23
|
#
|
24
24
|
# @param [Hash] obj Hash object
|
25
|
-
# @param [
|
25
|
+
# @param [true, false] pretty
|
26
26
|
#
|
27
27
|
# @return [String]
|
28
28
|
#
|
29
29
|
# @api private
|
30
|
-
def self.dump(obj,
|
31
|
-
if
|
30
|
+
def self.dump(obj, pretty: false)
|
31
|
+
if pretty
|
32
32
|
::JSON.pretty_generate(obj)
|
33
33
|
else
|
34
34
|
::JSON.generate(obj)
|
@@ -41,6 +41,16 @@ module Shale
|
|
41
41
|
::Nokogiri::XML::Element.new(name, @doc)
|
42
42
|
end
|
43
43
|
|
44
|
+
# Create CDATA node and add it to parent
|
45
|
+
#
|
46
|
+
# @param [String] text
|
47
|
+
# @param [::Nokogiri::XML::Element] parent
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
def create_cdata(text, parent)
|
51
|
+
parent.add_child(::Nokogiri::XML::CDATA.new(@doc, text))
|
52
|
+
end
|
53
|
+
|
44
54
|
# Add XML namespace to document
|
45
55
|
#
|
46
56
|
# @param [String] prefix
|
@@ -36,25 +36,26 @@ module Shale
|
|
36
36
|
# Serialize Nokogiri document into XML
|
37
37
|
#
|
38
38
|
# @param [::Nokogiri::XML::Document] doc Nokogiri document
|
39
|
-
# @param [
|
39
|
+
# @param [true, false] pretty
|
40
|
+
# @param [true, false] declaration
|
40
41
|
#
|
41
42
|
# @return [String]
|
42
43
|
#
|
43
44
|
# @api private
|
44
|
-
def self.dump(doc,
|
45
|
+
def self.dump(doc, pretty: false, declaration: false)
|
45
46
|
save_with = ::Nokogiri::XML::Node::SaveOptions::AS_XML
|
46
47
|
|
47
|
-
if
|
48
|
+
if pretty
|
48
49
|
save_with |= ::Nokogiri::XML::Node::SaveOptions::FORMAT
|
49
50
|
end
|
50
51
|
|
51
|
-
unless
|
52
|
+
unless declaration
|
52
53
|
save_with |= ::Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
|
53
54
|
end
|
54
55
|
|
55
56
|
result = doc.to_xml(save_with: save_with)
|
56
57
|
|
57
|
-
unless
|
58
|
+
unless pretty
|
58
59
|
result = result.sub(/\n/, '')
|
59
60
|
end
|
60
61
|
|
@@ -32,6 +32,16 @@ module Shale
|
|
32
32
|
::Ox::Element.new(name)
|
33
33
|
end
|
34
34
|
|
35
|
+
# Create CDATA node and add it to parent
|
36
|
+
#
|
37
|
+
# @param [String] text
|
38
|
+
# @param [::Ox::Element] parent
|
39
|
+
#
|
40
|
+
# @api private
|
41
|
+
def create_cdata(text, parent)
|
42
|
+
parent << ::Ox::CData.new(text)
|
43
|
+
end
|
44
|
+
|
35
45
|
# Add XML namespace to document
|
36
46
|
#
|
37
47
|
# Ox doesn't support XML namespaces so this method does nothing.
|