moxml 0.1.23 → 0.1.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +1 -1
  3. data/.github/workflows/opal.yml +18 -1
  4. data/.github/workflows/rake.yml +32 -3
  5. data/.github/workflows/round-trip.yml +72 -9
  6. data/.gitmodules +6 -0
  7. data/.rubocop_todo.yml +2 -0
  8. data/Gemfile +9 -1
  9. data/README.adoc +124 -0
  10. data/Rakefile +117 -5
  11. data/lib/compat/opal/moxml_boot.rb +57 -0
  12. data/lib/moxml/adapter/base.rb +5 -5
  13. data/lib/moxml/adapter/customized_libxml/cdata.rb +0 -2
  14. data/lib/moxml/adapter/customized_libxml/comment.rb +0 -2
  15. data/lib/moxml/adapter/customized_libxml/element.rb +3 -5
  16. data/lib/moxml/adapter/customized_libxml/node.rb +2 -2
  17. data/lib/moxml/adapter/customized_libxml/processing_instruction.rb +0 -2
  18. data/lib/moxml/adapter/customized_libxml/text.rb +0 -2
  19. data/lib/moxml/adapter/customized_rexml/formatter.rb +3 -4
  20. data/lib/moxml/adapter/headed_ox.rb +1 -5
  21. data/lib/moxml/adapter/libxml.rb +12 -10
  22. data/lib/moxml/adapter/nokogiri.rb +4 -2
  23. data/lib/moxml/adapter/oga.rb +10 -10
  24. data/lib/moxml/adapter/ox.rb +29 -16
  25. data/lib/moxml/adapter/rexml.rb +5 -6
  26. data/lib/moxml/adapter.rb +17 -7
  27. data/lib/moxml/attribute.rb +5 -2
  28. data/lib/moxml/config.rb +2 -1
  29. data/lib/moxml/context.rb +8 -8
  30. data/lib/moxml/declaration.rb +2 -7
  31. data/lib/moxml/document.rb +0 -11
  32. data/lib/moxml/document_builder.rb +12 -8
  33. data/lib/moxml/element.rb +4 -4
  34. data/lib/moxml/node.rb +37 -14
  35. data/lib/moxml/node_set.rb +2 -4
  36. data/lib/moxml/sax/block_handler.rb +0 -2
  37. data/lib/moxml/sax/element_handler.rb +0 -2
  38. data/lib/moxml/sax/namespace_splitter.rb +5 -4
  39. data/lib/moxml/sax.rb +4 -25
  40. data/lib/moxml/version.rb +1 -1
  41. data/lib/moxml/xml_utils.rb +1 -3
  42. data/lib/moxml/xpath/compiler.rb +1 -49
  43. data/lib/moxml/xpath/conversion.rb +7 -6
  44. data/lib/moxml/xpath/ruby/generator.rb +12 -19
  45. data/lib/moxml/xpath/ruby/node.rb +1 -9
  46. data/lib/moxml/xpath.rb +6 -14
  47. data/lib/moxml.rb +67 -20
  48. data/moxml.gemspec +1 -1
  49. data/spec/moxml/adapter/platform_spec.rb +64 -46
  50. data/spec/moxml/attribute_spec.rb +16 -0
  51. data/spec/moxml/context_spec.rb +14 -0
  52. data/spec/moxml/moxml_spec.rb +13 -0
  53. data/spec/moxml/node_spec.rb +58 -0
  54. data/spec/moxml/opal_oga_adapter_spec.rb +14 -0
  55. data/spec/moxml/opal_oga_features_spec.rb +212 -0
  56. data/spec/moxml/opal_oga_smoke_spec.rb +35 -0
  57. data/spec/moxml/xpath/functions/node_functions_spec.rb +1 -1
  58. data/spec/moxml/xpath/ruby/node_spec.rb +3 -3
  59. data/spec/spec_helper.rb +1 -1
  60. metadata +7 -2
@@ -2,69 +2,87 @@
2
2
 
3
3
  require "spec_helper"
4
4
 
