shale 0.2.2 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +270 -5
- data/exe/shaleb +75 -0
- data/lib/shale/adapter/json.rb +7 -2
- data/lib/shale/adapter/nokogiri.rb +48 -12
- data/lib/shale/adapter/ox.rb +28 -4
- data/lib/shale/adapter/rexml.rb +56 -13
- data/lib/shale/attribute.rb +1 -1
- data/lib/shale/error.rb +6 -0
- data/lib/shale/mapper.rb +11 -11
- data/lib/shale/mapping/descriptor/dict.rb +57 -0
- data/lib/shale/mapping/descriptor/xml.rb +43 -0
- data/lib/shale/mapping/descriptor/xml_namespace.rb +37 -0
- data/lib/shale/mapping/{key_value.rb → dict.rb} +8 -6
- data/lib/shale/mapping/validator.rb +51 -0
- data/lib/shale/mapping/xml.rb +86 -15
- data/lib/shale/schema/json/base.rb +41 -0
- data/lib/shale/schema/json/boolean.rb +23 -0
- data/lib/shale/schema/json/collection.rb +39 -0
- data/lib/shale/schema/json/date.rb +23 -0
- data/lib/shale/schema/json/float.rb +23 -0
- data/lib/shale/schema/json/integer.rb +23 -0
- data/lib/shale/schema/json/object.rb +37 -0
- data/lib/shale/schema/json/ref.rb +28 -0
- data/lib/shale/schema/json/schema.rb +57 -0
- data/lib/shale/schema/json/string.rb +23 -0
- data/lib/shale/schema/json/time.rb +23 -0
- data/lib/shale/schema/json.rb +165 -0
- data/lib/shale/schema/xml/attribute.rb +41 -0
- data/lib/shale/schema/xml/complex_type.rb +67 -0
- data/lib/shale/schema/xml/element.rb +55 -0
- data/lib/shale/schema/xml/import.rb +46 -0
- data/lib/shale/schema/xml/ref_attribute.rb +37 -0
- data/lib/shale/schema/xml/ref_element.rb +39 -0
- data/lib/shale/schema/xml/schema.rb +121 -0
- data/lib/shale/schema/xml/typed_attribute.rb +46 -0
- data/lib/shale/schema/xml/typed_element.rb +46 -0
- data/lib/shale/schema/xml.rb +316 -0
- data/lib/shale/schema.rb +47 -0
- data/lib/shale/type/boolean.rb +2 -2
- data/lib/shale/type/composite.rb +66 -54
- data/lib/shale/type/date.rb +35 -2
- data/lib/shale/type/float.rb +2 -2
- data/lib/shale/type/integer.rb +2 -2
- data/lib/shale/type/string.rb +2 -2
- data/lib/shale/type/time.rb +35 -2
- data/lib/shale/type/{base.rb → value.rb} +18 -7
- data/lib/shale/utils.rb +18 -2
- data/lib/shale/version.rb +1 -1
- data/lib/shale.rb +10 -10
- data/shale.gemspec +6 -2
- metadata +41 -13
- data/lib/shale/mapping/base.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9396e5fcba6ff4e1f1566abe356270ef9ddbd8ce9171063f011feeb36d234e7
|
4
|
+
data.tar.gz: 4495b40dca3e01b6ba908dfc6b7831aea3f1e2865423de59ced69db33ba633b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 404737157189f2312b4e189d095899588495c8f80cf52f9bd5935358b2ec8bedbd98b0d7676c44641383109701d62377d3086b36342950d6fd657dbbba2d06b9
|
7
|
+
data.tar.gz: '04818bdcac0290dfd96fbb0ebb1364350a444a9fc0533992d2cdf3bc3443e8afb4f00021346c66610400ddce12b4848c8bbed5c0bfb3835d40438c2b5acc1a6b'
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## [0.3.0] - 2022-04-29
|
2
|
+
|
3
|
+
### Added
|
4
|
+
- Support for XML namespaces
|
5
|
+
- Add option to pretty print JSON and XML and to include XML declaration
|
6
|
+
- Add support for generating JSON and XML Schema
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
- Various fixes to documentation
|
10
|
+
- Rename `hash` -> `hsh` (`hash` is used internally by Ruby)
|
11
|
+
- Rename `Shale::Type::Base` -> `Shale::Type::Value`
|
12
|
+
- Use ISO 8601 format for date and time in JSON, YAML and XML
|
13
|
+
|
1
14
|
## [0.2.2] - 2022-03-06
|
2
15
|
|
3
16
|
### Fixed
|
data/README.md
CHANGED
@@ -24,8 +24,35 @@ Or install it yourself as:
|
|
24
24
|
$ gem install shale
|
25
25
|
```
|
26
26
|
|
27
|
+
## Contents
|
28
|
+
|
29
|
+
* [Simple use case](#user-content-simple-use-case)
|
30
|
+
* [Creating objects](#creating-objects)
|
31
|
+
* [Converting JSON to object](#converting-json-to-object)
|
32
|
+
* [Converting object to JSON](#converting-object-to-json)
|
33
|
+
* [Converting YAML to object](#converting-yaml-to-object)
|
34
|
+
* [Converting object to YAML](#converting-object-to-yaml)
|
35
|
+
* [Converting Hash to object](#converting-hash-to-object)
|
36
|
+
* [Converting object to Hash](#converting-object-to-hash)
|
37
|
+
* [Converting XML to object](#converting-xml-to-object)
|
38
|
+
* [Converting object to XML](#converting-object-to-xml)
|
39
|
+
* [Mapping JSON keys to object attributes](#mapping-json-keys-to-object-attributes)
|
40
|
+
* [Mapping YAML keys to object attributes](#mapping-yaml-keys-to-object-attributes)
|
41
|
+
* [Mapping Hash keys to object attributes](#mapping-hash-keys-to-object-attributes)
|
42
|
+
* [Mapping XML elements and attributes to object attributes](#mapping-xml-elements-and-attributes-to-object-attributes)
|
43
|
+
* [Using XML namespaces](#using-xml-namespaces)
|
44
|
+
* [Using methods to extract and generate data](#using-methods-to-extract-and-generate-data)
|
45
|
+
* [Pretty printing and XML declaration](#pretty-printing-and-xml-declaration)
|
46
|
+
* [Supported types](#supported-types)
|
47
|
+
* [Writing your own type](#writing-your-own-type)
|
48
|
+
* [Adapters](#adapters)
|
49
|
+
* [Generating JSON Schema](#generating-json-schema)
|
50
|
+
* [Generating XML Schema](#generating-xml-schema)
|
51
|
+
|
27
52
|
## Usage
|
28
53
|
|
54
|
+
Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
|
55
|
+
|
29
56
|
### Simple use case
|
30
57
|
|
31
58
|
```ruby
|
@@ -264,7 +291,7 @@ class Person < Shale::Mapper
|
|
264
291
|
attribute :first_name, Shale::Type::String
|
265
292
|
attribute :last_name, Shale::Type::String
|
266
293
|
|
267
|
-
|
294
|
+
hsh do
|
268
295
|
map 'firstName', to: :first_name
|
269
296
|
map 'lastName', to: :last_name
|
270
297
|
end
|
@@ -327,6 +354,74 @@ DATA
|
|
327
354
|
- `map_attribute` - map element's attribute to attribute
|
328
355
|
- `map_content` - map first text node to attribute
|
329
356
|
|
357
|
+
### Using XML namespaces
|
358
|
+
|
359
|
+
:warning: **Ox doesn't support XML namespaces**
|
360
|
+
|
361
|
+
To map namespaced elements and attributes use `namespace` and `prefix` properties on
|
362
|
+
`map_element` and `map_attribute`
|
363
|
+
|
364
|
+
```ruby
|
365
|
+
class Person < Shale::Mapper
|
366
|
+
attribute :first_name, Shale::Type::String
|
367
|
+
attribute :last_name, Shale::Type::String
|
368
|
+
attribute :age, Shale::Type::Integer
|
369
|
+
|
370
|
+
xml do
|
371
|
+
root 'person'
|
372
|
+
|
373
|
+
map_element 'first_name', to: :first_name, namespace: 'http://ns1.com', prefix: 'ns1'
|
374
|
+
map_element 'last_name', to: :last_name, namespace: 'http://ns2.com', prefix: 'ns2'
|
375
|
+
map_attribute 'age', to: :age, namespace: 'http://ns2.com', prefix: 'ns2'
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
person = Person.from_xml(<<~DATA)
|
380
|
+
<person xmlns:ns1="http://ns1.com" xmlns:ns2="http://ns2.com" ns2:age="50">
|
381
|
+
<ns1:first_name>John</ns1:first_name>
|
382
|
+
<ns2:last_name>Doe</ns2:last_name>
|
383
|
+
</person>
|
384
|
+
DATA
|
385
|
+
```
|
386
|
+
|
387
|
+
To define default namespace for all elements use `namespace` declaration
|
388
|
+
(this will define namespace only on elements, if you want to define namespace on an attribute
|
389
|
+
explicitly declare it on `map_attribute`).
|
390
|
+
|
391
|
+
```ruby
|
392
|
+
class Person < Shale::Mapper
|
393
|
+
attribute :first_name, Shale::Type::String
|
394
|
+
attribute :middle_name, Shale::Type::String
|
395
|
+
attribute :last_name, Shale::Type::String
|
396
|
+
attribute :age, Shale::Type::Integer
|
397
|
+
attribute :hobby, Shale::Type::String
|
398
|
+
|
399
|
+
xml do
|
400
|
+
root 'person'
|
401
|
+
namespace 'http://ns1.com', 'ns1'
|
402
|
+
|
403
|
+
map_element 'first_name', to: :first_name
|
404
|
+
|
405
|
+
# undeclare namespace on 'middle_name' element
|
406
|
+
map_element 'middle_name', to: :middle_name, namespace: nil, prefix: nil
|
407
|
+
|
408
|
+
# overwrite default namespace
|
409
|
+
map_element 'last_name', to: :last_name, namespace: 'http://ns2.com', prefix: 'ns2'
|
410
|
+
|
411
|
+
map_attribute 'age', to: :age
|
412
|
+
map_attribute 'hobby', to: :hobby, namespace: 'http://ns1.com', prefix: 'ns1'
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
person = Person.from_xml(<<~DATA)
|
417
|
+
<ns1:person xmlns:ns1="http://ns1.com" xmlns:ns2="http://ns2.com" age="50" ns1:hobby="running">
|
418
|
+
<ns1:first_name>John</ns1:first_name>
|
419
|
+
<middle_name>Joe</middle_name>
|
420
|
+
<ns2:last_name>Doe</ns2:last_name>
|
421
|
+
</ns1:person>
|
422
|
+
DATA
|
423
|
+
```
|
424
|
+
|
330
425
|
### Using methods to extract and generate data
|
331
426
|
|
332
427
|
If you need full controll over extracting and generating data from/to document,
|
@@ -421,6 +516,36 @@ DATA
|
|
421
516
|
# @city="London">
|
422
517
|
```
|
423
518
|
|
519
|
+
### Pretty printing and XML declaration
|
520
|
+
|
521
|
+
If you need formatted output you can pass `:pretty` parameter to `#to_json` and `#to_xml`
|
522
|
+
|
523
|
+
```ruby
|
524
|
+
person.to_json(:pretty)
|
525
|
+
|
526
|
+
# =>
|
527
|
+
#
|
528
|
+
# {
|
529
|
+
# "name": "John Doe",
|
530
|
+
# "address": {
|
531
|
+
# "city": "London"
|
532
|
+
# }
|
533
|
+
# }
|
534
|
+
```
|
535
|
+
|
536
|
+
You can also add an XML declaration by passing `:declaration` to `#to_xml`
|
537
|
+
|
538
|
+
```ruby
|
539
|
+
person.to_xml(:pretty, :declaration)
|
540
|
+
|
541
|
+
# =>
|
542
|
+
#
|
543
|
+
# <?xml version="1.0"?>
|
544
|
+
# <Person>
|
545
|
+
# <Address city="London"/>
|
546
|
+
# </Person>
|
547
|
+
```
|
548
|
+
|
424
549
|
### Supported types
|
425
550
|
|
426
551
|
Shale supports these types out of the box:
|
@@ -434,12 +559,12 @@ Shale supports these types out of the box:
|
|
434
559
|
|
435
560
|
### Writing your own type
|
436
561
|
|
437
|
-
To add your own type extend it from `Shale::Type::
|
562
|
+
To add your own type extend it from `Shale::Type::Value` and implement `.cast` class method.
|
438
563
|
|
439
564
|
```ruby
|
440
|
-
require 'shale/type/
|
565
|
+
require 'shale/type/value'
|
441
566
|
|
442
|
-
class MyIntegerType < Shale::Type::
|
567
|
+
class MyIntegerType < Shale::Type::Value
|
443
568
|
def self.cast(value)
|
444
569
|
value.to_i
|
445
570
|
end
|
@@ -463,7 +588,7 @@ Shale.json_adapter = MultiJson
|
|
463
588
|
Shale.yaml_adapter = MyYamlAdapter
|
464
589
|
```
|
465
590
|
|
466
|
-
|
591
|
+
Shale provides adapters for most popular Ruby XML parsers:
|
467
592
|
|
468
593
|
```ruby
|
469
594
|
require 'shale'
|
@@ -484,6 +609,146 @@ require 'shale/adapter/ox'
|
|
484
609
|
Shale.xml_adapter = Shale::Adapter::Ox
|
485
610
|
```
|
486
611
|
|
612
|
+
### Generating JSON Schema
|
613
|
+
|
614
|
+
To generate JSON Schema from you Shale data model use:
|
615
|
+
|
616
|
+
```ruby
|
617
|
+
require 'shale/schema'
|
618
|
+
|
619
|
+
Shale::Schema.to_json(Person, id: 'My ID', description: 'My description', pretty: true)
|
620
|
+
|
621
|
+
# =>
|
622
|
+
#
|
623
|
+
# {
|
624
|
+
# "$schema": "https://json-schema.org/draft/2020-12/schema",
|
625
|
+
# "id": "My ID",
|
626
|
+
# "description": "My description",
|
627
|
+
# "$ref": "#/$defs/Person",
|
628
|
+
# "$defs": {
|
629
|
+
# "Address": {
|
630
|
+
# "type": [
|
631
|
+
# "object",
|
632
|
+
# "null"
|
633
|
+
# ],
|
634
|
+
# "properties": {
|
635
|
+
# "city": {
|
636
|
+
# "type": [
|
637
|
+
# "string",
|
638
|
+
# "null"
|
639
|
+
# ]
|
640
|
+
# }
|
641
|
+
# }
|
642
|
+
# },
|
643
|
+
# "Person": {
|
644
|
+
# "type": "object",
|
645
|
+
# "properties": {
|
646
|
+
# "name": {
|
647
|
+
# "type": [
|
648
|
+
# "string",
|
649
|
+
# "null"
|
650
|
+
# ]
|
651
|
+
# },
|
652
|
+
# "address": {
|
653
|
+
# "$ref": "#/$defs/Address"
|
654
|
+
# }
|
655
|
+
# }
|
656
|
+
# }
|
657
|
+
# }
|
658
|
+
# }
|
659
|
+
```
|
660
|
+
|
661
|
+
You can also use a command line tool to do it:
|
662
|
+
|
663
|
+
```
|
664
|
+
$ shaleb -i data_model.rb -c Person -p
|
665
|
+
```
|
666
|
+
|
667
|
+
If you want to convert your own types to JSON Schema types use:
|
668
|
+
|
669
|
+
```ruby
|
670
|
+
require 'shale'
|
671
|
+
require 'shale/schema'
|
672
|
+
|
673
|
+
class MyEmailType < Shale::Type::Value
|
674
|
+
...
|
675
|
+
end
|
676
|
+
|
677
|
+
class MyEmailJSONType < Shale::Schema::JSON::Base
|
678
|
+
def as_type
|
679
|
+
{ 'type' => 'string', 'format' => 'email' }
|
680
|
+
end
|
681
|
+
end
|
682
|
+
|
683
|
+
Shale::Schema::JSON.register_json_type(MyEmailType, MyEmailJSONType)
|
684
|
+
```
|
685
|
+
|
686
|
+
### Generating XML Schema
|
687
|
+
|
688
|
+
To generate XML Schema from you Shale data model use:
|
689
|
+
|
690
|
+
```ruby
|
691
|
+
require 'shale/schema'
|
692
|
+
|
693
|
+
Shale::Schema.to_xml(Person, pretty: true)
|
694
|
+
|
695
|
+
# =>
|
696
|
+
#
|
697
|
+
# {
|
698
|
+
# 'schema0.xsd' => '
|
699
|
+
# <xs:schema
|
700
|
+
# elementFormDefault="qualified"
|
701
|
+
# attributeFormDefault="qualified"
|
702
|
+
# xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
703
|
+
# xmlns:foo="http://foo.com"
|
704
|
+
# >
|
705
|
+
# <xs:import namespace="http://foo.com" schemaLocation="schema1.xsd"/>
|
706
|
+
# <xs:element name="person" type="Person"/>
|
707
|
+
# <xs:complexType name="Person">
|
708
|
+
# <xs:sequence>
|
709
|
+
# <xs:element name="name" type="xs:string" minOccurs="0"/>
|
710
|
+
# <xs:element ref="foo:address" minOccurs="0"/>
|
711
|
+
# </xs:sequence>
|
712
|
+
# </xs:complexType>
|
713
|
+
# </xs:schema>',
|
714
|
+
#
|
715
|
+
# 'schema1.xsd' => '
|
716
|
+
# <xs:schema
|
717
|
+
# elementFormDefault="qualified"
|
718
|
+
# attributeFormDefault="qualified"
|
719
|
+
# targetNamespace="http://foo.com"
|
720
|
+
# xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
721
|
+
# xmlns:foo="http://foo.com"
|
722
|
+
# >
|
723
|
+
# <xs:element name="address" type="foo:Address"/>
|
724
|
+
# <xs:complexType name="Address">
|
725
|
+
# <xs:sequence>
|
726
|
+
# <xs:element name="city" type="xs:string" minOccurs="0"/>
|
727
|
+
# </xs:sequence>
|
728
|
+
# </xs:complexType>
|
729
|
+
# </xs:schema>'
|
730
|
+
# }
|
731
|
+
```
|
732
|
+
|
733
|
+
You can also use a command line tool to do it:
|
734
|
+
|
735
|
+
```
|
736
|
+
$ shaleb -i data_model.rb -c Person -p -f xml
|
737
|
+
```
|
738
|
+
|
739
|
+
If you want to convert your own types to XML Schema types use:
|
740
|
+
|
741
|
+
```ruby
|
742
|
+
require 'shale'
|
743
|
+
require 'shale/schema'
|
744
|
+
|
745
|
+
class MyEmailType < Shale::Type::Value
|
746
|
+
...
|
747
|
+
end
|
748
|
+
|
749
|
+
Shale::Schema::XML.register_xml_type(MyEmailType, 'myEmailXMLType')
|
750
|
+
```
|
751
|
+
|
487
752
|
## Contributing
|
488
753
|
|
489
754
|
Bug reports and pull requests are welcome on GitHub at https://github.com/kgiszczak/shale.
|
data/exe/shaleb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
base_path = File.expand_path('../lib', __dir__)
|
7
|
+
|
8
|
+
if File.exist?(base_path)
|
9
|
+
require_relative '../lib/shale/schema'
|
10
|
+
else
|
11
|
+
require 'shale/schema'
|
12
|
+
end
|
13
|
+
|
14
|
+
params = {}
|
15
|
+
|
16
|
+
ARGV << '-h' if ARGV.empty?
|
17
|
+
|
18
|
+
OptionParser.new do |opts|
|
19
|
+
opts.banner = "Usage: shaleb [options]\nexample: shaleb -i data_model.rb -c MyRoot"
|
20
|
+
|
21
|
+
opts.on('-i INPUT', '--input', 'Input file')
|
22
|
+
opts.on('-o OUTPUT', '--output', 'Output file (defaults to STDOUT)')
|
23
|
+
opts.on('-c CLASS', '--class CLASS', 'Shale model class name')
|
24
|
+
opts.on('-f FORMAT', '--format FORMAT', 'Schema format: JSON (default), XML')
|
25
|
+
opts.on('-p', '--pretty', 'Pretty print generated schema')
|
26
|
+
|
27
|
+
opts.on('-v', '--version', 'Show version') do
|
28
|
+
puts "shaleb version #{Shale::VERSION}"
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
end.parse!(into: params)
|
32
|
+
|
33
|
+
input_path = File.expand_path(params[:input], Dir.pwd)
|
34
|
+
|
35
|
+
unless File.exist?(input_path)
|
36
|
+
puts "File '#{input_path}' does not exist"
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
|
40
|
+
unless params[:class]
|
41
|
+
puts 'Model class is required'
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
|
45
|
+
require input_path
|
46
|
+
|
47
|
+
klass = Object.const_get(params[:class])
|
48
|
+
|
49
|
+
if params[:format] == 'xml'
|
50
|
+
if params[:output]
|
51
|
+
base_name = File.basename(params[:output], File.extname(params[:output]))
|
52
|
+
schemas = Shale::Schema.to_xml(klass, base_name, pretty: params[:pretty])
|
53
|
+
|
54
|
+
schemas.map do |name, xml|
|
55
|
+
File.write(File.expand_path(name, Dir.pwd), xml)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
schemas = Shale::Schema.to_xml(klass, pretty: params[:pretty])
|
59
|
+
|
60
|
+
output = schemas.map do |name, xml|
|
61
|
+
"<!-- #{name} -->\n#{xml}\n"
|
62
|
+
end.join("\n")
|
63
|
+
|
64
|
+
puts output
|
65
|
+
end
|
66
|
+
else
|
67
|
+
schema = Shale::Schema.to_json(klass, pretty: params[:pretty])
|
68
|
+
|
69
|
+
if params[:output]
|
70
|
+
output_path = File.expand_path(params[:output], Dir.pwd)
|
71
|
+
File.write(output_path, schema)
|
72
|
+
else
|
73
|
+
puts schema
|
74
|
+
end
|
75
|
+
end
|
data/lib/shale/adapter/json.rb
CHANGED
@@ -22,12 +22,17 @@ module Shale
|
|
22
22
|
# Serialize Hash into JSON
|
23
23
|
#
|
24
24
|
# @param [Hash] obj Hash object
|
25
|
+
# @param [Array<Symbol>] options
|
25
26
|
#
|
26
27
|
# @return [String]
|
27
28
|
#
|
28
29
|
# @api private
|
29
|
-
def self.dump(obj)
|
30
|
-
|
30
|
+
def self.dump(obj, *options)
|
31
|
+
if options.include?(:pretty)
|
32
|
+
::JSON.pretty_generate(obj)
|
33
|
+
else
|
34
|
+
::JSON.generate(obj)
|
35
|
+
end
|
31
36
|
end
|
32
37
|
end
|
33
38
|
end
|
@@ -26,12 +26,29 @@ module Shale
|
|
26
26
|
# Serialize Nokogiri document into XML
|
27
27
|
#
|
28
28
|
# @param [::Nokogiri::XML::Document] doc Nokogiri document
|
29
|
+
# @param [Array<Symbol>] options
|
29
30
|
#
|
30
31
|
# @return [String]
|
31
32
|
#
|
32
33
|
# @api private
|
33
|
-
def self.dump(doc)
|
34
|
-
|
34
|
+
def self.dump(doc, *options)
|
35
|
+
save_with = ::Nokogiri::XML::Node::SaveOptions::AS_XML
|
36
|
+
|
37
|
+
if options.include?(:pretty)
|
38
|
+
save_with |= ::Nokogiri::XML::Node::SaveOptions::FORMAT
|
39
|
+
end
|
40
|
+
|
41
|
+
unless options.include?(:declaration)
|
42
|
+
save_with |= ::Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
|
43
|
+
end
|
44
|
+
|
45
|
+
result = doc.to_xml(save_with: save_with)
|
46
|
+
|
47
|
+
unless options.include?(:pretty)
|
48
|
+
result = result.sub(/\n/, '')
|
49
|
+
end
|
50
|
+
|
51
|
+
result
|
35
52
|
end
|
36
53
|
|
37
54
|
# Create Shale::Adapter::Nokogiri::Document instance
|
@@ -45,18 +62,27 @@ module Shale
|
|
45
62
|
#
|
46
63
|
# @api private
|
47
64
|
class Document
|
65
|
+
# Initialize object
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
def initialize
|
69
|
+
@doc = ::Nokogiri::XML::Document.new
|
70
|
+
@namespaces = {}
|
71
|
+
end
|
72
|
+
|
48
73
|
# Return Nokogiri document
|
49
74
|
#
|
50
75
|
# @return [::Nokogiri::XML::Document]
|
51
76
|
#
|
52
77
|
# @api private
|
53
|
-
|
78
|
+
def doc
|
79
|
+
if @doc.root
|
80
|
+
@namespaces.each do |prefix, namespace|
|
81
|
+
@doc.root.add_namespace(prefix, namespace)
|
82
|
+
end
|
83
|
+
end
|
54
84
|
|
55
|
-
|
56
|
-
#
|
57
|
-
# @api private
|
58
|
-
def initialize
|
59
|
-
@doc = ::Nokogiri::XML::Document.new
|
85
|
+
@doc
|
60
86
|
end
|
61
87
|
|
62
88
|
# Create Nokogiri element
|
@@ -70,6 +96,16 @@ module Shale
|
|
70
96
|
::Nokogiri::XML::Element.new(name, @doc)
|
71
97
|
end
|
72
98
|
|
99
|
+
# Add XML namespace to document
|
100
|
+
#
|
101
|
+
# @param [String] prefix
|
102
|
+
# @param [String] namespace
|
103
|
+
#
|
104
|
+
# @api private
|
105
|
+
def add_namespace(prefix, namespace)
|
106
|
+
@namespaces[prefix] = namespace if prefix && namespace
|
107
|
+
end
|
108
|
+
|
73
109
|
# Add attribute to Nokogiri element
|
74
110
|
#
|
75
111
|
# @param [::Nokogiri::XML::Element] element Nokogiri element
|
@@ -115,7 +151,7 @@ module Shale
|
|
115
151
|
@node = node
|
116
152
|
end
|
117
153
|
|
118
|
-
# Return
|
154
|
+
# Return name of the node in the format of
|
119
155
|
# namespace:name when the node is namespaced or just name when it's not
|
120
156
|
#
|
121
157
|
# @return [String]
|
@@ -124,11 +160,11 @@ module Shale
|
|
124
160
|
# node.name # => Bar
|
125
161
|
#
|
126
162
|
# @example with namespace
|
127
|
-
# node.name # => foo:Bar
|
163
|
+
# node.name # => http://foo:Bar
|
128
164
|
#
|
129
165
|
# @api private
|
130
166
|
def name
|
131
|
-
[@node.namespace&.
|
167
|
+
[@node.namespace&.href, @node.name].compact.join(':')
|
132
168
|
end
|
133
169
|
|
134
170
|
# Return all attributes associated with the node
|
@@ -138,7 +174,7 @@ module Shale
|
|
138
174
|
# @api private
|
139
175
|
def attributes
|
140
176
|
@node.attribute_nodes.each_with_object({}) do |node, hash|
|
141
|
-
name = [node.namespace&.
|
177
|
+
name = [node.namespace&.href, node.name].compact.join(':')
|
142
178
|
hash[name] = node.value
|
143
179
|
end
|
144
180
|
end
|
data/lib/shale/adapter/ox.rb
CHANGED
@@ -22,12 +22,24 @@ module Shale
|
|
22
22
|
# Serialize Ox document into XML
|
23
23
|
#
|
24
24
|
# @param [::Ox::Document, ::Ox::Element] doc Ox document
|
25
|
+
# @param [Array<Symbol>] options
|
25
26
|
#
|
26
27
|
# @return [String]
|
27
28
|
#
|
28
29
|
# @api private
|
29
|
-
def self.dump(doc)
|
30
|
-
|
30
|
+
def self.dump(doc, *options)
|
31
|
+
opts = { indent: -1, with_xml: false }
|
32
|
+
|
33
|
+
if options.include?(:pretty)
|
34
|
+
opts[:indent] = 2
|
35
|
+
end
|
36
|
+
|
37
|
+
if options.include?(:declaration)
|
38
|
+
doc[:version] = '1.0'
|
39
|
+
opts[:with_xml] = true
|
40
|
+
end
|
41
|
+
|
42
|
+
::Ox.dump(doc, opts).sub(/\A\n/, '')
|
31
43
|
end
|
32
44
|
|
33
45
|
# Create Shale::Adapter::Ox::Document instance
|
@@ -66,6 +78,18 @@ module Shale
|
|
66
78
|
::Ox::Element.new(name)
|
67
79
|
end
|
68
80
|
|
81
|
+
# Add XML namespace to document
|
82
|
+
#
|
83
|
+
# Ox doesn't support XML namespaces so this method does nothing.
|
84
|
+
#
|
85
|
+
# @param [String] prefix
|
86
|
+
# @param [String] namespace
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
def add_namespace(prefix, namespace)
|
90
|
+
# :noop:
|
91
|
+
end
|
92
|
+
|
69
93
|
# Add attribute to Ox element
|
70
94
|
#
|
71
95
|
# @param [::Ox::Element] element Ox element
|
@@ -111,8 +135,8 @@ module Shale
|
|
111
135
|
@node = node
|
112
136
|
end
|
113
137
|
|
114
|
-
# Return
|
115
|
-
#
|
138
|
+
# Return name of the node in the format of
|
139
|
+
# prefix:name when the node is namespaced or just name when it's not
|
116
140
|
#
|
117
141
|
# @return [String]
|
118
142
|
#
|