lutaml-model 0.8.11 → 0.8.12

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 (38) 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 +10 -1
  6. data/docs/_guides/index.adoc +4 -0
  7. data/docs/_guides/opal.adoc +221 -0
  8. data/docs/_guides/xml_mappings/07_best_practices.adoc +2 -1
  9. data/docs/_pages/configuration.adoc +9 -4
  10. data/docs/_pages/index.adoc +1 -0
  11. data/docs/_pages/serialization_adapters.adoc +3 -2
  12. data/docs/index.adoc +1 -0
  13. data/lib/lutaml/hash_format/adapter/mapping.rb +2 -4
  14. data/lib/lutaml/json/adapter/mapping.rb +2 -4
  15. data/lib/lutaml/jsonl/adapter/mapping.rb +2 -4
  16. data/lib/lutaml/key_value/adapter/hash/mapping.rb +2 -4
  17. data/lib/lutaml/key_value/adapter/json/mapping.rb +2 -4
  18. data/lib/lutaml/key_value/adapter/jsonl/mapping.rb +2 -4
  19. data/lib/lutaml/key_value/adapter/toml/mapping.rb +2 -4
  20. data/lib/lutaml/key_value/adapter/yaml/mapping.rb +2 -4
  21. data/lib/lutaml/key_value/adapter/yamls/mapping.rb +2 -4
  22. data/lib/lutaml/key_value/mapping.rb +4 -4
  23. data/lib/lutaml/model/adapter_resolver.rb +5 -8
  24. data/lib/lutaml/model/mapping/mapping.rb +12 -0
  25. data/lib/lutaml/model/version.rb +1 -1
  26. data/lib/lutaml/toml/adapter/mapping.rb +2 -4
  27. data/lib/lutaml/xml/schema/xsd.rb +5 -4
  28. data/lib/lutaml/xml/schema.rb +8 -5
  29. data/lib/lutaml/xml/xml_orderable.rb +17 -0
  30. data/lib/lutaml/xml.rb +8 -11
  31. data/lib/lutaml/yaml/adapter/mapping.rb +2 -4
  32. data/lib/lutaml/yamls/adapter/mapping.rb +7 -3
  33. data/lutaml-model.gemspec +1 -1
  34. data/spec/lutaml/model/opal_smoke_spec.rb +117 -0
  35. data/spec/lutaml/xml/opal_xml_spec.rb +145 -0
  36. data/spec/lutaml/xml/xml_spec.rb +64 -13
  37. data/spec/support/opal.rb +6 -0
  38. metadata +11 -4
@@ -218,17 +218,17 @@ module Lutaml
218
218
  end
219
219
 
220
220
  # Writers for deep_dup in subclasses
221
- attr_writer :mappings, :register_mappings
221
+ attr_writer :register_mappings
222
222
 
223
223
  def deep_dup
224
- self.class.new(@format).tap do |new_mapping|
224
+ dup_instance.tap do |new_mapping|
225
225
  new_mapping.mappings = duplicate_mappings
226
226
  new_mapping.register_mappings = Lutaml::Model::Utils.deep_dup(@register_mappings)
227
227
  end
228
228
  end
229
229
 
230
- def duplicate_mappings
231
- Lutaml::Model::Utils.deep_dup(@mappings)
230
+ def dup_instance
231
+ self.class.new(@format)
232
232
  end
233
233
 
234
234
  def find_by_to(to)
@@ -370,15 +370,12 @@ module Lutaml
370
370
 
371
371
  # Detect available XML adapter.
372
372
  #
373
- # @return [Symbol, nil] :nokogiri, :ox, :oga, :rexml, or nil
373
+ # Delegates to moxml which is the authority on XML adapter
374
+ # availability and platform constraints (Opal, MRI, etc.).
375
+ #
376
+ # @return [Symbol] adapter type name
374
377
  def detect_xml_adapter
