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.
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
  #