lutaml-model 0.8.11 → 0.8.13

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 (149) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/opal.yml +31 -0
  3. data/.rspec-opal +5 -0
  4. data/.rubocop_todo.yml +45 -34
  5. data/README.adoc +126 -104
  6. data/RELEASE_NOTES.adoc +3 -3
  7. data/benchmark/quick_benchmark.rb +2 -2
  8. data/benchmark/serialization_benchmark.rb +4 -4
  9. data/docs/_guides/advanced-mapping.adoc +1 -1
  10. data/docs/_guides/character-encoding.adoc +3 -3
  11. data/docs/_guides/index.adoc +4 -0
  12. data/docs/_guides/missing-values-handling.adoc +6 -6
  13. data/docs/_guides/ooxml-examples.adoc +7 -7
  14. data/docs/_guides/opal.adoc +221 -0
  15. data/docs/_guides/value-transformations.adoc +7 -7
  16. data/docs/_guides/xml/namespace-presentation.adoc +1 -1
  17. data/docs/_guides/xml/namespace-semantics.adoc +15 -15
  18. data/docs/_guides/xml/type-namespaces.adoc +9 -9
  19. data/docs/_guides/xml-mapping.adoc +32 -26
  20. data/docs/_guides/xml-namespace-qualification.adoc +4 -4
  21. data/docs/_guides/xml-namespaces.adoc +2 -2
  22. data/docs/_guides/xml_mappings/04_xml_namespace_class.adoc +18 -18
  23. data/docs/_guides/xml_mappings/05_common_patterns.adoc +16 -16
  24. data/docs/_guides/xml_mappings/06_migration_guide.adoc +5 -5
  25. data/docs/_guides/xml_mappings/07_best_practices.adoc +13 -12
  26. data/docs/_migrations/0-8-0-namespace-restructuring.adoc +2 -2
  27. data/docs/_pages/attributes.adoc +2 -2
  28. data/docs/_pages/collections.adoc +26 -20
  29. data/docs/_pages/configuration.adoc +9 -4
  30. data/docs/_pages/consolidation-mapping.adoc +4 -4
  31. data/docs/_pages/importable_models.adoc +14 -13
  32. data/docs/_pages/index.adoc +1 -0
  33. data/docs/_pages/quick-start.adoc +1 -1
  34. data/docs/_pages/serialization_adapters.adoc +3 -2
  35. data/docs/_pages/value_types.adoc +10 -10
  36. data/docs/_references/custom_registers.adoc +7 -7
  37. data/docs/_references/format-independent-features.adoc +4 -4
  38. data/docs/_references/instance-serialization.adoc +1 -1
  39. data/docs/_references/parent-root-context.adoc +3 -3
  40. data/docs/_tutorials/basic-model-definition.adoc +1 -1
  41. data/docs/_tutorials/first-xml-serialization.adoc +4 -4
  42. data/docs/_tutorials/lutaml-xml-architecture.adoc +4 -4
  43. data/docs/_tutorials/validation-basics.adoc +1 -1
  44. data/docs/_tutorials/working-with-collections.adoc +2 -2
  45. data/docs/_tutorials/xml-namespaces-basics.adoc +1 -1
  46. data/docs/_tutorials/xml-schema-primer-style-guide.adoc +29 -29
  47. data/docs/cli_compare.adoc +1 -1
  48. data/docs/index.adoc +2 -1
  49. data/docs/namespace-management.adoc +14 -14
  50. data/lib/lutaml/hash_format/adapter/mapping.rb +2 -4
  51. data/lib/lutaml/json/adapter/mapping.rb +2 -4
  52. data/lib/lutaml/jsonl/adapter/mapping.rb +2 -4
  53. data/lib/lutaml/key_value/adapter/hash/mapping.rb +2 -4
  54. data/lib/lutaml/key_value/adapter/json/mapping.rb +2 -4
  55. data/lib/lutaml/key_value/adapter/jsonl/mapping.rb +2 -4
  56. data/lib/lutaml/key_value/adapter/toml/mapping.rb +2 -4
  57. data/lib/lutaml/key_value/adapter/yaml/mapping.rb +2 -4
  58. data/lib/lutaml/key_value/adapter/yamls/mapping.rb +2 -4
  59. data/lib/lutaml/key_value/mapping.rb +35 -10
  60. data/lib/lutaml/model/adapter_resolver.rb +5 -8
  61. data/lib/lutaml/model/collection.rb +11 -11
  62. data/lib/lutaml/model/error/no_root_mapping_error.rb +6 -5
  63. data/lib/lutaml/model/error/no_root_namespace_error.rb +6 -5
  64. data/lib/lutaml/model/error/type_only_mapping_error.rb +13 -0
  65. data/lib/lutaml/model/error/type_only_namespace_error.rb +12 -0
  66. data/lib/lutaml/model/mapping/mapping.rb +12 -0
  67. data/lib/lutaml/model/version.rb +1 -1
  68. data/lib/lutaml/model.rb +3 -0
  69. data/lib/lutaml/toml/adapter/mapping.rb +2 -4
  70. data/lib/lutaml/xml/adapter/base_adapter.rb +0 -9
  71. data/lib/lutaml/xml/adapter/nokogiri_adapter.rb +0 -1
  72. data/lib/lutaml/xml/adapter/oga_adapter.rb +0 -1
  73. data/lib/lutaml/xml/adapter/ox_adapter.rb +0 -1
  74. data/lib/lutaml/xml/adapter/rexml_adapter.rb +0 -1
  75. data/lib/lutaml/xml/adapter/xml_serializer.rb +42 -22
  76. data/lib/lutaml/xml/adapter.rb +4 -0
  77. data/lib/lutaml/xml/builder/base.rb +64 -25
  78. data/lib/lutaml/xml/builder/nokogiri.rb +0 -2
  79. data/lib/lutaml/xml/builder/oga.rb +0 -2
  80. data/lib/lutaml/xml/builder/ox.rb +0 -2
  81. data/lib/lutaml/xml/builder/rexml.rb +0 -2
  82. data/lib/lutaml/xml/builder.rb +1 -0
  83. data/lib/lutaml/xml/configurable.rb +2 -2
  84. data/lib/lutaml/xml/declaration_handler.rb +3 -105
  85. data/lib/lutaml/xml/mapping.rb +3 -3
  86. data/lib/lutaml/xml/schema/xsd/documentation.rb +1 -1
  87. data/lib/lutaml/xml/schema/xsd.rb +5 -4
  88. data/lib/lutaml/xml/schema.rb +8 -5
  89. data/lib/lutaml/xml/serialization/collection_ext.rb +7 -7
  90. data/lib/lutaml/xml/serialization/format_conversion.rb +1 -1
  91. data/lib/lutaml/xml/serialization/instance_methods.rb +1 -1
  92. data/lib/lutaml/xml/xml_orderable.rb +17 -0
  93. data/lib/lutaml/xml.rb +9 -13
  94. data/lib/lutaml/yaml/adapter/mapping.rb +2 -4
  95. data/lib/lutaml/yamls/adapter/mapping.rb +7 -3
  96. data/lib/tasks/memory_profile.rb +2 -2
  97. data/lib/tasks/performance_benchmark.rb +5 -5
  98. data/lutaml-model.gemspec +1 -1
  99. data/spec/lutaml/key_value/transformation/rule_compiler_spec.rb +1 -1
  100. data/spec/lutaml/key_value/transformation/value_serializer_spec.rb +1 -1
  101. data/spec/lutaml/model/attribute_collection_spec.rb +1 -1
  102. data/spec/lutaml/model/cli_spec.rb +1 -1
  103. data/spec/lutaml/model/collection_spec.rb +1 -1
  104. data/spec/lutaml/model/collection_validation_spec.rb +6 -6
  105. data/spec/lutaml/model/consolidation_spec.rb +8 -8
  106. data/spec/lutaml/model/custom_collection_spec.rb +3 -3
  107. data/spec/lutaml/model/default_register_spec.rb +23 -23
  108. data/spec/lutaml/model/delegation_spec.rb +3 -10
  109. data/spec/lutaml/model/derived_attribute_serialization_spec.rb +1 -1
  110. data/spec/lutaml/model/dynamic_attribute_spec.rb +2 -2
  111. data/spec/lutaml/model/enum_spec.rb +1 -1
  112. data/spec/lutaml/model/group_spec.rb +12 -12
  113. data/spec/lutaml/model/lazy_collection_spec.rb +4 -4
  114. data/spec/lutaml/model/mixed_content_spec.rb +2 -2
  115. data/spec/lutaml/model/namespace_versioning_spec.rb +4 -4
  116. data/spec/lutaml/model/opal_smoke_spec.rb +117 -0
  117. data/spec/lutaml/model/processing_instruction_spec.rb +11 -11
  118. data/spec/lutaml/model/register_methods_spec.rb +2 -2
  119. data/spec/lutaml/model/render_empty_spec.rb +1 -1
  120. data/spec/lutaml/model/serialize_perf_guard_spec.rb +1 -1
  121. data/spec/lutaml/model/transform_dynamic_attributes_spec.rb +1 -1
  122. data/spec/lutaml/model/transformation_builder_spec.rb +2 -2
  123. data/spec/lutaml/model/xml_decoupling_spec.rb +3 -3
  124. data/spec/lutaml/model/xsd_patterns_spec.rb +2 -3
  125. data/spec/lutaml/xml/adapter/order_spec.rb +1 -1
  126. data/spec/lutaml/xml/clear_parse_state_spec.rb +1 -1
  127. data/spec/lutaml/xml/content_model_validation_spec.rb +4 -2
  128. data/spec/lutaml/xml/doubly_defined_namespace_spec.rb +5 -5
  129. data/spec/lutaml/xml/enhanced_mapping_spec.rb +2 -1
  130. data/spec/lutaml/xml/entity_fragmentation_spec.rb +5 -5
  131. data/spec/lutaml/xml/indent_spec.rb +109 -0
  132. data/spec/lutaml/xml/line_ending_spec.rb +66 -0
  133. data/spec/lutaml/xml/mapping_finalization_guard_spec.rb +2 -2
  134. data/spec/lutaml/xml/model_transform_guard_spec.rb +4 -4
  135. data/spec/lutaml/xml/namespace_alias_spec.rb +4 -4
  136. data/spec/lutaml/xml/namespace_aware_parsing_spec.rb +3 -3
  137. data/spec/lutaml/xml/namespace_bound_element_roundtrip_spec.rb +2 -2
  138. data/spec/lutaml/xml/namespace_format_preservation_spec.rb +1 -1
  139. data/spec/lutaml/xml/namespace_inheritance_spec.rb +3 -3
  140. data/spec/lutaml/xml/namespace_preservation_spec.rb +5 -5
  141. data/spec/lutaml/xml/opal_xml_spec.rb +145 -0
  142. data/spec/lutaml/xml/pipeline_integration_spec.rb +145 -0
  143. data/spec/lutaml/xml/schema_primer_spec.rb +5 -5
  144. data/spec/lutaml/xml/transformation_spec.rb +20 -20
  145. data/spec/lutaml/xml/type_namespace/collector_spec.rb +1 -1
  146. data/spec/lutaml/xml/type_namespace/planner_spec.rb +3 -3
  147. data/spec/lutaml/xml/xml_spec.rb +64 -13
  148. data/spec/support/opal.rb +6 -0
  149. metadata +16 -4
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "XML with REXML under Opal", if: RUBY_ENGINE == "opal" do
6
+ before do
7
+ Lutaml::Model::Config.xml_adapter_type = :rexml
8
+ end
9
+
10
+ it "round-trips XML through parse and serialize" do
11
+ klass = Class.new do
12
+ include Lutaml::Model::Serialize
13
+
14
+ attribute :name, :string
15
+ xml do
16
+ element "person"
17
+ map_element "name", to: :name
18
+ end
19
+ end
20
+
21
+ instance = klass.from_xml("<person><name>Alice</name></person>")
22
+ expect(instance.name).to eq("Alice")
23
+ expect(instance.to_xml).to include("<name>Alice</name>")
24
+ end
25
+
26
+ it "handles XML attributes" do
27
+ klass = Class.new do
28
+ include Lutaml::Model::Serialize
29
+
30
+ attribute :id, :string
31
+ attribute :value, :string
32
+ xml do
33
+ element "item"
34
+ map_attribute "id", to: :id
35
+ map_element "value", to: :value
36
+ end
37
+ end
38
+
39
+ instance = klass.from_xml('<item id="42"><value>test</value></item>')
40
+ expect(instance.id).to eq("42")
41
+ expect(instance.value).to eq("test")
42
+ end
43
+
44
+ it "handles nested elements" do
45
+ address = Class.new do
46
+ include Lutaml::Model::Serialize
47
+
48
+ attribute :city, :string
49
+ xml do
50
+ element "address"
51
+ map_element "city", to: :city
52
+ end
53
+ end
54
+
55
+ person = Class.new do
56
+ include Lutaml::Model::Serialize
57
+
58
+ attribute :name, :string
59
+ attribute :address, address
60
+ xml do
61
+ element "person"
62
+ map_element "name", to: :name
63
+ map_element "address", to: :address
64
+ end
65
+ end
66
+
67
+ instance = person.from_xml("<person><name>Alice</name><address><city>NYC</city></address></person>")
68
+ expect(instance.name).to eq("Alice")
69
+ expect(instance.address.city).to eq("NYC")
70
+ end
71
+
72
+ it "handles element collections" do
73
+ list = Class.new do
74
+ include Lutaml::Model::Serialize
75
+
76
+ attribute :items, :string, collection: true
77
+ xml do
78
+ element "list"
79
+ map_element "item", to: :items
80
+ end
81
+ end
82
+
83
+ instance = list.from_xml("<list><item>a</item><item>b</item><item>c</item></list>")
84
+ expect(instance.items).to eq(%w[a b c])
85
+ end
86
+
87
+ it "handles mixed content" do
88
+ paragraph = Class.new do
89
+ include Lutaml::Model::Serialize
90
+
91
+ attribute :content, :string, collection: true
92
+ xml do
93
+ element "p"
94
+ mixed_content
95
+ map_content to: :content
96
+ end
97
+ end
98
+
99
+ instance = paragraph.from_xml("<p>Hello <b>world</b> end</p>")
100
+ expect(instance.content.join).to include("Hello")
101
+ end
102
+
103
+ it "serializes to XML with correct structure" do
104
+ item = Class.new do
105
+ include Lutaml::Model::Serialize
106
+
107
+ attribute :title, :string
108
+ attribute :count, :integer
109
+ xml do
110
+ element "item"
111
+ map_element "title", to: :title
112
+ map_element "count", to: :count
113
+ end
114
+ end
115
+
116
+ instance = item.new(title: "Widget", count: 5)
117
+ xml = instance.to_xml
118
+ expect(xml).to include("<title>Widget</title>")
119
+ expect(xml).to include("<count>5</count>")
120
+ end
121
+
122
+ it "handles XmlOrderable on plain model classes" do
123
+ plain_model = Class.new do
124
+ attr_accessor :data
125
+ end
126
+
127
+ Class.new do
128
+ include Lutaml::Model::Serialize
129
+
130
+ const_set(:MODEL_CLASS, plain_model)
131
+ model plain_model
132
+ attribute :data, :string
133
+ xml do
134
+ element "data"
135
+ map_element "data", to: :data
136
+ end
137
+ end
138
+
139
+ expect(plain_model.ancestors).to include(Lutaml::Xml::XmlOrderable)
140
+ end
141
+
142
+ it "uses REXML adapter by default under Opal" do
143
+ expect(Lutaml::Model::Config.xml_adapter_type).to eq(:rexml)
144
+ end
145
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "Full serialization pipeline" do
6
+ before do
7
+ item_class = Class.new do
8
+ include Lutaml::Model::Serialize
9
+
10
+ attribute :name, :string
11
+ attribute :value, :string
12
+
13
+ xml do
14
+ element "item"
15
+ map_element "name", to: :name
16
+ map_element "value", to: :value
17
+ end
18
+ end
19
+
20
+ stub_const("PipeItem", item_class)
21
+
22
+ doc_class = Class.new do
23
+ include Lutaml::Model::Serialize
24
+
25
+ attribute :title, :string
26
+ attribute :items, PipeItem, collection: true
27
+
28
+ xml do
29
+ element "document"
30
+ map_element "title", to: :title
31
+ map_element "item", to: :items
32
+ end
33
+ end
34
+
35
+ stub_const("PipeDoc", doc_class)
36
+ end
37
+
38
+ let(:model) do
39
+ PipeDoc.new(
40
+ title: "Test",
41
+ items: [
42
+ PipeItem.new(name: "a", value: "1"),
43
+ PipeItem.new(name: "b", value: "2"),
44
+ ],
45
+ )
46
+ end
47
+
48
+ it "produces identical output for identical models across multiple runs" do
49
+ first = model.to_xml
50
+ second = model.to_xml
51
+ expect(first).to eq(second)
52
+ end
53
+
54
+ it "handles declaration + doctype + content together" do
55
+ xml = model.to_xml(
56
+ declaration: true,
57
+ doctype: { name: "document", system_id: "doc.dtd" },
58
+ )
59
+
60
+ expect(xml).to start_with("<?xml")
61
+ expect(xml).to include("<!DOCTYPE document SYSTEM \"doc.dtd\">")
62
+ expect(xml).to include("<title>Test</title>")
63
+ expect(xml).to include("<name>a</name>")
64
+ end
65
+
66
+ it "CRLF + indent=4 + declaration produces correct output" do
67
+ xml = model.to_xml(
68
+ line_ending: "\r\n",
69
+ indent: 4,
70
+ declaration: true,
71
+ )
72
+
73
+ expect(xml.lines).to all(end_with("\r\n"))
74
+ expect(xml).to include(" <title>Test</title>")
75
+ expect(xml).to include("<?xml")
76
+ end
77
+
78
+ it "round-trips complex XML with all features preserved" do
79
+ input = <<~XML
80
+ <?xml version="1.0" encoding="UTF-8"?>
81
+ <document>
82
+ <title>Original</title>
83
+ <item>
84
+ <name>x</name>
85
+ <value>42</value>
86
+ </item>
87
+ </document>
88
+ XML
89
+
90
+ parsed = PipeDoc.from_xml(input)
91
+ expect(parsed.title).to eq("Original")
92
+ expect(parsed.items.length).to eq(1)
93
+ expect(parsed.items.first.name).to eq("x")
94
+ expect(parsed.items.first.value).to eq("42")
95
+
96
+ output = parsed.to_xml(declaration: true)
97
+ expect(output).to start_with("<?xml")
98
+ expect(output).to include("<title>Original</title>")
99
+ expect(output).to include("<name>x</name>")
100
+ end
101
+
102
+ it "round-trip preserves namespace declarations" do
103
+ ns = Class.new(Lutaml::Xml::W3c::XmlNamespace) do
104
+ uri "http://example.com/ns"
105
+ prefix_default "ex"
106
+ end
107
+
108
+ stub_const("PipeNs", ns)
109
+
110
+ ns_child = Class.new do
111
+ include Lutaml::Model::Serialize
112
+
113
+ attribute :content, :string
114
+
115
+ xml do
116
+ element "child"
117
+ namespace PipeNs
118
+ map_element "content", to: :content
119
+ end
120
+ end
121
+
122
+ stub_const("PipeNsChild", ns_child)
123
+
124
+ ns_parent = Class.new do
125
+ include Lutaml::Model::Serialize
126
+
127
+ attribute :child, PipeNsChild
128
+
129
+ xml do
130
+ element "parent"
131
+ map_element "child", to: :child
132
+ end
133
+ end
134
+
135
+ stub_const("PipeNsParent", ns_parent)
136
+
137
+ instance = PipeNsParent.new(child: PipeNsChild.new(content: "data"))
138
+ xml = instance.to_xml
139
+
140
+ expect(xml).to include("xmlns")
141
+
142
+ parsed = PipeNsParent.from_xml(xml)
143
+ expect(parsed.child.content).to eq("data")
144
+ end
145
+ end
@@ -44,7 +44,7 @@ module XmlSchemaPrimerFeaturesSpec
44
44
  attribute :work_address, AddressType