375
- return :oga if RuntimeCompatibility.opal?
376
- return :nokogiri if Utils.safe_load("nokogiri", :Nokogiri)
377
- return :ox if Utils.safe_load("ox", :Ox)
378
- return :oga if Utils.safe_load("oga", :Oga)
379
- return :rexml if Utils.safe_load("rexml", :REXML)
380
-
381
- nil
378
+ Moxml::Config.runtime_default_adapter
382
379
  end
383
380
 
384
381
  # Detect available TOML adapter.
@@ -3,6 +3,8 @@ module Lutaml
3
3
  class Mapping
4
4
  include DeepDupable
5
5
 
6
+ attr_writer :mappings
7
+
6
8
  def initialize
7
9
  @mappings = []
8
10
  @listeners = {} # target => [Listener, ...]
@@ -11,6 +13,16 @@ module Lutaml
11
13
  @mappings_imported = ::Hash.new { |h, k| h[k] = false }
12
14
  end
13
15
 
16
+ def deep_dup
17
+ duped = self.class.new
18
+ duped.mappings = duplicate_mappings
19
+ duped
20
+ end
21
+
22
+ def duplicate_mappings
23
+ Lutaml::Model::Utils.deep_dup(@mappings)
24
+ end
25
+
14
26
  # Get listeners for a specific target (element name/key).
15
27
  #
16
28
  # @param target [String, Symbol] The element name or key
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.8.11"
5
+ VERSION = "0.8.12"
6
6
  end
7
7
  end
@@ -8,10 +8,8 @@ module Lutaml
8
8
  super(:toml)
9
9
  end
10
10
 
11
- def deep_dup
12
- self.class.new.tap do |new_mapping|
13
- new_mapping.mappings = duplicate_mappings
14
- end
11
+ def dup_instance
12
+ self.class.new
15
13
  end
16
14
 
17
15
  def validate!(key, to, with, render_nil, render_empty)
@@ -2,11 +2,12 @@
2
2
 
3
3
  # NOTE: Do NOT require lutaml/model here. This file is autoloaded via
4
4
  # Lutaml::Xml::Schema::Xsd, and requiring lutaml/model creates a circular
5
- # dependency since lutaml/xml.rb requires lutaml/model. The adapter type
6
- # is already set by lutaml/xml.rb at the end.
5
+ # dependency since lutaml/xml.rb requires lutaml/model. This file only sets
6
+ # a fallback XML adapter when no adapter has been selected yet.
7
7
 
8
- adapter = RUBY_ENGINE == "opal" ? :oga : :nokogiri
9
- Lutaml::Model::Config.xml_adapter_type = adapter unless defined?(Lutaml::Model::Config.xml_adapter_type)
8
+ unless defined?(Lutaml::Model::Config.xml_adapter_type)
9
+ Lutaml::Model::Config.xml_adapter_type = Lutaml::Model::AdapterResolver.detect_xml_adapter
10
+ end
10
11
 
11
12
  # Require the XsdNamespace class for XSD schema support
12
13
  require_relative "xsd_namespace"
@@ -3,11 +3,14 @@
3
3
  module Lutaml
4
4
  module Xml
5
5
  module Schema
6
- autoload :Xsd, "#{__dir__}/schema/xsd"
7
- autoload :XsdSchema, "#{__dir__}/schema/xsd_schema"
8
- autoload :RelaxngSchema, "#{__dir__}/schema/relaxng_schema"
9
- autoload :Builder, "#{__dir__}/schema/builder"
10
- autoload :BuiltinTypes, "#{__dir__}/schema/builtin_types"
6
+ Lutaml::Model::RuntimeCompatibility.autoload_native(
7
+ self,
8
+ Xsd: "#{__dir__}/schema/xsd",
9
+ XsdSchema: "#{__dir__}/schema/xsd_schema",
10
+ RelaxngSchema: "#{__dir__}/schema/relaxng_schema",
11
+ Builder: "#{__dir__}/schema/builder",
12
+ BuiltinTypes: "#{__dir__}/schema/builtin_types",
13
+ )
11
14
  end
12
15
  end