5
- RSpec.describe Moxml::Adapter, ".platform_adapters" do
6
- it "includes all known adapters under MRI" do
7
- expect(described_class.platform_adapters).to include(:nokogiri, :oga,
8
- :rexml, :ox)
9
- end
10
-
11
- it "uses the AVAILABLE_ADAPTERS constant under MRI" do
12
- expect(described_class.platform_adapters).to eq(Moxml::Adapter::AVAILABLE_ADAPTERS)
13
- end
14
- end
15
-
16
- RSpec.describe Moxml::Adapter, ".available?" do
17
- it "returns true for :oga" do
18
- expect(described_class.available?(:oga)).to be true
19
- end
5
+ RSpec.describe Moxml::Adapter do
6
+ describe ".platform_adapters" do
7
+ it "includes all known adapters under MRI" do
8
+ expect(described_class.platform_adapters).to include(:nokogiri, :oga,
9
+ :rexml, :ox)
10
+ end
20
11
 
21
- it "returns true for :nokogiri under MRI" do
22
- expect(described_class.available?(:nokogiri)).to be true
12
+ it "uses the AVAILABLE_ADAPTERS constant under MRI" do
13
+ expect(described_class.platform_adapters).to eq(Moxml::Adapter::AVAILABLE_ADAPTERS)
14
+ end
23
15
  end
24
16
 
25
- context "when Opal platform adapters are in effect" do
26
- before do
27
- allow(described_class).to receive(:platform_adapters)
28
- .and_return(Moxml::Adapter::OPAL_AVAILABLE_ADAPTERS)
17
+ describe ".available?" do
18
+ it "returns true for :oga" do
19
+ expect(described_class.available?(:oga)).to be true
29
20
  end
30
21
 
31
- it "returns false for :nokogiri" do
32
- expect(described_class.available?(:nokogiri)).to be false
22
+ it "returns true for :nokogiri under MRI" do
23
+ expect(described_class.available?(:nokogiri)).to be true
33
24
  end
34
25
 
35
- it "returns true for :rexml" do
36
- expect(described_class.available?(:rexml)).to be true
26
+ context "when Opal platform adapters are in effect" do
27
+ before do
28
+ allow(described_class).to receive(:platform_adapters)
29
+ .and_return(Moxml::Adapter::OPAL_AVAILABLE_ADAPTERS)
30
+ end
31
+
32
+ it "returns false for :nokogiri" do
33
+ expect(described_class.available?(:nokogiri)).to be false
34
+ end
35
+
36
+ it "returns true for :oga" do
37
+ expect(described_class.available?(:oga)).to be true
38
+ end
39
+
40
+ it "returns true for :rexml" do
41
+ expect(described_class.available?(:rexml)).to be true
42
+ end
37
43
  end
38
44
  end
39
- end
40
45
 
41
- RSpec.describe Moxml::Adapter, ".load" do
42
- context "when Opal platform adapters are in effect" do
43
- before do
44
- allow(described_class).to receive(:platform_adapters)
45
- .and_return(Moxml::Adapter::OPAL_AVAILABLE_ADAPTERS)
46
- end
46
+ describe ".load" do
47
+ context "when Opal platform adapters are in effect" do
48
+ before do
49
+ allow(described_class).to receive(:platform_adapters)
50
+ .and_return(Moxml::Adapter::OPAL_AVAILABLE_ADAPTERS)
51
+ end
47
52
 
48
- it "raises AdapterError for :nokogiri" do
49
- expect { described_class.load(:nokogiri) }.to raise_error(
50
- Moxml::AdapterError, /not available on this platform/
51
- )
53
+ it "raises AdapterError for :nokogiri" do
54
+ expect { described_class.load(:nokogiri) }.to raise_error(
55
+ Moxml::AdapterError, /not available on this platform/
56
+ )
57
+ end
52
58
  end
53
59
  end
54
- end
55
60
 