45
45
 
46
46
  xml do
47
- root "Person"
47
+ element "Person"
48
48
  map_element "homeAddress", to: :home_address
49
49
  map_element "workAddress", to: :work_address
50
50
  end
@@ -69,7 +69,7 @@ module XmlSchemaPrimerFeaturesSpec
69
69
  attribute :ship_to, UsAddress
70
70
 
71
71
  xml do
72
- root "purchaseOrder"
72
+ element "purchaseOrder"
73
73
  namespace PoNamespace
74
74
  map_element "shipTo", to: :ship_to
75
75
  end
@@ -85,7 +85,7 @@ module XmlSchemaPrimerFeaturesSpec
85
85
  attribute :comment, :string
86
86
 
87
87
  xml do
88
- root "purchaseOrder"
88
+ element "purchaseOrder"
89
89
  namespace PoNamespaceQualified
90
90
 
91
91
  map_element "shipTo", to: :ship_to
@@ -103,7 +103,7 @@ module XmlSchemaPrimerFeaturesSpec
103
103
  attribute :comment, :string
104
104
 
105
105
  xml do
106
- root "purchaseOrder"
106
+ element "purchaseOrder"
107
107
  namespace PoNamespacePrefixed
108
108
 
109
109
  map_element "shipTo", to: :ship_to
