lutaml-model 0.3.24 → 0.3.25

Sign up to get free protection for your applications and to get access to all the features.
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