56
- RSpec.describe "Moxml::Adapter::OPAL_AVAILABLE_ADAPTERS" do
57
- it "contains only :rexml" do
58
- expect(Moxml::Adapter::OPAL_AVAILABLE_ADAPTERS).to eq(%i[rexml])
61
+ describe "OPAL_AVAILABLE_ADAPTERS" do
62
+ it "lists :oga as the primary Opal adapter, with :rexml as opt-in" do
63
+ expect(Moxml::Adapter::OPAL_AVAILABLE_ADAPTERS).to eq(%i[oga rexml])
64
+ end
59
65
  end
60
- end
61
66
 
62
- RSpec.describe "Moxml::Adapter::CONST_NAME_MAP" do
63
- it "maps :headed_ox to HeadedOx" do
64
- expect(Moxml::Adapter::CONST_NAME_MAP[:headed_ox]).to eq("HeadedOx")
67
+ describe "CONST_NAME_MAP" do
68
+ it "maps :headed_ox to HeadedOx" do
69
+ expect(Moxml::Adapter::CONST_NAME_MAP[:headed_ox]).to eq("HeadedOx")
70
+ end
71
+
72
+ it "falls back to capitalize for unmapped adapters" do
73
+ expect(Moxml::Adapter::CONST_NAME_MAP[:nokogiri]).to be_nil
74
+ end
65
75
  end
66
76
 
67
- it "falls back to capitalize for unmapped adapters" do
68
- expect(Moxml::Adapter::CONST_NAME_MAP[:nokogiri]).to be_nil
77
+ describe "default adapter / available list invariants" do
78
+ it "DEFAULT_ADAPTER is a member of AVAILABLE_ADAPTERS" do
79
+ expect(Moxml::Adapter::AVAILABLE_ADAPTERS)
80
+ .to include(Moxml::Config::DEFAULT_ADAPTER)
81
+ end
82
+
83
+ it "OPAL_DEFAULT_ADAPTER is a member of OPAL_AVAILABLE_ADAPTERS" do
84
+ expect(Moxml::Adapter::OPAL_AVAILABLE_ADAPTERS)
85
+ .to include(Moxml::Config::OPAL_DEFAULT_ADAPTER)
86
+ end
69
87
  end
70
88
  end
@@ -27,4 +27,20 @@ RSpec.describe Moxml::Attribute do
27
27
  expect(attr.to_s).to match(/\w+="\w+"/)
28
28
  end
29
29
  end
30
+
31
+ describe "#element" do
32
+ it "returns wrapped Element" do
33
+ attr = element.attributes.find { |a| a.name == "id" }
34
+ parent = attr.element
35
+ expect(parent).to be_a(Moxml::Element)
36
+ expect(parent.name).to eq("root")
37
+ end
38
+ end
39
+
40
+ describe "#content" do
41
+ it "returns attribute value" do
42
+ attr = element.attributes.find { |a| a.name == "id" }
43
+ expect(attr.content).to eq("123")
44
+ end
45
+ end
30
46
  end
@@ -25,4 +25,18 @@ RSpec.describe Moxml::Context do
25
25
  expect(context.config.adapter.ancestors).to include(Moxml::Adapter::Base)
26
26
  end
27
27
  end
28
+
29
+ describe "#build" do
30
+ it "creates a document via builder DSL" do
31
+ doc = context.build do
32
+ element("root") do
33
+ element("child") do
34
+ text("hello")
35
+ end
36
+ end
37
+ end
38
+ expect(doc).to be_a(Moxml::Document)
39
+ expect(doc.root.name).to eq("root")
40
+ end
41
+ end
28
42
  end
@@ -52,4 +52,17 @@ RSpec.describe Moxml do
52
52
  expect(Moxml::Config::VALID_ADAPTERS).to include(Moxml::Config.runtime_default_adapter)
53
53
  end
54
54
  end
55
+
56
+ describe ".parse" do
57
+ it "parses XML with shorthand" do
58
+ doc = described_class.parse("<root>hello</root>")
59
+ expect(doc).to be_a(Moxml::Document)
60
+ expect(doc.root.text).to eq("hello")
61
+ end
62
+
63
+ it "accepts adapter option" do
64
+ doc = described_class.parse("<root/>", adapter: :rexml)
65
+ expect(doc).to be_a(Moxml::Document)
66
+ end
67
+ end
55
68
  end
