nokogiri 1.13.6 → 1.16.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +43 -0
  3. data/LICENSE-DEPENDENCIES.md +830 -509
  4. data/LICENSE.md +1 -1
  5. data/README.md +21 -11
  6. data/dependencies.yml +34 -15
  7. data/ext/nokogiri/extconf.rb +167 -48
  8. data/ext/nokogiri/gumbo.c +21 -11
  9. data/ext/nokogiri/html4_document.c +3 -4
  10. data/ext/nokogiri/html4_element_description.c +20 -15
  11. data/ext/nokogiri/html4_entity_lookup.c +2 -2
  12. data/ext/nokogiri/html4_sax_parser_context.c +11 -22
  13. data/ext/nokogiri/html4_sax_push_parser.c +4 -4
  14. data/ext/nokogiri/nokogiri.c +84 -75
  15. data/ext/nokogiri/nokogiri.h +46 -16
  16. data/ext/nokogiri/test_global_handlers.c +2 -2
  17. data/ext/nokogiri/xml_attr.c +3 -3
  18. data/ext/nokogiri/xml_attribute_decl.c +5 -5
  19. data/ext/nokogiri/xml_cdata.c +31 -18
  20. data/ext/nokogiri/xml_comment.c +2 -2
  21. data/ext/nokogiri/xml_document.c +135 -38
  22. data/ext/nokogiri/xml_document_fragment.c +2 -2
  23. data/ext/nokogiri/xml_dtd.c +9 -9
  24. data/ext/nokogiri/xml_element_content.c +34 -31
  25. data/ext/nokogiri/xml_element_decl.c +10 -10
  26. data/ext/nokogiri/xml_encoding_handler.c +15 -7
  27. data/ext/nokogiri/xml_entity_decl.c +6 -6
  28. data/ext/nokogiri/xml_entity_reference.c +2 -2
  29. data/ext/nokogiri/xml_namespace.c +75 -14
  30. data/ext/nokogiri/xml_node.c +365 -87
  31. data/ext/nokogiri/xml_node_set.c +129 -111
  32. data/ext/nokogiri/xml_processing_instruction.c +2 -2
  33. data/ext/nokogiri/xml_reader.c +126 -64
  34. data/ext/nokogiri/xml_relax_ng.c +67 -82
  35. data/ext/nokogiri/xml_sax_parser.c +45 -20
  36. data/ext/nokogiri/xml_sax_parser_context.c +50 -30
  37. data/ext/nokogiri/xml_sax_push_parser.c +31 -12
  38. data/ext/nokogiri/xml_schema.c +95 -118
  39. data/ext/nokogiri/xml_syntax_error.c +4 -4
  40. data/ext/nokogiri/xml_text.c +27 -14
  41. data/ext/nokogiri/xml_xpath_context.c +213 -136
  42. data/ext/nokogiri/xslt_stylesheet.c +126 -67
  43. data/gumbo-parser/Makefile +28 -0
  44. data/gumbo-parser/src/attribute.h +1 -1
  45. data/gumbo-parser/src/error.c +10 -6
  46. data/gumbo-parser/src/error.h +1 -1
  47. data/gumbo-parser/src/foreign_attrs.c +15 -16
  48. data/gumbo-parser/src/foreign_attrs.gperf +1 -1
  49. data/gumbo-parser/src/{gumbo.h → nokogiri_gumbo.h} +1 -0
  50. data/gumbo-parser/src/parser.c +29 -10
  51. data/gumbo-parser/src/replacement.h +1 -1
  52. data/gumbo-parser/src/string_buffer.h +1 -1
  53. data/gumbo-parser/src/string_piece.c +1 -1
  54. data/gumbo-parser/src/svg_attrs.c +2 -2
  55. data/gumbo-parser/src/svg_tags.c +2 -2
  56. data/gumbo-parser/src/tag.c +2 -1
  57. data/gumbo-parser/src/tag_lookup.c +7 -7
  58. data/gumbo-parser/src/tag_lookup.gperf +1 -0
  59. data/gumbo-parser/src/tag_lookup.h +1 -1
  60. data/gumbo-parser/src/token_buffer.h +1 -1
  61. data/gumbo-parser/src/tokenizer.c +2 -1
  62. data/gumbo-parser/src/tokenizer.h +1 -1
  63. data/gumbo-parser/src/utf8.c +1 -1
  64. data/gumbo-parser/src/utf8.h +1 -1
  65. data/gumbo-parser/src/util.c +1 -3
  66. data/gumbo-parser/src/util.h +4 -0
  67. data/gumbo-parser/src/vector.h +1 -1
  68. data/lib/nokogiri/css/node.rb +2 -2
  69. data/lib/nokogiri/css/parser_extras.rb +1 -1
  70. data/lib/nokogiri/css/xpath_visitor.rb +8 -26
  71. data/lib/nokogiri/css.rb +6 -0
  72. data/lib/nokogiri/decorators/slop.rb +1 -1
  73. data/lib/nokogiri/encoding_handler.rb +57 -0
  74. data/lib/nokogiri/extension.rb +4 -3
  75. data/lib/nokogiri/html4/document.rb +3 -122
  76. data/lib/nokogiri/html4/document_fragment.rb +1 -1
  77. data/lib/nokogiri/html4/element_description_defaults.rb +1827 -365
  78. data/lib/nokogiri/html4/encoding_reader.rb +121 -0
  79. data/lib/nokogiri/html4.rb +1 -0
  80. data/lib/nokogiri/html5/document.rb +113 -36
  81. data/lib/nokogiri/html5/document_fragment.rb +10 -3
  82. data/lib/nokogiri/html5/node.rb +8 -5
  83. data/lib/nokogiri/html5.rb +74 -226
  84. data/lib/nokogiri/jruby/dependencies.rb +1 -19
  85. data/lib/nokogiri/jruby/nokogiri_jars.rb +43 -0
  86. data/lib/nokogiri/version/constant.rb +1 -1
  87. data/lib/nokogiri/version/info.rb +16 -14
  88. data/lib/nokogiri/xml/attr.rb +49 -0
  89. data/lib/nokogiri/xml/attribute_decl.rb +4 -2
  90. data/lib/nokogiri/xml/builder.rb +1 -1
  91. data/lib/nokogiri/xml/document.rb +103 -56
  92. data/lib/nokogiri/xml/document_fragment.rb +50 -7
  93. data/lib/nokogiri/xml/element_content.rb +10 -2
  94. data/lib/nokogiri/xml/element_decl.rb +4 -2
  95. data/lib/nokogiri/xml/entity_decl.rb +4 -2
  96. data/lib/nokogiri/xml/namespace.rb +41 -0
  97. data/lib/nokogiri/xml/node/save_options.rb +14 -4
  98. data/lib/nokogiri/xml/node.rb +241 -70
  99. data/lib/nokogiri/xml/node_set.rb +90 -11
  100. data/lib/nokogiri/xml/parse_options.rb +129 -50
  101. data/lib/nokogiri/xml/pp/node.rb +28 -15
  102. data/lib/nokogiri/xml/processing_instruction.rb +2 -1
  103. data/lib/nokogiri/xml/reader.rb +16 -17
  104. data/lib/nokogiri/xml/sax/document.rb +1 -1
  105. data/lib/nokogiri/xml/sax/parser.rb +2 -3
  106. data/lib/nokogiri/xml/searchable.rb +21 -13
  107. data/lib/nokogiri/xml/syntax_error.rb +1 -1
  108. data/lib/nokogiri/xml.rb +1 -1
  109. data/lib/nokogiri/xslt/stylesheet.rb +29 -7
  110. data/lib/nokogiri/xslt.rb +75 -5
  111. data/lib/nokogiri.rb +15 -15
  112. data/lib/xsd/xmlparser/nokogiri.rb +4 -2
  113. data/patches/libxml2/0010-update-config.guess-and-config.sub-for-libxml2.patch +224 -0
  114. data/patches/libxml2/0011-rip-out-libxml2-s-libc_single_threaded-support.patch +30 -0
  115. data/patches/libxslt/0001-update-config.guess-and-config.sub-for-libxslt.patch +224 -0
  116. data/ports/archives/libxml2-2.12.9.tar.xz +0 -0
  117. data/ports/archives/libxslt-1.1.39.tar.xz +0 -0
  118. metadata +21 -248
  119. data/patches/libxml2/0004-use-glibc-strlen.patch +0 -53
  120. data/patches/libxml2/0005-avoid-isnan-isinf.patch +0 -81
  121. data/patches/libxml2/0006-update-automake-files-for-arm64.patch +0 -3040
  122. data/patches/libxml2/0008-htmlParseComment-handle-abruptly-closed-comments.patch +0 -61
  123. data/patches/libxslt/0001-update-automake-files-for-arm64.patch +0 -3037
  124. data/ports/archives/libxml2-2.9.14.tar.xz +0 -0
  125. data/ports/archives/libxslt-1.1.35.tar.xz +0 -0