13
16
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Xml
5
+ # Mixin for XML element/attribute ordering state.
6
+ #
7
+ # Included by:
8
+ # - Lutaml::Xml::Serialization::InstanceMethods (Serialize instances)
9
+ # - Plain model classes via add_format_specific_model_methods
10
+ #
11
+ # Enables Lutaml::Model to check ordering capability with is_a? instead
12
+ # of respond_to?, following proper OOP type-checking.
13
+ module XmlOrderable
14
+ attr_accessor :element_order, :attribute_order, :ordered, :mixed
15
+ end
16
+ end
17
+ end
data/lib/lutaml/xml.rb CHANGED
@@ -42,16 +42,13 @@ module Lutaml
42
42
  # XML Schema modules
43
43
  autoload :Schema, "#{__dir__}/xml/schema"
44
44
 
45
- # Detect available XML adapter
46
- # @return [Symbol, nil] :nokogiri, :ox, :oga, :rexml, or nil
45
+ # Detect available XML adapter.
46
+ # Delegates to moxml, which is the authority on XML adapter
47
+ # availability and platform constraints.
48
+ #
49
+ # @return [Symbol] adapter type name
47
50
  def self.detect_xml_adapter
48
- return :oga if Lutaml::Model::RuntimeCompatibility.opal?
49
- return :nokogiri if Lutaml::Model::Utils.safe_load("nokogiri", :Nokogiri)
50
- return :ox if Lutaml::Model::Utils.safe_load("ox", :Ox)
51
- return :oga if Lutaml::Model::Utils.safe_load("oga", :Oga)
52
- return :rexml if Lutaml::Model::Utils.safe_load("rexml", :REXML)
53
-
54
- nil
51
+ Moxml::Config.runtime_default_adapter
55
52
  end
56
53
 
57
54
  # Get the current XML adapter
@@ -174,8 +171,8 @@ Lutaml::Model::FormatRegistry.register(
174
171
  ],
175
172
  adapter_options: if Lutaml::Model::RuntimeCompatibility.opal?
176
173
  {
177
- available: %i[oga],
178
- default: :oga,
174
+ available: %i[rexml],
175
+ default: :rexml,
179
176
  }
180
177
  else
181
178
  {
@@ -8,10 +8,8 @@ module Lutaml
8
8
  super(:yaml)
9
9
  end
10
10
 
11
- def deep_dup
12
- self.class.new.tap do |new_mapping|
13
- new_mapping.mappings = duplicate_mappings
14
- end
11
+ def dup_instance
12
+ self.class.new
15
13
  end
16
14
  end
17
15
  end
@@ -4,7 +4,7 @@ module Lutaml
4
4
  module Yamls
5
5
  module Adapter
6
6
  class Mapping < Lutaml::KeyValue::Mapping
7
- attr_reader :yamls_sequence
7
+ attr_accessor :yamls_sequence
8
8
 
9
9
  def initialize
10
10
  super(:yaml)
@@ -15,9 +15,13 @@ module Lutaml
15
15
  @yamls_sequence.instance_eval(&)
16
16
  end
17
17
 
18
+ def dup_instance
19
+ self.class.new
20
+ end
21
+
18
22
  def deep_dup
19
- self.class.new.tap do |new_mapping|
20
- new_mapping.mappings = duplicate_mappings
23
+ super.tap do |new_mapping|
24
+ new_mapping.yamls_sequence = @yamls_sequence&.dup
21
25
  end
22
26
  end
23
27
  end
data/lutaml-model.gemspec CHANGED
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_dependency "canon"
37
37
  spec.add_dependency "concurrent-ruby"
38
38
  spec.add_dependency "liquid", ">= 4.0", "< 6.0"
39
- spec.add_dependency "moxml", ">= 0.1.20"
39
+ spec.add_dependency "moxml", ">= 0.1.22"
40
40
  spec.add_dependency "ostruct"
41
41
  spec.add_dependency "rubyzip", "~> 2.3"
42
42
  spec.add_dependency "thor"
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "Opal compatibility", if: RUBY_ENGINE == "opal" do
6
+ it "includes Serialize in a class" do
7
+ klass = Class.new { include Lutaml::Model::Serialize }
8
+ expect(klass.include?(Lutaml::Model::Serialize)).to be true
9
+ end
10
+
11
+ it "defines attributes and serializes to hash" do
12
+ person = Class.new do
13
+ include Lutaml::Model::Serialize
14
+
15
+ attribute :name, :string
16
+ attribute :age, :integer
17
+ end
18
+
19
+ instance = person.new(name: "Alice", age: 30)
20
+ expect(instance.to_hash).to eq({ "name" => "Alice", "age" => 30 })
21
+ end
22
+
23
+ it "round-trips JSON serialization" do
24
+ person = Class.new do
25
+ include Lutaml::Model::Serialize
26
+
27
+ attribute :name, :string
28
+ attribute :age, :integer
29
+ end
30
+
31
+ instance = person.from_json('{"name":"Bob","age":25}')
32
+ expect(instance.name).to eq("Bob")
33
+ expect(instance.age).to eq(25)
34
+ end
35
+
36
+ it "handles collections" do
37
+ team = Class.new do
38
+ include Lutaml::Model::Serialize
39
+
40
+ attribute :members, :string, collection: true
41
+ end
42
+
43
+ instance = team.new(members: %w[Alice Bob Carol])
44
+ expect(instance.members).to eq(%w[Alice Bob Carol])
45
+ end
46
+
47
+ it "handles defaults" do
48
+ widget = Class.new do
49
+ include Lutaml::Model::Serialize
50
+
51
+ attribute :name, :string
52
+ attribute :active, :boolean, default: -> { true }
53
+ end
54
+
55
+ instance = widget.new(name: "test")
56
+ expect(instance.active).to be true
57
+ end
58
+
59
+ it "handles type coercion" do
60
+ record = Class.new do
61
+ include Lutaml::Model::Serialize
62
+
63
+ attribute :count, :integer
64
+ attribute :ratio, :float
65
+ attribute :flag, :boolean
66
+ end
67
+
68
+ instance = record.new(count: "42", ratio: "3.14", flag: "true")
69
+ expect(instance.count).to eq(42)
70
+ expect(instance.ratio).to eq(3.14)
71
+ expect(instance.flag).to be true
72
+ end
73
+
74
+ it "handles nested models" do
75
+ address = Class.new do
76
+ include Lutaml::Model::Serialize
77
+
78
+ attribute :city, :string
79
+ attribute :zip, :string
80
+ end
81
+
82
+ person = Class.new do
83
+ include Lutaml::Model::Serialize
84
+
85
+ attribute :name, :string
86
+ attribute :address, address
87
+ end
88
+
89
+ instance = person.new(name: "Alice",
90
+ address: address.new(
91
+ city: "NYC", zip: "10001",
92
+ ))
93
+ expect(instance.address.city).to eq("NYC")
94
+ end
95
+
96
+ it "handles YAML serialization" do
97
+ config = Class.new do
98
+ include Lutaml::Model::Serialize
99
+
100
+ attribute :host, :string
101
+ attribute :port, :integer
102
+ end
103
+
104
+ instance = config.from_yaml("host: localhost\nport: 8080\n")
105
+ expect(instance.host).to eq("localhost")
106
+ expect(instance.port).to eq(8080)
107
+ end
108
+
109
+ it "RuntimeCompatibility detects Opal" do
110
+ expect(Lutaml::Model::RuntimeCompatibility.opal?).to be true
111
+ end
112
+
113
+ it "AdapterResolver auto-detects REXML" do
114
+ adapter = Lutaml::Model::AdapterResolver.detect_xml_adapter
115
+ expect(adapter).to eq(:rexml)
116
+ end
117
+ end
@@ -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
+ root "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
+ root "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
+ root "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
+ root "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
+ root "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
+ root "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
+ root "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
+ root "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
@@ -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