From 30bac72490b9b65fc40cec78a28af60f68bafaff Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Sun, 8 Dec 2024 10:18:16 -0500 Subject: [PATCH] XML::DocumentFragment#initialize takes kwargs and improve the documentation Part of #3323 --- lib/nokogiri/xml/document_fragment.rb | 75 +++++++++++++++++++++++---- test/xml/test_document_fragment.rb | 21 ++++++-- 2 files changed, 81 insertions(+), 15 deletions(-) diff --git a/lib/nokogiri/xml/document_fragment.rb b/lib/nokogiri/xml/document_fragment.rb index baf0540d602..a1a1f44c57d 100644 --- a/lib/nokogiri/xml/document_fragment.rb +++ b/lib/nokogiri/xml/document_fragment.rb @@ -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: @@ -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 context node 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("#{tags}", options).children + context.parse("#{tags}", options).children else - ctx.parse(tags, options) + context.parse(tags, options) end else wrapper_doc = XML::Document.parse("#{tags}", nil, nil, options) diff --git a/test/xml/test_document_fragment.rb b/test/xml/test_document_fragment.rb index 8be61d062f6..28407a4b2df 100644 --- a/test/xml/test_document_fragment.rb +++ b/test/xml/test_document_fragment.rb @@ -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("foo", 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| @@ -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 @@ -541,8 +551,11 @@ def initialize(*args) end it "passes args to #initialize" do - fragment = klass.new(xml, "
a
") - assert_equal([xml, "
a
"], fragment.initialized_with) + fragment = klass.new(xml, "
a
", options: ParseOptions::DEFAULT_XML) + assert_equal( + [[xml, "
a
"], { options: ParseOptions::DEFAULT_XML }], + fragment.initialized_with, + ) end end