@@ -19,63 +19,72 @@ module Nokogiri
19
19
  NCNAME_CHAR = NCNAME_START_CHAR + "\\-\\.0-9"
20
20
  NCNAME_RE = /^xmlns(?::([#{NCNAME_START_CHAR}][#{NCNAME_CHAR}]*))?$/
21
21
 
22
- ##
23
- # Parse an XML file.
24
- #
25
- # +string_or_io+ may be a String, or any object that responds to
26
- # _read_ and _close_ such as an IO, or StringIO.
27
- #
28
- # +url+ (optional) is the URI where this document is located.
29
- #
30
- # +encoding+ (optional) is the encoding that should be used when processing
31
- # the document.
32
- #
33
- # +options+ (optional) is a configuration object that sets options during
34
- # parsing, such as Nokogiri::XML::ParseOptions::RECOVER. See the
35
- # Nokogiri::XML::ParseOptions for more information.
36
- #
37
- # +block+ (optional) is passed a configuration object on which
38
- # parse options may be set.
39
- #
40
- # By default, Nokogiri treats documents as untrusted, and so
41
- # does not attempt to load DTDs or access the network. See
42
- # Nokogiri::XML::ParseOptions for a complete list of options;
43
- # and that module's DEFAULT_XML constant for what's set (and not
44
- # set) by default.
45
- #
46
- # Nokogiri.XML() is a convenience method which will call this method.
47
- #
48
- def self.parse(string_or_io, url = nil, encoding = nil, options = ParseOptions::DEFAULT_XML)
49
- options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
50
- yield options if block_given?
22
+ class << self
23
+ # Parse an XML file.
24
+ #
25
+ # +string_or_io+ may be a String, or any object that responds to
26
+ # _read_ and _close_ such as an IO, or StringIO.
27
+ #
28
+ # +url+ (optional) is the URI where this document is located.
29
+ #
30
+ # +encoding+ (optional) is the encoding that should be used when processing
31
+ # the document.
32
+ #
33
+ # +options+ (optional) is a configuration object that sets options during
34
+ # parsing, such as Nokogiri::XML::ParseOptions::RECOVER. See the
35
+ # Nokogiri::XML::ParseOptions for more information.
36
+ #
37
+ # +block+ (optional) is passed a configuration object on which
38
+ # parse options may be set.
39
+ #
40
+ # By default, Nokogiri treats documents as untrusted, and so
41
+ # does not attempt to load DTDs or access the network. See
42
+ # Nokogiri::XML::ParseOptions for a complete list of options;
43
+ # and that module's DEFAULT_XML constant for what's set (and not
44
+ # set) by default.
45
+ #
46
+ # Nokogiri.XML() is a convenience method which will call this method.
47
+ #
48
+ def parse(string_or_io, url = nil, encoding = nil, options = ParseOptions::DEFAULT_XML)
49
+ options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
50
+ yield options if block_given?
51
+
52
+ url ||= string_or_io.respond_to?(:path) ? string_or_io.path : nil
53
+
54
+ if empty_doc?(string_or_io)
55
+ if options.strict?
56
+ raise Nokogiri::XML::SyntaxError, "Empty document"
57
+ else
58
+ return encoding ? new.tap { |i| i.encoding = encoding } : new
59
+ end
60
+ end
51
61
 
52
- url ||= string_or_io.respond_to?(:path) ? string_or_io.path : nil
62
+ doc = if string_or_io.respond_to?(:read)
63
+ if string_or_io.is_a?(Pathname)
64
+ # resolve the Pathname to the file and open it as an IO object, see #2110
65
+ string_or_io = string_or_io.expand_path.open
66
+ url ||= string_or_io.path
67
+ end
53
68
 
54
- if empty_doc?(string_or_io)
55
- if options.strict?
56
- raise Nokogiri::XML::SyntaxError, "Empty document"
69
+ read_io(string_or_io, url, encoding, options.to_i)
57
70
  else
58
- return encoding ? new.tap { |i| i.encoding = encoding } : new
71
+ # read_memory pukes on empty docs
72
+ read_memory(string_or_io, url, encoding, options.to_i)
59
73
  end
60
- end
61
74
 
62
- doc = if string_or_io.respond_to?(:read)
63
- if string_or_io.is_a?(Pathname)
64
- # resolve the Pathname to the file and open it as an IO object, see #2110
65
- string_or_io = string_or_io.expand_path.open
66
- url ||= string_or_io.path
67
- end
75
+ # do xinclude processing
76
+ doc.do_xinclude(options) if options.xinclude?
68
77
 
69
- read_io(string_or_io, url, encoding, options.to_i)
70
- else
71
- # read_memory pukes on empty docs
72
- read_memory(string_or_io, url, encoding, options.to_i)
78
+ doc
73
79
  end
74
80
 
75
- # do xinclude processing
76
- doc.do_xinclude(options) if options.xinclude?
81
+ private
77
82
 
78
- doc
83
+ def empty_doc?(string_or_io)
84
+ string_or_io.nil? ||
85
+ (string_or_io.respond_to?(:empty?) && string_or_io.empty?) ||
86
+ (string_or_io.respond_to?(:eof?) && string_or_io.eof?)
87
+ end
79
88
  end
80
89
 
81
90
  ##
@@ -165,7 +174,7 @@ module Nokogiri
165
174
  # Since v1.12.4
166
175
  attr_accessor :namespace_inheritance
167
176
 
168
- def initialize(*args) # :nodoc:
177
+ def initialize(*args) # :nodoc: # rubocop:disable Lint/MissingSuper
169
178
  @errors = []
170
179
  @decorators = nil
171
180
  @namespace_inheritance = false
@@ -320,7 +329,7 @@ module Nokogiri
320
329
  # Validate this Document against it's DTD. Returns a list of errors on
321
330
  # the document or +nil+ when there is no DTD.
322
331
  def validate
323
- return nil unless internal_subset
332
+ return unless internal_subset
324
333
 
325
334
  internal_subset.validate(self)
326
335
  end
@@ -405,14 +414,52 @@ module Nokogiri
405
414
  Nokogiri::CSS::XPathVisitor::DoctypeConfig::XML
406
415
  end
407
416
 
408
- private
409
-
410
- def self.empty_doc?(string_or_io)
411
- string_or_io.nil? ||
412
- (string_or_io.respond_to?(:empty?) && string_or_io.empty?) ||
413
- (string_or_io.respond_to?(:eof?) && string_or_io.eof?)
417
+ #
418
+ # :call-seq: deconstruct_keys(array_of_names) → Hash
419
+ #
420
+ # Returns a hash describing the Document, to use in pattern matching.
421
+ #
422
+ # Valid keys and their values:
423
+ # - +root+ → (Node, nil) The root node of the Document, or +nil+ if the document is empty.
424
+ #
425
+ # In the future, other keys may allow accessing things like doctype and processing
426
+ # instructions. If you have a use case and would like this functionality, please let us know
427
+ # by opening an issue or a discussion on the github project.
428
+ #
429
+ # *Example*
430
+ #
431
+ # doc = Nokogiri::XML.parse(<<~XML)
432
+ # <?xml version="1.0"?>
433
+ # <root>
434
+ # <child>
435
+ # </root>
436
+ # XML
437
+ #
438
+ # doc.deconstruct_keys([:root])
439
+ # # => {:root=>
440
+ # # #(Element:0x35c {
441
+ # # name = "root",
442
+ # # children = [
443
+ # # #(Text "\n" + " "),
444
+ # # #(Element:0x370 { name = "child", children = [ #(Text "\n")] }),
445
+ # # #(Text "\n")]
446
+ # # })}
447
+ #
448
+ # *Example* of an empty document
449
+ #
450
+ # doc = Nokogiri::XML::Document.new
451
+ #
452
+ # doc.deconstruct_keys([:root])
453
+ # # => {:root=>nil}
454
+ #
455
+ # Since v1.14.0
456
+ #
457
+ def deconstruct_keys(keys)
458
+ { root: root }
414
459
  end
415
460
 
461
+ private
462
+
416
463
  IMPLIED_XPATH_CONTEXTS = ["//"].freeze # :nodoc:
417
464
 
418
465
  def inspect_attributes
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Nokogiri
@@ -15,7 +16,7 @@ module Nokogiri
15
16
  # If +ctx+ is present, it is used as a context node for the
16
17
  # subtree created, e.g., namespaces will be resolved relative
17
18
  # to +ctx+.
18
- def initialize(document, tags = nil, ctx = nil, options = ParseOptions::DEFAULT_XML)
19
+ def initialize(document, tags = nil, ctx = nil, options = ParseOptions::DEFAULT_XML) # rubocop:disable Lint/MissingSuper
19
20
  return self unless tags
20
21
 
21
22
  options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
@@ -66,9 +67,7 @@ module Nokogiri
66
67
  def to_html(*args)
67
68
  if Nokogiri.jruby?
68
69
  options = args.first.is_a?(Hash) ? args.shift : {}
69
- unless options[:save_with]
70
- options[:save_with] = Node::SaveOptions::NO_DECLARATION | Node::SaveOptions::NO_EMPTY_TAGS | Node::SaveOptions::AS_HTML
71
- end
70
+ options[:save_with] ||= Node::SaveOptions::DEFAULT_HTML
72
71
  args.insert(0, options)
73
72
  end
74
73
  children.to_html(*args)
@@ -80,9 +79,7 @@ module Nokogiri
80
79
  def to_xhtml(*args)
81
80
  if Nokogiri.jruby?
82
81
  options = args.first.is_a?(Hash) ? args.shift : {}
83
- unless options[:save_with]
84
- options[:save_with] = Node::SaveOptions::NO_DECLARATION | Node::SaveOptions::NO_EMPTY_TAGS | Node::SaveOptions::AS_XHTML
85
- end
82
+ options[:save_with] ||= Node::SaveOptions::DEFAULT_XHTML
86
83
  args.insert(0, options)
87
84
  end
88
85
  children.to_xhtml(*args)
@@ -148,6 +145,52 @@ module Nokogiri
148
145
  document.fragment(data)
149
146
  end
150
147
 
148
+ #
149
+ # :call-seq: deconstruct() → Array
150
+ #
151
+ # Returns the root nodes of this document fragment as an array, to use in pattern matching.
152
+ #
153
+ # 💡 Note that text nodes are returned as well as elements. If you wish to operate only on
154
+ # root elements, you should deconstruct the array returned by
155
+ # <tt>DocumentFragment#elements</tt>.
156
+ #
157
+ # *Example*
158
+ #
159
+ # frag = Nokogiri::HTML5.fragment(<<~HTML)
160
+ # <div>Start</div>
161
+ # This is a <a href="#jump">shortcut</a> for you.
162
+ # <div>End</div>
163
+ # HTML
164
+ #
165
+ # frag.deconstruct
166
+ # # => [#(Element:0x35c { name = "div", children = [ #(Text "Start")] }),
167
+ # # #(Text "\n" + "This is a "),
168
+ # # #(Element:0x370 {
169
+ # # name = "a",
170
+ # # attributes = [ #(Attr:0x384 { name = "href", value = "#jump" })],
171
+ # # children = [ #(Text "shortcut")]
172
+ # # }),
173
+ # # #(Text " for you.\n"),
174
+ # # #(Element:0x398 { name = "div", children = [ #(Text "End")] }),
175
+ # # #(Text "\n")]
176
+ #
177
+ # *Example* only the elements, not the text nodes.
178
+ #
179
+ # frag.elements.deconstruct
180
+ # # => [#(Element:0x35c { name = "div", children = [ #(Text "Start")] }),
181
+ # # #(Element:0x370 {
182
+ # # name = "a",
183
+ # # attributes = [ #(Attr:0x384 { name = "href", value = "#jump" })],
184
+ # # children = [ #(Text "shortcut")]
185
+ # # }),
186
+ # # #(Element:0x398 { name = "div", children = [ #(Text "End")] })]
187
+ #
188
+ # Since v1.14.0
189
+ #
190
+ def deconstruct
191
+ children.to_a
192
+ end
193
+
151
194
  private
152
195
 
153
196
  # fix for issue 770
@@ -11,9 +11,11 @@ module Nokogiri
11
11
  # ]>
12
12
  # </root>
13
13
  #
14
- # ElementContent represents the tree inside the <!ELEMENT> tag shown above
15
- # that lists the possible content for the div1 tag.
14
+ # ElementContent represents the binary tree inside the <!ELEMENT> tag shown above that lists the
15
+ # possible content for the div1 tag.
16
16
  class ElementContent
17
+ include Nokogiri::XML::PP::Node
18
+
17
19
  # Possible definitions of type
18
20
  PCDATA = 1
19
21
  ELEMENT = 2
@@ -33,6 +35,12 @@ module Nokogiri
33
35
  def children
34
36
  [c1, c2].compact
35
37
  end
38
+
39
+ private
40
+
41
+ def inspect_attributes
42
+ [:prefix, :name, :type, :occur, :children]
43
+ end
36
44
  end
37
45
  end
38
46
  end
@@ -7,8 +7,10 @@ module Nokogiri
7
7
  undef_method :namespace_definitions
8
8
  undef_method :line if method_defined?(:line)
9
9
 
10
- def inspect
11
- "#<#{self.class.name}:#{format("0x%x", object_id)} #{to_s.inspect}>"
10
+ private
11
+
12
+ def inspect_attributes
13
+ [:to_s]
12
14
  end
13
15
  end
14
16
  end
@@ -13,8 +13,10 @@ module Nokogiri
13
13
  doc.create_entity(name, *args)
14
14
  end
15
15
 
16
- def inspect
17
- "#<#{self.class.name}:#{format("0x%x", object_id)} #{to_s.inspect}>"
16
+ private
17
+
18
+ def inspect_attributes
19
+ [:to_s]
18
20
  end
19
21
  end
20
22
  end
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Nokogiri
@@ -6,6 +7,46 @@ module Nokogiri
6
7
  include Nokogiri::XML::PP::Node
7
8
  attr_reader :document
8
9
 
10
+ #
11
+ # :call-seq: deconstruct_keys(array_of_names) → Hash
12
+ #
13
+ # Returns a hash describing the Namespace, to use in pattern matching.
14
+ #
15
+ # Valid keys and their values:
16
+ # - +prefix+ → (String, nil) The namespace's prefix, or +nil+ if there is no prefix (e.g., default namespace).
17
+ # - +href+ → (String) The namespace's URI
18
+ #
19
+ # *Example*
20
+ #
21
+ # doc = Nokogiri::XML.parse(<<~XML)
22
+ # <?xml version="1.0"?>
23
+ # <root xmlns="http://nokogiri.org/ns/default" xmlns:noko="http://nokogiri.org/ns/noko">
24
+ # <child1 foo="abc" noko:bar="def"/>
25
+ # <noko:child2 foo="qwe" noko:bar="rty"/>
26
+ # </root>
27
+ # XML
28
+ #
29
+ # doc.root.elements.first.namespace
30
+ # # => #(Namespace:0x35c { href = "http://nokogiri.org/ns/default" })
31
+ #
32
+ # doc.root.elements.first.namespace.deconstruct_keys([:prefix, :href])
33
+ # # => {:prefix=>nil, :href=>"http://nokogiri.org/ns/default"}
34
+ #
35
+ # doc.root.elements.last.namespace
36
+ # # => #(Namespace:0x370 {
37
+ # # prefix = "noko",
38
+ # # href = "http://nokogiri.org/ns/noko"
39
+ # # })
40
+ #
41
+ # doc.root.elements.last.namespace.deconstruct_keys([:prefix, :href])
42
+ # # => {:prefix=>"noko", :href=>"http://nokogiri.org/ns/noko"}
43
+ #
44
+ # Since v1.14.0
45
+ #
46
+ def deconstruct_keys(keys)
47
+ { prefix: prefix, href: href }
48
+ end
49
+
9
50
  private
10
51
 
11
52
  def inspect_attributes
@@ -29,14 +29,16 @@ module Nokogiri
29
29
  DEFAULT_XML = AS_XML # https://github.com/sparklemotion/nokogiri/issues/#issue/415
30
30
  # the default for HTML document
31
31
  DEFAULT_HTML = NO_DECLARATION | NO_EMPTY_TAGS | AS_HTML
32
+ # the default for XHTML document
33
+ DEFAULT_XHTML = NO_DECLARATION | AS_XHTML
32
34
  else
33
35
  # the default for XML documents
34
36
  DEFAULT_XML = FORMAT | AS_XML
35
37
  # the default for HTML document
36
38
  DEFAULT_HTML = FORMAT | NO_DECLARATION | NO_EMPTY_TAGS | AS_HTML
39
+ # the default for XHTML document
40
+ DEFAULT_XHTML = FORMAT | NO_DECLARATION | AS_XHTML
37
41
  end
38
- # the default for XHTML document
39
- DEFAULT_XHTML = FORMAT | NO_DECLARATION | AS_XHTML
40
42
 
41
43
  # Integer representation of the SaveOptions
42
44
  attr_reader :options
@@ -47,7 +49,7 @@ module Nokogiri
47
49
  end
48
50
 
49
51
  constants.each do |constant|
50
- class_eval %{
52
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
51
53
  def #{constant.downcase}
52
54
  @options |= #{constant}
53
55
  self
@@ -56,10 +58,18 @@ module Nokogiri
56
58
  def #{constant.downcase}?
57
59
  #{constant} & @options == #{constant}
58
60
  end
59
- }
61
+ RUBY
60
62
  end
61
63
 
62
64
  alias_method :to_i, :options
65
+
66
+ def inspect
67
+ options = []
68
+ self.class.constants.each do |k|
69
+ options << k.downcase if send(:"#{k.downcase}?")
70
+ end
71
+ super.sub(/>$/, " " + options.join(", ") + ">")
72
+ end
63
73
  end
64
74
  end
65
75
  end