Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions ext/nokolexbor/nl_node.c
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,19 @@ nl_node_equals(VALUE self, VALUE other)
return node1 == node2 ? Qtrue : Qfalse;
}

static VALUE
nl_node_hash(VALUE self)
{
lxb_dom_node_t *node = nl_rb_node_unwrap(self);
return ULL2NUM((unsigned long long)(uintptr_t)node);
}

static VALUE
nl_node_eql(VALUE self, VALUE other)
{
return nl_node_equals(self, other);
}

const lxb_char_t *
lxb_dom_node_name_qualified(lxb_dom_node_t *node, size_t *len)
{
Expand Down Expand Up @@ -1241,6 +1254,8 @@ void Init_nl_node(void)
rb_define_method(cNokolexborNode, "[]=", nl_node_set_attr, 2);
rb_define_method(cNokolexborNode, "remove_attr", nl_node_remove_attr, 1);
rb_define_method(cNokolexborNode, "==", nl_node_equals, 1);
rb_define_method(cNokolexborNode, "eql?", nl_node_eql, 1);
rb_define_method(cNokolexborNode, "hash", nl_node_hash, 0);
rb_define_method(cNokolexborNode, "css_impl", nl_node_css, 1);
rb_define_method(cNokolexborNode, "at_css_impl", nl_node_at_css, 1);
rb_define_method(cNokolexborNode, "inner_html", nl_node_inner_html, -1);
Expand Down
71 changes: 70 additions & 1 deletion spec/node_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1053,4 +1053,73 @@
_(doc.at_css('.a').path).must_equal '/html/body/div/span/a[1]'
_(doc.at_css('.b').path).must_equal '/html/body/div/span/a[2]'
end
end

describe 'hash and eql?' do
it 'eql? returns true for same node' do
doc = Nokolexbor::HTML('<html><body><div id="x"></div></body></html>')
a = doc.at_css('#x')
b = doc.at_css('#x')
_(a.eql?(b)).must_equal true
end

it 'hash is consistent across wrappers' do
doc = Nokolexbor::HTML('<html><body><div id="x"></div></body></html>')
a = doc.at_css('#x')
b = doc.at_css('#x')
_(a.hash == b.hash).must_equal true
end

it 'works as Hash key' do
doc = Nokolexbor::HTML('<html><body><div id="x"></div></body></html>')
a = doc.at_css('#x')
b = doc.at_css('#x')
map = { a => 'value' }
_(map[b]).must_equal 'value'
end

it 'uniq removes duplicate node wrappers' do
doc = Nokolexbor::HTML('<html><body><div id="x"></div><p></p></body></html>')
a = doc.at_css('#x')
b = doc.at_css('#x')
c = doc.at_css('p')
_(([a, b, c].uniq.size)).must_equal 2
end

it 'hash is stable across DOM mutations' do
doc = Nokolexbor::HTML('<html><body><div><p id="y"></p></div></body></html>')
el = doc.at_css('#y')
h1 = el.hash

# Move the element
new_div = Nokolexbor::Element.new('section', doc)
doc.at_css('body').add_child(new_div)
new_div.add_child(el)

h2 = doc.at_css('section #y').hash
_(h1 == h2).must_equal true
end

it 'different nodes have different hashes' do
doc = Nokolexbor::HTML('<html><body><div id="x"></div><p></p></body></html>')
a = doc.at_css('#x')
p_el = doc.at_css('p')
_(a.hash == p_el.hash).must_equal false
end

it 'eql? returns false for different nodes' do
doc = Nokolexbor::HTML('<html><body><div id="x"></div><p></p></body></html>')
a = doc.at_css('#x')
b = doc.at_css('p')
_(a.eql?(b)).must_equal false
end

it 'equal? and eql? are different' do
doc = Nokolexbor::HTML('<html><body><div id="x"></div></body></html>')
a = doc.at_css('#x')
b = doc.at_css('#x')
_(a == b).must_equal true
_(a.eql?(b)).must_equal true
_(a.equal?(b)).must_equal false
end
end
end