Module: Canon::DiffFormatter::DiffDetailFormatterHelpers::NodeUtils

Defined in:
lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb

Overview

Node utility methods for the diff detail formatter.

All node queries delegate to NodeInspector / XmlParsing. No respond_to? — types are known at every call site.

Constant Summary collapse

ASCII_WHITESPACE_PATTERN =
/[ \t\r\n]/

Class Method Summary collapse

Class Method Details

.find_all_differing_attributes(node1, node2) ⇒ Object



23
24
25
26
27
28
29
30
31
32
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 23

def self.find_all_differing_attributes(node1, node2)
  return [] unless node1 && node2

  attrs1 = get_attributes_hash(node1)
  attrs2 = get_attributes_hash(node2)

  (attrs1.keys | attrs2.keys).reject do |key|
    attrs1[key.to_s] == attrs2[key.to_s]
  end
end

.format_node_brief(node) ⇒ Object

— Display helpers —



101
102
103
104
105
106
107
108
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 101

def self.format_node_brief(node)
  return "" unless node

  name = get_element_name_for_display(node)
  text = get_node_text(node)

  text && !text.empty? ? "#{name}(\"#{text}\")" : name
end

.get_attribute_names(node) ⇒ Object

— Attribute extraction —



15
16
17
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 15

def self.get_attribute_names(node)
  extract_attribute_names(node)
end

.get_attribute_names_in_order(node) ⇒ Object



19
20
21
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 19

def self.get_attribute_names_in_order(node)
  extract_attribute_names(node)
end

.get_attribute_value(node, attr_name) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 45

def self.get_attribute_value(node, attr_name)
  return nil unless node && attr_name

  case node
  when Canon::Xml::Nodes::ElementNode
    attr = node.attribute_nodes.find { |a| a.name == attr_name.to_s }
    attr&.value
  else
    XmlParsing.attribute_value(node, attr_name)
  end
end

.get_attributes_hash(node) ⇒ Object



34
35
36
37
38
39
40
41
42
43
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 34

def self.get_attributes_hash(node)
  return {} unless node

  case node
  when Canon::Xml::Nodes::ElementNode
    node.attribute_nodes.to_h { |a| [a.name.to_s, a.value.to_s] }
  else
    backend_attributes_hash(node)
  end
end

.get_element_name_for_display(node) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 80

def self.get_element_name_for_display(node)
  return "" unless node

  case node
  when Canon::Xml::Nodes::TextNode
    "text"
  when Canon::Xml::Nodes::CommentNode
    "comment"
  else
    Canon::Comparison::NodeInspector.name(node).to_s
  end
end

.get_namespace_uri_for_display(node) ⇒ Object



93
94
95
96
97
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 93

def self.get_namespace_uri_for_display(node)
  return "" unless node

  Canon::Comparison::NodeInspector.namespace_uri(node).to_s
end

.get_node_text(node) ⇒ Object

— Text / name / namespace —



59
60
61
62
63
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 59

def self.get_node_text(node)
  return "" unless node

  strip_ascii_whitespace(Canon::Comparison::NodeInspector.text_content(node).to_s)
end

.inside_preserve_element?(node) ⇒ Boolean

Returns:

  • (Boolean)


162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 162

def self.inside_preserve_element?(node)
  return false unless node

  preserve_elements = %w[pre code textarea script style]

  current = node
  while current
    name = Canon::Comparison::NodeInspector.name(current)
    return true if name && preserve_elements.include?(name.to_s.downcase)

    parent = Canon::Comparison::NodeInspector.parent(current)
    break if parent.nil? || parent == current

    current = parent
  end

  false
end

.node_to_display(node, compact: false) ⇒ Object



110
111
112
113
114
115
116
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 110

def self.node_to_display(node, compact: false)
  if compact && node.is_a?(Canon::Xml::Nodes::ElementNode)
    serialize_node_compact(node)
  else
    get_node_text(node)
  end
end

.parent_of(node) ⇒ Object

— Traversal —



158
159
160
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 158

def self.parent_of(node)
  Canon::Comparison::NodeInspector.parent(node)
end

.raw_text_value(node) ⇒ Object



150
151
152
153
154
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 150

def self.raw_text_value(node)
  return "" unless node

  Canon::Comparison::NodeInspector.text_content(node).to_s
end

.serialize_node_compact(node) ⇒ Object

— Serialization —



120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 120

def self.serialize_node_compact(node)
  return "" unless node

  case node
  when Canon::Xml::Nodes::TextNode
    CGI.escapeHTML(node.value.to_s)
  when Canon::Xml::Nodes::CommentNode
    "<!--#{CGI.escapeHTML(node.value.to_s)}-->"
  when Canon::Xml::Nodes::ElementNode
    serialize_element_compact(node)
  else
    serialize_backend_node_compact(node)
  end
end

.serialize_open_tag(node) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 135

def self.serialize_open_tag(node)
  return "" unless node

  case node
  when Canon::Xml::Nodes::ElementNode
    tag = node.name.to_s
    attrs = node.attribute_nodes.map do |a|
      " #{a.name}=\"#{CGI.escapeHTML(a.value.to_s)}\""
    end.join
    "<#{tag}#{attrs}>"
  else
    serialize_backend_open_tag(node)
  end
end

.strip_ascii_whitespace(str) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb', line 67

def self.strip_ascii_whitespace(str)
  return "" if str.nil?
  return str if str.empty?

  first_pos = str.index(/[^ \t\r\n]/)
  return "" unless first_pos

  reversed_pos = str.reverse.index(/[^ \t\r\n]/)
  last_pos = str.length - 1 - reversed_pos

  str[first_pos..last_pos]
end