lutaml-model 0.3.24 → 0.3.25

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +35 -16
  3. data/README.adoc +274 -28
  4. data/lib/lutaml/model/attribute.rb +18 -8
  5. data/lib/lutaml/model/error/type_error.rb +9 -0
  6. data/lib/lutaml/model/error/unknown_type_error.rb +9 -0
  7. data/lib/lutaml/model/error/validation_error.rb +0 -1
  8. data/lib/lutaml/model/error.rb +2 -0
  9. data/lib/lutaml/model/serialize.rb +6 -1
  10. data/lib/lutaml/model/type/boolean.rb +38 -0
  11. data/lib/lutaml/model/type/date.rb +35 -0
  12. data/lib/lutaml/model/type/date_time.rb +32 -4
  13. data/lib/lutaml/model/type/decimal.rb +42 -0
  14. data/lib/lutaml/model/type/float.rb +37 -0
  15. data/lib/lutaml/model/type/hash.rb +62 -0
  16. data/lib/lutaml/model/type/integer.rb +41 -0
  17. data/lib/lutaml/model/type/string.rb +49 -0
  18. data/lib/lutaml/model/type/time.rb +49 -0
  19. data/lib/lutaml/model/type/time_without_date.rb +37 -5
  20. data/lib/lutaml/model/type/value.rb +52 -0
  21. data/lib/lutaml/model/type.rb +50 -114
  22. data/lib/lutaml/model/version.rb +1 -1
  23. data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +5 -2
  24. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +2 -1
  25. data/lib/lutaml/model/xml_adapter/xml_document.rb +0 -2
  26. data/lutaml-model.gemspec +1 -1
  27. data/spec/address_spec.rb +170 -0
  28. data/spec/fixtures/address.rb +33 -0
  29. data/spec/fixtures/person.rb +73 -0
  30. data/spec/fixtures/sample_model.rb +40 -0
  31. data/spec/fixtures/vase.rb +38 -0
  32. data/spec/fixtures/xml/special_char.xml +13 -0
  33. data/spec/lutaml/model/attribute_spec.rb +112 -0
  34. data/spec/lutaml/model/collection_spec.rb +299 -0
  35. data/spec/lutaml/model/comparable_model_spec.rb +106 -0
  36. data/spec/lutaml/model/custom_model_spec.rb +410 -0
  37. data/spec/lutaml/model/custom_serialization_spec.rb +170 -0
  38. data/spec/lutaml/model/defaults_spec.rb +221 -0
  39. data/spec/lutaml/model/delegation_spec.rb +340 -0
  40. data/spec/lutaml/model/inheritance_spec.rb +92 -0
  41. data/spec/lutaml/model/json_adapter_spec.rb +37 -0
  42. data/spec/lutaml/model/key_value_mapping_spec.rb +86 -0
  43. data/spec/lutaml/model/map_content_spec.rb +118 -0
  44. data/spec/lutaml/model/mixed_content_spec.rb +625 -0
  45. data/spec/lutaml/model/namespace_spec.rb +57 -0
  46. data/spec/lutaml/model/ordered_content_spec.rb +83 -0
  47. data/spec/lutaml/model/render_nil_spec.rb +138 -0
  48. data/spec/lutaml/model/schema/json_schema_spec.rb +79 -0
  49. data/spec/lutaml/model/schema/relaxng_schema_spec.rb +60 -0
  50. data/spec/lutaml/model/schema/xsd_schema_spec.rb +55 -0
  51. data/spec/lutaml/model/schema/yaml_schema_spec.rb +47 -0
  52. data/spec/lutaml/model/serializable_spec.rb +297 -0
  53. data/spec/lutaml/model/serializable_validation_spec.rb +85 -0
  54. data/spec/lutaml/model/simple_model_spec.rb +314 -0
  55. data/spec/lutaml/model/toml_adapter_spec.rb +39 -0
  56. data/spec/lutaml/model/type/boolean_spec.rb +54 -0
  57. data/spec/lutaml/model/type/date_spec.rb +118 -0
  58. data/spec/lutaml/model/type/date_time_spec.rb +127 -0
  59. data/spec/lutaml/model/type/decimal_spec.rb +125 -0
  60. data/spec/lutaml/model/type/float_spec.rb +191 -0
  61. data/spec/lutaml/model/type/hash_spec.rb +63 -0
  62. data/spec/lutaml/model/type/integer_spec.rb +145 -0
  63. data/spec/lutaml/model/type/string_spec.rb +150 -0
  64. data/spec/lutaml/model/type/time_spec.rb +142 -0
  65. data/spec/lutaml/model/type/time_without_date_spec.rb +125 -0
  66. data/spec/lutaml/model/type_spec.rb +276 -0
  67. data/spec/lutaml/model/utils_spec.rb +79 -0
  68. data/spec/lutaml/model/validation_spec.rb +83 -0
  69. data/spec/lutaml/model/with_child_mapping_spec.rb +174 -0
  70. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +56 -0
  71. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +56 -0
  72. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +61 -0
  73. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +251 -0
  74. data/spec/lutaml/model/xml_adapter_spec.rb +178 -0
  75. data/spec/lutaml/model/xml_mapping_spec.rb +863 -0
  76. data/spec/lutaml/model/yaml_adapter_spec.rb +30 -0
  77. data/spec/lutaml/model_spec.rb +1 -0
  78. data/spec/person_spec.rb +161 -0
  79. data/spec/spec_helper.rb +33 -0
  80. metadata +66 -2
