shale 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +270 -5
  4. data/exe/shaleb +75 -0
  5. data/lib/shale/adapter/json.rb +7 -2
  6. data/lib/shale/adapter/nokogiri.rb +48 -12
  7. data/lib/shale/adapter/ox.rb +28 -4
  8. data/lib/shale/adapter/rexml.rb +56 -13
  9. data/lib/shale/attribute.rb +1 -1
  10. data/lib/shale/error.rb +6 -0
  11. data/lib/shale/mapper.rb +11 -11
  12. data/lib/shale/mapping/descriptor/dict.rb +57 -0
  13. data/lib/shale/mapping/descriptor/xml.rb +43 -0
  14. data/lib/shale/mapping/descriptor/xml_namespace.rb +37 -0
  15. data/lib/shale/mapping/{key_value.rb → dict.rb} +8 -6
  16. data/lib/shale/mapping/validator.rb +51 -0
  17. data/lib/shale/mapping/xml.rb +86 -15
  18. data/lib/shale/schema/json/base.rb +41 -0
  19. data/lib/shale/schema/json/boolean.rb +23 -0
  20. data/lib/shale/schema/json/collection.rb +39 -0
  21. data/lib/shale/schema/json/date.rb +23 -0
  22. data/lib/shale/schema/json/float.rb +23 -0
  23. data/lib/shale/schema/json/integer.rb +23 -0
  24. data/lib/shale/schema/json/object.rb +37 -0
  25. data/lib/shale/schema/json/ref.rb +28 -0
  26. data/lib/shale/schema/json/schema.rb +57 -0
  27. data/lib/shale/schema/json/string.rb +23 -0
  28. data/lib/shale/schema/json/time.rb +23 -0
  29. data/lib/shale/schema/json.rb +165 -0
  30. data/lib/shale/schema/xml/attribute.rb +41 -0
  31. data/lib/shale/schema/xml/complex_type.rb +67 -0
  32. data/lib/shale/schema/xml/element.rb +55 -0
  33. data/lib/shale/schema/xml/import.rb +46 -0
  34. data/lib/shale/schema/xml/ref_attribute.rb +37 -0
  35. data/lib/shale/schema/xml/ref_element.rb +39 -0
  36. data/lib/shale/schema/xml/schema.rb +121 -0
  37. data/lib/shale/schema/xml/typed_attribute.rb +46 -0
  38. data/lib/shale/schema/xml/typed_element.rb +46 -0
  39. data/lib/shale/schema/xml.rb +316 -0
  40. data/lib/shale/schema.rb +47 -0
  41. data/lib/shale/type/boolean.rb +2 -2
  42. data/lib/shale/type/composite.rb +66 -54
  43. data/lib/shale/type/date.rb +35 -2
  44. data/lib/shale/type/float.rb +2 -2
  45. data/lib/shale/type/integer.rb +2 -2
  46. data/lib/shale/type/string.rb +2 -2
  47. data/lib/shale/type/time.rb +35 -2
  48. data/lib/shale/type/{base.rb → value.rb} +18 -7
  49. data/lib/shale/utils.rb +18 -2
  50. data/lib/shale/version.rb +1 -1
  51. data/lib/shale.rb +10 -10
  52. data/shale.gemspec +6 -2
  53. metadata +41 -13
  54. data/lib/shale/mapping/base.rb +0 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5907ba25342167fa8079e683c671f83f89b51777d0b27e8d0126ba404304c2bd
4
- data.tar.gz: d88f5acb763a1aaa5f3ceff194613df16377107035cf2c5528bbda593a70b6fa
3
+ metadata.gz: f9396e5fcba6ff4e1f1566abe356270ef9ddbd8ce9171063f011feeb36d234e7
4
+ data.tar.gz: 4495b40dca3e01b6ba908dfc6b7831aea3f1e2865423de59ced69db33ba633b8
5
5
  SHA512:
6
- metadata.gz: 967646fcc21c1735acc67ad67b467013dae4da76c2e7cfc3a863b8bc811ae3fcee73cc3a99e1344ab0ea38ef03fed181ba14286666afc6e965399c8929de045a
7
- data.tar.gz: bebfbda2bb8fab102ae8b70506cd3fad3e1b178c02ed0486576d2fb26ef5a98ac45a0f62d410888e95b40d9a21ace32c76da36e2a75151ce104c4764a98b1b68
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
- hash do
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::Base` and implement `.cast` class method.
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/base'
565
+ require 'shale/type/value'
441
566
 
442
- class MyIntegerType < Shale::Type::Base
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
- For XML, Shale provides adapters for most popular Ruby XML parsers:
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
@@ -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
- ::JSON.generate(obj)
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
- doc.to_xml
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
- attr_reader :doc
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
- # Initialize object
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 fully qualified name of the node in the format of
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&.prefix, @node.name].compact.join(':')
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&.prefix, node.name].compact.join(':')
177
+ name = [node.namespace&.href, node.name].compact.join(':')
142
178
  hash[name] = node.value
143
179
  end
144
180
  end
@@ -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
- ::Ox.dump(doc)
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 fully qualified name of the node in the format of
115
- # namespace:name when the node is namespaced or just name when it's not
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
  #