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
data/spec/moxml/error_spec.rb
CHANGED
|
@@ -4,68 +4,312 @@
|
|
|
4
4
|
RSpec.describe "Moxml errors" do
|
|
5
5
|
describe Moxml::Error do
|
|
6
6
|
it "is a StandardError" do
|
|
7
|
-
expect(
|
|
7
|
+
expect(described_class.new).to be_a(StandardError)
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
describe Moxml::ParseError do
|
|
12
12
|
it "includes line and column information" do
|
|
13
|
-
error =
|
|
13
|
+
error = described_class.new("Invalid XML", line: 5, column: 10)
|
|
14
14
|
expect(error.line).to eq(5)
|
|
15
15
|
expect(error.column).to eq(10)
|
|
16
|
-
|
|
16
|
+
# The base message should be unchanged
|
|
17
|
+
expect(error.message).to include("Invalid XML")
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
it "works without line and column" do
|
|
20
|
-
error =
|
|
21
|
+
error = described_class.new("Invalid XML")
|
|
21
22
|
expect(error.line).to be_nil
|
|
22
23
|
expect(error.column).to be_nil
|
|
23
24
|
end
|
|
24
|
-
end
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
error
|
|
29
|
-
|
|
26
|
+
it "includes source context" do
|
|
27
|
+
error = described_class.new("Invalid XML", source: "<invalid>")
|
|
28
|
+
expect(error.source).to eq("<invalid>")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "provides helpful error message with context" do
|
|
32
|
+
error = described_class.new("Invalid XML", line: 5, column: 10)
|
|
33
|
+
message = error.to_s
|
|
34
|
+
expect(message).to include("Invalid XML")
|
|
35
|
+
expect(message).to include("Line: 5")
|
|
36
|
+
expect(message).to include("Column: 10")
|
|
37
|
+
expect(message).to include("Hint:")
|
|
30
38
|
end
|
|
31
39
|
end
|
|
32
40
|
|
|
33
41
|
describe Moxml::XPathError do
|
|
34
|
-
it "
|
|
35
|
-
error =
|
|
36
|
-
|
|
42
|
+
it "includes expression and adapter information" do
|
|
43
|
+
error = described_class.new(
|
|
44
|
+
"Invalid XPath",
|
|
45
|
+
expression: "//invalid[",
|
|
46
|
+
adapter: "Nokogiri",
|
|
47
|
+
)
|
|
48
|
+
expect(error.expression).to eq("//invalid[")
|
|
49
|
+
expect(error.adapter).to eq("Nokogiri")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "provides helpful error message" do
|
|
53
|
+
error = described_class.new(
|
|
54
|
+
"Invalid XPath",
|
|
55
|
+
expression: "//test",
|
|
56
|
+
adapter: "REXML",
|
|
57
|
+
)
|
|
58
|
+
message = error.to_s
|
|
59
|
+
expect(message).to include("Invalid XPath")
|
|
60
|
+
expect(message).to include("Expression: //test")
|
|
61
|
+
expect(message).to include("Adapter: REXML")
|
|
62
|
+
expect(message).to include("Hint:")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe Moxml::ValidationError do
|
|
67
|
+
it "includes node, constraint, and value information" do
|
|
68
|
+
error = described_class.new(
|
|
69
|
+
"Invalid version",
|
|
70
|
+
constraint: "version",
|
|
71
|
+
value: "2.0",
|
|
72
|
+
)
|
|
73
|
+
expect(error.constraint).to eq("version")
|
|
74
|
+
expect(error.value).to eq("2.0")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "provides helpful error message" do
|
|
78
|
+
error = described_class.new(
|
|
79
|
+
"Invalid encoding",
|
|
80
|
+
constraint: "encoding",
|
|
81
|
+
value: "INVALID",
|
|
82
|
+
)
|
|
83
|
+
message = error.to_s
|
|
84
|
+
expect(message).to include("Invalid encoding")
|
|
85
|
+
expect(message).to include("Constraint: encoding")
|
|
86
|
+
expect(message).to include("Value: \"INVALID\"")
|
|
87
|
+
expect(message).to include("Hint:")
|
|
37
88
|
end
|
|
38
89
|
end
|
|
39
90
|
|
|
40
91
|
describe Moxml::NamespaceError do
|
|
41
|
-
it "
|
|
42
|
-
error =
|
|
43
|
-
|
|
92
|
+
it "includes prefix, uri, and element information" do
|
|
93
|
+
error = described_class.new(
|
|
94
|
+
"Invalid namespace",
|
|
95
|
+
prefix: "ns",
|
|
96
|
+
uri: "http://example.com",
|
|
97
|
+
)
|
|
98
|
+
expect(error.prefix).to eq("ns")
|
|
99
|
+
expect(error.uri).to eq("http://example.com")
|
|
100
|
+
expect(error.message).to eq("Invalid namespace")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "provides access to error attributes" do
|
|
104
|
+
error = described_class.new(
|
|
105
|
+
"Invalid namespace",
|
|
106
|
+
prefix: "ns",
|
|
107
|
+
uri: "invalid-uri",
|
|
108
|
+
)
|
|
109
|
+
expect(error.message).to include("Invalid namespace")
|
|
110
|
+
expect(error.prefix).to eq("ns")
|
|
111
|
+
expect(error.uri).to eq("invalid-uri")
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe Moxml::AdapterError do
|
|
116
|
+
it "includes adapter name and operation" do
|
|
117
|
+
error = described_class.new(
|
|
118
|
+
"Failed to load adapter",
|
|
119
|
+
adapter: "nokogiri",
|
|
120
|
+
operation: "load",
|
|
121
|
+
)
|
|
122
|
+
expect(error.adapter_name).to eq("nokogiri")
|
|
123
|
+
expect(error.operation).to eq("load")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "includes native error information" do
|
|
127
|
+
native_err = StandardError.new("Gem not found")
|
|
128
|
+
error = described_class.new(
|
|
129
|
+
"Failed to load adapter",
|
|
130
|
+
adapter: "nokogiri",
|
|
131
|
+
native_error: native_err,
|
|
132
|
+
)
|
|
133
|
+
expect(error.native_error).to eq(native_err)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "provides helpful error message" do
|
|
137
|
+
native_err = LoadError.new("cannot load such file")
|
|
138
|
+
error = described_class.new(
|
|
139
|
+
"Failed to load adapter",
|
|
140
|
+
adapter: "oga",
|
|
141
|
+
operation: "require",
|
|
142
|
+
native_error: native_err,
|
|
143
|
+
)
|
|
144
|
+
message = error.to_s
|
|
145
|
+
expect(message).to include("Failed to load adapter")
|
|
146
|
+
expect(message).to include("Adapter: oga")
|
|
147
|
+
expect(message).to include("Operation: require")
|
|
148
|
+
expect(message).to include("Native Error: LoadError")
|
|
149
|
+
expect(message).to include("Hint:")
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
describe Moxml::SerializationError do
|
|
154
|
+
it "includes node, adapter, and format information" do
|
|
155
|
+
error = described_class.new(
|
|
156
|
+
"Failed to serialize",
|
|
157
|
+
adapter: "LibXML",
|
|
158
|
+
format: "xml",
|
|
159
|
+
)
|
|
160
|
+
expect(error.adapter).to eq("LibXML")
|
|
161
|
+
expect(error.format).to eq("xml")
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
it "provides helpful error message" do
|
|
165
|
+
error = described_class.new(
|
|
166
|
+
"Failed to serialize",
|
|
167
|
+
adapter: "Ox",
|
|
168
|
+
format: "xml",
|
|
169
|
+
)
|
|
170
|
+
message = error.to_s
|
|
171
|
+
expect(message).to include("Failed to serialize")
|
|
172
|
+
expect(message).to include("Adapter: Ox")
|
|
173
|
+
expect(message).to include("Format: xml")
|
|
174
|
+
expect(message).to include("Hint:")
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
describe Moxml::DocumentStructureError do
|
|
179
|
+
it "includes attempted operation and state" do
|
|
180
|
+
error = described_class.new(
|
|
181
|
+
"Invalid operation",
|
|
182
|
+
operation: "add_child",
|
|
183
|
+
state: "no_root",
|
|
184
|
+
)
|
|
185
|
+
expect(error.attempted_operation).to eq("add_child")
|
|
186
|
+
expect(error.current_state).to eq("no_root")
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "provides helpful error message" do
|
|
190
|
+
error = described_class.new(
|
|
191
|
+
"Invalid operation",
|
|
192
|
+
operation: "set_root",
|
|
193
|
+
state: "root_already_exists",
|
|
194
|
+
)
|
|
195
|
+
message = error.to_s
|
|
196
|
+
expect(message).to include("Invalid operation")
|
|
197
|
+
expect(message).to include("Operation: set_root")
|
|
198
|
+
expect(message).to include("Current State: root_already_exists")
|
|
199
|
+
expect(message).to include("Hint:")
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
describe Moxml::AttributeError do
|
|
204
|
+
it "includes attribute name, element, and value" do
|
|
205
|
+
error = described_class.new(
|
|
206
|
+
"Invalid attribute",
|
|
207
|
+
name: "id",
|
|
208
|
+
value: 123,
|
|
209
|
+
)
|
|
210
|
+
expect(error.attribute_name).to eq("id")
|
|
211
|
+
expect(error.value).to eq(123)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it "provides helpful error message" do
|
|
215
|
+
error = described_class.new(
|
|
216
|
+
"Invalid attribute name",
|
|
217
|
+
name: "123invalid",
|
|
218
|
+
value: "test",
|
|
219
|
+
)
|
|
220
|
+
message = error.to_s
|
|
221
|
+
expect(message).to include("Invalid attribute name")
|
|
222
|
+
expect(message).to include("Attribute: 123invalid")
|
|
223
|
+
expect(message).to include("Value: \"test\"")
|
|
224
|
+
expect(message).to include("Hint:")
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
describe Moxml::NotImplementedError do
|
|
229
|
+
it "includes feature and adapter information" do
|
|
230
|
+
error = described_class.new(
|
|
231
|
+
"Feature not supported",
|
|
232
|
+
feature: "xpath",
|
|
233
|
+
adapter: "CustomAdapter",
|
|
234
|
+
)
|
|
235
|
+
expect(error.feature).to eq("xpath")
|
|
236
|
+
expect(error.adapter).to eq("CustomAdapter")
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it "provides helpful error message" do
|
|
240
|
+
error = described_class.new(
|
|
241
|
+
"Feature not supported",
|
|
242
|
+
feature: "namespaces",
|
|
243
|
+
adapter: "MinimalAdapter",
|
|
244
|
+
)
|
|
245
|
+
message = error.to_s
|
|
246
|
+
expect(message).to include("Feature not supported")
|
|
247
|
+
expect(message).to include("Feature: namespaces")
|
|
248
|
+
expect(message).to include("Adapter: MinimalAdapter")
|
|
249
|
+
expect(message).to include("Hint:")
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it "has a default message" do
|
|
253
|
+
error = described_class.new
|
|
254
|
+
expect(error.message).to include("Feature not implemented")
|
|
44
255
|
end
|
|
45
256
|
end
|
|
46
257
|
|
|
47
258
|
describe "error handling in context" do
|
|
48
259
|
let(:context) { Moxml.new }
|
|
49
260
|
|
|
50
|
-
it "raises ParseError for invalid XML" do
|
|
261
|
+
it "raises ParseError for invalid XML with enhanced context" do
|
|
51
262
|
expect do
|
|
52
|
-
context.parse("<invalid>")
|
|
53
|
-
end.to raise_error(Moxml::ParseError)
|
|
263
|
+
context.parse("<invalid>", strict: true)
|
|
264
|
+
end.to raise_error(Moxml::ParseError) do |error|
|
|
265
|
+
expect(error.to_s).to include("Hint:")
|
|
266
|
+
end
|
|
54
267
|
end
|
|
55
268
|
|
|
56
|
-
it "raises XPathError for invalid XPath" do
|
|
269
|
+
it "raises XPathError for invalid XPath with enhanced context" do
|
|
57
270
|
doc = context.parse("<root/>")
|
|
58
271
|
expect do
|
|
59
272
|
doc.xpath("///")
|
|
60
|
-
end.to raise_error(Moxml::XPathError)
|
|
273
|
+
end.to raise_error(Moxml::XPathError) do |error|
|
|
274
|
+
expect(error.expression).to eq("///")
|
|
275
|
+
expect(error.to_s).to include("Hint:")
|
|
276
|
+
end
|
|
61
277
|
end
|
|
62
278
|
|
|
63
|
-
it "raises NamespaceError for invalid namespace" do
|
|
279
|
+
it "raises NamespaceError for invalid namespace with enhanced context" do
|
|
64
280
|
doc = context.parse("<root/>")
|
|
65
281
|
|
|
66
282
|
expect do
|
|
67
283
|
doc.root.add_namespace("xml", "http//invalid.com")
|
|
68
|
-
end.to raise_error(Moxml::NamespaceError
|
|
284
|
+
end.to raise_error(Moxml::NamespaceError) do |error|
|
|
285
|
+
expect(error.uri).to eq("http//invalid.com")
|
|
286
|
+
expect(error.prefix).to eq("xml")
|
|
287
|
+
expect(error.message).to include("Invalid URI")
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
it "raises ValidationError for invalid XML constructs" do
|
|
292
|
+
expect do
|
|
293
|
+
context.parse('<?xml version="2.0"?><root/>', strict: true)
|
|
294
|
+
end.to raise_error(Moxml::ParseError)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
it "raises AdapterError for invalid adapter configuration" do
|
|
298
|
+
expect do
|
|
299
|
+
Moxml::Config.new.adapter = :invalid_adapter
|
|
300
|
+
end.to raise_error(Moxml::AdapterError) do |error|
|
|
301
|
+
expect(error.adapter_name).to eq(:invalid_adapter)
|
|
302
|
+
expect(error.to_s).to include("Hint:")
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
it "raises DocumentStructureError for invalid node operations" do
|
|
307
|
+
doc = context.parse("<root/>")
|
|
308
|
+
expect do
|
|
309
|
+
doc.root.add_child(Object.new)
|
|
310
|
+
end.to raise_error(Moxml::DocumentStructureError) do |error|
|
|
311
|
+
expect(error.to_s).to include("Hint:")
|
|
312
|
+
end
|
|
69
313
|
end
|
|
70
314
|
end
|
|
71
315
|
end
|
|
@@ -8,42 +8,42 @@ RSpec.describe Moxml do
|
|
|
8
8
|
|
|
9
9
|
describe ".new" do
|
|
10
10
|
it "creates a new context" do
|
|
11
|
-
expect(
|
|
11
|
+
expect(described_class.new).to be_a(Moxml::Context)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
it "accepts adapter specification" do
|
|
15
|
-
context =
|
|
15
|
+
context = described_class.new(:nokogiri)
|
|
16
16
|
expect(context.config.adapter_name).to eq(:nokogiri)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
it "raises error for invalid adapter" do
|
|
20
|
-
expect {
|
|
20
|
+
expect { described_class.new(:invalid) }.to raise_error(Moxml::AdapterError)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
describe ".configure" do
|
|
25
25
|
around do |example|
|
|
26
26
|
# preserve the original config because it may be changed in examples
|
|
27
|
-
|
|
27
|
+
described_class.with_config { example.run }
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
it "sets default values without a block" do
|
|
31
|
-
|
|
31
|
+
described_class.configure
|
|
32
32
|
|
|
33
|
-
context =
|
|
33
|
+
context = described_class.new
|
|
34
34
|
expect(context.config.adapter_name).to eq(:nokogiri)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
it "uses configured options from the block" do
|
|
38
|
-
|
|
38
|
+
described_class.configure do |config|
|
|
39
39
|
config.default_adapter = :oga
|
|
40
40
|
config.strict_parsing = false
|
|
41
41
|
config.default_encoding = "US-ASCII"
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
context =
|
|
44
|
+
context = described_class.new
|
|
45
45
|
expect(context.config.adapter_name).to eq(:oga)
|
|
46
|
-
expect(context.config.strict_parsing).to
|
|
46
|
+
expect(context.config.strict_parsing).to be(false)
|
|
47
47
|
expect(context.config.default_encoding).to eq("US-ASCII")
|
|
48
48
|
end
|
|
49
49
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Moxml::Namespace do
|
|
6
|
+
let(:context) { Moxml.new }
|
|
7
|
+
let(:doc) do
|
|
8
|
+
context.parse('<root xmlns:ns="http://example.com"><ns:child/></root>')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe "#prefix" do
|
|
12
|
+
it "returns namespace prefix" do
|
|
13
|
+
ns = doc.root.namespace
|
|
14
|
+
expect(ns).to be_nil # root has no prefix
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe "#uri" do
|
|
19
|
+
it "returns namespace URI" do
|
|
20
|
+
child = doc.root.children.first
|
|
21
|
+
ns = child.namespace
|
|
22
|
+
expect(ns.uri).to eq("http://example.com") if ns
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe "namespace handling" do
|
|
27
|
+
it "handles namespaces on elements" do
|
|
28
|
+
elem = doc.root.children.first
|
|
29
|
+
expect(elem.name).to include("child")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Moxml::NodeSet do
|
|
6
|
+
let(:context) { Moxml.new }
|
|
7
|
+
let(:doc) { context.parse("<root><child/><child/><child/></root>") }
|
|
8
|
+
|
|
9
|
+
describe "#length" do
|
|
10
|
+
it "returns number of nodes" do
|
|
11
|
+
nodes = doc.root.children
|
|
12
|
+
expect(nodes.length).to eq(3)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe "#[]" do
|
|
17
|
+
it "accesses nodes by index" do
|
|
18
|
+
nodes = doc.root.children
|
|
19
|
+
expect(nodes[0]).to be_a(Moxml::Element)
|
|
20
|
+
expect(nodes[0].name).to eq("child")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe "#each" do
|
|
25
|
+
it "iterates over nodes" do
|
|
26
|
+
nodes = doc.root.children
|
|
27
|
+
count = 0
|
|
28
|
+
nodes.each { |_node| count += 1 }
|
|
29
|
+
expect(count).to eq(3)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe "#empty?" do
|
|
34
|
+
it "returns true when empty" do
|
|
35
|
+
nodes = doc.create_element("empty").children
|
|
36
|
+
expect(nodes).to be_empty
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Moxml::Node do
|
|
6
|
+
let(:context) { Moxml.new }
|
|
7
|
+
let(:doc) { context.parse("<root><child>text</child></root>") }
|
|
8
|
+
let(:node) { doc.root }
|
|
9
|
+
|
|
10
|
+
describe "#parent" do
|
|
11
|
+
it "returns parent node" do
|
|
12
|
+
child = node.children.first
|
|
13
|
+
expect(child.parent).to eq(node)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "#document" do
|
|
18
|
+
it "returns document" do
|
|
19
|
+
expect(node.document).to eq(doc)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe "#to_xml" do
|
|
24
|
+
it "serializes node to XML" do
|
|
25
|
+
expect(node.to_xml).to include("<root>")
|
|
26
|
+
expect(node.to_xml).to include("<child>")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "#remove" do
|
|
31
|
+
it "removes node from parent" do
|
|
32
|
+
child = node.children.first
|
|
33
|
+
child.remove
|
|
34
|
+
expect(node.children).to be_empty
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Moxml::ProcessingInstruction do
|
|
6
|
+
let(:context) { Moxml.new }
|
|
7
|
+
let(:doc) do
|
|
8
|
+
context.parse('<?xml version="1.0"?><?xml-stylesheet type="text/xsl" href="style.xsl"?><root/>')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe "#target" do
|
|
12
|
+
it "returns PI target" do
|
|
13
|
+
pi = doc.children.find { |n| n.is_a?(described_class) }
|
|
14
|
+
expect(pi.target).to eq("xml-stylesheet")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe "#content" do
|
|
19
|
+
it "returns PI content" do
|
|
20
|
+
pi = doc.children.find { |n| n.is_a?(described_class) }
|
|
21
|
+
expect(pi.content).to include("type=")
|
|
22
|
+
expect(pi.content).to include("href=")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe "creation" do
|
|
27
|
+
it "creates a processing instruction" do
|
|
28
|
+
pi = doc.create_processing_instruction("target", "content")
|
|
29
|
+
expect(pi).to be_a(described_class)
|
|
30
|
+
expect(pi.target).to eq("target")
|
|
31
|
+
expect(pi.content).to eq("content")
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|