@@ -34,4 +34,62 @@ RSpec.describe Moxml::Node do
34
34
  expect(node.children).to be_empty
35
35
  end
36
36
  end
37
+
38
+ describe "#each" do
39
+ it "yields direct children" do
40
+ children = node.map { |c| c }
41
+ expect(children.size).to eq(1)
42
+ expect(children.first).to be_a(Moxml::Element)
43
+ end
44
+
45
+ it "includes Enumerable" do
46
+ expect(node.map(&:name)).to eq(["child"])
47
+ end
48
+ end
49
+
50
+ describe "#outer_xml" do
51
+ it "returns same as to_xml" do
52
+ child = node.children.first
53
+ expect(child.outer_xml).to eq(child.to_xml)
54
+ end
55
+ end
56
+
57
+ describe "#before" do
58
+ it "adds a node before self" do
59
+ child = node.children.first
60
+ child.before("before")
61
+ siblings = node.children.map(&:content).join
62
+ expect(siblings).to include("before")
63
+ end
64
+ end
65
+
66
+ describe "#after" do
67
+ it "adds a node after self" do
68
+ child = node.children.first
69
+ child.after("after")
70
+ expect(node.children.size).to be >= 2
71
+ end
72
+ end
73
+
74
+ describe "#blank?" do
75
+ it "returns true for whitespace-only text" do
76
+ doc2 = context.parse("<root> </root>")
77
+ expect(doc2.root).to be_blank
78
+ end
79
+
80
+ it "returns false for non-blank content" do
81
+ expect(node).not_to be_blank
82
+ end
83
+ end
84
+
85
+ describe "#content" do
86
+ it "returns empty string on base Node" do
87
+ expect(described_class.new(nil, context).content).to eq("")
88
+ end
89
+
90
+ it "returns text on Element" do
91
+ child = node.children.first
92
+ expect(child.content).to eq("text")
93
+ end
94
+ end
37
95
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "moxml/adapter/shared_examples/adapter_contract"
5
+
6
+ RSpec.describe Moxml::Adapter::Oga, if: RUBY_ENGINE == "opal" do
7
+ around do |example|
8
+ Moxml.with_config(:oga, true, "UTF-8") do
9
+ example.run
10
+ end
11
+ end
12
+
13
+ it_behaves_like "xml adapter"
14
+ end
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # Exercises feature areas under Opal that are not covered by the shared
6
+ # adapter contract (opal_oga_adapter_spec.rb) or the basic smoke spec
7
+ # (opal_oga_smoke_spec.rb). Targets the default Opal adapter (:oga).
8
+ # Uses an explicit :oga context so leaked global config from other specs
9
+ # cannot flip the adapter under test.
10
+ RSpec.describe "Moxml Opal oga feature coverage", if: RUBY_ENGINE == "opal" do
11
+ let(:context) { Moxml.new(:oga) }
12
+
13
+ describe "builder DSL" do
14
+ it "builds a document with the block DSL" do
15
+ doc = Moxml::Builder.new(context).build do
16
+ root do
17
+ child "text"
18
+ end
19
+ end
20
+
21
+ expect(doc.root.name).to eq("root")
22
+ expect(doc.root.children.first.name).to eq("child")
23
+ expect(doc.root.children.first.text).to eq("text")
24
+ end
25
+
26
+ it "creates nested elements via blocks" do
27
+ doc = Moxml::Builder.new(context).build do
28
+ library do
29
+ book(id: "1") { title "A" }
30
+ book(id: "2") { title "B" }
31
+ end
32
+ end
33
+
34
+ books = doc.root.children.grep(Moxml::Element)
35
+ expect(books.length).to eq(2)
36
+ expect(books.map { |b| b["id"] }).to eq(%w[1 2])
37
+ end
38
+
39
+ it "supports method_missing DSL with attributes and text" do
40
+ doc = Moxml::Builder.new(context).build do
41
+ person(name: "Alice", age: "30") { email "alice@example.com" }
42
+ end
43
+
44
+ person = doc.root
45
+ expect(person.name).to eq("person")
46
+ expect(person["name"]).to eq("Alice")
47
+ expect(person["age"]).to eq("30")
48
+ expect(person.children.first.name).to eq("email")
49
+ expect(person.children.first.text).to eq("alice@example.com")
50
+ end
51
+
52
+ it "strips trailing underscore for reserved-name tags" do
53
+ doc = Moxml::Builder.new(context).build do
54
+ class_ { name "Foo" }
55
+ end
56
+
57
+ expect(doc.root.name).to eq("class")
58
+ end
59
+
60
+ it "attaches namespace declarations" do
61
+ builder = Moxml::Builder.new(context)
62
+ doc = builder.build do
63
+ root("xmlns:dc": "http://purl.org/dc/elements/1.1/") do
64
+ builder.element("dc:title") { builder.text "Hello" }
65
+ end
66
+ end
67
+
68
+ title = doc.root.children.find { |c| c.is_a?(Moxml::Element) }
69
+ expect(title.name).to eq("dc:title")
70
+ expect(doc.to_xml).to include("xmlns:dc")
71
+ expect(doc.to_xml).to include("dc:title")
72
+ end
73
+ end
74
+
75
+ describe "XML declaration preservation" do
76
+ it "does not add a declaration when the input has none" do
77
+ doc = context.parse("<root><child/></root>")
78
+ output = doc.to_xml
79
+
80
+ expect(output).not_to include("<?xml")
81
+ expect(doc.has_xml_declaration).to be false
82
+ end
83
+
84
+ it "preserves the declaration when the input has one" do
85
+ xml = '<?xml version="1.0" encoding="UTF-8"?><root/>'
86
+ doc = context.parse(xml)
87
+
88
+ expect(doc.has_xml_declaration).to be true
89
+ expect(doc.to_xml).to include("<?xml")
90
+ expect(doc.to_xml).to include('version="1.0"')
91
+ end
92
+
93
+ it "forces the declaration on with declaration: true" do
94
+ doc = context.parse("<root/>")
95
+ expect(doc.to_xml(declaration: true)).to include("<?xml")
96
+ end
97
+
98
+ it "forces the declaration off with declaration: false" do
99
+ doc = context.parse('<?xml version="1.0"?><root/>')
100
+ expect(doc.to_xml(declaration: false)).not_to include("<?xml")
101
+ end
102
+
103
+ it "preserves standalone attribute" do
104
+ xml = '<?xml version="1.0" standalone="yes"?><root/>'
105
+ doc = context.parse(xml)
106
+ expect(doc.to_xml).to include("standalone")
107
+ end
108
+ end
109
+
110
+ describe "doctype handling" do
111
+ it "parses a SIMPLE doctype" do
112
+ doc = context.parse("<!DOCTYPE root><root/>")
113
+ doctype = doc.children.find { |c| c.is_a?(Moxml::Doctype) }
114
+ expect(doctype).not_to be_nil
115
+ expect(doctype.name).to eq("root")
116
+ end
117
+
118
+ it "parses a PUBLIC doctype with external and system ids" do
119
+ xml = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html/>'
120
+ doc = context.parse(xml)
121
+ doctype = doc.children.find { |c| c.is_a?(Moxml::Doctype) }
122
+
123
+ expect(doctype).not_to be_nil
124
+ expect(doctype.name).to eq("html")
125
+ expect(doctype.external_id).to eq("-//W3C//DTD HTML 4.01//EN")
126
+ expect(doctype.system_id).to eq("http://www.w3.org/TR/html4/strict.dtd")
127
+ end
128
+
129
+ it "round-trips the doctype through serialization" do
130
+ xml = "<!DOCTYPE root><root/>"
131
+ doc = context.parse(xml)
132
+ expect(doc.to_xml).to include("DOCTYPE")
133
+ expect(doc.to_xml).to include("root")
134
+ end
135
+ end
136
+
137
+ describe "round-trip stability" do
138
+ it "round-trips a document with mixed content" do
139
+ xml = <<~XML.strip
140
+ <root attr="value">
141
+ <child>text</child>
142
+ <!-- comment -->
143
+ <nested><deep>data</deep></nested>
144
+ </root>
145
+ XML
146
+
147
+ doc1 = context.parse(xml)
148
+ serialized1 = doc1.to_xml
149
+ doc2 = context.parse(serialized1)
150
+ serialized2 = doc2.to_xml
151
+
152
+ expect(serialized2).to eq(serialized1)
153
+ end
154
+
155
+ it "round-trips a document with namespaces" do
156
+ xml = '<root xmlns:ns="http://example.com/ns"><ns:child>content</ns:child></root>'
157
+ doc1 = context.parse(xml)
158
+ serialized1 = doc1.to_xml
159
+
160
+ doc2 = context.parse(serialized1)
161
+ child = doc2.root.children.find { |c| c.is_a?(Moxml::Element) }
162
+ expect(child.name).to eq("child")
163
+ expect(child.namespace_prefix).to eq("ns")
164
+ expect(doc2.to_xml).to include("xmlns:ns")
165
+ end
166
+
167
+ it "round-trips a document with CDATA" do
168
+ xml = "<root><![CDATA[<unparsed>content</unparsed>]]></root>"
169
+ doc1 = context.parse(xml)
170
+ serialized1 = doc1.to_xml
171
+
172
+ expect(serialized1).to include("<![CDATA[")
173
+ doc2 = context.parse(serialized1)
174
+ expect(doc2.to_xml).to include("<![CDATA[")
175
+ end
176
+ end
177
+
178
+ describe "XPath under Opal" do
179
+ it "evaluates element-only XPath" do
180
+ doc = context.parse("<root><a><b>1</b></a><a><b>2</b></a></root>")
181
+ results = doc.xpath("//a/b")
182
+ expect(results.length).to eq(2)
183
+ expect(results.map(&:text)).to eq(%w[1 2])
184
+ end
185
+
186
+ it "supports attribute predicates" do
187
+ doc = context.parse('<root><item id="x">1</item><item id="y">2</item></root>')
188
+ found = doc.xpath("//item").find { |i| i["id"] == "y" }
189
+ expect(found.text).to eq("2")
190
+ end
191
+
192
+ it "supports at_xpath" do
193
+ doc = context.parse("<root><a>1</a><a>2</a></root>")
194
+ first = doc.at_xpath("//a")
195
+ expect(first.text).to eq("1")
196
+ end
197
+ end
198
+
199
+ describe "comment and processing instruction handling" do
200
+ it "preserves comments through parse/serialize" do
201
+ xml = "<root><!-- my comment --></root>"
202
+ doc = context.parse(xml)
203
+ expect(doc.to_xml).to include("my comment")
204
+ end
205
+
206
+ it "preserves processing instructions through parse/serialize" do
207
+ xml = "<?pi-target pi-data?><root/>"
208
+ doc = context.parse(xml)
209
+ expect(doc.to_xml).to include("pi-target")
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # Verifies that the Opal default adapter (:oga, set in Moxml::Config)
6
+ # loads correctly via the Opal-compatible oga fork. The fork's
7
+ # lib/oga.rb dispatches `require 'liboga'` to `require 'oga/native/lexer'`
8
+ # under Opal, which resolves to the pure-Ruby lexer vendored at
9
+ # vendor/opal-oga/ext/pureruby/.
10
+ RSpec.describe "Moxml Opal oga default", if: RUBY_ENGINE == "opal" do
11
+ let(:context) { Moxml.new }
12
+
13
+ it "defaults to :oga under Opal" do
14
+ expect(Moxml::Config::OPAL_DEFAULT_ADAPTER).to eq(:oga)
15
+ end
16
+
17
+ it "parses XML through the oga adapter" do
18
+ doc = context.parse("<root><child>text</child></root>")
19
+ expect(doc.root.name).to eq("root")
20
+ expect(doc.root.children.first.name).to eq("child")
21
+ end
22
+
23
+ it "decodes entities single-pass" do
24
+ doc = context.parse("<root>&amp;#38;</root>")
25
+ expect(doc.root.text).to eq("&#38;")
26
+ end
27
+
28
+ it "serializes back to XML" do
29
+ xml = '<person name="Alice"><age>30</age></person>'
30
+ doc = context.parse(xml)
31
+ serialized = doc.to_xml
32
+ expect(serialized).to include("person")
33
+ expect(serialized).to include("Alice")
34
+ end
35
+ end
@@ -57,7 +57,7 @@ RSpec.describe "XPath Node Functions" do
57
57
  result = proc.call(doc)