@@ -129,7 +129,7 @@ module XmlSchemaPrimerFeaturesSpec
129
129
  attribute :notes, :string
130
130
 
131
131
  xml do
132
- root "supplier"
132
+ element "supplier"
133
133
  namespace MixedNamespace
134
134
 
135
135
  map_element "contact", to: :contact
@@ -10,7 +10,7 @@ module TransformationSpecModels
10
10
  attribute :value, :integer
11
11
 
12
12
  xml do
13
- root "SimpleModel"
13
+ element "SimpleModel"
14
14
  map_element "name", to: :name
15
15
  map_element "value", to: :value
16
16
  end
@@ -21,7 +21,7 @@ module TransformationSpecModels
21
21
  attribute :type, :string
22
22
 
23
23
  xml do
24
- root "Model"
24
+ element "Model"
25
25
  map_attribute "id", to: :id
26
26
  map_attribute "type", to: :type
27
27
  end
@@ -31,7 +31,7 @@ module TransformationSpecModels
31
31
  attribute :text, :string
32
32
 
33
33
  xml do
34
- root "Model"
34
+ element "Model"
35
35
  map_content to: :text
36
36
  end
37
37
  end
@@ -41,7 +41,7 @@ module TransformationSpecModels
41
41
  attribute :email, :string
