Module: Dommy::Backend

Defined in:
lib/dommy/backend.rb,
lib/dommy/backend/makiri_adapter.rb

Overview

Dommy::Backend — pluggable HTML parser abstraction. Lets Dommy work with Makiri (Lexbor-based, HTML5-only). Internally, all DOM library code goes through this facade rather than referencing the parser directly.

Defaults to Makiri.

Switching backends:

require "dommy"
Dommy::Backend.use(:makiri)

Or set directly:

Dommy::Backend.current = Dommy::Backend::Makiri

All adapters must implement the same interface — see Backend::Makiri for the canonical reference.

Defined Under Namespace

Modules: Makiri Classes: BackendNotAvailable

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.currentObject



27
28
29
# File 'lib/dommy/backend.rb', line 27

def current
  @current ||= detect_default
end

Class Method Details

.add_namespace_definition(node, prefix, href) ⇒ Object



208
209
210
# File 'lib/dommy/backend.rb', line 208

def add_namespace_definition(node, prefix, href)
  current.add_namespace_definition(node, prefix, href)
end

.adopt(node, target_doc) ⇒ Object

Bring node (already detached from its old tree) into target_doc, returning the backend node now owned by target_doc. Makiri can't move a node between arenas, so it imports a copy — callers must reseat any wrapper onto the returned node.



83
84
85
# File 'lib/dommy/backend.rb', line 83

def adopt(node, target_doc)
  current.adopt(node, target_doc)
end

.attribute_nodes(node) ⇒ Object

The element's attribute nodes (each readable via attribute_ns_info). The single choke point so DOM code doesn't touch parser internals.



238
239
240
# File 'lib/dommy/backend.rb', line 238

def attribute_nodes(node)
  current.attribute_nodes(node)
end

.attribute_ns_info(attr_node) ⇒ Object

Reads a backend attribute node into prefix:, local_name:, qualified_name:, value: (namespace-aware).



232
233
234
# File 'lib/dommy/backend.rb', line 232

def attribute_ns_info(attr_node)
  current.attribute_ns_info(attr_node)
end

.cdata_classObject



176
177
178
# File 'lib/dommy/backend.rb', line 176

def cdata_class
  current.respond_to?(:cdata_class) ? current.cdata_class : nil
end

.clone_document(doc) ⇒ Object

Deep copy of a whole document (DOM cloneNode on the document).



57
58
59
# File 'lib/dommy/backend.rb', line 57

def clone_document(doc)
  current.clone_document(doc)
end

.clone_node(node, deep:) ⇒ Object

Deep (or shallow) copy of an element/node, detached and owned by the same document — the backing for DOM cloneNode.



52
53
54
# File 'lib/dommy/backend.rb', line 52

def clone_node(node, deep:)
  current.clone_node(node, deep: deep)
end

.comment_classObject



256
257
258
# File 'lib/dommy/backend.rb', line 256

def comment_class
  current::Comment
end

.create_cdata(content, doc) ⇒ Object

CDATA section node (XML documents). Backends without CDATA fall back to a text node.



172
173
174
# File 'lib/dommy/backend.rb', line 172

def create_cdata(content, doc)
  current.respond_to?(:create_cdata) ? current.create_cdata(content, doc) : current.create_text(content, doc)
end

.create_comment(content, doc) ⇒ Object



166
167
168
# File 'lib/dommy/backend.rb', line 166

def create_comment(content, doc)
  current.create_comment(content, doc)
end

.create_document_type(name, public_id, system_id, doc) ⇒ Object

A detached DocumentType node owned by doc (for DOMImplementation.createDocumentType). Returns nil when the backend has no doctype factory (the caller falls back to a synthetic DocumentType); raises ArgumentError for a name the factory rejects (the caller then also falls back, since createDocumentType is permissive).



147
148
149
# File 'lib/dommy/backend.rb', line 147

def create_document_type(name, public_id, system_id, doc)
  current.respond_to?(:create_document_type) ? current.create_document_type(name, public_id, system_id, doc) : nil
end

.create_element(name, doc) ⇒ Object



129
130
131
# File 'lib/dommy/backend.rb', line 129

def create_element(name, doc)
  current.create_element(name, doc)
end

