Module: Canon::DiffFormatter::DiffDetailFormatterHelpers::LocationExtractor

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

Overview

Location extraction from diffs

Extracts and formats location information (XPath, file position).

Class Method Summary collapse

Class Method Details

.calculate_sibling_index(node, name) ⇒ Integer

Calculate sibling index for XPath

Parameters:

  • node (Object)

    Node to calculate index for

  • name (String)

    Node name

Returns:

  • (Integer)

    1-based index



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/canon/diff_formatter/diff_detail_formatter/location_extractor.rb', line 98

def self.calculate_sibling_index(node, name)
  parent = case node
           when Canon::Xml::Node, Nokogiri::XML::Node
             node.parent
           end

  return 1 unless parent

  siblings = case parent
             when Canon::Xml::Node, Nokogiri::XML::Node
               parent.children.select do |n|
                 case n
                 when Canon::Xml::Node, Nokogiri::XML::Node
                   n.name == name
                 else
                   false
                 end
               end
             else
               [node]
             end

  siblings.index(node) + 1
end

.extract_location(diff) ⇒ String

Extract location information from a diff

Parameters:

  • diff (DiffNode, Hash)

    Difference node

Returns:

  • (String)

    Location string



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/canon/diff_formatter/diff_detail_formatter/location_extractor.rb', line 16

def self.extract_location(diff)
  return "" unless diff

  # Prefer pre-computed path if available (populated by MetadataEnricher)
  if diff.is_a?(Canon::Diff::DiffNode) && diff.path && !diff.path.empty?
    return diff.path
  end

  # Fall back to extracting from nodes
  node = if diff.is_a?(Canon::Diff::DiffNode)
           diff.node1 || diff.node2
         elsif diff.is_a?(Hash)
           diff[:node1] || diff[:node2]
         end

  return "" unless node

  extract_xpath(node)
end

.extract_xpath(node) ⇒ String

Extract XPath from a node

Parameters:

  • node (Object)

    Node to extract XPath from

Returns:

  • (String)

    XPath string



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/canon/diff_formatter/diff_detail_formatter/location_extractor.rb', line 40

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

  # Use PathBuilder if available
  if defined?(Canon::Diff::PathBuilder)
    begin
      path = Canon::Diff::PathBuilder.build_path(node)
      return path unless path.nil? || path.empty?
    rescue StandardError
      # Fall through to manual extraction
    end
  end

  # Manual XPath extraction
  manual_xpath(node)
end

.manual_xpath(node) ⇒ String

Manual XPath extraction fallback

Parameters:

  • node (Object)

    Node to extract XPath from

Returns:

  • (String)

    XPath string



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/canon/diff_formatter/diff_detail_formatter/location_extractor.rb', line 61

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

  parts = []
  current = node

  while current
    name = case current
           when Canon::Xml::Node, Nokogiri::XML::Node
             current.name
           else
             break
           end
    break if name.nil? || name.empty?

    index = calculate_sibling_index(current, name)
    parts.unshift("#{name}[#{index}]")

    current = case current
              when Canon::Xml::Node, Nokogiri::XML::Node
                current.parent
              else
                break
              end

    break if current.is_a?(Nokogiri::XML::Document) ||
      current.is_a?(Canon::Xml::Nodes::RootNode)
  end

  parts.empty? ? "" : "/#{parts.join('/')}"
end