42
42
 
43
43
  xml do
44
- root "Contact"
44
+ element "Contact"
45
45
  map_element "name", to: :name
46
46
  map_element "email", to: :email
47
47
  end
@@ -52,7 +52,7 @@ module TransformationSpecModels
52
52
  attribute :name, :string
53
53
 
54
54
  xml do
55
- root "Person"
55
+ element "Person"
56
56
  map_attribute "id", to: :id
57
57
  map_element "name", to: :name
58
58
  end
@@ -62,7 +62,7 @@ module TransformationSpecModels
62
62
  attribute :content, :string
63
63
 
64
64
  xml do
65
- root "Note"
65
+ element "Note"
66
66
  map_content to: :content
67
67
  end
68
68
  end
@@ -72,7 +72,7 @@ module TransformationSpecModels
72
72
  attribute :city, :string
73
73
 
74
74
  xml do
75
- root "Address"
75
+ element "Address"
76
76
  map_element "street", to: :street
77
77
  map_element "city", to: :city
78
78
  end
@@ -83,7 +83,7 @@ module TransformationSpecModels
83
83
  attribute :address, TransformationSpecModels::Address
84
84
 
85
85
  xml do
86
- root "Person"
86
+ element "Person"
87
87
  map_element "name", to: :name
88
88
  map_element "Address", to: :address
