Module: Canon::XmlParsing

Defined in:
lib/canon/xml_parsing.rb

Overview

Backend-agnostic XML parsing, serialization, and type dispatch.

Provides a unified API that delegates to the active backend (Nokogiri or moxml/Oga). Uses backend-branching (‘if XmlBackend.nokogiri?`) rather than `case/when` with constant references — this ensures Nokogiri constants are never resolved under Opal, preventing NameError at runtime.

OCP: adding a new backend only requires updating this module. DRY: all backend dispatch centralized here, not scattered across comparator/formatter files.

Class Method Summary collapse

Class Method Details

.attribute_value(node, attr_name) ⇒ Object



157
158
159
160
161
162
163
# File 'lib/canon/xml_parsing.rb', line 157

def attribute_value(node, attr_name)
  if XmlBackend.nokogiri?
    node.is_a?(Nokogiri::XML::Element) ? node[attr_name.to_s] : nil
  else
    node.is_a?(Moxml::Element) ? node[attr_name.to_s] : nil
  end
end

.attributes(node) ⇒ Object



149
150
151
152
153
154
155
# File 'lib/canon/xml_parsing.rb', line 149

def attributes(node)
  if XmlBackend.nokogiri?
    node.is_a?(Nokogiri::XML::Element) ? node.attributes.values : []
  else
    node.is_a?(Moxml::Element) ? node.attributes : []
  end
end

.canonicalize(node, options = {}) ⇒ Object



199
200
201
202
203
204
205
# File 'lib/canon/xml_parsing.rb', line 199

def canonicalize(node, options = {})
  if XmlBackend.nokogiri?
    node.canonicalize(options)
  else
    moxml_canonicalize(node, options)
  end
end

.cdata?(node) ⇒ Boolean

Returns:

  • (Boolean)


91
92
93
94
95
96
97
# File 'lib/canon/xml_parsing.rb', line 91

def cdata?(node)
  if XmlBackend.nokogiri?
    node.is_a?(Nokogiri::XML::CDATA)
  else
    node.is_a?(Moxml::Cdata)
  end
end

.children(node) ⇒ Object

— Node traversal —



125
126
127
128
129
130
131
# File 'lib/canon/xml_parsing.rb', line 125

def children(node)
  if XmlBackend.nokogiri?
    node.is_a?(Nokogiri::XML::Node) ? node.children.to_a : []
  else
    node.is_a?(Moxml::Node) ? node.children.to_a : []
  end
end

.comment?(node) ⇒ Boolean

Returns:

  • (Boolean)


83
84
85
86
87
88
89
# File 'lib/canon/xml_parsing.rb', line 83

def comment?(node)
  if XmlBackend.nokogiri?
    node.is_a?(Nokogiri::XML::Comment)
  else
    node.is_a?(Moxml::Comment)
  end
end

.document?(obj) ⇒ Boolean

— Type checks (backend-safe) —

Returns:

  • (Boolean)


51
52
53
54
55
56
57
# File 'lib/canon/xml_parsing.rb', line 51

def document?(obj)
  if XmlBackend.nokogiri?
    obj.is_a?(Nokogiri::XML::Document)
  else
    obj.is_a?(Moxml::Document)
  end
end

.document_fragment?(obj) ⇒ Boolean

Returns:

  • (Boolean)


107
108
109
110
111
112
113
# File 'lib/canon/xml_parsing.rb', line 107

def document_fragment?(obj)
  if XmlBackend.nokogiri?
    obj.is_a?(Nokogiri::XML::DocumentFragment)
  else
    obj.is_a?(Moxml::DocumentFragment)
  end
end

.dtd?(node) ⇒ Boolean

Returns:

  • (Boolean)


115
116
117
118
119
120
121
# File 'lib/canon/xml_parsing.rb', line 115

def dtd?(node)
  if XmlBackend.nokogiri?
    node.is_a?(Nokogiri::XML::DTD)
  else
    false
  end
end

.element?(node) ⇒ Boolean

Returns:

  • (Boolean)


67
68
69
70
71
72
73
# File 'lib/canon/xml_parsing.rb', line 67

def element?(node)
  if XmlBackend.nokogiri?
    node.is_a?(Nokogiri::XML::Element)
  else
    node.is_a?(Moxml::Element)
  end
end

.moxml_contextObject



16
17
18
# File 'lib/canon/xml_parsing.rb', line 16

def moxml_context
  @moxml_context ||= Moxml.new(:oga)
end

.name(node) ⇒ Object



133
134
135
136
137
138
139
# File 'lib/canon/xml_parsing.rb', line 133

def name(node)
  if XmlBackend.nokogiri?
    node.is_a?(Nokogiri::XML::Node) ? node.name : nil
  else
    node.is_a?(Moxml::Node) ? node.name : nil
  end
end

.namespace_definitions(node) ⇒ Object



165
166
167
168
169
170
171
# File 'lib/canon/xml_parsing.rb', line 165

def namespace_definitions(node)
  if XmlBackend.nokogiri?
    node.is_a?(Nokogiri::XML::Element) ? node.namespace_definitions : []
  else
    node.is_a?(Moxml::Element) ? node.namespace_definitions : []
  end
end

.namespace_uri(node) ⇒ Object



181
182
183
184
185
186
187
# File 'lib/canon/xml_parsing.rb', line 181

def namespace_uri(node)
  if XmlBackend.nokogiri?
    node.namespace&.href if node.is_a?(Nokogiri::XML::Element)
  elsif node.is_a?(Moxml::Element)
    node.namespace_uri
  end
end

.node_type(node) ⇒ Object

Returns a symbol for all backends (:element, :text, :comment, etc.) or nil for unrecognised nodes.



191
192
193
194
195
196
197
# File 'lib/canon/xml_parsing.rb', line 191

def node_type(node)
  if XmlBackend.nokogiri?
    nokogiri_node_type(node)
  else
    moxml_node_type(node)
  end
end

.parent(node) ⇒ Object



173
174
175
176
177
178
179
# File 'lib/canon/xml_parsing.rb', line 173

def parent(node)
  return nil unless xml_node?(node)
  # Document nodes have no parent
  return nil if document?(node)

  node.parent
end

.parse(xml_string, options = {}) ⇒ Object

— Parsing —



22
23
24
25
26
27
28
# File 'lib/canon/xml_parsing.rb', line 22

def parse(xml_string, options = {})
  if XmlBackend.nokogiri?
    nokogiri_parse(xml_string, options)
  else
    moxml_parse(xml_string, options)
  end
end

.parse_fragment(xml_string) ⇒ Object



30
31
32
33
34
35
36
37
# File 'lib/canon/xml_parsing.rb', line 30

def parse_fragment(xml_string)
  if XmlBackend.nokogiri?
    Nokogiri::XML.fragment(xml_string).children.to_a
  else
    doc = moxml_context.parse("<__frag__>#{xml_string}</__frag__>")
    doc.root.children.to_a
  end
end

.processing_instruction?(node) ⇒ Boolean

Returns:

  • (Boolean)


99
100
101
102
103
104
105
# File 'lib/canon/xml_parsing.rb', line 99

def processing_instruction?(node)
  if XmlBackend.nokogiri?
    node.is_a?(Nokogiri::XML::ProcessingInstruction)
  else
    node.is_a?(Moxml::ProcessingInstruction)
  end
end

.serialize(node) ⇒ Object

— Serialization —



41
42
43
44
45
46
47
# File 'lib/canon/xml_parsing.rb', line 41

def serialize(node)
  if XmlBackend.nokogiri?
    nokogiri_serialize(node)
  else
    moxml_serialize(node)
  end
end

.text_content(node) ⇒ Object



141
142
143
144
145
146
147
# File 'lib/canon/xml_parsing.rb', line 141

def text_content(node)
  if XmlBackend.nokogiri?
    node.is_a?(Nokogiri::XML::Node) ? node.content : node.to_s
  else
    node.is_a?(Moxml::Node) ? node.text : node.to_s
  end
end

.text_node?(node) ⇒ Boolean

Returns:

  • (Boolean)


75
76
77
78
79
80
81
# File 'lib/canon/xml_parsing.rb', line 75

def text_node?(node)
  if XmlBackend.nokogiri?
    node.is_a?(Nokogiri::XML::Text)
  else
    node.is_a?(Moxml::Text)
  end
end

.xml_node?(obj) ⇒ Boolean

Returns:

  • (Boolean)


59
60
61
62
63
64
65
# File 'lib/canon/xml_parsing.rb', line 59

def xml_node?(obj)
  if XmlBackend.nokogiri?
    obj.is_a?(Nokogiri::XML::Node)
  else
    obj.is_a?(Moxml::Node)
  end
end