Skip to content

Commit

Permalink
XML::DocumentFragment#initialize takes kwargs
Browse files Browse the repository at this point in the history
and improve the documentation

Part of #3323
  • Loading branch information
flavorjones committed Dec 8, 2024
1 parent 98a878c commit 30bac72
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 15 deletions.
75 changes: 64 additions & 11 deletions lib/nokogiri/xml/document_fragment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,28 @@ class DocumentFragment < Nokogiri::XML::Node
attr_reader :parse_options

class << self
# Create a Nokogiri::XML::DocumentFragment from +tags+
# :call-seq:
# parse(input) { |options| ... } → XML::DocumentFragment
# parse(input, options:) → XML::DocumentFragment
#
# Parse \XML fragment input from a String, and return a new XML::DocumentFragment. This
# method creates a new, empty XML::Document to contain the fragment.
#
# [Required Parameters]
# - +input+ (String) The content to be parsed.
#
# [Optional Keyword Arguments]
# - +options+ (Nokogiri::XML::ParseOptions) Configuration object that determines some
# behaviors during parsing. See ParseOptions for more information. The default value is
# +ParseOptions::DEFAULT_XML+.
#
# [Yields]
# If a block is given, a Nokogiri::XML::ParseOptions object is yielded to the block which
# can be configured before parsing. See Nokogiri::XML::ParseOptions for more information.
#
# [Returns] Nokogiri::XML::DocumentFragment
def parse(tags, options_ = ParseOptions::DEFAULT_XML, options: options_, &block)
new(XML::Document.new, tags, nil, options, &block)
new(XML::Document.new, tags, options: options, &block)
end

# Wrapper method to separate the concerns of:
Expand All @@ -27,26 +46,60 @@ def new(document, ...) # :nodoc:
end
end

##
# Create a new DocumentFragment from +tags+.
# :call-seq:
# new(document, input=nil) { |options| ... } → DocumentFragment
# new(document, input=nil, context:, options:) → DocumentFragment
#
# If +ctx+ is present, it is used as a context node for the
# subtree created, e.g., namespaces will be resolved relative
# to +ctx+.
def initialize(document, tags = nil, ctx = nil, options = ParseOptions::DEFAULT_XML) # rubocop:disable Lint/MissingSuper
# Parse \XML fragment input from a String, and return a new DocumentFragment that is
# associated with the given +document+.
#
# 💡 It's recommended to use either XML::DocumentFragment.parse or Node#parse rather than call
# this method directly.
#
# [Required Parameters]
# - +document+ (XML::Document) The parent document to associate the returned fragment with.
#
# [Optional Parameters]
# - +input+ (String) The content to be parsed.
#
# [Optional Keyword Arguments]
# - +context:+ (Nokogiri::XML::Node) The <b>context node</b> for the subtree created. See
# below for more information.
#
# - +options:+ (Nokogiri::XML::ParseOptions) Configuration object that determines some
# behaviors during parsing. See ParseOptions for more information. The default value is
# +ParseOptions::DEFAULT_XML+.
#
# [Yields]
# If a block is given, a Nokogiri::XML::ParseOptions object is yielded to the block which
# can be configured before parsing. See ParseOptions for more information.
#
# [Returns] XML::DocumentFragment
#
# === Context \Node
#
# If a context node is specified using +context:+, then the fragment will be created by
# calling Node#parse on that node, so the parser will behave as if that Node is the parent of
# the fragment subtree, and will resolve namespaces relative to that node.
#
def initialize(
document, tags = nil,
context_ = nil, options_ = ParseOptions::DEFAULT_XML,
context: context_, options: options_
) # rubocop:disable Lint/MissingSuper
return self unless tags

options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
@parse_options = options
yield options if block_given?

children = if ctx
children = if context
# Fix for issue#490
if Nokogiri.jruby?
# fix for issue #770
ctx.parse("<root #{namespace_declarations(ctx)}>#{tags}</root>", options).children
context.parse("<root #{namespace_declarations(context)}>#{tags}</root>", options).children
else
ctx.parse(tags, options)
context.parse(tags, options)
end
else
wrapper_doc = XML::Document.parse("<root>#{tags}</root>", nil, nil, options)
Expand Down
21 changes: 17 additions & 4 deletions test/xml/test_document_fragment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,16 @@ def test_for_libxml_in_context_memory_badness_when_encountering_encoding_errors
end
end

it "accepts options as kwargs" do
frag = Nokogiri::XML::DocumentFragment.new(Nokogiri::XML::Document.new, input, options: xml_default)
assert_equal("<a>foo</a>", frag.to_html)
refute_empty(frag.errors)

assert_raises(Nokogiri::SyntaxError) do
Nokogiri::XML::DocumentFragment.new(Nokogiri::XML::Document.new, input, options: xml_strict)
end
end

it "takes a config block" do
default_config = nil
Nokogiri::XML::DocumentFragment.new(Nokogiri::XML::Document.new, input) do |config|
Expand Down Expand Up @@ -520,9 +530,9 @@ def test_for_libxml_in_context_memory_badness_when_encountering_encoding_errors
Class.new(Nokogiri::XML::DocumentFragment) do
attr_accessor :initialized_with, :initialized_count

def initialize(*args)
def initialize(*args, **kwargs)
super
@initialized_with = args
@initialized_with = [args, kwargs]
@initialized_count ||= 0
@initialized_count += 1
end
Expand All @@ -541,8 +551,11 @@ def initialize(*args)
end

it "passes args to #initialize" do
fragment = klass.new(xml, "<div>a</div>")
assert_equal([xml, "<div>a</div>"], fragment.initialized_with)
fragment = klass.new(xml, "<div>a</div>", options: ParseOptions::DEFAULT_XML)
assert_equal(
[[xml, "<div>a</div>"], { options: ParseOptions::DEFAULT_XML }],
fragment.initialized_with,
)
end
end

Expand Down

0 comments on commit 30bac72

Please sign in to comment.