89
89
  end
@@ -93,7 +93,7 @@ module TransformationSpecModels
93
93
  attribute :title, :string
94
94
 
95
95
  xml do
96
- root "Book"
96
+ element "Book"
97
97
  map_element "title", to: :title
98
98
  end
99
99
  end
@@ -103,7 +103,7 @@ module TransformationSpecModels
103
103
  attribute :book, TransformationSpecModels::Book
104
104
 
105
105
  xml do
106
- root "Library"
106
+ element "Library"
107
107
  map_element "name", to: :name
108
108
  map_element "Book", to: :book
109
109
  end
@@ -113,7 +113,7 @@ module TransformationSpecModels
113
113
  attribute :tags, :string, collection: true
114
114
 
115
115
  xml do
116
- root "TagList"
116
+ element "TagList"
117
117
  map_element "tag", to: :tags
118
118
  end
119
119
  end
@@ -123,7 +123,7 @@ module TransformationSpecModels
123
123
  attribute :price, :float
124
124
 
125
125
  xml do
126
- root "Item"
126
+ element "Item"
127
127
  map_element "name", to: :name
128
128
  map_element "price", to: :price
129
129
  end
@@ -133,7 +133,7 @@ module TransformationSpecModels
133
133
  attribute :items, TransformationSpecModels::Item, collection: true
