moxml 0.1.7 → 0.1.9
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.
- checksums.yaml +4 -4
- data/.github/workflows/dependent-repos.json +5 -0
- data/.github/workflows/dependent-tests.yml +20 -0
- data/.github/workflows/docs.yml +59 -0
- data/.github/workflows/rake.yml +10 -10
- data/.github/workflows/release.yml +5 -3
- data/.gitignore +37 -0
- data/.rubocop.yml +15 -7
- data/.rubocop_todo.yml +224 -43
- data/Gemfile +14 -9
- data/LICENSE.md +6 -2
- data/README.adoc +535 -373
- data/Rakefile +53 -0
- data/benchmarks/.gitignore +6 -0
- data/benchmarks/generate_report.rb +550 -0
- data/docs/Gemfile +13 -0
- data/docs/_config.yml +138 -0
- data/docs/_guides/advanced-features.adoc +87 -0
- data/docs/_guides/development-testing.adoc +165 -0
- data/docs/_guides/index.adoc +51 -0
- data/docs/_guides/modifying-xml.adoc +292 -0
- data/docs/_guides/parsing-xml.adoc +230 -0
- data/docs/_guides/sax-parsing.adoc +603 -0
- data/docs/_guides/working-with-documents.adoc +118 -0
- data/docs/_guides/xml-declaration.adoc +450 -0
- data/docs/_pages/adapter-compatibility.adoc +369 -0
- data/docs/_pages/adapters/headed-ox.adoc +237 -0
- data/docs/_pages/adapters/index.adoc +97 -0
- data/docs/_pages/adapters/libxml.adoc +285 -0
- data/docs/_pages/adapters/nokogiri.adoc +251 -0
- data/docs/_pages/adapters/oga.adoc +291 -0
- data/docs/_pages/adapters/ox.adoc +56 -0
- data/docs/_pages/adapters/rexml.adoc +292 -0
- data/docs/_pages/best-practices.adoc +429 -0
- data/docs/_pages/compatibility.adoc +467 -0
- data/docs/_pages/configuration.adoc +250 -0
- data/docs/_pages/error-handling.adoc +349 -0
- data/docs/_pages/headed-ox-limitations.adoc +574 -0
- data/docs/_pages/headed-ox.adoc +1025 -0
- data/docs/_pages/index.adoc +35 -0
- data/docs/_pages/installation.adoc +140 -0
- data/docs/_pages/node-api-reference.adoc +49 -0
- data/docs/_pages/performance.adoc +35 -0
- data/docs/_pages/quick-start.adoc +243 -0
- data/docs/_pages/thread-safety.adoc +28 -0
- data/docs/_references/document-api.adoc +407 -0
- data/docs/_references/index.adoc +48 -0
- data/docs/_tutorials/basic-usage.adoc +267 -0
- data/docs/_tutorials/builder-pattern.adoc +342 -0
- data/docs/_tutorials/index.adoc +33 -0
- data/docs/_tutorials/namespace-handling.adoc +324 -0
- data/docs/_tutorials/xpath-queries.adoc +358 -0
- data/docs/index.adoc +122 -0
- data/examples/README.md +124 -0
- data/examples/api_client/README.md +424 -0
- data/examples/api_client/api_client.rb +394 -0
- data/examples/api_client/example_response.xml +48 -0
- data/examples/headed_ox_example/README.md +90 -0
- data/examples/headed_ox_example/headed_ox_demo.rb +71 -0
- data/examples/rss_parser/README.md +194 -0
- data/examples/rss_parser/example_feed.xml +93 -0
- data/examples/rss_parser/rss_parser.rb +189 -0
- data/examples/sax_parsing/README.md +50 -0
- data/examples/sax_parsing/data_extractor.rb +75 -0
- data/examples/sax_parsing/example.xml +21 -0
- data/examples/sax_parsing/large_file.rb +78 -0
- data/examples/sax_parsing/simple_parser.rb +55 -0
- data/examples/web_scraper/README.md +352 -0
- data/examples/web_scraper/example_page.html +201 -0
- data/examples/web_scraper/web_scraper.rb +312 -0
- data/lib/moxml/adapter/base.rb +107 -28
- data/lib/moxml/adapter/customized_libxml/cdata.rb +28 -0
- data/lib/moxml/adapter/customized_libxml/comment.rb +24 -0
- data/lib/moxml/adapter/customized_libxml/declaration.rb +85 -0
- data/lib/moxml/adapter/customized_libxml/element.rb +39 -0
- data/lib/moxml/adapter/customized_libxml/node.rb +44 -0
- data/lib/moxml/adapter/customized_libxml/processing_instruction.rb +31 -0
- data/lib/moxml/adapter/customized_libxml/text.rb +27 -0
- data/lib/moxml/adapter/customized_oga/xml_generator.rb +1 -1
- data/lib/moxml/adapter/customized_ox/attribute.rb +28 -1
- data/lib/moxml/adapter/customized_rexml/formatter.rb +13 -8
- data/lib/moxml/adapter/headed_ox.rb +161 -0
- data/lib/moxml/adapter/libxml.rb +1564 -0
- data/lib/moxml/adapter/nokogiri.rb +156 -9
- data/lib/moxml/adapter/oga.rb +190 -15
- data/lib/moxml/adapter/ox.rb +322 -28
- data/lib/moxml/adapter/rexml.rb +157 -28
- data/lib/moxml/adapter.rb +21 -4
- data/lib/moxml/attribute.rb +6 -0
- data/lib/moxml/builder.rb +40 -4
- data/lib/moxml/config.rb +8 -3
- data/lib/moxml/context.rb +57 -2
- data/lib/moxml/declaration.rb +9 -0
- data/lib/moxml/doctype.rb +13 -1
- data/lib/moxml/document.rb +53 -6
- data/lib/moxml/document_builder.rb +34 -5
- data/lib/moxml/element.rb +71 -2
- data/lib/moxml/error.rb +175 -6
- data/lib/moxml/node.rb +155 -4
- data/lib/moxml/node_set.rb +34 -0
- data/lib/moxml/sax/block_handler.rb +194 -0
- data/lib/moxml/sax/element_handler.rb +124 -0
- data/lib/moxml/sax/handler.rb +113 -0
- data/lib/moxml/sax.rb +31 -0
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xml_utils/encoder.rb +4 -4
- data/lib/moxml/xml_utils.rb +7 -4
- data/lib/moxml/xpath/ast/node.rb +159 -0
- data/lib/moxml/xpath/cache.rb +91 -0
- data/lib/moxml/xpath/compiler.rb +1770 -0
- data/lib/moxml/xpath/context.rb +26 -0
- data/lib/moxml/xpath/conversion.rb +124 -0
- data/lib/moxml/xpath/engine.rb +52 -0
- data/lib/moxml/xpath/errors.rb +101 -0
- data/lib/moxml/xpath/lexer.rb +304 -0
- data/lib/moxml/xpath/parser.rb +485 -0
- data/lib/moxml/xpath/ruby/generator.rb +269 -0
- data/lib/moxml/xpath/ruby/node.rb +193 -0
- data/lib/moxml/xpath.rb +37 -0
- data/lib/moxml.rb +5 -2
- data/moxml.gemspec +3 -1
- data/old-specs/moxml/adapter/customized_libxml/.gitkeep +6 -0
- data/spec/consistency/README.md +77 -0
- data/spec/{moxml/examples/adapter_spec.rb → consistency/adapter_parity_spec.rb} +4 -4
- data/spec/examples/README.md +75 -0
- data/spec/{support/shared_examples/examples/attribute.rb → examples/attribute_examples_spec.rb} +1 -1
- data/spec/{support/shared_examples/examples/basic_usage.rb → examples/basic_usage_spec.rb} +2 -2
- data/spec/{support/shared_examples/examples/namespace.rb → examples/namespace_examples_spec.rb} +3 -3
- data/spec/{support/shared_examples/examples/readme_examples.rb → examples/readme_examples_spec.rb} +6 -4
- data/spec/{support/shared_examples/examples/xpath.rb → examples/xpath_examples_spec.rb} +10 -6
- data/spec/integration/README.md +71 -0
- data/spec/{moxml/all_with_adapters_spec.rb → integration/all_adapters_spec.rb} +3 -2
- data/spec/integration/headed_ox_integration_spec.rb +326 -0
- data/spec/{support → integration}/shared_examples/edge_cases.rb +37 -10
- data/spec/integration/shared_examples/high_level/.gitkeep +0 -0
- data/spec/{support/shared_examples/context.rb → integration/shared_examples/high_level/context_behavior.rb} +2 -1
- data/spec/{support/shared_examples/integration.rb → integration/shared_examples/integration_workflows.rb} +23 -6
- data/spec/integration/shared_examples/node_wrappers/.gitkeep +0 -0
- data/spec/{support/shared_examples/cdata.rb → integration/shared_examples/node_wrappers/cdata_behavior.rb} +6 -1
- data/spec/{support/shared_examples/comment.rb → integration/shared_examples/node_wrappers/comment_behavior.rb} +2 -1
- data/spec/{support/shared_examples/declaration.rb → integration/shared_examples/node_wrappers/declaration_behavior.rb} +5 -5
- data/spec/{support/shared_examples/doctype.rb → integration/shared_examples/node_wrappers/doctype_behavior.rb} +2 -2
- data/spec/{support/shared_examples/document.rb → integration/shared_examples/node_wrappers/document_behavior.rb} +1 -1
- data/spec/{support/shared_examples/node.rb → integration/shared_examples/node_wrappers/node_behavior.rb} +9 -2
- data/spec/{support/shared_examples/node_set.rb → integration/shared_examples/node_wrappers/node_set_behavior.rb} +1 -18
- data/spec/{support/shared_examples/processing_instruction.rb → integration/shared_examples/node_wrappers/processing_instruction_behavior.rb} +6 -2
- data/spec/moxml/README.md +41 -0
- data/spec/moxml/adapter/.gitkeep +0 -0
- data/spec/moxml/adapter/README.md +61 -0
- data/spec/moxml/adapter/base_spec.rb +27 -0
- data/spec/moxml/adapter/headed_ox_spec.rb +311 -0
- data/spec/moxml/adapter/libxml_spec.rb +14 -0
- data/spec/moxml/adapter/ox_spec.rb +9 -8
- data/spec/moxml/adapter/shared_examples/.gitkeep +0 -0
- data/spec/{support/shared_examples/xml_adapter.rb → moxml/adapter/shared_examples/adapter_contract.rb} +39 -12
- data/spec/moxml/adapter_spec.rb +16 -0
- data/spec/moxml/attribute_spec.rb +30 -0
- data/spec/moxml/builder_spec.rb +33 -0
- data/spec/moxml/cdata_spec.rb +31 -0
- data/spec/moxml/comment_spec.rb +31 -0
- data/spec/moxml/config_spec.rb +3 -3
- data/spec/moxml/context_spec.rb +28 -0
- data/spec/moxml/declaration_preservation_spec.rb +217 -0
- data/spec/moxml/declaration_spec.rb +36 -0
- data/spec/moxml/doctype_spec.rb +33 -0
- data/spec/moxml/document_builder_spec.rb +30 -0
- data/spec/moxml/document_spec.rb +105 -0
- data/spec/moxml/element_spec.rb +143 -0
- data/spec/moxml/error_spec.rb +266 -22
- data/spec/{moxml_spec.rb → moxml/moxml_spec.rb} +9 -9
- data/spec/moxml/namespace_spec.rb +32 -0
- data/spec/moxml/node_set_spec.rb +39 -0
- data/spec/moxml/node_spec.rb +37 -0
- data/spec/moxml/processing_instruction_spec.rb +34 -0
- data/spec/moxml/sax_spec.rb +1067 -0
- data/spec/moxml/text_spec.rb +31 -0
- data/spec/moxml/version_spec.rb +14 -0
- data/spec/moxml/xml_utils/.gitkeep +0 -0
- data/spec/moxml/xml_utils/encoder_spec.rb +27 -0
- data/spec/moxml/xml_utils_spec.rb +49 -0
- data/spec/moxml/xpath/ast/node_spec.rb +83 -0
- data/spec/moxml/xpath/axes_spec.rb +296 -0
- data/spec/moxml/xpath/cache_spec.rb +358 -0
- data/spec/moxml/xpath/compiler_spec.rb +406 -0
- data/spec/moxml/xpath/context_spec.rb +210 -0
- data/spec/moxml/xpath/conversion_spec.rb +365 -0
- data/spec/moxml/xpath/fixtures/sample.xml +25 -0
- data/spec/moxml/xpath/functions/boolean_functions_spec.rb +114 -0
- data/spec/moxml/xpath/functions/node_functions_spec.rb +145 -0
- data/spec/moxml/xpath/functions/numeric_functions_spec.rb +164 -0
- data/spec/moxml/xpath/functions/position_functions_spec.rb +93 -0
- data/spec/moxml/xpath/functions/special_functions_spec.rb +89 -0
- data/spec/moxml/xpath/functions/string_functions_spec.rb +381 -0
- data/spec/moxml/xpath/lexer_spec.rb +488 -0
- data/spec/moxml/xpath/parser_integration_spec.rb +210 -0
- data/spec/moxml/xpath/parser_spec.rb +364 -0
- data/spec/moxml/xpath/ruby/generator_spec.rb +421 -0
- data/spec/moxml/xpath/ruby/node_spec.rb +291 -0
- data/spec/moxml/xpath_capabilities_spec.rb +199 -0
- data/spec/moxml/xpath_spec.rb +77 -0
- data/spec/performance/README.md +83 -0
- data/spec/performance/benchmark_spec.rb +64 -0
- data/spec/{support/shared_examples/examples/memory.rb → performance/memory_usage_spec.rb} +4 -1
- data/spec/{support/shared_examples/examples/thread_safety.rb → performance/thread_safety_spec.rb} +3 -1
- data/spec/performance/xpath_benchmark_spec.rb +259 -0
- data/spec/spec_helper.rb +58 -1
- data/spec/support/xml_matchers.rb +1 -1
- metadata +178 -34
- data/spec/support/shared_examples/examples/benchmark_spec.rb +0 -51
- /data/spec/{support/shared_examples/builder.rb → integration/shared_examples/high_level/builder_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/document_builder.rb → integration/shared_examples/high_level/document_builder_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/attribute.rb → integration/shared_examples/node_wrappers/attribute_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/element.rb → integration/shared_examples/node_wrappers/element_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/namespace.rb → integration/shared_examples/node_wrappers/namespace_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/text.rb → integration/shared_examples/node_wrappers/text_behavior.rb} +0 -0
|
@@ -5,6 +5,7 @@ RSpec.shared_examples "Moxml Integration" do
|
|
|
5
5
|
|
|
6
6
|
describe "complete document workflow" do
|
|
7
7
|
let(:doc) { context.create_document }
|
|
8
|
+
|
|
8
9
|
before do
|
|
9
10
|
# Create document with declaration
|
|
10
11
|
doc.add_child(doc.create_declaration("1.0", "UTF-8"))
|
|
@@ -16,7 +17,8 @@ RSpec.shared_examples "Moxml Integration" do
|
|
|
16
17
|
doc.add_child(root)
|
|
17
18
|
|
|
18
19
|
# Add processing instruction
|
|
19
|
-
style_pi = doc.create_processing_instruction("xml-stylesheet",
|
|
20
|
+
style_pi = doc.create_processing_instruction("xml-stylesheet",
|
|
21
|
+
'type="text/xsl" href="style.xsl"')
|
|
20
22
|
root.add_previous_sibling(style_pi)
|
|
21
23
|
|
|
22
24
|
# Add mixed content
|
|
@@ -44,7 +46,7 @@ RSpec.shared_examples "Moxml Integration" do
|
|
|
44
46
|
"<!-- Mixed content example -->",
|
|
45
47
|
"<text>Some text <![CDATA[<with><markup/>]]> and more text</text>",
|
|
46
48
|
'<item id="123" xs:type="custom"></item>',
|
|
47
|
-
"</root>"
|
|
49
|
+
"</root>",
|
|
48
50
|
]
|
|
49
51
|
|
|
50
52
|
expected_xml_tags.each do |expected_xml|
|
|
@@ -54,7 +56,12 @@ RSpec.shared_examples "Moxml Integration" do
|
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
it "handles xpath queries" do
|
|
57
|
-
|
|
59
|
+
if context.config.adapter_name == :ox
|
|
60
|
+
pending "Ox doesn't support namespace-aware XPath with predicates"
|
|
61
|
+
end
|
|
62
|
+
if context.config.adapter_name == :headed_ox
|
|
63
|
+
skip "HeadedOx limitation: Namespace-aware XPath with predicates needs investigation. See docs/HEADED_OX_LIMITATIONS.md"
|
|
64
|
+
end
|
|
58
65
|
# Test XPath queries
|
|
59
66
|
#
|
|
60
67
|
# XPath with a default namespace is a problem
|
|
@@ -69,7 +76,12 @@ RSpec.shared_examples "Moxml Integration" do
|
|
|
69
76
|
|
|
70
77
|
describe "namespace handling" do
|
|
71
78
|
it "handles complex namespace scenarios" do
|
|
72
|
-
|
|
79
|
+
if context.config.adapter_name == :ox
|
|
80
|
+
pending "Ox doesn't have a native XPath"
|
|
81
|
+
end
|
|
82
|
+
if context.config.adapter_name == :headed_ox
|
|
83
|
+
skip "HeadedOx limitation: Namespace methods not implemented in adapter. Requires Ox namespace API enhancement. See docs/HEADED_OX_LIMITATIONS.md"
|
|
84
|
+
end
|
|
73
85
|
xml = <<~XML
|
|
74
86
|
<root xmlns="http://default.org" xmlns:a="http://a.org" xmlns:b="http://b.org">
|
|
75
87
|
<child>
|
|
@@ -110,7 +122,12 @@ RSpec.shared_examples "Moxml Integration" do
|
|
|
110
122
|
let(:doc) { context.parse("<root><a>1</a><b>2</b></root>") }
|
|
111
123
|
|
|
112
124
|
it "handles complex modifications" do
|
|
113
|
-
|
|
125
|
+
if context.config.adapter_name == :headed_ox
|
|
126
|
+
skip "HeadedOx limitation: Parent setter not implemented. Requires Ox node reparenting API. See docs/HEADED_OX_LIMITATIONS.md"
|
|
127
|
+
end
|
|
128
|
+
if context.config.adapter_name == :ox
|
|
129
|
+
skip "Ox doesn't have a native XPath"
|
|
130
|
+
end
|
|
114
131
|
|
|
115
132
|
# Move nodes
|
|
116
133
|
b_node = doc.at_xpath("//b")
|
|
@@ -132,7 +149,7 @@ RSpec.shared_examples "Moxml Integration" do
|
|
|
132
149
|
expect(doc.root.children.map(&:name)).to eq(%w[b c a])
|
|
133
150
|
expect(doc.to_xml).to include(
|
|
134
151
|
'<root id="main">',
|
|
135
|
-
"<b>2<!-- comment --><![CDATA[<tag>]]></b>"
|
|
152
|
+
"<b>2<!-- comment --><![CDATA[<tag>]]></b>",
|
|
136
153
|
)
|
|
137
154
|
end
|
|
138
155
|
end
|
|
File without changes
|
|
@@ -37,7 +37,12 @@ RSpec.shared_examples "Moxml::Cdata" do
|
|
|
37
37
|
|
|
38
38
|
it "escapes CDATA end marker" do
|
|
39
39
|
# pending for Ox: https://github.com/ohler55/ox/issues/377
|
|
40
|
-
|
|
40
|
+
if context.config.adapter_name == :ox
|
|
41
|
+
pending "Ox doesn't escape the end token"
|
|
42
|
+
end
|
|
43
|
+
if context.config.adapter_name == :headed_ox
|
|
44
|
+
skip "HeadedOx limitation: Ox doesn't escape CDATA end markers. See docs/HEADED_OX_LIMITATIONS.md"
|
|
45
|
+
end
|
|
41
46
|
cdata.content = "content]]>more"
|
|
42
47
|
expect(cdata.to_xml).to eq("<![CDATA[content]]]]><![CDATA[>more]]>")
|
|
43
48
|
end
|
|
@@ -32,7 +32,8 @@ RSpec.shared_examples "Moxml::Comment" do
|
|
|
32
32
|
|
|
33
33
|
it "raises an error on double hyphens" do
|
|
34
34
|
expect { comment.content = "test -- comment" }
|
|
35
|
-
.to raise_error(Moxml::ValidationError,
|
|
35
|
+
.to raise_error(Moxml::ValidationError,
|
|
36
|
+
"XML comment cannot contain double hyphens (--)")
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
it "handles special characters" do
|
|
@@ -36,7 +36,9 @@ RSpec.shared_examples "Moxml::Declaration" do
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
it "normalizes encoding" do
|
|
39
|
-
|
|
39
|
+
if Moxml.new.config.adapter.name.include?("Rexml")
|
|
40
|
+
pending("Rexml Encoding upcases the string")
|
|
41
|
+
end
|
|
40
42
|
declaration.encoding = "utf-8"
|
|
41
43
|
expect(declaration.encoding).to eq("utf-8")
|
|
42
44
|
end
|
|
@@ -54,7 +56,8 @@ RSpec.shared_examples "Moxml::Declaration" do
|
|
|
54
56
|
|
|
55
57
|
it "validates standalone value" do
|
|
56
58
|
expect { declaration.standalone = "maybe" }
|
|
57
|
-
.to raise_error(Moxml::ValidationError,
|
|
59
|
+
.to raise_error(Moxml::ValidationError,
|
|
60
|
+
"Invalid standalone value: maybe")
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
it "allows nil standalone" do
|
|
@@ -95,9 +98,6 @@ RSpec.shared_examples "Moxml::Declaration" do
|
|
|
95
98
|
end
|
|
96
99
|
|
|
97
100
|
it "removes from document" do
|
|
98
|
-
if Moxml.new.config.adapter.name.match?(/Nokogiri|Rexml|Ox/)
|
|
99
|
-
pending("The document contains a default declaration")
|
|
100
|
-
end
|
|
101
101
|
doc.add_child(declaration)
|
|
102
102
|
declaration.remove
|
|
103
103
|
expect(doc.to_xml).not_to include("<?xml")
|
|
@@ -7,7 +7,7 @@ RSpec.shared_examples "Moxml::Doctype" do
|
|
|
7
7
|
doc.create_doctype(
|
|
8
8
|
"html",
|
|
9
9
|
"-//W3C//DTD HTML 4.01 Transitional//EN",
|
|
10
|
-
"http://www.w3.org/TR/html4/loose.dtd"
|
|
10
|
+
"http://www.w3.org/TR/html4/loose.dtd",
|
|
11
11
|
)
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -19,7 +19,7 @@ RSpec.shared_examples "Moxml::Doctype" do
|
|
|
19
19
|
it "wraps content in doctype markers" do
|
|
20
20
|
doc.add_child(doctype)
|
|
21
21
|
expect(doc.to_xml.strip).to end_with(
|
|
22
|
-
'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
|
|
22
|
+
'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
|
|
23
23
|
)
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -96,7 +96,9 @@ RSpec.shared_examples "Moxml::Node" do
|
|
|
96
96
|
let(:doc) { context.parse("<root><a><b>1</b></a><a><b>2</b></a></root>") }
|
|
97
97
|
|
|
98
98
|
it "finds nodes by xpath" do
|
|
99
|
-
|
|
99
|
+
if context.config.adapter_name == :ox
|
|
100
|
+
pending "Ox doesn't have a native XPath"
|
|
101
|
+
end
|
|
100
102
|
|
|
101
103
|
nodes = doc.xpath("//b")
|
|
102
104
|
expect(nodes.size).to eq(2)
|
|
@@ -104,7 +106,12 @@ RSpec.shared_examples "Moxml::Node" do
|
|
|
104
106
|
end
|
|
105
107
|
|
|
106
108
|
it "finds first node by xpath" do
|
|
107
|
-
|
|
109
|
+
if context.config.adapter_name == :ox
|
|
110
|
+
pending "Ox doesn't have a native XPath"
|
|
111
|
+
end
|
|
112
|
+
if context.config.adapter_name == :headed_ox
|
|
113
|
+
skip "HeadedOx limitation: Text content access from nested elements needs investigation. See docs/HEADED_OX_LIMITATIONS.md"
|
|
114
|
+
end
|
|
108
115
|
|
|
109
116
|
node = doc.at_xpath("//b")
|
|
110
117
|
expect(node.text).to eq("1")
|
|
@@ -25,47 +25,34 @@ RSpec.shared_examples "Moxml::NodeSet" do
|
|
|
25
25
|
|
|
26
26
|
describe "enumeration" do
|
|
27
27
|
it "iterates over nodes" do
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
texts = []
|
|
31
|
-
nodes.each { |node| texts << node.text }
|
|
28
|
+
texts = nodes.map(&:text)
|
|
32
29
|
expect(texts).to eq(%w[First Second Third])
|
|
33
30
|
end
|
|
34
31
|
|
|
35
32
|
it "maps nodes" do
|
|
36
|
-
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
|
37
|
-
|
|
38
33
|
texts = nodes.map(&:text)
|
|
39
34
|
expect(texts).to eq(%w[First Second Third])
|
|
40
35
|
end
|
|
41
36
|
|
|
42
37
|
it "selects nodes" do
|
|
43
|
-
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
|
44
|
-
|
|
45
38
|
selected = nodes.select { |node| node.text.include?("i") }
|
|
46
39
|
expect(selected.size).to eq(2)
|
|
47
40
|
expect(selected.map(&:text)).to eq(%w[First Third])
|
|
48
41
|
end
|
|
49
42
|
|
|
50
43
|
it "compares nodes" do
|
|
51
|
-
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
|
52
|
-
|
|
53
44
|
expect(doc.xpath("//child")).to eq(doc.root.children)
|
|
54
45
|
end
|
|
55
46
|
end
|
|
56
47
|
|
|
57
48
|
describe "access methods" do
|
|
58
49
|
it "accesses by index" do
|
|
59
|
-
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
|
60
|
-
|
|
61
50
|
expect(nodes[0].text).to eq("First")
|
|
62
51
|
expect(nodes[1].text).to eq("Second")
|
|
63
52
|
expect(nodes[-1].text).to eq("Third")
|
|
64
53
|
end
|
|
65
54
|
|
|
66
55
|
it "accesses by range" do
|
|
67
|
-
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
|
68
|
-
|
|
69
56
|
subset = nodes[0..1]
|
|
70
57
|
expect(subset).to be_a(described_class)
|
|
71
58
|
expect(subset.size).to eq(2)
|
|
@@ -73,8 +60,6 @@ RSpec.shared_examples "Moxml::NodeSet" do
|
|
|
73
60
|
end
|
|
74
61
|
|
|
75
62
|
it "provides first and last" do
|
|
76
|
-
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
|
77
|
-
|
|
78
63
|
expect(nodes.first.text).to eq("First")
|
|
79
64
|
expect(nodes.last.text).to eq("Third")
|
|
80
65
|
end
|
|
@@ -95,8 +80,6 @@ RSpec.shared_examples "Moxml::NodeSet" do
|
|
|
95
80
|
|
|
96
81
|
describe "concatenation" do
|
|
97
82
|
it "combines node sets" do
|
|
98
|
-
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
|
99
|
-
|
|
100
83
|
other_doc = context.parse("<root><item>Fourth</item></root>")
|
|
101
84
|
other_nodes = other_doc.xpath("//item")
|
|
102
85
|
combined = nodes + other_nodes
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
RSpec.shared_examples "Moxml::ProcessingInstruction" do
|
|
4
4
|
let(:context) { Moxml.new }
|
|
5
5
|
let(:doc) { context.create_document }
|
|
6
|
-
let(:pi)
|
|
6
|
+
let(:pi) do
|
|
7
|
+
doc.create_processing_instruction("xml-stylesheet",
|
|
8
|
+
'href="style.xsl" type="text/xsl"')
|
|
9
|
+
end
|
|
7
10
|
|
|
8
11
|
it "identifies as processing instruction node" do
|
|
9
12
|
expect(pi).to be_processing_instruction
|
|
@@ -76,7 +79,8 @@ RSpec.shared_examples "Moxml::ProcessingInstruction" do
|
|
|
76
79
|
|
|
77
80
|
describe "common use cases" do
|
|
78
81
|
it "creates stylesheet instruction" do
|
|
79
|
-
pi = doc.create_processing_instruction("xml-stylesheet",
|
|
82
|
+
pi = doc.create_processing_instruction("xml-stylesheet",
|
|
83
|
+
'type="text/xsl" href="style.xsl"')
|
|
80
84
|
expect(pi.to_xml).to eq('<?xml-stylesheet type="text/xsl" href="style.xsl"?>')
|
|
81
85
|
end
|
|
82
86
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Unit Tests
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
This directory contains pure unit tests for individual classes without adapter switching. Each test focuses on a single class's behavior, state management, and convenience APIs.
|
|
6
|
+
|
|
7
|
+
## What Should Be Placed Here
|
|
8
|
+
|
|
9
|
+
- ✅ Tests for individual class methods
|
|
10
|
+
- ✅ Tests for convenience APIs (e.g., `Element#set_attributes`)
|
|
11
|
+
- ✅ Tests for internal utilities
|
|
12
|
+
- ✅ Tests for configuration and setup
|
|
13
|
+
- ✅ Tests that use a single adapter (usually the default)
|
|
14
|
+
- ✅ Tests for class-level functionality independent of adapter implementation
|
|
15
|
+
|
|
16
|
+
## What Should NOT Be Placed Here
|
|
17
|
+
|
|
18
|
+
- ❌ Tests that switch between multiple adapters
|
|
19
|
+
- ❌ Tests for cross-adapter behavior
|
|
20
|
+
- ❌ Integration tests that involve multiple classes
|
|
21
|
+
- ❌ Performance benchmarks
|
|
22
|
+
- ❌ Documentation examples
|
|
23
|
+
|
|
24
|
+
## How to Run
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Run all unit tests
|
|
28
|
+
bundle exec rake spec:unit
|
|
29
|
+
|
|
30
|
+
# Run specific unit test file
|
|
31
|
+
bundle exec rspec spec/unit/moxml/element_spec.rb
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Directory Structure
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
unit/
|
|
38
|
+
├── moxml/ # Core moxml classes
|
|
39
|
+
│ ├── adapter/ # Adapter registry and base
|
|
40
|
+
│ └── xml_utils/ # Utility classes
|
|
41
|
+
└── moxml_spec.rb # Top-level module tests
|
|
File without changes
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Adapter Tests
|
|
2
|
+
#
|
|
3
|
+
# ## Purpose
|
|
4
|
+
#
|
|
5
|
+
# This directory contains tests for adapter implementations. Each test verifies that an adapter correctly implements the adapter API contract and tests adapter-specific features.
|
|
6
|
+
#
|
|
7
|
+
# ## What Should Be Placed Here
|
|
8
|
+
#
|
|
9
|
+
# - ✅ Tests for adapter API contract compliance (parse, create_*, serialize, xpath, etc.)
|
|
10
|
+
# - ✅ Tests for adapter-specific features
|
|
11
|
+
# - ✅ Tests for adapter workarounds/customizations
|
|
12
|
+
# - ✅ Each adapter tested independently
|
|
13
|
+
# - ✅ Adapter initialization and configuration tests
|
|
14
|
+
#
|
|
15
|
+
# ## What Should NOT Be Placed Here
|
|
16
|
+
#
|
|
17
|
+
# - ❌ Tests for high-level wrapper behavior (use integration/ instead)
|
|
18
|
+
# - ❌ Tests for cross-adapter consistency (use consistency/ instead)
|
|
19
|
+
# - ❌ Tests for Moxml wrapper classes
|
|
20
|
+
# - ❌ Performance benchmarks (use performance/ instead)
|
|
21
|
+
#
|
|
22
|
+
# ## How to Run
|
|
23
|
+
#
|
|
24
|
+
# ```bash
|
|
25
|
+
# # Run all adapter tests
|
|
26
|
+
# bundle exec rake spec:adapter
|
|
27
|
+
#
|
|
28
|
+
# # Run specific adapter test
|
|
29
|
+
# bundle exec rspec spec/moxml/adapter/nokogiri_spec.rb
|
|
30
|
+
# ```
|
|
31
|
+
#
|
|
32
|
+
# ## Directory Structure
|
|
33
|
+
#
|
|
34
|
+
# ```
|
|
35
|
+
# moxml/adapter/
|
|
36
|
+
# ├── shared_examples/
|
|
37
|
+
# │ └── adapter_contract.rb # Defines the adapter API contract
|
|
38
|
+
# ├── base_spec.rb # Base adapter tests
|
|
39
|
+
# ├── libxml_spec.rb # LibXML adapter implementation
|
|
40
|
+
# ├── nokogiri_spec.rb # Nokogiri adapter implementation
|
|
41
|
+
# ├── oga_spec.rb # Oga adapter implementation
|
|
42
|
+
# ├── ox_spec.rb # Ox adapter implementation
|
|
43
|
+
# └── rexml_spec.rb # REXML adapter implementation
|
|
44
|
+
# ```
|
|
45
|
+
#
|
|
46
|
+
# ## Adding a New Adapter
|
|
47
|
+
#
|
|
48
|
+
# When adding a new adapter:
|
|
49
|
+
#
|
|
50
|
+
# 1. Create `spec/moxml/adapter/<adapter_name>_spec.rb`
|
|
51
|
+
# 2. Include the adapter contract:
|
|
52
|
+
# ```ruby
|
|
53
|
+
# RSpec.describe Moxml::Adapter::YourAdapter do
|
|
54
|
+
# around do |example|
|
|
55
|
+
# Moxml.with_config(:your_adapter, true, "UTF-8") do
|
|
56
|
+
# example.run
|
|
57
|
+
# end
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
# it_behaves_like "adapter contract"
|
|
61
|
+
# end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Moxml::Adapter::Base do
|
|
6
|
+
describe ".name" do
|
|
7
|
+
it "returns adapter name" do
|
|
8
|
+
expect(described_class.name).to include("Base")
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe "interface methods" do
|
|
13
|
+
it "defines parse method" do
|
|
14
|
+
expect(described_class).to respond_to(:parse)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "defines create_document method" do
|
|
18
|
+
expect(described_class).to respond_to(:create_document)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "defines serialize method" do
|
|
22
|
+
# Base class doesn't implement serialize - each adapter does
|
|
23
|
+
# This is tested in the individual adapter specs
|
|
24
|
+
skip "Serialize is adapter-specific, not in Base"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|