From 46a3c7de1310072d6d68ab992c70b3dd588d94c8 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Sun, 28 Jan 2018 23:59:33 -0500 Subject: [PATCH] DocumentFragment#dup avoids unnecessary copies. [fixes #1063] --- CHANGELOG.md | 1 + lib/nokogiri/xml/document_fragment.rb | 9 +++++++++ test/xml/test_document_fragment.rb | 14 ++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03093ae4ebf..d25a66ec195 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -104,6 +104,7 @@ If you're offended by what happened here, I'd kindly ask that you comment on the * [MRI] OpenBSD installation should be a bit easier now. [#1685] (Thanks, @jeremyevans!) * [MRI] Cross-built Windows gems now support Ruby 2.5 * Node#dup supports copying a node directly to a new document. See the method documentation for details. +* [MRI] DocumentFragment#dup is now more memory-efficient, avoiding making unnecessary copies. [#1063] ## Bug fixes diff --git a/lib/nokogiri/xml/document_fragment.rb b/lib/nokogiri/xml/document_fragment.rb index c2c5bec7350..57c266179d4 100644 --- a/lib/nokogiri/xml/document_fragment.rb +++ b/lib/nokogiri/xml/document_fragment.rb @@ -25,6 +25,15 @@ def initialize document, tags = nil, ctx = nil children.each { |child| child.parent = self } end + def dup + new_document = document.dup + new_fragment = XML::DocumentFragment.new(new_document) + children.each do |child| + child.dup(1, new_document).parent = new_fragment + end + new_fragment + end + ### # return the name for DocumentFragment def name diff --git a/test/xml/test_document_fragment.rb b/test/xml/test_document_fragment.rb index c919ee2068f..3ff801e847d 100644 --- a/test/xml/test_document_fragment.rb +++ b/test/xml/test_document_fragment.rb @@ -263,6 +263,20 @@ def test_issue_1077_parsing_of_frozen_strings Nokogiri::XML::DocumentFragment.parse(input) # assert_nothing_raised end + def test_dup_creates_tree_with_identical_structure + original = Nokogiri::XML::DocumentFragment.parse("

hello

") + duplicate = original.dup + assert_equal original.to_html, duplicate.to_html + end + + def test_dup_creates_mutable_tree + original = Nokogiri::XML::DocumentFragment.parse("

hello

") + duplicate = original.dup + duplicate.at_css("div").add_child("hello there") + assert_nil original.at_css("b") + assert_not_nil duplicate.at_css("b") + end + if Nokogiri.uses_libxml? def test_for_libxml_in_context_fragment_parsing_bug_workaround 10.times do