Class: Dommy::Internal::NodeWrapperCache

Inherits:
Object
  • Object
show all
Defined in:
lib/dommy/internal/node_wrapper_cache.rb

Overview

Manages DOM node identity via wrapper caching. Ensures that wrap_node(nokogiri_node) always returns the same Ruby object. Separates identity/caching management from Document’s public DOM API.

Constant Summary collapse

NAME_RE =
/\A[a-z][\w.\-]*\z/i

Instance Method Summary collapse

Constructor Details

#initialize(document) ⇒ NodeWrapperCache

Returns a new instance of NodeWrapperCache.



11
12
13
14
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 11

def initialize(document)
  @document = document
  @wrappers = {}
end

Instance Method Details

#create_attribute(name) ⇒ Object



51
52
53
54
55
56
57
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 51

def create_attribute(name)
  str = name.to_s
  raise DOMException::InvalidCharacterError, "name must not be empty" if str.empty?
  raise DOMException::InvalidCharacterError, "invalid attribute name: #{str.inspect}" unless str.match?(NAME_RE)

  Attr.new(str)
end

#create_attribute_ns(_namespace_uri, qualified_name) ⇒ Object



59
60
61
62
63
64
65
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 59

def create_attribute_ns(_namespace_uri, qualified_name)
  str = qualified_name.to_s
  raise DOMException::InvalidCharacterError, "name must not be empty" if str.empty?
  raise DOMException::InvalidCharacterError, "invalid qualified name: #{str.inspect}" unless str.match?(NAME_RE)

  Attr.new(str)
end

#create_comment(text) ⇒ Object



43
44
45
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 43

def create_comment(text)
  wrap_node(Nokogiri::XML::Comment.new(@document.nokogiri_doc, text.to_s))
end

#create_document_fragmentObject



47
48
49
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 47

def create_document_fragment
  wrap_node(@document.nokogiri_doc.fragment(""))
end

#create_element(name) ⇒ Object

Factory methods



31
32
33
34
35
36
37
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 31

def create_element(name)
  str = name.to_s
  raise DOMException::InvalidCharacterError, "name must not be empty" if str.empty?
  raise DOMException::InvalidCharacterError, "invalid element name: #{str.inspect}" unless str.match?(NAME_RE)

  wrap_node(Nokogiri::XML::Node.new(str.downcase, @document.nokogiri_doc))
end

#create_element_ns(namespace_uri, qualified_name) ⇒ Object



67
68
69
70
71
72
73
74
75
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 67

def create_element_ns(namespace_uri, qualified_name)
  str = qualified_name.to_s
  raise DOMException::InvalidCharacterError, "name must not be empty" if str.empty?
  raise DOMException::InvalidCharacterError, "invalid qualified name: #{str.inspect}" unless str.match?(NAME_RE)

  el = Nokogiri::XML::Node.new(str, @document.nokogiri_doc)
  el.add_namespace_definition(nil, namespace_uri.to_s) if namespace_uri && !namespace_uri.to_s.empty?
  wrap(el)
end

#create_text_node(text) ⇒ Object



39
40
41
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 39

def create_text_node(text)
  wrap_node(Nokogiri::XML::Text.new(text.to_s, @document.nokogiri_doc))
end

#get_element_by_id(id) ⇒ Object



91
92
93
94
95
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 91

def get_element_by_id(id)
  return nil if id.nil? || id.to_s.empty?

  wrap(@document.nokogiri_doc.at_css("##{id}"))
end

#get_elements_by_class_name(name) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 117

def get_elements_by_class_name(name)
  tokens = name.to_s.split(/\s+/).reject(&:empty?)
  doc = @document.nokogiri_doc
  cache = self
  HTMLCollection.new do
    next [] if tokens.empty?

    selector = tokens.map { |t| ".#{t}" }.join("")
    doc.css(selector).map { |n| cache.wrap(n) }.compact
  end
end

#get_elements_by_name(name) ⇒ Object



108
109
110
111
112
113
114
115
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 108

def get_elements_by_name(name)
  doc = @document.nokogiri_doc
  cache = self
  key = name.to_s
  HTMLCollection.new do
    doc.css("[name='#{key}']").map { |x| cache.wrap(x) }.compact
  end
end

#get_elements_by_tag_name(name) ⇒ Object



97
98
99
100
101
102
103
104
105
106
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 97

def get_elements_by_tag_name(name)
  n = name.to_s.downcase
  doc = @document.nokogiri_doc
  cache = self
  if n == "*"
    HTMLCollection.new { doc.css("*").map { |x| cache.wrap(x) }.compact }
  else
    HTMLCollection.new { doc.css(n).map { |x| cache.wrap(x) }.compact }
  end
end

#query_selector(selector) ⇒ Object

Query methods



79
80
81
82
83
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 79

def query_selector(selector)
  return nil if selector.nil? || selector.to_s.empty?

  wrap(@document.nokogiri_doc.at_css(selector.to_s))
end

#query_selector_all(selector) ⇒ Object



85
86
87
88
89
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 85

def query_selector_all(selector)
  return NodeList.new if selector.nil? || selector.to_s.empty?

  NodeList.new(@document.nokogiri_doc.css(selector.to_s).map { |node| wrap(node) }.compact)
end

#reset_wrapper(nokogiri_node) ⇒ Object

Clear cached wrapper (used by customElements.define for upgrades)



130
131
132
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 130

def reset_wrapper(nokogiri_node)
  @wrappers.delete(nokogiri_node.object_id)
end

#wrap(node) ⇒ Object

Returns the wrapped node, creating and caching if needed. Maintains DOM identity across repeated traversals.



18
19
20
21
22
23
24
25
26
27
# File 'lib/dommy/internal/node_wrapper_cache.rb', line 18

def wrap(node)
  return nil unless node

  cached = @wrappers[node.object_id]
  return cached if cached

  wrapper = build_wrapper_for(node)
  @wrappers[node.object_id] = wrapper if wrapper
  wrapper
end