Skip to content

Instantly share code, notes, and snippets.

@yanowitz
Forked from nickyp/gist:451544
Created December 20, 2010 16:06
Show Gist options
  • Save yanowitz/748561 to your computer and use it in GitHub Desktop.
Save yanowitz/748561 to your computer and use it in GitHub Desktop.
require 'nokogiri'
# \
# \
# \\
# \\
# >\/7
# _.-(6' \
# (=___._/` \
# ) \ |
# / / |
# / > /
# j < _\
# _.-' : ``.
# \ r=._\ `.
# <`\\_ \ .`-.
# \ r-7 `-. ._ ' . `\
# \`, `-.`7 7) )
# \/ \| \' / `-._
# || .'
# cjr \\ (
# 10mar02 >\ >
# ,.-' >.'
# <.'_.''
# <'
# adapted from https://gist.github.com/451544, added namespace "support" and fixed some
# bugs
module Nokogiri
module XML
class Node
BLANK = /^[ \n]*$/
# Comparing with other node
# - same name
# - same attributes
# - same namespaces
# - order doesn't matter
# - recursively through all children
# This will allow to compare two documents (doc1.root =~ doc2.root)
# Pass in a namespace_hash for testing. You can build one thusly:
# h = {}; doc1.root.traverse {|e| h[e.namespace.prefix] = e.namespace.href if e && e.namespace }
def =~(other, namespace_hash = nil)
my_namespace_hash = {}
other_namespace_hash = {}
self.traverse {|e| my_namespace_hash[e.namespace.prefix] = e.namespace.href if e && e.namespace }
other.traverse {|e| other_namespace_hash[e.namespace.prefix] = e.namespace.href if e && e.namespace }
namespace_hash ||= my_namespace_hash
my_namespace_hash == other_namespace_hash && match_nodes(other, namespace_hash) && other.match_nodes(self, namespace_hash)
end
def match_nodes(other, namespace_hash)
self.traverse do |elem|
xpath_without_index = elem.path.sub(/\[[0-9]+\]/,'')
other_nodes = other.xpath(xpath_without_index, namespace_hash)
return false if other_nodes.empty?
# just compare highest level
same_element = elem.element? && elem.match_element(other_nodes)
same_text = elem.text? && elem.match_text(other_nodes)
return false unless same_element || same_text
end
true
end
def match_element(other_nodes)
other_nodes.any? do |node|
((namespace && namespace.href) == (node.namespace && node.namespace.href)) and
name == node.name and
match_attributes(node)
end
end
# if both are blank, the number of spaces doesn't matter
def match_text(other_nodes)
other_nodes.first.text == text || ((other_nodes.first.text =~ BLANK && text =~ BLANK) == 0)
end
private
# the compared node has same attributes as self
def match_attributes(other)
attributes.inject(true) do |memo,attr|
memo && other.attributes.include?(attr[0]) &&
other.attributes[attr[0]].name == attr[1].name &&
other.attributes[attr[0]].value == attr[1].value
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment