nokolexbor 0.3.0-x64-mingw-ucrt

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 75ac569a01b9281d1392eb495d5358eef2ce76fe420c4b2fd768d46df47904c1
4
+ data.tar.gz: b4d46dff993b2b8bc73b63356bb6abd2ca9721297226887d56d5e5f453406b58
5
+ SHA512:
6
+ metadata.gz: 8b4db936db5a609ad470033f1946cbaf495ed399d84b6a994ad51e86fd2a163e9c879103e5b2e5c51d23afc04ef5154896b68d2cc97e15b12c460b6c81588f2b
7
+ data.tar.gz: ec47117ce93ffe34e622df54cc981b0a52d2b8d344f17a730093becc46309d1650966872c222d00d076a13a014285dd2ace63ac996c67be75441ff47393e5ddd
Binary file
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nokolexbor
4
+ class Attribute
5
+ attr_accessor :name
6
+ attr_accessor :value
7
+
8
+ def initialize(name, value)
9
+ @name = name
10
+ @value = value
11
+ end
12
+
13
+ alias_method :text, :value
14
+ alias_method :content, :value
15
+ alias_method :to_s, :value
16
+ alias_method :to_str, :value
17
+ end
18
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nokolexbor
4
+ class Document < Nokolexbor::Node
5
+ def create_element(name, *contents_or_attrs, &block)
6
+ elm = Nokolexbor::Element.new(name, self, &block)
7
+ contents_or_attrs.each do |arg|
8
+ case arg
9
+ when Hash
10
+ arg.each do |k, v|
11
+ elm[k.to_s] = v.to_s
12
+ end
13
+ else
14
+ elm.content = arg
15
+ end
16
+ end
17
+ elm
18
+ end
19
+
20
+ # Create a Text Node with +string+
21
+ def create_text_node(string, &block)
22
+ Nokolexbor::Text.new(string.to_s, self, &block)
23
+ end
24
+
25
+ # Create a CDATA Node containing +string+
26
+ def create_cdata(string, &block)
27
+ Nokolexbor::CDATA.new(string.to_s, self, &block)
28
+ end
29
+
30
+ # Create a Comment Node containing +string+
31
+ def create_comment(string, &block)
32
+ Nokolexbor::Comment.new(string.to_s, self, &block)
33
+ end
34
+
35
+ # A reference to +self+
36
+ def document
37
+ self
38
+ end
39
+
40
+ def meta_encoding
41
+ if (meta = at_css("meta[charset]"))
42
+ meta[:charset]
43
+ elsif (meta = meta_content_type)
44
+ meta["content"][/charset\s*=\s*([\w-]+)/i, 1]
45
+ end
46
+ end
47
+
48
+ def meta_encoding=(encoding)
49
+ if (meta = meta_content_type)
50
+ meta["content"] = format("text/html; charset=%s", encoding)
51
+ encoding
52
+ elsif (meta = at_css("meta[charset]"))
53
+ meta["charset"] = encoding
54
+ else
55
+ meta = Nokolexbor::Node.new("meta", self)
56
+ meta["charset"] = encoding
57
+
58
+ if (head = at_css("head"))
59
+ head.prepend_child(meta)
60
+ else
61
+ set_metadata_element(meta)
62
+ end
63
+ encoding
64
+ end
65
+ end
66
+
67
+ def meta_content_type
68
+ xpath("//meta[@http-equiv and boolean(@content)]").find do |node|
69
+ node["http-equiv"] =~ /\AContent-Type\z/i
70
+ end
71
+ end
72
+ private :meta_content_type
73
+
74
+ def set_metadata_element(element)
75
+ if (head = at_css("head"))
76
+ head << element
77
+ elsif (html = at_css("html"))
78
+ head = html.prepend_child(Nokolexbor::Node.new("head", self))
79
+ head.prepend_child(element)
80
+ elsif (first = children.find do |node|
81
+ case node
82
+ when Nokolexbor::Node
83
+ true
84
+ end
85
+ end)
86
+ # We reach here only if the underlying document model
87
+ # allows <html>/<head> elements to be omitted and does not
88
+ # automatically supply them.
89
+ first.add_previous_sibling(element)
90
+ else
91
+ html = add_child(Nokolexbor::Node.new("html", self))
92
+ head = html.add_child(Nokolexbor::Node.new("head", self))
93
+ head.prepend_child(element)
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,348 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nokolexbor
4
+ class Node
5
+ include Enumerable
6
+
7
+ ELEMENT_NODE = 1
8
+ ATTRIBUTE_NODE = 2
9
+ TEXT_NODE = 3
10
+ CDATA_SECTION_NODE = 4
11
+ ENTITY_REF_NODE = 5
12
+ ENTITY_NODE = 6
13
+ PI_NODE = 7
14
+ COMMENT_NODE = 8
15
+ DOCUMENT_NODE = 9
16
+ DOCUMENT_TYPE_NODE = 10
17
+ DOCUMENT_FRAG_NODE = 11
18
+ NOTATION_NODE = 12
19
+
20
+ attr_reader :document
21
+
22
+ LOOKS_LIKE_XPATH = %r{^(\./|/|\.\.|\.$)}
23
+
24
+ def comment?
25
+ type == COMMENT_NODE
26
+ end
27
+
28
+ def cdata?
29
+ type == CDATA_SECTION_NODE
30
+ end
31
+
32
+ def processing_instruction?
33
+ type == PI_NODE
34
+ end
35
+
36
+ def text?
37
+ type == TEXT_NODE
38
+ end
39
+
40
+ def fragment?
41
+ type == DOCUMENT_FRAG_NODE
42
+ end
43
+
44
+ def element?
45
+ type == ELEMENT_NODE
46
+ end
47
+
48
+ def document?
49
+ is_a?(Nokolexbor::Document)
50
+ end
51
+
52
+ def ancestors(selector = nil)
53
+ return NodeSet.new(@document) unless respond_to?(:parent)
54
+ return NodeSet.new(@document) unless parent
55
+
56
+ parents = [parent]
57
+
58
+ while parents.last.respond_to?(:parent)
59
+ break unless (ctx_parent = parents.last.parent)
60
+
61
+ parents << ctx_parent
62
+ end
63
+
64
+ return NodeSet.new(@document, parents) unless selector
65
+
66
+ root = parents.last
67
+ search_results = root.search(selector)
68
+
69
+ NodeSet.new(@document, parents.find_all do |parent|
70
+ search_results.include?(parent)
71
+ end)
72
+ end
73
+
74
+ def wrap(node)
75
+ case node
76
+ when String
77
+ new_parent = fragment(node).child
78
+ when Node
79
+ new_parent = node.dup
80
+ else
81
+ raise ArgumentError, "Requires a String or Node argument, and cannot accept a #{node.class}"
82
+ end
83
+
84
+ if parent
85
+ add_sibling(:next, new_parent)
86
+ else
87
+ new_parent.remove
88
+ end
89
+ new_parent.add_child(self)
90
+
91
+ self
92
+ end
93
+
94
+ def add_previous_sibling(node_or_tags)
95
+ raise ArgumentError,
96
+ "A document may not have multiple root nodes." if parent&.document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
97
+
98
+ add_sibling(:previous, node_or_tags)
99
+ end
100
+
101
+ def add_next_sibling(node_or_tags)
102
+ raise ArgumentError,
103
+ "A document may not have multiple root nodes." if parent&.document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
104
+
105
+ add_sibling(:next, node_or_tags)
106
+ end
107
+
108
+ def before(node_or_tags)
109
+ add_previous_sibling(node_or_tags)
110
+ self
111
+ end
112
+
113
+ def after(node_or_tags)
114
+ add_next_sibling(node_or_tags)
115
+ self
116
+ end
117
+
118
+ alias_method :next_sibling, :next
119
+ alias_method :previous_sibling, :previous
120
+ alias_method :next=, :add_next_sibling
121
+ alias_method :previous=, :add_previous_sibling
122
+
123
+ def <<(node_or_tags)
124
+ add_child(node_or_tags)
125
+ self
126
+ end
127
+
128
+ def prepend_child(node)
129
+ if (first = children.first)
130
+ # Mimic the error add_child would raise.
131
+ raise "Document already has a root node" if document? && !(node.comment? || node.processing_instruction?)
132
+
133
+ first.add_sibling(:previous, node)
134
+ else
135
+ add_child(node)
136
+ end
137
+ end
138
+
139
+ def traverse(&block)
140
+ children.each { |j| j.traverse(&block) }
141
+ yield(self)
142
+ end
143
+
144
+ def matches?(selector)
145
+ ancestors.last.css(selector).any? { |node| node == self }
146
+ end
147
+
148
+ def attribute(name)
149
+ return nil unless key?(name)
150
+ Attribute.new(name, attr(name))
151
+ end
152
+
153
+ def attributes
154
+ attrs.map { |k, v| [k, Attribute.new(k, v)] }.to_h
155
+ end
156
+
157
+ def replace(node)
158
+ if node.is_a?(NodeSet)
159
+ node.each { |n| add_sibling(:previous, n) }
160
+ else
161
+ add_sibling(:previous, node)
162
+ end
163
+ remove
164
+ end
165
+
166
+ def children=(node)
167
+ children.remove
168
+ if node.is_a?(NodeSet)
169
+ node.each { |n| add_child(n) }
170
+ else
171
+ add_child(node)
172
+ end
173
+ end
174
+
175
+ def parent=(parent_node)
176
+ parent_node.add_child(self)
177
+ end
178
+
179
+ def each
180
+ attributes.each do |name, node|
181
+ yield [name, node.value]
182
+ end
183
+ end
184
+
185
+ alias_method :inner_html=, :children=
186
+
187
+ def css(*args)
188
+ css_impl(args.join(', '))
189
+ end
190
+
191
+ def at_css(*args)
192
+ at_css_impl(args.join(', '))
193
+ end
194
+
195
+ def xpath(*args)
196
+ paths, handler, ns, binds = extract_params(args)
197
+
198
+ xpath_internal(self, paths, handler, ns, binds)
199
+ end
200
+
201
+ def at_xpath(*args)
202
+ xpath(*args).first
203
+ end
204
+
205
+ def search(*args)
206
+ paths, handler, ns, binds = extract_params(args)
207
+
208
+ if paths.size == 1 && !LOOKS_LIKE_XPATH.match?(paths.first)
209
+ return css(paths.first)
210
+ end
211
+
212
+ xpath(*(paths + [ns, handler, binds].compact))
213
+ end
214
+
215
+ alias_method :/, :search
216
+
217
+ def at(*args)
218
+ paths, handler, ns, binds = extract_params(args)
219
+
220
+ if paths.size == 1 && !LOOKS_LIKE_XPATH.match?(paths.first)
221
+ return at_css(paths.first)
222
+ end
223
+
224
+ at_xpath(*(paths + [ns, handler, binds].compact))
225
+ end
226
+
227
+ alias_method :%, :at
228
+
229
+ def classes
230
+ kwattr_values("class")
231
+ end
232
+
233
+ def add_class(names)
234
+ kwattr_add("class", names)
235
+ end
236
+
237
+ def append_class(names)
238
+ kwattr_append("class", names)
239
+ end
240
+
241
+ def remove_class(names = nil)
242
+ kwattr_remove("class", names)
243
+ end
244
+
245
+ def kwattr_values(attribute_name)
246
+ keywordify(attr(attribute_name) || [])
247
+ end
248
+
249
+ def kwattr_add(attribute_name, keywords)
250
+ keywords = keywordify(keywords)
251
+ current_kws = kwattr_values(attribute_name)
252
+ new_kws = (current_kws + (keywords - current_kws)).join(" ")
253
+ set_attr(attribute_name, new_kws)
254
+ self
255
+ end
256
+
257
+ def kwattr_append(attribute_name, keywords)
258
+ keywords = keywordify(keywords)
259
+ current_kws = kwattr_values(attribute_name)
260
+ new_kws = (current_kws + keywords).join(" ")
261
+ set_attr(attribute_name, new_kws)
262
+ self
263
+ end
264
+
265
+ def kwattr_remove(attribute_name, keywords)
266
+ if keywords.nil?
267
+ remove_attr(attribute_name)
268
+ return self
269
+ end
270
+
271
+ keywords = keywordify(keywords)
272
+ current_kws = kwattr_values(attribute_name)
273
+ new_kws = current_kws - keywords
274
+ if new_kws.empty?
275
+ remove_attr(attribute_name)
276
+ else
277
+ set_attr(attribute_name, new_kws.join(" "))
278
+ end
279
+ self
280
+ end
281
+
282
+ def keywordify(keywords)
283
+ case keywords
284
+ when Enumerable
285
+ keywords
286
+ when String
287
+ keywords.scan(/\S+/)
288
+ else
289
+ raise ArgumentError,
290
+ "Keyword attributes must be passed as either a String or an Enumerable, but received #{keywords.class}"
291
+ end
292
+ end
293
+
294
+ def write_to(io, *options)
295
+ io.write(to_html(*options))
296
+ end
297
+
298
+ alias_method :write_html_to, :write_to
299
+
300
+ private
301
+
302
+ def xpath_internal(node, paths, handler, ns, binds)
303
+ # document = node.document
304
+ # return NodeSet.new(document) unless document
305
+
306
+ if paths.length == 1
307
+ return xpath_impl(node, paths.first, handler, ns, binds)
308
+ end
309
+
310
+ NodeSet.new(@document) do |combined|
311
+ paths.each do |path|
312
+ xpath_impl(node, path, handler, ns, binds).each { |set| combined << set }
313
+ end
314
+ end
315
+ end
316
+
317
+ def xpath_impl(node, path, handler, ns, binds)
318
+ ctx = XPathContext.new(node)
319
+ ctx.register_namespaces(ns)
320
+ # path = path.gsub(/xmlns:/, " :") unless Nokogiri.uses_libxml?
321
+
322
+ binds&.each do |key, value|
323
+ ctx.register_variable(key.to_s, value)
324
+ end
325
+
326
+ ctx.evaluate(path, handler)
327
+ end
328
+
329
+ def extract_params(params)
330
+ handler = params.find do |param|
331
+ ![Hash, String, Symbol].include?(param.class)
332
+ end
333
+ params -= [handler] if handler
334
+
335
+ hashes = []
336
+ while Hash === params.last || params.last.nil?
337
+ hashes << params.pop
338
+ break if params.empty?
339
+ end
340
+ ns, binds = hashes.reverse
341
+
342
+ # ns ||= (document.root&.namespaces || {})
343
+ ns ||= {}
344
+
345
+ [params, handler, ns, binds]
346
+ end
347
+ end
348
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nokolexbor
4
+ class NodeSet < Nokolexbor::Node
5
+ include Enumerable
6
+
7
+ def self.new(document, list = [])
8
+ obj = allocate
9
+ obj.instance_variable_set(:@document, document)
10
+ list.each { |x| obj << x }
11
+ yield obj if block_given?
12
+ obj
13
+ end
14
+
15
+ def each
16
+ return to_enum unless block_given?
17
+
18
+ 0.upto(length - 1) do |x|
19
+ yield self[x]
20
+ end
21
+ self
22
+ end
23
+
24
+ def first(n = nil)
25
+ return self[0] unless n
26
+
27
+ list = []
28
+ [n, length].min.times { |i| list << self[i] }
29
+ list
30
+ end
31
+
32
+ def last
33
+ self[-1]
34
+ end
35
+
36
+ def empty?
37
+ length == 0
38
+ end
39
+
40
+ def index(node = nil)
41
+ if node
42
+ each_with_index { |member, j| return j if member == node }
43
+ elsif block_given?
44
+ each_with_index { |member, j| return j if yield(member) }
45
+ end
46
+ nil
47
+ end
48
+
49
+ def content
50
+ self.map(&:content).join
51
+ end
52
+
53
+ alias_method :text, :content
54
+ alias_method :inner_text, :content
55
+ alias_method :to_str, :content
56
+
57
+ def inner_html(*args)
58
+ self.map { |n| n.inner_html(*args) }.join
59
+ end
60
+
61
+ def outer_html(*args)
62
+ self.map { |n| n.outer_html(*args) }.join
63
+ end
64
+
65
+ alias_method :to_s, :outer_html
66
+ alias_method :to_html, :outer_html
67
+ alias_method :serialize, :outer_html
68
+
69
+ def remove
70
+ self.each(&:remove)
71
+ end
72
+
73
+ alias_method :unlink, :remove
74
+ alias_method :to_ary, :to_a
75
+
76
+ def destroy
77
+ self.each(&:destroy)
78
+ end
79
+
80
+ def pop
81
+ return nil if length == 0
82
+
83
+ delete(last)
84
+ end
85
+
86
+ def shift
87
+ return nil if length == 0
88
+
89
+ delete(first)
90
+ end
91
+
92
+ def ==(other)
93
+ return false unless other.is_a?(NodeSet)
94
+ return false unless length == other.length
95
+
96
+ each_with_index do |node, i|
97
+ return false unless node == other[i]
98
+ end
99
+ true
100
+ end
101
+
102
+ def children
103
+ node_set = NodeSet.new(@document)
104
+ each do |node|
105
+ node.children.each { |n| node_set.push(n) }
106
+ end
107
+ node_set
108
+ end
109
+
110
+ def reverse
111
+ node_set = NodeSet.new(@document)
112
+ (length - 1).downto(0) do |x|
113
+ node_set.push(self[x])
114
+ end
115
+ node_set
116
+ end
117
+
118
+ def xpath(*args)
119
+ paths, handler, ns, binds = extract_params(args)
120
+
121
+ NodeSet.new(@document) do |set|
122
+ each do |node|
123
+ node.send(:xpath_internal, node, paths, handler, ns, binds).each do |inner_node|
124
+ set << inner_node
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nokolexbor
4
+ VERSION = '0.3.0'
5
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nokolexbor
4
+ module XPath
5
+ class SyntaxError < StandardError
6
+ attr_reader :domain
7
+ attr_reader :code
8
+ attr_reader :level
9
+ attr_reader :file
10
+ attr_reader :line
11
+ attr_reader :str1
12
+ attr_reader :str2
13
+ attr_reader :str3
14
+ attr_reader :int1
15
+ attr_reader :column
16
+
17
+ ###
18
+ # return true if this is a non error
19
+ def none?
20
+ level == 0
21
+ end
22
+
23
+ ###
24
+ # return true if this is a warning
25
+ def warning?
26
+ level == 1
27
+ end
28
+
29
+ ###
30
+ # return true if this is an error
31
+ def error?
32
+ level == 2
33
+ end
34
+
35
+ ###
36
+ # return true if this error is fatal
37
+ def fatal?
38
+ level == 3
39
+ end
40
+
41
+ def to_s
42
+ message = super.chomp
43
+ [location_to_s, level_to_s, message]
44
+ .compact.join(": ")
45
+ .force_encoding(message.encoding)
46
+ end
47
+
48
+ private
49
+
50
+ def level_to_s
51
+ case level
52
+ when 3 then "FATAL"
53
+ when 2 then "ERROR"
54
+ when 1 then "WARNING"
55
+ end
56
+ end
57
+
58
+ def nil_or_zero?(attribute)
59
+ attribute.nil? || attribute.zero?
60
+ end
61
+
62
+ def location_to_s
63
+ return nil if nil_or_zero?(line) && nil_or_zero?(column)
64
+
65
+ "#{line}:#{column}"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nokolexbor
4
+ class XPathContext
5
+ ###
6
+ # Register namespaces in +namespaces+
7
+ def register_namespaces(namespaces)
8
+ namespaces.each do |k, v|
9
+ k = k.to_s.gsub(/.*:/, "") # strip off 'xmlns:' or 'xml:'
10
+ register_ns(k, v)
11
+ end
12
+ end
13
+ end
14
+ end
data/lib/nokolexbor.rb ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ # pre-compiled extension by rake-compiler is located inside lib/nokolexbor/<ruby_version>/
5
+ RUBY_VERSION =~ /(\d+\.\d+)/
6
+ require "nokolexbor/#{Regexp.last_match(1)}/nokolexbor"
7
+ rescue LoadError => e
8
+ if /GLIBC/.match?(e.message)
9
+ warn(<<~EOM)
10
+ ERROR: It looks like you're trying to use Nokolexbor as a precompiled native gem on a system
11
+ with an unsupported version of glibc.
12
+ #{e.message}
13
+ If that's the case, then please install Nokolexbor via the `ruby` platform gem:
14
+ gem install nokolexbor --platform=ruby
15
+ or:
16
+ bundle config set force_ruby_platform true
17
+ EOM
18
+ raise e
19
+ end
20
+
21
+ require 'nokolexbor/nokolexbor'
22
+ end
23
+
24
+ require 'nokolexbor/version'
25
+ require 'nokolexbor/node'
26
+ require 'nokolexbor/document'
27
+ require 'nokolexbor/node_set'
28
+ require 'nokolexbor/attribute'
29
+ require 'nokolexbor/xpath'
30
+ require 'nokolexbor/xpath_context'
31
+
32
+ module Nokolexbor
33
+ class << self
34
+ def HTML(*args)
35
+ Document.parse(*args)
36
+ end
37
+ end
38
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nokolexbor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: x64-mingw-ucrt
6
+ authors:
7
+ - Yicheng Zhou
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-12-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake-compiler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ description: Nokolexbor is a high-performance HTML5 parser, with support for both
42
+ CSS selectors and XPath. It's API is designed to be compatible with Nokogiri.
43
+ email: zyc9012@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - lib/nokolexbor.rb
49
+ - lib/nokolexbor/3.1/nokolexbor.so
50
+ - lib/nokolexbor/attribute.rb
51
+ - lib/nokolexbor/document.rb
52
+ - lib/nokolexbor/node.rb
53
+ - lib/nokolexbor/node_set.rb
54
+ - lib/nokolexbor/version.rb
55
+ - lib/nokolexbor/xpath.rb
56
+ - lib/nokolexbor/xpath_context.rb
57
+ homepage: https://github.com/serpapi/nokolexbor
58
+ licenses:
59
+ - MIT
60
+ metadata: {}
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '3.1'
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: 3.2.dev
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubygems_version: 3.3.4
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: High-performance HTML5 parser, with support for both CSS selectors and XPath.
83
+ test_files: []