.create_element_loose(qualified_name, prefix, local, namespace, doc) ⇒ Object

Create a namespaced element permitting a DOM-valid qualified name that a strict XML backend would reject (an internal invalid char like "f}oo"), preserving case/prefix. Returns nil when the backend has no loose path (fall back to #create_element); raises ArgumentError for a genuinely invalid name (the caller maps it to InvalidCharacterError).



138
139
140
# File 'lib/dommy/backend.rb', line 138

def create_element_loose(qualified_name, prefix, local, namespace, doc)
  current.create_element_loose(qualified_name, prefix, local, namespace, doc)
end

.create_processing_instruction(target, data, doc) ⇒ Object

ProcessingInstruction node (<?target data?>). Supported by every backend Dommy ships (both Makiri document families), so — unlike CDATA — there is no text-node fallback.



183
184
185
# File 'lib/dommy/backend.rb', line 183

def create_processing_instruction(target, data, doc)
  current.create_processing_instruction(target, data, doc)
end

.create_text(content, doc) ⇒ Object



162
163
164
# File 'lib/dommy/backend.rb', line 162

def create_text(content, doc)
  current.create_text(content, doc)
end

.document_classObject



248
249
250
# File 'lib/dommy/backend.rb', line 248

def document_class
  current::Document
end

.document_fragment_classObject



260
261
262
# File 'lib/dommy/backend.rb', line 260

def document_fragment_class
  current::DocumentFragment
end

.document_type_classObject

The backend class for a DocumentType node (nil if unsupported), so the wrapper cache can route it to Dommy::DocumentType.



158
159
160
# File 'lib/dommy/backend.rb', line 158

def document_type_class
  current.respond_to?(:document_type_class) ? current.document_type_class : nil
end

.element_classObject

Type constants — proxy through to the current backend so node.is_a?(Backend::Element) resolves dynamically.



244
245
246
# File 'lib/dommy/backend.rb', line 244

def element_class
  current::Element
end

.empty_documentObject

A fresh, empty HTML-backed document.



62
63
64
# File 'lib/dommy/backend.rb', line 62

def empty_document
  current.empty_document
end

.empty_document_like(doc) ⇒ Object

An empty backing document matching doc's kind (HTML stays HTML, XML stays XML) — for a shallow document clone, whose result keeps the source flavor.



75
76
77
# File 'lib/dommy/backend.rb', line 75

def empty_document_like(doc)
  current.empty_document_like(doc)
end

.empty_xml_documentObject

A fresh, empty XML-backed document — for new Document() / createDocument, which the DOM defines as XML documents (case-preserving, real CDATA nodeType, namespaces).



69
70
71
# File 'lib/dommy/backend.rb', line 69

def empty_xml_document
  current.empty_xml_document
end

.fragment(html, owner_doc:) ⇒ Object



118
119
120
# File 'lib/dommy/backend.rb', line 118

def fragment(html, owner_doc:)
  current.fragment(html, owner_doc: owner_doc)
end

.get_attribute_ns(node, namespace, local_name) ⇒ Object

Namespaced attribute access (DOM *AttributeNS). namespace is an href String or nil.



214
215
216
# File 'lib/dommy/backend.rb', line 214

def get_attribute_ns(node, namespace, local_name)
  current.get_attribute_ns(node, namespace, local_name)
end

.has_attribute_ns?(node, namespace, local_name) ⇒ Boolean

Returns:

  • (Boolean)


226
227
228
# File 'lib/dommy/backend.rb', line 226

def has_attribute_ns?(node, namespace, local_name)
  current.has_attribute_ns?(node, namespace, local_name)
end

.identity_key(node) ⇒ Object

Stable per-document identity key for a backend node, used to cache DOM wrappers and key per-node side tables. Makiri mints a fresh wrapper per traversal but never frees nodes (arena-owned), so it keys on the stable node pointer.



46
47
48
# File 'lib/dommy/backend.rb', line 46

def identity_key(node)
  current.identity_key(node)
end

.internal_subset(doc) ⇒ Object

The parsed document's DocumentType node, or nil when it declares none.



152
153
154
# File 'lib/dommy/backend.rb', line 152

def internal_subset(doc)
  current.respond_to?(:internal_subset) ? current.internal_subset(doc) : nil
end

.moves_nodes_across_documents?Boolean

Whether the backend can move a node between documents in place or must adopt a copy first (Lexbor's arenas can't move a node, so inserting a foreign node requires importing it). Lets callers skip a needless — and on an empty target, root-less and therefore crashing — adoption on backends that don't need it.

Returns:

  • (Boolean)


91
92
93
# File 'lib/dommy/backend.rb', line 91

def moves_nodes_across_documents?
  current.respond_to?(:moves_nodes_across_documents?) ? current.moves_nodes_across_documents? : true
end

.namespace_definitions(node) ⇒ Object

The element's in-scope namespace declarations (each responds to prefix/href). Empty on backends without an XML namespace model.



197
198
199
# File 'lib/dommy/backend.rb', line 197

def namespace_definitions(node)
  current.namespace_definitions(node)
end

.namespace_of(node) ⇒ Object



191
192
193
# File 'lib/dommy/backend.rb', line 191

def namespace_of(node)
  current.namespace_of(node)
end

.node_classObject



264
265
266
# File 'lib/dommy/backend.rb', line 264

def node_class
  current::Node
end

.parse(html) ⇒ Object

Delegate calls so internal code can use Backend.parse(...).



108
109
110
# File 'lib/dommy/backend.rb', line 108

def parse(html)
  current.parse(html)
end

.parse_xml(xml) ⇒ Object

Parse XML input into an XML document. fall back to the HTML parser.



114
115
116
# File 'lib/dommy/backend.rb', line 114

def parse_xml(xml)
  current.parse_xml(xml)
end

.processing_instruction_classObject



187
188
189
# File 'lib/dommy/backend.rb', line 187

def processing_instruction_class
  current.processing_instruction_class
end

.remove_attribute_ns(node, namespace, local_name) ⇒ Object



222
223
224
# File 'lib/dommy/backend.rb', line 222

def remove_attribute_ns(node, namespace, local_name)
  current.remove_attribute_ns(node, namespace, local_name)
end

.select_all(node, selector, scope_node: nil) ⇒ Object

CSS query that honors Dommy's custom pseudo-classes (:disabled/:enabled/:checked/:scope). Each backend applies its own mechanism (Lexbor native pseudos plus a :scope rewrite). scope_node binds :scope to that element.



99
100
101
# File 'lib/dommy/backend.rb', line 99

def select_all(node, selector, scope_node: nil)
  current.select_all(node, selector, scope_node: scope_node)
end

.select_first(node, selector, scope_node: nil) ⇒ Object



103
104
105
# File 'lib/dommy/backend.rb', line 103

def select_first(node, selector, scope_node: nil)
  current.select_first(node, selector, scope_node: scope_node)
end

.set_attribute_ns(node, namespace, prefix, local_name, qualified_name, value) ⇒ Object



218
219
220
# File 'lib/dommy/backend.rb', line 218

def set_attribute_ns(node, namespace, prefix, local_name, qualified_name, value)
  current.set_attribute_ns(node, namespace, prefix, local_name, qualified_name, value)
end

.set_document_root(doc, node) ⇒ Object

Make node the sole document element of doc (used by DOMImplementation.createDocument). Lexbor-backed documents are seeded with an shell that must be cleared first.



125
126
127
# File 'lib/dommy/backend.rb', line 125

def set_document_root(doc, node)
  current.set_document_root(doc, node)
end

.template_content_nodes(node) ⇒ Object

The content child nodes of a <template> element. HTML5 parsers model template contents differently — Lexbor/Makiri in a separate content fragment — so reading them goes through the backend. Used by the template-content registry's migration.



204
205
206
# File 'lib/dommy/backend.rb', line 204

def template_content_nodes(node)
  current.template_content_nodes(node)
end

.text_classObject



252
253
254
# File 'lib/dommy/backend.rb', line 252

def text_class
  current::Text
end

.use(name) ⇒ Object



33
34
35
36
37
38
39
40
41
# File 'lib/dommy/backend.rb', line 33

def use(name)
  @current = case name.to_sym
  when :makiri
    require_relative "backend/makiri_adapter"
    Makiri
  else
    raise ArgumentError, "Unknown backend: #{name.inspect}. Use :makiri."
  end
end