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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aaa3043e612d332fab30ff87ecab722e3812e7769eba982db27c38f74c205ce1
4
- data.tar.gz: bb5cdb963bce5756c48edb37d28bddc40a816901e87b4714ea512f07f8ec4c28
3
+ metadata.gz: 468fc4f3fa1ccd796958748eea653aaa9aec0dbf223c2e5486ddba00350d79fc
4
+ data.tar.gz: 4b78746e97bc6fd080b8a6566be2534c3a1c05017acacabd3935f7e2ea81b097
5
5
  SHA512:
6
- metadata.gz: ed1f1ffc88cabb5f9403ff957c02efc4565aee73b9a8a1fd2297ff13bfbb6bdf0e94664b865f8d2d812ddd66fe97d154e34982eea3318ba01c6d16b4a6922c70
7
- data.tar.gz: d41392de36bcace9efee12a869f49beffba235ae09e09f33c6973159b96320e3f657a6edd4d634e53da430f8127e74fa1025fa2f5c75dad2e7568abe6a0794ce
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
@@ -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,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
- * [Pretty printing and XML declaration](#pretty-printing-and-xml-declaration)
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
- self.hobbies = value.split(',').map(&:strip)
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
- self.street = value['street']
482
- self.city = value['city']
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
- self.hobbies = value.split(',').map(&:strip)
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
- self.street = node.children.find { |e| e.name == 'Street' }.text
499
- 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
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
- </person>
691
+ </Person>
533
692
  DATA
534
693
 
535
694
  # =>
@@ -540,12 +699,100 @@ DATA
540
699
  # @city="London">
541
700
  ```
542
701
 
543
- ### Pretty printing and XML declaration
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
- If you need formatted output you can pass `:pretty` parameter to `#to_json` and `#to_xml`
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
- 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)
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 `:declaration` to `#to_xml`
807
+ You can also add an XML declaration by passing `declaration: true` to `#to_xml`
561
808
 
562
809
  ```ruby
563
- person.to_xml(:pretty, :declaration)
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 and YAML parsers are used for handling JSON and YAML documents.
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 YAML, adapter must implement
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
 
@@ -22,13 +22,13 @@ module Shale
22
22
  # Serialize Hash into JSON
23
23
  #
24
24
  # @param [Hash] obj Hash object
25
- # @param [Array<Symbol>] options
25
+ # @param [true, false] pretty
26
26
  #
27
27
  # @return [String]
28
28
  #
29
29
  # @api private
30
- def self.dump(obj, *options)
31
- if options.include?(:pretty)
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
@@ -89,7 +89,7 @@ module Shale
89
89
  first = @node
90
90
  .children
91
91
  .to_a
92
- .filter(&:text?)
92
+ .filter { |e| e.text? || e.cdata? }
93
93
  .first
94
94
 
95
95
  first&.text
@@ -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 [Array<Symbol>] options
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, *options)
45
+ def self.dump(doc, pretty: false, declaration: false)
45
46
  save_with = ::Nokogiri::XML::Node::SaveOptions::AS_XML
46
47
 
47
- if options.include?(:pretty)
48
+ if pretty
48
49
  save_with |= ::Nokogiri::XML::Node::SaveOptions::FORMAT
49
50
  end
50
51
 
51
- unless options.include?(:declaration)
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 options.include?(:pretty)
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.
@@ -80,7 +80,16 @@ module Shale
80
80
  #
81
81
  # @api private
82
82
  def text
83
- @node.text
83
+ texts = @node.nodes.map do |e|
84
+ case e
85
+ when ::Ox::CData
86
+ e.value
87
+ when ::String
88
+ e
89
+ end
90
+ end
91
+
92
+ texts.compact.first
84
93
  end
85
94
  end
86
95
  end