134
134
 
135
135
  xml do
136
- root "Cart"
136
+ element "Cart"
137
137
  map_element "Item", to: :items
138
138
  end
139
139
  end
@@ -149,7 +149,7 @@ module TransformationSpecModels
149
149
  attribute :value, :string
150
150
 
151
151
  xml do
152
- root "Model"
152
+ element "Model"
153
153
  namespace TestNs::MyNamespace
154
154
  map_element "value", to: :value
155
155
  end
@@ -171,7 +171,7 @@ module TransformationSpecModels
171
171
  attribute :value, :string
172
172
 
173
173
  xml do
174
- root "Child"
174
+ element "Child"
175
175
  namespace NsTest::Ns2
176
176
  map_element "value", to: :value
177
177
  end
@@ -182,7 +182,7 @@ module TransformationSpecModels
182
182
  attribute :child, TransformationSpecModels::Child
183
183
 
184
184
  xml do
185
- root "Parent"
185
+ element "Parent"
186
186
  namespace NsTest::Ns1
187
187
  map_element "name", to: :name
188
188
  map_element "Child", to: :child
@@ -195,7 +195,7 @@ module TransformationSpecModels
195
195
  }
196
196
 
197
197
  xml do
198
- root "Model"
198
+ element "Model"
199
199
  map_element "name", to: :name