58
58
 
59
59
  # Depending on adapter, may include ns: prefix
60
- expect(result).to match(/item/)
60
+ expect(result).to include("item")
61
61
  end
62
62
 
63
63
  it "returns empty string when no node matched" do
@@ -113,13 +113,13 @@ RSpec.describe Moxml::XPath::Ruby::Node do
113
113
  describe "#is_a?" do
114
114
  it "creates a :send node for is_a? method call" do
115
115
  node = described_class.new(:lit, ["obj"])
116
- result = node.is_a?(String)
116
+ klass_node = described_class.new(:const, ["String"])
117
+ result = node.is_a?(klass_node)
117
118
 
118
119
  expect(result.type).to eq(:send)
119
120
  expect(result.to_a[0]).to eq(node)
120
121
  expect(result.to_a[1]).to eq("is_a?")
121
- expect(result.to_a[2].type).to eq(:lit)
122
- expect(result.to_a[2].to_a[0]).to eq("String")
122
+ expect(result.to_a[2]).to eq(klass_node)
123
123
  end
124
124
  end
125
125
 
data/spec/spec_helper.rb CHANGED
@@ -101,7 +101,7 @@ RSpec.configure do |config|
101
101
  end
102
102
 
103
103
  Moxml.configure do |config|
104
- config.adapter = RUBY_ENGINE == "opal" ? :rexml : :nokogiri
104
+ config.adapter = RUBY_ENGINE == "opal" ? Moxml::Config::OPAL_DEFAULT_ADAPTER : Moxml::Config::DEFAULT_ADAPTER
105
105
  config.strict_parsing = true
