moxml 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +15 -0
  3. data/.github/workflows/release.yml +23 -0
  4. data/.gitignore +3 -0
  5. data/.rubocop.yml +2 -0
  6. data/.rubocop_todo.yml +65 -0
  7. data/.ruby-version +1 -0
  8. data/Gemfile +10 -3
  9. data/README.adoc +401 -594
  10. data/lib/moxml/adapter/base.rb +102 -0
  11. data/lib/moxml/adapter/customized_oga/xml_declaration.rb +18 -0
  12. data/lib/moxml/adapter/customized_oga/xml_generator.rb +104 -0
  13. data/lib/moxml/adapter/nokogiri.rb +319 -0
  14. data/lib/moxml/adapter/oga.rb +318 -0
  15. data/lib/moxml/adapter/ox.rb +325 -0
  16. data/lib/moxml/adapter.rb +26 -170
  17. data/lib/moxml/attribute.rb +47 -14
  18. data/lib/moxml/builder.rb +64 -0
  19. data/lib/moxml/cdata.rb +4 -26
  20. data/lib/moxml/comment.rb +6 -22
  21. data/lib/moxml/config.rb +39 -15
  22. data/lib/moxml/context.rb +29 -0
  23. data/lib/moxml/declaration.rb +16 -26
  24. data/lib/moxml/doctype.rb +9 -0
  25. data/lib/moxml/document.rb +51 -63
  26. data/lib/moxml/document_builder.rb +87 -0
  27. data/lib/moxml/element.rb +63 -97
  28. data/lib/moxml/error.rb +20 -0
  29. data/lib/moxml/namespace.rb +12 -37
  30. data/lib/moxml/node.rb +78 -58
  31. data/lib/moxml/node_set.rb +19 -222
  32. data/lib/moxml/processing_instruction.rb +6 -25
  33. data/lib/moxml/text.rb +4 -26
  34. data/lib/moxml/version.rb +1 -1
  35. data/lib/moxml/xml_utils/encoder.rb +55 -0
  36. data/lib/moxml/xml_utils.rb +80 -0
  37. data/lib/moxml.rb +33 -33
  38. data/moxml.gemspec +1 -1
  39. data/spec/moxml/adapter/nokogiri_spec.rb +14 -0
  40. data/spec/moxml/adapter/oga_spec.rb +14 -0
  41. data/spec/moxml/adapter/ox_spec.rb +49 -0
  42. data/spec/moxml/all_with_adapters_spec.rb +46 -0
  43. data/spec/moxml/config_spec.rb +55 -0
  44. data/spec/moxml/error_spec.rb +71 -0
  45. data/spec/moxml/examples/adapter_spec.rb +27 -0
  46. data/spec/moxml_spec.rb +50 -0
  47. data/spec/spec_helper.rb +32 -0
  48. data/spec/support/shared_examples/attribute.rb +165 -0
  49. data/spec/support/shared_examples/builder.rb +25 -0
  50. data/spec/support/shared_examples/cdata.rb +70 -0
  51. data/spec/support/shared_examples/comment.rb +65 -0
  52. data/spec/support/shared_examples/context.rb +35 -0
  53. data/spec/support/shared_examples/declaration.rb +93 -0
  54. data/spec/support/shared_examples/doctype.rb +25 -0
  55. data/spec/support/shared_examples/document.rb +110 -0
  56. data/spec/support/shared_examples/document_builder.rb +43 -0
  57. data/spec/support/shared_examples/edge_cases.rb +185 -0
  58. data/spec/support/shared_examples/element.rb +130 -0
  59. data/spec/support/shared_examples/examples/attribute.rb +42 -0
  60. data/spec/support/shared_examples/examples/basic_usage.rb +67 -0
  61. data/spec/support/shared_examples/examples/memory.rb +54 -0
  62. data/spec/support/shared_examples/examples/namespace.rb +65 -0
  63. data/spec/support/shared_examples/examples/readme_examples.rb +100 -0
  64. data/spec/support/shared_examples/examples/thread_safety.rb +43 -0
  65. data/spec/support/shared_examples/examples/xpath.rb +39 -0
  66. data/spec/support/shared_examples/integration.rb +135 -0
  67. data/spec/support/shared_examples/namespace.rb +96 -0
  68. data/spec/support/shared_examples/node.rb +110 -0
  69. data/spec/support/shared_examples/node_set.rb +90 -0
  70. data/spec/support/shared_examples/processing_instruction.rb +88 -0
  71. data/spec/support/shared_examples/text.rb +66 -0
  72. data/spec/support/shared_examples/xml_adapter.rb +191 -0
  73. data/spec/support/xml_matchers.rb +27 -0
  74. metadata +55 -6
  75. data/.github/workflows/main.yml +0 -27
  76. data/lib/moxml/error_handler.rb +0 -77
  77. data/lib/moxml/errors.rb +0 -169
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples "xml adapter" do
4
+ let(:xml) do
5
+ <<~XML
6
+ <?xml version="1.0"?>
7
+ <root xmlns="http://example.org" xmlns:x="http://example.org/x">
8
+ <child id="1">Text</child>
9
+ <child id="2"/>
10
+ <x:special>
11
+ <![CDATA[Some <special> text]]>
12
+ <!-- A comment -->
13
+ <?pi target?>
14
+ </x:special>
15
+ </root>
16
+ XML
17
+ end
18
+
19
+ describe ".parse" do
20
+ it "parses XML string" do
21
+ doc = described_class.parse(xml).native
22
+ expect(described_class.node_type(doc)).to eq(:document)
23
+ expect(described_class.node_name(described_class.root(doc))).to eq("root")
24
+ end
25
+
26
+ it "handles malformed XML according to strict setting" do
27
+ malformed = "<root><unclosed>"
28
+
29
+ expect do
30
+ described_class.parse(malformed, strict: true)
31
+ end.to raise_error(Moxml::ParseError)
32
+
33
+ expect do
34
+ described_class.parse(malformed, strict: false)
35
+ end.not_to raise_error
36
+ end
37
+ end
38
+
39
+ describe "node creation" do
40
+ let(:doc) { described_class.create_document }
41
+
42
+ it "creates element" do
43
+ element = described_class.create_element("test")
44
+ expect(described_class.node_type(element)).to eq(:element)
45
+ expect(described_class.node_name(element)).to eq("test")
46
+ end
47
+
48
+ it "creates text" do
49
+ text = described_class.create_text("content")
50
+ expect(described_class.node_type(text)).to eq(:text)
51
+ end
52
+
53
+ it "creates CDATA" do
54
+ cdata = described_class.create_cdata("<content>")
55
+ expect(described_class.node_type(cdata)).to eq(:cdata)
56
+ end
57
+
58
+ it "creates comment" do
59
+ comment = described_class.create_comment("comment")
60
+ expect(described_class.node_type(comment)).to eq(:comment)
61
+ end
62
+
63
+ it "creates processing instruction" do
64
+ pi = described_class.create_processing_instruction("target", "content")
65
+ expect(described_class.node_type(pi)).to eq(:processing_instruction)
66
+ end
67
+ end
68
+
69
+ describe "node manipulation" do
70
+ let(:doc) { described_class.parse(xml).native }
71
+ let(:root) { described_class.root(doc) }
72
+
73
+ it "gets parent" do
74
+ child = described_class.children(root).first
75
+ expect(described_class.parent(child)).to eq(root)
76
+ end
77
+
78
+ it "gets children" do
79
+ children = described_class.children(root)
80
+ expect(children.length).to eq(3)
81
+ end
82
+
83
+ it "gets siblings", skip: "Oga includes text nodes into siblings" do
84
+ children = described_class.children(root)
85
+ first = children[0]
86
+ second = children[1]
87
+
88
+ expect(described_class.next_sibling(first)).to eq(second)
89
+ expect(described_class.previous_sibling(second)).to eq(first)
90
+ end
91
+
92
+ it "adds child" do
93
+ element = described_class.create_element("new")
94
+ described_class.add_child(root, element)
95
+ expect(described_class.children(root).last).to eq(element)
96
+ end
97
+
98
+ it "adds text child" do
99
+ described_class.add_child(root, "text")
100
+ expect(described_class.children(root).last.text).to eq("text")
101
+ end
102
+ end
103
+
104
+ describe "attributes" do
105
+ let(:doc) { described_class.parse(xml) }
106
+ let(:element) { described_class.children(described_class.root(doc.native)).first }
107
+
108
+ it "gets attributes" do
109
+ attrs = described_class.attributes(element)
110
+ expect(attrs.count).to eq(1)
111
+ expect(attrs.first.value).to eq("1")
112
+ end
113
+
114
+ it "sets attribute" do
115
+ described_class.set_attribute(element, "new", "value")
116
+ expect(described_class.get_attribute(element, "new").value).to eq("value")
117
+ end
118
+
119
+ it "removes attribute" do
120
+ described_class.remove_attribute(element, "id")
121
+ expect(described_class.get_attribute(element, "id")&.value).to be_nil
122
+ end
123
+
124
+ it "handles special characters in attributes" do
125
+ described_class.set_attribute(element, "special", '< > & " \'')
126
+ value = described_class.get_attribute(element, "special")&.to_xml
127
+ expect(value).to match(/&lt; &gt; &amp; (&quot;|") ('|&apos;)/)
128
+ end
129
+ end
130
+
131
+ describe "namespaces" do
132
+ let(:doc) { described_class.parse(xml).native }
133
+ let(:root) { described_class.root(doc) }
134
+ let(:special) { described_class.children(root).last }
135
+
136
+ it "creates namespace" do
137
+ ns = described_class.create_namespace(root, "test", "http://test.org")
138
+ expect(ns).not_to be_nil
139
+ end
140
+
141
+ it "handles namespaced elements" do
142
+ expect(described_class.node_name(special)).to eq("special")
143
+ end
144
+ end
145
+
146
+ describe "serialization" do
147
+ let(:doc) { described_class.parse(xml).native }
148
+
149
+ it "serializes to XML" do
150
+ result = described_class.serialize(doc)
151
+
152
+ expect(result).to include("<?xml")
153
+ expect(result).to include("<root")
154
+ expect(result).to include('xmlns="http://example.org"').once
155
+ expect(result).to include('xmlns:x="http://example.org/x"').once
156
+ expect(result).to include("</root>")
157
+ end
158
+
159
+ it "respects indentation settings", skip: "Indent cannot be negative, and zero indent doesn't remove newlines" do
160
+ unindented = described_class.serialize(doc, indent: 0)
161
+ indented = described_class.serialize(doc, indent: 2)
162
+
163
+ expect(unindented).not_to include("\n ")
164
+ expect(indented).to include("\n ")
165
+ end
166
+
167
+ it "preserves XML declaration" do
168
+ result = described_class.serialize(doc)
169
+ expect(result).to match(/^<\?xml/)
170
+ end
171
+
172
+ it "handles encoding specification" do
173
+ result = described_class.serialize(doc, encoding: "UTF-8")
174
+ expect(result).to include('encoding="UTF-8"')
175
+ end
176
+ end
177
+
178
+ describe "xpath" do
179
+ let(:doc) { described_class.parse(xml) }
180
+
181
+ it "finds nodes by xpath" do
182
+ nodes = described_class.xpath(doc, "//xmlns:child")
183
+ expect(nodes.length).to eq(2)
184
+ end
185
+
186
+ it "finds first node by xpath" do
187
+ node = described_class.at_xpath(doc, "//xmlns:child")
188
+ expect(described_class.get_attribute_value(node, "id")).to eq("1")
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # spec/support/xml_matchers.rb
4
+ # RSpec::Matchers.define :be_equivalent_to do |expected|
5
+ # match do |actual|
6
+ # normalize_xml(actual) == normalize_xml(expected)
7
+ # end
8
+
9
+ # failure_message do |actual|
10
+ # "expected XML to be equivalent to:\n#{normalize_xml(expected)}\n\nbut was:\n#{normalize_xml(actual)}"
11
+ # end
12
+
13
+ # def normalize_xml(xml)
14
+ # xml.to_s.gsub(/>\s+</, '><').strip
15
+ # end
16
+ # end
17
+
18
+ RSpec::Matchers.define :have_xpath do |xpath, text|
19
+ match do |xml_node|
20
+ nodes = xml_node.xpath(xpath)
21
+ text.nil? ? !nodes.empty? : nodes.any? { |node| node.text == text }
22
+ end
23
+
24
+ failure_message do |_xml_node|
25
+ "expected to find xpath #{xpath} #{text ? "with text '#{text}'" : ""}"
26
+ end
27
+ end
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.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-30 00:00:00.000000000 Z
11
+ date: 2025-02-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'Moxml is a unified XML manipulation library that provides a common API.
14
14
 
@@ -19,10 +19,13 @@ executables: []
19
19
  extensions: []
20
20
  extra_rdoc_files: []
21
21
  files:
22
- - ".github/workflows/main.yml"
22
+ - ".github/workflows/rake.yml"
23
+ - ".github/workflows/release.yml"
23
24
  - ".gitignore"
24
25
  - ".rspec"
25
26
  - ".rubocop.yml"
27
+ - ".rubocop_todo.yml"
28
+ - ".ruby-version"
26
29
  - Gemfile
27
30
  - README.adoc
28
31
  - Rakefile
@@ -30,23 +33,69 @@ files:
30
33
  - bin/setup
31
34
  - lib/moxml.rb
32
35
  - lib/moxml/adapter.rb
36
+ - lib/moxml/adapter/base.rb
37
+ - lib/moxml/adapter/customized_oga/xml_declaration.rb
38
+ - lib/moxml/adapter/customized_oga/xml_generator.rb
39
+ - lib/moxml/adapter/nokogiri.rb
40
+ - lib/moxml/adapter/oga.rb
41
+ - lib/moxml/adapter/ox.rb
33
42
  - lib/moxml/attribute.rb
43
+ - lib/moxml/builder.rb
34
44
  - lib/moxml/cdata.rb
35
45
  - lib/moxml/comment.rb
36
46
  - lib/moxml/config.rb
47
+ - lib/moxml/context.rb
37
48
  - lib/moxml/declaration.rb
49
+ - lib/moxml/doctype.rb
38
50
  - lib/moxml/document.rb
51
+ - lib/moxml/document_builder.rb
39
52
  - lib/moxml/element.rb
40
- - lib/moxml/error_handler.rb
41
- - lib/moxml/errors.rb
53
+ - lib/moxml/error.rb
42
54
  - lib/moxml/namespace.rb
43
55
  - lib/moxml/node.rb
44
56
  - lib/moxml/node_set.rb
45
57
  - lib/moxml/processing_instruction.rb
46
58
  - lib/moxml/text.rb
47
59
  - lib/moxml/version.rb
60
+ - lib/moxml/xml_utils.rb
61
+ - lib/moxml/xml_utils/encoder.rb
48
62
  - moxml.gemspec
49
63
  - sig/moxml.rbs
64
+ - spec/moxml/adapter/nokogiri_spec.rb
65
+ - spec/moxml/adapter/oga_spec.rb
66
+ - spec/moxml/adapter/ox_spec.rb
67
+ - spec/moxml/all_with_adapters_spec.rb
68
+ - spec/moxml/config_spec.rb
69
+ - spec/moxml/error_spec.rb
70
+ - spec/moxml/examples/adapter_spec.rb
71
+ - spec/moxml_spec.rb
72
+ - spec/spec_helper.rb
73
+ - spec/support/shared_examples/attribute.rb
74
+ - spec/support/shared_examples/builder.rb
75
+ - spec/support/shared_examples/cdata.rb
76
+ - spec/support/shared_examples/comment.rb
77
+ - spec/support/shared_examples/context.rb
78
+ - spec/support/shared_examples/declaration.rb
79
+ - spec/support/shared_examples/doctype.rb
80
+ - spec/support/shared_examples/document.rb
81
+ - spec/support/shared_examples/document_builder.rb
82
+ - spec/support/shared_examples/edge_cases.rb
83
+ - spec/support/shared_examples/element.rb
84
+ - spec/support/shared_examples/examples/attribute.rb
85
+ - spec/support/shared_examples/examples/basic_usage.rb
86
+ - spec/support/shared_examples/examples/memory.rb
87
+ - spec/support/shared_examples/examples/namespace.rb
88
+ - spec/support/shared_examples/examples/readme_examples.rb
89
+ - spec/support/shared_examples/examples/thread_safety.rb
90
+ - spec/support/shared_examples/examples/xpath.rb
91
+ - spec/support/shared_examples/integration.rb
92
+ - spec/support/shared_examples/namespace.rb
93
+ - spec/support/shared_examples/node.rb
94
+ - spec/support/shared_examples/node_set.rb
95
+ - spec/support/shared_examples/processing_instruction.rb
96
+ - spec/support/shared_examples/text.rb
97
+ - spec/support/shared_examples/xml_adapter.rb
98
+ - spec/support/xml_matchers.rb
50
99
  homepage: https://github.com/lutaml/moxml
51
100
  licenses:
52
101
  - BSD-2-Clause
@@ -67,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
116
  - !ruby/object:Gem::Version
68
117
  version: '0'
69
118
  requirements: []
70
- rubygems_version: 3.5.22
119
+ rubygems_version: 3.3.27
71
120
  signing_key:
72
121
  specification_version: 4
73
122
  summary: Unified XML manipulation library
@@ -1,27 +0,0 @@
1
- name: Ruby
2
-
3
- on:
4
- push:
5
- branches:
6
- - main
7
-
8
- pull_request:
9
-
10
- jobs:
11
- build:
12
- runs-on: ubuntu-latest
13
- name: Ruby ${{ matrix.ruby }}
14
- strategy:
15
- matrix:
16
- ruby:
17
- - '3.3.2'
18
-
19
- steps:
20
- - uses: actions/checkout@v4
21
- - name: Set up Ruby
22
- uses: ruby/setup-ruby@v1
23
- with:
24
- ruby-version: ${{ matrix.ruby }}
25
- bundler-cache: true
26
- - name: Run the default task
27
- run: bundle exec rake
@@ -1,77 +0,0 @@
1
- # lib/moxml/error_handler.rb
2
- module Moxml
3
- class ErrorHandler
4
- class << self
5
- def handle_parse_error(error, backend)
6
- case backend
7
- when :nokogiri
8
- handle_nokogiri_error(error)
9
- when :ox
10
- handle_ox_error(error)
11
- when :oga
12
- handle_oga_error(error)
13
- else
14
- handle_generic_error(error)
15
- end
16
- end
17
-
18
- private
19
-
20
- def handle_nokogiri_error(error)
21
- case error
22
- when ::Nokogiri::XML::SyntaxError
23
- raise ParseError.new(
24
- error.message,
25
- line: error.line,
26
- column: error.column,
27
- source: error.source,
28
- )
29
- when ::Nokogiri::XML::XPath::SyntaxError
30
- raise XPathError.new(error.message)
31
- else
32
- handle_generic_error(error)
33
- end
34
- end
35
-
36
- def handle_ox_error(error)
37
- case error
38
- when ::Ox::ParseError
39
- raise ParseError.new(
40
- error.message,
41
- line: error.line,
42
- column: error.column,
43
- )
44
- else
45
- handle_generic_error(error)
46
- end
47
- end
48
-
49
- def handle_oga_error(error)
50
- case error
51
- when ::Oga::XML::ParseError
52
- raise ParseError.new(
53
- error.message,
54
- line: error.line,
55
- column: error.column,
56
- )
57
- when ::Oga::XML::XPath::Error
58
- raise XPathError.new(error.message)
59
- else
60
- handle_generic_error(error)
61
- end
62
- end
63
-
64
- def handle_generic_error(error)
65
- case error
66
- when NameError, NoMethodError
67
- raise BackendError.new(
68
- "Backend operation failed: #{error.message}",
69
- Moxml.config.backend
70
- )
71
- else
72
- raise Error, "XML operation failed: #{error.message}"
73
- end
74
- end
75
- end
76
- end
77
- end
data/lib/moxml/errors.rb DELETED
@@ -1,169 +0,0 @@
1
- # lib/moxml/errors.rb
2
- module Moxml
3
- # Base error class for all Moxml errors
4
- class Error < StandardError; end
5
-
6
- # Parsing related errors
7
- class ParseError < Error
8
- attr_reader :line, :column, :source
9
-
10
- def initialize(message, line: nil, column: nil, source: nil)
11
- @line = line
12
- @column = column
13
- @source = source
14
- super(build_message(message))
15
- end
16
-
17
- private
18
-
19
- def build_message(message)
20
- parts = [message]
21
- parts << "Line: #{line}" if line
22
- parts << "Column: #{column}" if column
23
- parts << "\nSource: #{source}" if source
24
- parts.join(" | ")
25
- end
26
- end
27
-
28
- # Validation errors
29
- class ValidationError < Error; end
30
- class DTDValidationError < ValidationError; end
31
- class SchemaValidationError < ValidationError; end
32
- class NamespaceError < ValidationError; end
33
-
34
- # Structure errors
35
- class MalformedXMLError < ParseError; end
36
- class UnbalancedTagError < ParseError; end
37
- class UndefinedEntityError < ParseError; end
38
- class DuplicateAttributeError < ParseError; end
39
-
40
- # Encoding errors
41
- class EncodingError < Error
42
- attr_reader :encoding
43
-
44
- def initialize(message, encoding)
45
- @encoding = encoding
46
- super("#{message} (Encoding: #{encoding})")
47
- end
48
- end
49
-
50
- # Security related errors
51
- class SecurityError < Error; end
52
- class MaxDepthExceededError < SecurityError; end
53
- class MaxAttributesExceededError < SecurityError; end
54
- class MaxNameLengthExceededError < SecurityError; end
55
- class EntityExpansionError < SecurityError; end
56
-
57
- # Backend errors
58
- class BackendError < Error
59
- attr_reader :backend
60
-
61
- def initialize(message, backend)
62
- @backend = backend
63
- super("#{message} (Backend: #{backend})")
64
- end
65
- end
66
-
67
- class BackendNotFoundError < BackendError; end
68
- class BackendConfigurationError < BackendError; end
69
-
70
- # Node manipulation errors
71
- class NodeError < Error
72
- attr_reader :node
73
-
74
- def initialize(message, node)
75
- @node = node
76
- super("#{message} (Node: #{node.class})")
77
- end
78
- end
79
-
80
- class InvalidNodeTypeError < NodeError; end
81
- class InvalidOperationError < NodeError; end
82
- class NodeNotFoundError < NodeError; end
83
-
84
- # Visitor pattern errors
85
- class VisitorError < Error; end
86
-
87
- class InvalidSelectorError < VisitorError
88
- attr_reader :selector
89
-
90
- def initialize(message, selector)
91
- @selector = selector
92
- super("#{message} (Selector: #{selector})")
93
- end
94
- end
95
-
96
- class VisitorMethodError < VisitorError
97
- attr_reader :method_name
98
-
99
- def initialize(message, method_name)
100
- @method_name = method_name
101
- super("#{message} (Method: #{method_name})")
102
- end
103
- end
104
-
105
- # Serialization errors
106
- class SerializationError < Error; end
107
-
108
- class InvalidOptionsError < SerializationError
109
- attr_reader :options
110
-
111
- def initialize(message, options)
112
- @options = options
113
- super("#{message} (Options: #{options})")
114
- end
115
- end
116
-
117
- # IO errors
118
- class IOError < Error
119
- attr_reader :path
120
-
121
- def initialize(message, path)
122
- @path = path
123
- super("#{message} (Path: #{path})")
124
- end
125
- end
126
-
127
- class FileNotFoundError < IOError; end
128
- class WriteError < IOError; end
129
-
130
- # Memory errors
131
- class MemoryError < Error
132
- attr_reader :size
133
-
134
- def initialize(message, size)
135
- @size = size
136
- super("#{message} (Size: #{size} bytes)")
137
- end
138
- end
139
-
140
- class DocumentTooLargeError < MemoryError; end
141
-
142
- # CDATA related errors
143
- class CDATAError < Error; end
144
- class NestedCDATAError < CDATAError; end
145
- class InvalidCDATAContentError < CDATAError; end
146
-
147
- # Namespace related errors
148
- class NamespaceDeclarationError < Error
149
- attr_reader :prefix, :uri
150
-
151
- def initialize(message, prefix, uri)
152
- @prefix = prefix
153
- @uri = uri
154
- super("#{message} (Prefix: #{prefix}, URI: #{uri})")
155
- end
156
- end
157
-
158
- # XPath related errors
159
- class XPathError < Error
160
- attr_reader :expression
161
-
162
- def initialize(message, expression)
163
- @expression = expression
164
- super("#{message} (Expression: #{expression})")
165
- end
166
- end
167
-
168
- class InvalidXPathError < XPathError; end
169
- end