@@ -0,0 +1,83 @@
1
+ # spec/lutaml/model/ordered_content_spec.rb
2
+
3
+ require "spec_helper"
4
+ require "lutaml/model"
5
+ require "lutaml/model/xml_adapter/nokogiri_adapter"
6
+ require "lutaml/model/xml_adapter/ox_adapter"
7
+ require "lutaml/model/xml_adapter/oga_adapter"
8
+ require_relative "../../fixtures/sample_model"
9
+
10
+ module OrderedContentSpec
11
+ class RootOrderedContent < Lutaml::Model::Serializable
12
+ attribute :id, :string
13
+ attribute :bold, :string, collection: true
14
+ attribute :italic, :string, collection: true
15
+ attribute :underline, :string
16
+ attribute :content, :string
17
+
18
+ xml do
19
+ root "RootOrderedContent", ordered: true
20
+ map_attribute :id, to: :id
21
+ map_element :bold, to: :bold
22
+ map_element :italic, to: :italic
23
+ map_element :underline, to: :underline
24
+ map_content to: :content
25
+ end
26
+ end
27
+ end
28
+
29
+ RSpec.describe "OrderedContent" do
30
+ shared_examples "ordered content behavior" do |adapter_class|
31
+ around do |example|
32
+ old_adapter = Lutaml::Model::Config.xml_adapter
33
+ Lutaml::Model::Config.xml_adapter = adapter_class
34
+ example.run
35
+ ensure
36
+ Lutaml::Model::Config.xml_adapter = old_adapter
37
+ end
38
+
39
+ context "when ordered: true is set at root" do
40
+ let(:xml) do
41
+ <<~XML
42
+ <RootOrderedContent id="123">
43
+ The Earth's Moon rings like a <bold>bell</bold> when struck by
44
+ meteroids. Distanced from the Earth by <italic>384,400 km</italic>,
45
+ its surface is covered in <underline>craters</underline>.
46
+ Ain't that <bold>cool</bold>?
47
+ </RootOrderedContent>
48
+ XML
49
+ end
50
+
51
+ let(:expected_xml) do
52
+ <<~XML
53
+ <RootOrderedContent id="123">
54
+ <bold>bell</bold>
55
+ <italic>384,400 km</italic>
56
+ <underline>craters</underline>
57
+ <bold>cool</bold>
58
+ The Earth's Moon rings like a when struck by
59
+ meteroids. Distanced from the Earth by ,
60
+ its surface is covered in . Ain't that ?
61
+ </RootOrderedContent>
62
+ XML
63
+ end
64
+
65
+ it "deserializes and serializes ordered content correctly" do
66
+ serialized = OrderedContentSpec::RootOrderedContent.from_xml(xml).to_xml
67
+ expect(serialized).to be_equivalent_to(expected_xml)
68
+ end
69
+ end
70
+ end
71
+
72
+ describe Lutaml::Model::XmlAdapter::NokogiriAdapter do
73
+ it_behaves_like "ordered content behavior", described_class
74
+ end
75
+
76
+ describe Lutaml::Model::XmlAdapter::OxAdapter do
77
+ it_behaves_like "ordered content behavior", described_class
78
+ end
79
+
80
+ describe Lutaml::Model::XmlAdapter::OgaAdapter, skip: "Not implemented yet" do
81
+ it_behaves_like "ordered content behavior", described_class
82
+ end
83
+ end
@@ -0,0 +1,138 @@
1
+ require "spec_helper"
2
+ require "lutaml/model"
3
+
4
+ class RenderNilNested < Lutaml::Model::Serializable
5
+ attribute :name, Lutaml::Model::Type::String
6
+
7
+ xml do
8
+ root "render_nil_nested"
9
+
10
+ map_element "name", to: :name
11
+ end
12
+ end
13
+
14
+ class RenderNil < Lutaml::Model::Serializable
15
+ attribute :name, Lutaml::Model::Type::String, default: -> {
16
+ "Unnamed Pottery"
17
+ }
18
+ attribute :clay_type, Lutaml::Model::Type::String
19
+ attribute :glaze, Lutaml::Model::Type::String
20
+ attribute :dimensions, Lutaml::Model::Type::String, collection: true
21
+ attribute :render_nil_nested, RenderNilNested
22
+
23
+ json do
24
+ map "name", to: :name, render_nil: true
25
+ map "clay_type", to: :clay_type, render_nil: true
26
+ map "glaze", to: :glaze, render_nil: true
27
+ map "dimensions", to: :dimensions, render_nil: false
28
+ end
29
+
30
+ xml do
31
+ root "render_nil"
32
+ map_element "name", to: :name, render_nil: true
33
+ map_element "clay_type", to: :clay_type, render_nil: false
34
+ map_element "glaze", to: :glaze, render_nil: true
35
+ map_element "render_nil_nested", to: :render_nil_nested, render_nil: true,
36
+ render_default: true
37
+ map_element "dimensions", to: :dimensions, render_nil: false
38
+ end
39
+
40
+ yaml do
41
+ map "name", to: :name, render_nil: true
42
+ map "clay_type", to: :clay_type, render_nil: false
43
+ map "glaze", to: :glaze, render_nil: true
44
+ map "dimensions", to: :dimensions, render_nil: false
45
+ end
46
+
47
+ toml do
48
+ map "name", to: :name, render_nil: true
49
+ map "clay_type", to: :clay_type, render_nil: false
50
+ map "glaze", to: :glaze, render_nil: true
51
+ map "dimensions", to: :dimensions, render_nil: false
52
+ end
53
+ end
54
+
55
+ RSpec.describe RenderNil do
56
+ let(:attributes) do
57
+ {
58
+ name: nil,
59
+ clay_type: nil,
60
+ glaze: nil,
61
+ dimensions: nil,
62
+ }
63
+ end
64
+ let(:model) { described_class.new(attributes) }
65
+
66
+ it "serializes to JSON with render_nil option" do
67
+ expected_json = {
68
+ name: nil,
69
+ clay_type: nil,
70
+ glaze: nil,
71
+ dimensions: [],
72
+ }.to_json
73
+
74
+ expect(model.to_json).to eq(expected_json)
75
+ end
76
+
77
+ it "deserializes from JSON with render_nil option" do
78
+ json = attributes.to_json
79
+ pottery = described_class.from_json(json)
80
+ expect(pottery.name).to be_nil
81
+ expect(pottery.clay_type).to be_nil
82
+ expect(pottery.glaze).to be_nil
83
+ expect(pottery.dimensions).to eq([])
84
+ end
85
+
86
+ it "serializes to XML with render_nil option" do
87
+ expected_xml = <<~XML
88
+ <render_nil>
89
+ <name/>
90
+ <glaze/>
91
+ <render_nil_nested/>
92
+ </render_nil>
93
+ XML
94
+
95
+ expect(model.to_xml).to be_equivalent_to(expected_xml)
96
+ end
97
+
98
+ it "deserializes from XML with render_nil option" do
99
+ xml = <<~XML
100
+ <render_nil>
101
+ <name/>
102
+ <glaze/>
103
+ </render_nil>
104
+ XML
105
+
106
+ pottery = described_class.from_xml(xml)
107
+ expect(pottery.name).to be_nil
108
+ expect(pottery.glaze).to be_nil
109
+ end
110
+
111
+ it "serializes to YAML with render_nil option" do
112
+ expected_yaml = <<~YAML
113
+ ---
114
+ name:
115
+ glaze:
116
+ dimensions: []
117
+ YAML
118
+
119
+ generated_yaml = model.to_yaml.strip
120
+
121
+ # Removing empty spaces from the end of the line because of and issue in
122
+ # libyaml -> https://github.com/yaml/libyaml/issues/46
123
+ generated_yaml = generated_yaml.gsub(": \n", ":\n")
124
+
125
+ expect(generated_yaml).to eq(expected_yaml.strip)
126
+ end
127
+
128
+ it "deserializes from YAML with render_nil option" do
129
+ yaml = <<~YAML
130
+ ---
131
+ glaze:
132
+ YAML
133
+
134
+ pottery = described_class.from_yaml(yaml)
135
+ expect(pottery.name).to eq("Unnamed Pottery")
136
+ expect(pottery.glaze).to be_nil
137
+ end
138
+ end
@@ -0,0 +1,79 @@
1
+ require "spec_helper"
2
+ require "lutaml/model/schema"
3
+
4
+ RSpec.describe Lutaml::Model::Schema::JsonSchema do
5
+ module SchemaGeneration
6
+ class Glaze < Lutaml::Model::Serializable
7
+ attribute :color, Lutaml::Model::Type::String
8
+ attribute :finish, Lutaml::Model::Type::String
9
+ end
10
+
11
+ class Vase < Lutaml::Model::Serializable
12
+ attribute :height, Lutaml::Model::Type::Float
13
+ attribute :diameter, Lutaml::Model::Type::Float
14
+ attribute :glaze, Glaze
15
+ attribute :materials, Lutaml::Model::Type::String, collection: true
16
+ end
17
+ end
18
+
19
+ describe ".generate" do
20
+ it "generates a JSON schema for nested Serialize objects" do
21
+ schema = described_class.generate(SchemaGeneration::Vase,
22
+ id: "https://example.com/vase.schema.json", description: "A vase schema", pretty: true)
23
+
24
+ expected_schema = <<~JSON.chomp
25
+ {
26
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
27
+ "$id": "https://example.com/vase.schema.json",
28
+ "description": "A vase schema",
29
+ "$ref": "#/$defs/SchemaGeneration::Vase",
30
+ "$defs": {
31
+ "SchemaGeneration::Vase": {
32
+ "type": "object",
33
+ "properties": {
34
+ "height": {
35
+ "type": "number"
36
+ },
37
+ "diameter": {
38
+ "type": "number"
39
+ },
40
+ "glaze": {
41
+ "$ref": "#/$defs/SchemaGeneration::Glaze"
42
+ },
43
+ "materials": {
44
+ "type": "array",
45
+ "items": {
46
+ "type": "string"
47
+ }
48
+ }
49
+ },
50
+ "required": [
51
+ "height",
52
+ "diameter",
53
+ "glaze",
54
+ "materials"
55
+ ]
56
+ },
57
+ "SchemaGeneration::Glaze": {
58
+ "type": "object",
59
+ "properties": {
60
+ "color": {
61
+ "type": "string"
62
+ },
63
+ "finish": {
64
+ "type": "string"
65
+ }
66
+ },
67
+ "required": [
68
+ "color",
69
+ "finish"
70
+ ]
71
+ }
72
+ }
73
+ }
74
+ JSON
75
+
76
+ expect(schema).to eq(expected_schema)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,60 @@
1
+ require "spec_helper"
2
+ require "lutaml/model/schema"
3
+
4
+ RSpec.describe Lutaml::Model::Schema::RelaxngSchema do
5
+ module SchemaGeneration
6
+ class Glaze < Lutaml::Model::Serializable
7
+ attribute :color, Lutaml::Model::Type::String
8
+ attribute :finish, Lutaml::Model::Type::String
9
+ end
10
+
11
+ class Vase < Lutaml::Model::Serializable
12
+ attribute :height, Lutaml::Model::Type::Float
13
+ attribute :diameter, Lutaml::Model::Type::Float
14
+ attribute :glaze, Glaze
15
+ attribute :materials, Lutaml::Model::Type::String, collection: true
16
+ end
17
+ end
18
+
19
+ describe ".generate" do
20
+ it "generates a RELAX NG schema for nested Serialize objects" do
21
+ schema = described_class.generate(SchemaGeneration::Vase, pretty: true)
22
+
23
+ expected_schema = <<~RELAXNG
24
+ <?xml version="1.0" encoding="UTF-8"?>
25
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0">
26
+ <start>
27
+ <ref name="SchemaGeneration::Vase"/>
28
+ </start>
29
+ <define name="SchemaGeneration::Vase">
30
+ <element name="SchemaGeneration::Vase">
31
+ <element name="height">
32
+ <data type="float"/>
33
+ </element>
34
+ <element name="diameter">
35
+ <data type="float"/>
36
+ </element>
37
+ <ref name="SchemaGeneration::Glaze"/>
38
+ <zeroOrMore>
39
+ <element name="materials">
40
+ <data type="string"/>
41
+ </element>
42
+ </zeroOrMore>
43
+ </element>
44
+ </define>
45
+ <define name="SchemaGeneration::Glaze">
46
+ <element name="SchemaGeneration::Glaze">
47
+ <element name="color">
48
+ <data type="string"/>
49
+ </element>
50
+ <element name="finish">
51
+ <data type="string"/>
52
+ </element>
53
+ </element>
54
+ </define>
55
+ </grammar>
56
+ RELAXNG
57
+ expect(schema).to eq(expected_schema)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,55 @@
1
+ require "spec_helper"
2
+ require "lutaml/model/schema"
3
+
4
+ RSpec.describe Lutaml::Model::Schema::XsdSchema do
5
+ module SchemaGeneration
6
+ class Glaze < Lutaml::Model::Serializable
7
+ attribute :color, Lutaml::Model::Type::String
8
+ attribute :finish, Lutaml::Model::Type::String
9
+ end
10
+
11
+ class Vase < Lutaml::Model::Serializable
12
+ attribute :height, Lutaml::Model::Type::Float
13
+ attribute :diameter, Lutaml::Model::Type::Float
14
+ attribute :glaze, Glaze
15
+ attribute :materials, Lutaml::Model::Type::String, collection: true
16
+ end
17
+ end
18
+
19
+ describe ".generate" do
20
+ it "generates an XSD schema for nested Serialize objects" do
21
+ schema = described_class.generate(SchemaGeneration::Vase, pretty: true)
22
+
23
+ expected_schema = <<~XSD
24
+ <?xml version="1.0" encoding="UTF-8"?>
25
+ <schema xmlns="http://www.w3.org/2001/XMLSchema">
26
+ <element name="SchemaGeneration::Vase">
27
+ <complexType>
28
+ <sequence>
29
+ <element name="height" type="xs:float"/>
30
+ <element name="diameter" type="xs:float"/>
31
+ <element name="glaze">
32
+ <complexType>
33
+ <sequence>
34
+ <element name="color" type="xs:string"/>
35
+ <element name="finish" type="xs:string"/>
36
+ </sequence>
37
+ </complexType>
38
+ </element>
39
+ <element name="materials" minOccurs="0" maxOccurs="unbounded">
40
+ <complexType>
41
+ <sequence>
42
+ <element name="item" type="xs:string"/>
43
+ </sequence>
44
+ </complexType>
45
+ </element>
46
+ </sequence>
47
+ </complexType>
48
+ </element>
49
+ </schema>
50
+ XSD
51
+
52
+ expect(schema).to eq(expected_schema)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,47 @@
1
+ require "spec_helper"
2
+ require "lutaml/model/schema"
3
+
4
+ RSpec.describe Lutaml::Model::Schema::YamlSchema do
5
+ module SchemaGeneration
6
+ class Glaze < Lutaml::Model::Serializable
7
+ attribute :color, Lutaml::Model::Type::String
8
+ attribute :finish, Lutaml::Model::Type::String
9
+ end
10
+
11
+ class Vase < Lutaml::Model::Serializable
12
+ attribute :height, Lutaml::Model::Type::Float
13
+ attribute :diameter, Lutaml::Model::Type::Float
14
+ attribute :glaze, Glaze
15
+ attribute :materials, Lutaml::Model::Type::String, collection: true
16
+ end
17
+ end
18
+
19
+ describe ".generate" do
20
+ it "generates a YAML schema for nested Serialize objects" do
21
+ schema = described_class.generate(SchemaGeneration::Vase)
22
+
23
+ expected_schema = <<~YAML
24
+ ---
25
+ type: map
26
+ mapping:
27
+ height:
28
+ type: float
29
+ diameter:
30
+ type: float
31
+ glaze:
32
+ type: map
33
+ mapping:
34
+ color:
35
+ type: str
36
+ finish:
37
+ type: str
38
+ materials:
39
+ type: seq
40
+ sequence:
41
+ - type: str
42
+ YAML
43
+
44
+ expect(schema).to eq(expected_schema)
45
+ end
46
+ end
47
+ end