106
106
  config.default_encoding = "UTF-8"
107
107
  config.entity_load_mode = :optional if RUBY_ENGINE == "opal"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.23
4
+ version: 0.1.25
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-01 00:00:00.000000000 Z
11
+ date: 2026-06-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Moxml is a unified XML manipulation library that provides a common API
@@ -28,6 +28,7 @@ files:
28
28
  - ".github/workflows/release.yml"
29
29
  - ".github/workflows/round-trip.yml"
30
30
  - ".gitignore"
31
+ - ".gitmodules"
31
32
  - ".rspec"
32
33
  - ".rspec-opal"
33
34
  - ".rubocop.yml"
@@ -98,6 +99,7 @@ files:
98
99
  - examples/web_scraper/README.md
99
100
  - examples/web_scraper/example_page.html
100
101
  - examples/web_scraper/web_scraper.rb
102
+ - lib/compat/opal/moxml_boot.rb
101
103
  - lib/compat/opal/rexml/namespace.rb
102
104
  - lib/compat/opal/rexml/parsers/baseparser.rb
103
105
  - lib/compat/opal/rexml/source.rb
@@ -351,6 +353,9 @@ files:
351
353
  - spec/moxml/node_set_spec.rb
352
354
  - spec/moxml/node_spec.rb
353
355
  - spec/moxml/node_type_map_spec.rb
356
+ - spec/moxml/opal_oga_adapter_spec.rb
357
+ - spec/moxml/opal_oga_features_spec.rb
358
+ - spec/moxml/opal_oga_smoke_spec.rb
354
359
  - spec/moxml/opal_rexml_adapter_spec.rb
355
360
  - spec/moxml/opal_smoke_spec.rb
356
361
  - spec/moxml/processing_instruction_spec.rb