200
200
  end
201
201
  end
@@ -204,7 +204,7 @@ module TransformationSpecModels
204
204
  attribute :status, :string, default: -> { "active" }
205
205
 
206
206
  xml do
207
- root "Model"
207
+ element "Model"
208
208
  map_element "status", to: :status, render_default: false
209
209
  end
210
210
  end
@@ -213,7 +213,7 @@ module TransformationSpecModels
213
213
  attribute :optional, :string
214
214
 
215
215
  xml do
216
- root "Model"
216
+ element "Model"
217
217
  map_element "optional", to: :optional, render_nil: true
218
218
  end
219
219
  end
@@ -222,7 +222,7 @@ module TransformationSpecModels
222
222
  attribute :value, :string
223
223
 
224
224
  xml do
225
- root "Model"
225
+ element "Model"
226
226
  map_element "value", to: :value
227
227
  end
228
228
  end
@@ -30,7 +30,7 @@ RSpec.describe Lutaml::Xml::TypeNamespace::Collector do
30
30
  attribute :author, :string
31
31
 
32
32
  xml do
33
- root "document"
33
+ element "document"
34
34
  map_element "title", to: :title
35
35
  map_element "author", to: :author
36
36
  end
@@ -33,7 +33,7 @@ RSpec.describe Lutaml::Xml::TypeNamespace::Planner do
33
33
  attribute :author, :string
34
34
 
35
35
  xml do
36
- root "document"
36
+ element "document"
37
37
  map_element "title", to: :title
38
38
  map_element "author", to: :author
39
39
  end
@@ -155,7 +155,7 @@ RSpec.describe Lutaml::Xml::TypeNamespace::Planner do
155
155
  attribute :title, type
156
156
 
157
157
  xml do
158
- root "document"
158
+ element "document"
159
159
  map_element "title", to: :title
160
160
  end
161
161
  end
@@ -183,7 +183,7 @@ RSpec.describe Lutaml::Xml::TypeNamespace::Planner do
183
183
  attribute :title, :string
184
184
 
185
185
  xml do
186
- root "document"
186
+ element "document"
187
187
  map_element "title", to: :title
188
188
  end
189
189
  end
@@ -1,18 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "spec_helper"
4
+ require "open3"
4
5
  require "lutaml/xml"
5
6
 
6
7
  RSpec.describe Lutaml::Xml do
7
- describe "standalone loading" do
8
- let(:lib_path) { File.expand_path("../../../lib", __dir__) }
8
+ let(:lib_path) { File.expand_path("../../../lib", __dir__) }
9
9
 
