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



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

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 —



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

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 —



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

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

.get_attribute_names_in_order(node) ⇒ Object



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

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

.get_attribute_value(node, attr_name) ⇒ Object



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

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



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

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



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

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



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

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

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

.get_node_text(node) ⇒ Object

— Text / name / namespace —



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

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)


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

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



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

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 —



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

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

.raw_text_value(node) ⇒ Object



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

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

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

.serialize_node_compact(node) ⇒ Object

— Serialization —



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

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



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

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



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

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