10
- it "lazy-loads the schema namespace from the XML entry point" do
11
- # rubocop:disable Style/CommandLiteral
12
- result = `#{RbConfig.ruby} -I#{lib_path} -e 'require "lutaml/xml"; abort "missing Schema autoload" unless Lutaml::Xml.autoload?(:Schema); abort "missing Xsd autoload" unless Lutaml::Xml::Schema.const_defined?(:Xsd, false); puts :ok' 2>&1`
13
- # rubocop:enable Style/CommandLiteral
10
+ def run_ruby(script)
11
+ stdout, stderr, status = Open3.capture3(
12
+ RbConfig.ruby, "-I#{lib_path}", "-e", script
13
+ )
14
14
 
15
- expect($?.success?).to be(true), result
15
+ [stdout + stderr, status]
16
+ end
17
+
18
+ describe "standalone loading" do
19
+ it "lazy-loads the schema namespace from the XML entry point" do
20
+ script = <<~RUBY
21
+ require "lutaml/xml"
22
+ abort "missing Schema autoload" unless Lutaml::Xml.autoload?(:Schema)
23
+ unless Lutaml::Xml::Schema.const_defined?(:Xsd, false)
24
+ abort "missing Xsd autoload"
25
+ end
26
+ puts :ok
27
+ RUBY
28
+
29
+ result, status = run_ruby(script)
30
+
31
+ expect(status.success?).to be(true), result
16
32
  expect(result).to include("ok")
17
33
  end
18
34
  end
@@ -22,6 +38,44 @@ RSpec.describe Lutaml::Xml do
22
38
  expect(described_class::Schema.const_defined?(:Xsd, false))
23
39
  .to be(!Lutaml::Model::RuntimeCompatibility.opal?)
24
40
  end
41
+
42
+ it "does not autoload schema constants when loaded under Opal" do
43
+ script = <<~'RUBY'
44
+ require "lutaml/model/runtime_compatibility"
45
+
46
+ module Lutaml
47
+ module Model
48
+ module RuntimeCompatibility
49
+ def self.opal?
50
+ true
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ require "lutaml/xml/schema"
57
+
58
+ schema_constants = %i[
59
+ Xsd XsdSchema RelaxngSchema Builder BuiltinTypes
60
+ ]
61
+
62
+ loaded_constants = schema_constants.select do |constant_name|
63
+ Lutaml::Xml::Schema.autoload?(constant_name) ||
64
+ Lutaml::Xml::Schema.const_defined?(constant_name, false)
65
+ end
66
+
67
+ if loaded_constants.any?
68
+ abort "loaded schema constants: #{loaded_constants.join(', ')}"
69
+ end
70
+
71
+ puts :ok
72
+ RUBY
73
+
74
+ result, status = run_ruby(script)
75
+
76
+ expect(status.success?).to be(true), result
77
+ expect(result).to include("ok")
78
+ end
25
79
  end
26
80
 
27
81
  describe "schema methods" do
@@ -66,9 +120,6 @@ RSpec.describe Lutaml::Xml do
66
120
  hide_const("Ox") if Object.const_defined?(:Ox)
67
121
  hide_const("Oga") if Object.const_defined?(:Oga)
68
122
  hide_const("REXML") if Object.const_defined?(:REXML)
69
-
70
- # Stub require to prevent any actual requires during testing
71
- allow(Lutaml::Model::Utils).to receive(:require).and_return(true)
72
123
  end
73
124
 
74
125
  context "when Nokogiri is available" do
@@ -101,9 +152,9 @@ RSpec.describe Lutaml::Xml do
101
152
  end
102
153
  end
103
154
 
104
- context "when no adapters are available" do
105
- it "returns nil" do
106
- expect(described_class.detect_xml_adapter).to be_nil
155
+ context "when no adapters are detected" do
156
+ it "falls back to moxml default (:nokogiri)" do
157
+ expect(described_class.detect_xml_adapter).to eq(:nokogiri)
107
158
  end
108
159
  end
109
160
 
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Opal runtime patches for lutaml-model specs
4
+ if RUBY_ENGINE == "opal"
5
+ Lutaml::Model::Config.xml_adapter_type = :rexml
6
+ end