nokogiri 1.11.0.rc1 → 1.11.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of nokogiri might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/LICENSE-DEPENDENCIES.md +1015 -947
- data/README.md +164 -92
- data/ext/nokogiri/depend +476 -357
- data/ext/nokogiri/extconf.rb +467 -326
- data/ext/nokogiri/html_document.c +79 -78
- data/ext/nokogiri/html_sax_parser_context.c +4 -2
- data/ext/nokogiri/html_sax_push_parser.c +14 -8
- data/ext/nokogiri/nokogiri.c +37 -46
- data/ext/nokogiri/nokogiri.h +25 -17
- data/ext/nokogiri/test_global_handlers.c +41 -0
- data/ext/nokogiri/xml_document.c +8 -3
- data/ext/nokogiri/xml_io.c +8 -6
- data/ext/nokogiri/xml_node.c +1 -1
- data/ext/nokogiri/xml_node_set.c +1 -1
- data/ext/nokogiri/xml_reader.c +6 -17
- data/ext/nokogiri/xml_relax_ng.c +29 -11
- data/ext/nokogiri/xml_sax_parser.c +2 -7
- data/ext/nokogiri/xml_sax_parser_context.c +4 -2
- data/ext/nokogiri/xml_sax_push_parser.c +2 -0
- data/ext/nokogiri/xml_schema.c +84 -13
- data/ext/nokogiri/xml_syntax_error.c +23 -0
- data/ext/nokogiri/xml_syntax_error.h +15 -3
- data/ext/nokogiri/xml_xpath_context.c +80 -4
- data/ext/nokogiri/xslt_stylesheet.c +1 -4
- data/lib/nokogiri.rb +20 -3
- data/lib/nokogiri/css/parser.rb +62 -62
- data/lib/nokogiri/css/parser.y +2 -2
- data/lib/nokogiri/css/parser_extras.rb +38 -36
- data/lib/nokogiri/css/xpath_visitor.rb +70 -42
- data/lib/nokogiri/html/document.rb +12 -26
- data/lib/nokogiri/version.rb +2 -148
- data/lib/nokogiri/version/constant.rb +5 -0
- data/lib/nokogiri/version/info.rb +182 -0
- data/lib/nokogiri/xml/builder.rb +2 -2
- data/lib/nokogiri/xml/document.rb +17 -7
- data/lib/nokogiri/xml/document_fragment.rb +4 -6
- data/lib/nokogiri/xml/node.rb +562 -238
- data/lib/nokogiri/xml/parse_options.rb +6 -0
- data/lib/nokogiri/xml/relax_ng.rb +6 -2
- data/lib/nokogiri/xml/schema.rb +12 -4
- data/lib/nokogiri/xml/searchable.rb +24 -16
- data/patches/libxml2/0005-Fix-infinite-loop-in-xmlStringLenDecodeEntities.patch +32 -0
- data/patches/libxml2/0006-htmlParseComment-treat-as-if-it-closed-the-comment.patch +73 -0
- data/patches/libxml2/0007-use-new-htmlParseLookupCommentEnd-to-find-comment-en.patch +103 -0
- data/patches/libxml2/0008-use-glibc-strlen.patch +53 -0
- data/patches/libxml2/0009-avoid-isnan-isinf.patch +81 -0
- metadata +84 -114
data/lib/nokogiri/css/parser.y
CHANGED
@@ -88,7 +88,7 @@ rule
|
|
88
88
|
)
|
89
89
|
}
|
90
90
|
| LSQUARE NUMBER RSQUARE {
|
91
|
-
#
|
91
|
+
# non-standard, from hpricot
|
92
92
|
result = Node.new(:PSEUDO_CLASS,
|
93
93
|
[Node.new(:FUNCTION, ['nth-child(', val[1]])]
|
94
94
|
)
|
@@ -139,7 +139,7 @@ rule
|
|
139
139
|
when 'n'
|
140
140
|
result = Node.new(:NTH, ['1','n','+','0'])
|
141
141
|
else
|
142
|
-
#
|
142
|
+
# non-standard to support custom functions:
|
143
143
|
# assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)'))
|
144
144
|
# assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)'))
|
145
145
|
# assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)'))
|
@@ -1,64 +1,66 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require
|
2
|
+
require "thread"
|
3
3
|
|
4
4
|
module Nokogiri
|
5
5
|
module CSS
|
6
6
|
class Parser < Racc::Parser
|
7
|
-
|
8
|
-
|
9
|
-
@
|
7
|
+
CACHE_SWITCH_NAME = :nokogiri_css_parser_cache_is_off
|
8
|
+
|
9
|
+
@cache = {}
|
10
|
+
@mutex = Mutex.new
|
10
11
|
|
11
12
|
class << self
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
# Return a thread-local boolean indicating whether the CSS-to-XPath cache is active. (Default is `true`.)
|
14
|
+
def cache_on?
|
15
|
+
!Thread.current[CACHE_SWITCH_NAME]
|
16
|
+
end
|
17
|
+
|
18
|
+
# Set a thread-local boolean to turn cacheing on and off. Truthy values turn the cache on, falsey values turn the cache off.
|
19
|
+
def set_cache(value)
|
20
|
+
Thread.current[CACHE_SWITCH_NAME] = !value
|
21
|
+
end
|
16
22
|
|
17
23
|
# Get the css selector in +string+ from the cache
|
18
|
-
def []
|
19
|
-
return unless
|
24
|
+
def [](string)
|
25
|
+
return unless cache_on?
|
20
26
|
@mutex.synchronize { @cache[string] }
|
21
27
|
end
|
22
28
|
|
23
29
|
# Set the css selector in +string+ in the cache to +value+
|
24
|
-
def []=
|
25
|
-
return value unless
|
30
|
+
def []=(string, value)
|
31
|
+
return value unless cache_on?
|
26
32
|
@mutex.synchronize { @cache[string] = value }
|
27
33
|
end
|
28
34
|
|
29
35
|
# Clear the cache
|
30
|
-
def clear_cache
|
31
|
-
@mutex.synchronize
|
36
|
+
def clear_cache(create_new_object = false)
|
37
|
+
@mutex.synchronize do
|
38
|
+
if create_new_object
|
39
|
+
@cache = {}
|
40
|
+
else
|
41
|
+
@cache.clear
|
42
|
+
end
|
43
|
+
end
|
32
44
|
end
|
33
45
|
|
34
46
|
# Execute +block+ without cache
|
35
|
-
def without_cache
|
36
|
-
|
37
|
-
|
47
|
+
def without_cache(&block)
|
48
|
+
original_cache_setting = cache_on?
|
49
|
+
set_cache false
|
38
50
|
block.call
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
###
|
43
|
-
# Parse this CSS selector in +selector+. Returns an AST.
|
44
|
-
def parse selector
|
45
|
-
@warned ||= false
|
46
|
-
unless @warned
|
47
|
-
$stderr.puts('Nokogiri::CSS::Parser.parse is deprecated, call Nokogiri::CSS.parse(), this will be removed August 1st or version 1.4.0 (whichever is first)')
|
48
|
-
@warned = true
|
49
|
-
end
|
50
|
-
new.parse selector
|
51
|
+
ensure
|
52
|
+
set_cache original_cache_setting
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
56
|
# Create a new CSS parser with respect to +namespaces+
|
55
|
-
def initialize
|
56
|
-
@tokenizer
|
57
|
+
def initialize(namespaces = {})
|
58
|
+
@tokenizer = Tokenizer.new
|
57
59
|
@namespaces = namespaces
|
58
60
|
super()
|
59
61
|
end
|
60
62
|
|
61
|
-
def parse
|
63
|
+
def parse(string)
|
62
64
|
@tokenizer.scan_setup string
|
63
65
|
do_parse
|
64
66
|
end
|
@@ -68,14 +70,14 @@ module Nokogiri
|
|
68
70
|
end
|
69
71
|
|
70
72
|
# Get the xpath for +string+ using +options+
|
71
|
-
def xpath_for
|
73
|
+
def xpath_for(string, options = {})
|
72
74
|
key = "#{string}#{options[:ns]}#{options[:prefix]}"
|
73
75
|
v = self.class[key]
|
74
76
|
return v if v
|
75
77
|
|
76
78
|
args = [
|
77
|
-
options[:prefix] ||
|
78
|
-
options[:visitor] || XPathVisitor.new
|
79
|
+
options[:prefix] || "//",
|
80
|
+
options[:visitor] || XPathVisitor.new,
|
79
81
|
]
|
80
82
|
self.class[key] = parse(string).map { |ast|
|
81
83
|
ast.to_xpath(*args)
|
@@ -83,7 +85,7 @@ module Nokogiri
|
|
83
85
|
end
|
84
86
|
|
85
87
|
# On CSS parser error, raise an exception
|
86
|
-
def on_error
|
88
|
+
def on_error(error_token_id, error_value, value_stack)
|
87
89
|
after = value_stack.compact.last
|
88
90
|
raise SyntaxError.new("unexpected '#{error_value}' after '#{after}'")
|
89
91
|
end
|
@@ -3,7 +3,6 @@ module Nokogiri
|
|
3
3
|
module CSS
|
4
4
|
class XPathVisitor # :nodoc:
|
5
5
|
def visit_function node
|
6
|
-
|
7
6
|
msg = :"visit_function_#{node.value.first.gsub(/[(]/, '')}"
|
8
7
|
return self.send(msg, node) if self.respond_to?(msg)
|
9
8
|
|
@@ -13,50 +12,51 @@ module Nokogiri
|
|
13
12
|
when /^self\(/
|
14
13
|
"self::#{node.value[1]}"
|
15
14
|
when /^eq\(/
|
16
|
-
"position()
|
15
|
+
"position()=#{node.value[1]}"
|
17
16
|
when /^(nth|nth-of-type)\(/
|
18
17
|
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :NTH
|
19
18
|
nth(node.value[1])
|
20
19
|
else
|
21
|
-
"position()
|
20
|
+
"position()=#{node.value[1]}"
|
22
21
|
end
|
23
22
|
when /^nth-child\(/
|
24
23
|
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :NTH
|
25
24
|
nth(node.value[1], :child => true)
|
26
25
|
else
|
27
|
-
"count(preceding-sibling::*)
|
26
|
+
"count(preceding-sibling::*)=#{node.value[1].to_i-1}"
|
28
27
|
end
|
29
28
|
when /^nth-last-of-type\(/
|
30
29
|
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :NTH
|
31
30
|
nth(node.value[1], :last => true)
|
32
31
|
else
|
33
32
|
index = node.value[1].to_i - 1
|
34
|
-
index == 0 ? "position()
|
33
|
+
index == 0 ? "position()=last()" : "position()=last()-#{index}"
|
35
34
|
end
|
36
35
|
when /^nth-last-child\(/
|
37
36
|
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :NTH
|
38
37
|
nth(node.value[1], :last => true, :child => true)
|
39
38
|
else
|
40
|
-
"count(following-sibling::*)
|
39
|
+
"count(following-sibling::*)=#{node.value[1].to_i-1}"
|
41
40
|
end
|
42
41
|
when /^(first|first-of-type)\(/
|
43
|
-
"position()
|
42
|
+
"position()=1"
|
44
43
|
when /^(last|last-of-type)\(/
|
45
|
-
"position()
|
44
|
+
"position()=last()"
|
46
45
|
when /^contains\(/
|
47
|
-
"contains(
|
46
|
+
"contains(.,#{node.value[1]})"
|
48
47
|
when /^gt\(/
|
49
|
-
"position()
|
48
|
+
"position()>#{node.value[1]}"
|
50
49
|
when /^only-child\(/
|
51
|
-
"last()
|
50
|
+
"last()=1"
|
52
51
|
when /^comment\(/
|
53
52
|
"comment()"
|
54
53
|
when /^has\(/
|
55
54
|
is_direct = node.value[1].value[0].nil? # e.g. "has(> a)", "has(~ a)", "has(+ a)"
|
56
55
|
".#{"//" if !is_direct}#{node.value[1].accept(self)}"
|
57
56
|
else
|
57
|
+
# non-standard. this looks like a function call.
|
58
58
|
args = ['.'] + node.value[1..-1]
|
59
|
-
"#{node.value.first}#{args.join(',
|
59
|
+
"#{node.value.first}#{args.join(',')})"
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -71,18 +71,18 @@ module Nokogiri
|
|
71
71
|
|
72
72
|
def visit_id node
|
73
73
|
node.value.first =~ /^#(.*)$/
|
74
|
-
"@id
|
74
|
+
"@id='#{$1}'"
|
75
75
|
end
|
76
76
|
|
77
77
|
def visit_attribute_condition node
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
78
|
+
attribute = if (node.value.first.type == :FUNCTION) or (node.value.first.value.first =~ /::/)
|
79
|
+
''
|
80
|
+
else
|
81
|
+
'@'
|
82
|
+
end
|
83
83
|
attribute += node.value.first.accept(self)
|
84
84
|
|
85
|
-
#
|
85
|
+
# non-standard. attributes starting with '@'
|
86
86
|
attribute.gsub!(/^@@/, '@')
|
87
87
|
|
88
88
|
return attribute unless node.value.length == 3
|
@@ -90,29 +90,30 @@ module Nokogiri
|
|
90
90
|
value = node.value.last
|
91
91
|
value = "'#{value}'" if value !~ /^['"]/
|
92
92
|
|
93
|
+
# quoted values - see test_attribute_value_with_quotes in test/css/test_parser.rb
|
93
94
|
if (value[0]==value[-1]) && %q{"'}.include?(value[0])
|
94
95
|
str_value = value[1..-2]
|
95
96
|
if str_value.include?(value[0])
|
96
|
-
value = 'concat("' + str_value.split('"', -1).join(%q{",
|
97
|
+
value = 'concat("' + str_value.split('"', -1).join(%q{",'"',"}) + '","")'
|
97
98
|
end
|
98
99
|
end
|
99
100
|
|
100
101
|
case node.value[1]
|
101
102
|
when :equal
|
102
|
-
attribute + "
|
103
|
+
attribute + "=" + "#{value}"
|
103
104
|
when :not_equal
|
104
|
-
attribute + "
|
105
|
+
attribute + "!=" + "#{value}"
|
105
106
|
when :substring_match
|
106
|
-
"contains(#{attribute}
|
107
|
+
"contains(#{attribute},#{value})"
|
107
108
|
when :prefix_match
|
108
|
-
"starts-with(#{attribute}
|
109
|
+
"starts-with(#{attribute},#{value})"
|
109
110
|
when :dash_match
|
110
|
-
"#{attribute}
|
111
|
+
"#{attribute}=#{value} or starts-with(#{attribute},concat(#{value},'-'))"
|
111
112
|
when :includes
|
112
|
-
|
113
|
+
value = value[1..-2] # strip quotes
|
114
|
+
css_class(attribute, value)
|
113
115
|
when :suffix_match
|
114
|
-
"substring(#{attribute},
|
115
|
-
"string-length(#{value}) + 1, string-length(#{value})) = #{value}"
|
116
|
+
"substring(#{attribute},string-length(#{attribute})-string-length(#{value})+1,string-length(#{value}))=#{value}"
|
116
117
|
else
|
117
118
|
attribute + " #{node.value[1]} " + "#{value}"
|
118
119
|
end
|
@@ -126,14 +127,14 @@ module Nokogiri
|
|
126
127
|
return self.send(msg, node) if self.respond_to?(msg)
|
127
128
|
|
128
129
|
case node.value.first
|
129
|
-
when "first" then "position()
|
130
|
-
when "first-child" then "count(preceding-sibling::*)
|
131
|
-
when "last" then "position()
|
132
|
-
when "last-child" then "count(following-sibling::*)
|
133
|
-
when "first-of-type" then "position()
|
134
|
-
when "last-of-type" then "position()
|
135
|
-
when "only-child" then "count(preceding-sibling::*)
|
136
|
-
when "only-of-type" then "last()
|
130
|
+
when "first" then "position()=1"
|
131
|
+
when "first-child" then "count(preceding-sibling::*)=0"
|
132
|
+
when "last" then "position()=last()"
|
133
|
+
when "last-child" then "count(following-sibling::*)=0"
|
134
|
+
when "first-of-type" then "position()=1"
|
135
|
+
when "last-of-type" then "position()=last()"
|
136
|
+
when "only-child" then "count(preceding-sibling::*)=0 and count(following-sibling::*)=0"
|
137
|
+
when "only-of-type" then "last()=1"
|
137
138
|
when "empty" then "not(node())"
|
138
139
|
when "parent" then "node()"
|
139
140
|
when "root" then "not(parent::*)"
|
@@ -144,7 +145,7 @@ module Nokogiri
|
|
144
145
|
end
|
145
146
|
|
146
147
|
def visit_class_condition node
|
147
|
-
"
|
148
|
+
css_class("@class", node.value.first)
|
148
149
|
end
|
149
150
|
|
150
151
|
def visit_combinator node
|
@@ -181,25 +182,26 @@ module Nokogiri
|
|
181
182
|
node.accept(self)
|
182
183
|
end
|
183
184
|
|
184
|
-
|
185
|
+
private
|
186
|
+
|
185
187
|
def nth node, options={}
|
186
188
|
raise ArgumentError, "expected an+b node to contain 4 tokens, but is #{node.value.inspect}" unless node.value.size == 4
|
187
189
|
|
188
190
|
a, b = read_a_and_positive_b node.value
|
189
191
|
position = if options[:child]
|
190
|
-
options[:last] ? "(count(following-sibling::*)
|
192
|
+
options[:last] ? "(count(following-sibling::*)+1)" : "(count(preceding-sibling::*)+1)"
|
191
193
|
else
|
192
194
|
options[:last] ? "(last()-position()+1)" : "position()"
|
193
195
|
end
|
194
196
|
|
195
197
|
if b.zero?
|
196
|
-
"(#{position} mod #{a})
|
198
|
+
"(#{position} mod #{a})=0"
|
197
199
|
else
|
198
200
|
compare = a < 0 ? "<=" : ">="
|
199
201
|
if a.abs == 1
|
200
|
-
"#{position}
|
202
|
+
"#{position}#{compare}#{b}"
|
201
203
|
else
|
202
|
-
"(#{position}
|
204
|
+
"(#{position}#{compare}#{b}) and (((#{position}-#{b}) mod #{a.abs})=0)"
|
203
205
|
end
|
204
206
|
end
|
205
207
|
end
|
@@ -227,6 +229,32 @@ module Nokogiri
|
|
227
229
|
end =~ /(nth|first|last|only)-of-type(\()?/
|
228
230
|
end
|
229
231
|
end
|
232
|
+
|
233
|
+
# use only ordinary xpath functions
|
234
|
+
def css_class_standard(hay, needle)
|
235
|
+
"contains(concat(' ',normalize-space(#{hay}),' '),' #{needle} ')"
|
236
|
+
end
|
237
|
+
|
238
|
+
# use the builtin implementation
|
239
|
+
def css_class_builtin(hay, needle)
|
240
|
+
"nokogiri-builtin:css-class(#{hay},'#{needle}')"
|
241
|
+
end
|
242
|
+
|
243
|
+
alias_method :css_class, :css_class_standard
|
244
|
+
end
|
245
|
+
|
246
|
+
class XPathVisitorAlwaysUseBuiltins < XPathVisitor # :nodoc:
|
247
|
+
private
|
248
|
+
alias_method :css_class, :css_class_builtin
|
249
|
+
end
|
250
|
+
|
251
|
+
class XPathVisitorOptimallyUseBuiltins < XPathVisitor # :nodoc:
|
252
|
+
private
|
253
|
+
if Nokogiri.uses_libxml?
|
254
|
+
alias_method :css_class, :css_class_builtin
|
255
|
+
else
|
256
|
+
alias_method :css_class, :css_class_standard
|
257
|
+
end
|
230
258
|
end
|
231
259
|
end
|
232
260
|
end
|
@@ -1,4 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
|
2
5
|
module Nokogiri
|
3
6
|
module HTML
|
4
7
|
class Document < Nokogiri::XML::Document
|
@@ -161,11 +164,12 @@ module Nokogiri
|
|
161
164
|
# Nokogiri::XML::ParseOptions::RECOVER. See the constants in
|
162
165
|
# Nokogiri::XML::ParseOptions.
|
163
166
|
def parse string_or_io, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_HTML
|
164
|
-
|
165
167
|
options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
|
166
|
-
|
168
|
+
|
167
169
|
yield options if block_given?
|
168
170
|
|
171
|
+
url ||= string_or_io.respond_to?(:path) ? string_or_io.path : nil
|
172
|
+
|
169
173
|
if string_or_io.respond_to?(:encoding)
|
170
174
|
unless string_or_io.encoding.name == "ASCII-8BIT"
|
171
175
|
encoding ||= string_or_io.encoding.name
|
@@ -173,7 +177,12 @@ module Nokogiri
|
|
173
177
|
end
|
174
178
|
|
175
179
|
if string_or_io.respond_to?(:read)
|
176
|
-
|
180
|
+
if string_or_io.is_a?(Pathname)
|
181
|
+
# resolve the Pathname to the file and open it as an IO object, see #2110
|
182
|
+
string_or_io = string_or_io.expand_path.open
|
183
|
+
url ||= string_or_io.path
|
184
|
+
end
|
185
|
+
|
177
186
|
unless encoding
|
178
187
|
# Libxml2's parser has poor support for encoding
|
179
188
|
# detection. First, it does not recognize the HTML5
|
@@ -252,9 +261,6 @@ module Nokogiri
|
|
252
261
|
end
|
253
262
|
|
254
263
|
def self.detect_encoding(chunk)
|
255
|
-
if Nokogiri.jruby? && EncodingReader.is_jruby_without_fix?
|
256
|
-
return EncodingReader.detect_encoding_for_jruby_without_fix(chunk)
|
257
|
-
end
|
258
264
|
m = chunk.match(/\A(<\?xml[ \t\r\n]+[^>]*>)/) and
|
259
265
|
return Nokogiri.XML(m[1]).encoding
|
260
266
|
|
@@ -273,26 +279,6 @@ module Nokogiri
|
|
273
279
|
end
|
274
280
|
end
|
275
281
|
|
276
|
-
def self.is_jruby_without_fix?
|
277
|
-
JRUBY_VERSION.split('.').join.to_i < 165
|
278
|
-
end
|
279
|
-
|
280
|
-
def self.detect_encoding_for_jruby_without_fix(chunk)
|
281
|
-
m = chunk.match(/\A(<\?xml[ \t\r\n]+[^>]*>)/) and
|
282
|
-
return Nokogiri.XML(m[1]).encoding
|
283
|
-
|
284
|
-
m = chunk.match(/(<meta\s)(.*)(charset\s*=\s*([\w-]+))(.*)/i) and
|
285
|
-
return m[4]
|
286
|
-
|
287
|
-
catch(:encoding_found) {
|
288
|
-
Nokogiri::HTML::SAX::Parser.new(JumpSAXHandler.new(:encoding_found.to_s)).parse(chunk)
|
289
|
-
nil
|
290
|
-
}
|
291
|
-
rescue Nokogiri::SyntaxError, RuntimeError
|
292
|
-
# Ignore parser errors that nokogiri may raise
|
293
|
-
nil
|
294
|
-
end
|
295
|
-
|
296
282
|
def initialize(io)
|
297
283
|
@io = io
|
298
284
|
@firstchunk = nil
|
data/lib/nokogiri/version.rb
CHANGED
@@ -1,149 +1,3 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
|
4
|
-
VERSION = "1.11.0.rc1"
|
5
|
-
|
6
|
-
class VersionInfo # :nodoc:
|
7
|
-
def jruby?
|
8
|
-
::JRUBY_VERSION if RUBY_PLATFORM == "java"
|
9
|
-
end
|
10
|
-
|
11
|
-
def engine
|
12
|
-
defined?(RUBY_ENGINE) ? RUBY_ENGINE : "mri"
|
13
|
-
end
|
14
|
-
|
15
|
-
def loaded_libxml_version
|
16
|
-
Gem::Version.new(LIBXML_LOADED_VERSION.
|
17
|
-
scan(/^(\d+)(\d\d)(\d\d)(?!\d)/).first.
|
18
|
-
collect(&:to_i).
|
19
|
-
join("."))
|
20
|
-
end
|
21
|
-
|
22
|
-
def compiled_libxml_version
|
23
|
-
Gem::Version.new LIBXML_COMPILED_VERSION
|
24
|
-
end
|
25
|
-
|
26
|
-
def loaded_libxslt_version
|
27
|
-
Gem::Version.new(LIBXSLT_LOADED_VERSION.
|
28
|
-
scan(/^(\d+)(\d\d)(\d\d)(?!\d)/).first.
|
29
|
-
collect(&:to_i).
|
30
|
-
join("."))
|
31
|
-
end
|
32
|
-
|
33
|
-
def compiled_libxslt_version
|
34
|
-
Gem::Version.new LIBXSLT_COMPILED_VERSION
|
35
|
-
end
|
36
|
-
|
37
|
-
def libxml2?
|
38
|
-
defined?(LIBXML_COMPILED_VERSION)
|
39
|
-
end
|
40
|
-
|
41
|
-
def libxml2_using_system?
|
42
|
-
!libxml2_using_packaged?
|
43
|
-
end
|
44
|
-
|
45
|
-
def libxml2_using_packaged?
|
46
|
-
NOKOGIRI_USE_PACKAGED_LIBRARIES
|
47
|
-
end
|
48
|
-
|
49
|
-
def warnings
|
50
|
-
warnings = []
|
51
|
-
|
52
|
-
if libxml2?
|
53
|
-
if compiled_libxml_version != loaded_libxml_version
|
54
|
-
warnings << "Nokogiri was built against libxml version #{compiled_libxml_version}, but has dynamically loaded #{loaded_libxml_version}"
|
55
|
-
end
|
56
|
-
|
57
|
-
if compiled_libxslt_version != loaded_libxslt_version
|
58
|
-
warnings << "Nokogiri was built against libxslt version #{compiled_libxslt_version}, but has dynamically loaded #{loaded_libxslt_version}"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
warnings
|
63
|
-
end
|
64
|
-
|
65
|
-
def to_hash
|
66
|
-
{}.tap do |vi|
|
67
|
-
vi["warnings"] = []
|
68
|
-
vi["nokogiri"] = Nokogiri::VERSION
|
69
|
-
vi["ruby"] = {}.tap do |ruby|
|
70
|
-
ruby["version"] = ::RUBY_VERSION
|
71
|
-
ruby["platform"] = ::RUBY_PLATFORM
|
72
|
-
ruby["description"] = ::RUBY_DESCRIPTION
|
73
|
-
ruby["engine"] = engine
|
74
|
-
ruby["jruby"] = jruby? if jruby?
|
75
|
-
end
|
76
|
-
|
77
|
-
if libxml2?
|
78
|
-
vi["libxml"] = {}.tap do |libxml|
|
79
|
-
if libxml2_using_packaged?
|
80
|
-
libxml["source"] = "packaged"
|
81
|
-
libxml["patches"] = NOKOGIRI_LIBXML2_PATCHES
|
82
|
-
else
|
83
|
-
libxml["source"] = "system"
|
84
|
-
end
|
85
|
-
libxml["compiled"] = compiled_libxml_version.to_s
|
86
|
-
libxml["loaded"] = loaded_libxml_version.to_s
|
87
|
-
end
|
88
|
-
|
89
|
-
vi["libxslt"] = {}.tap do |libxslt|
|
90
|
-
if libxml2_using_packaged?
|
91
|
-
libxslt["source"] = "packaged"
|
92
|
-
libxslt["patches"] = NOKOGIRI_LIBXSLT_PATCHES
|
93
|
-
else
|
94
|
-
libxslt["source"] = "system"
|
95
|
-
end
|
96
|
-
libxslt["compiled"] = compiled_libxslt_version.to_s
|
97
|
-
libxslt["loaded"] = loaded_libxslt_version.to_s
|
98
|
-
end
|
99
|
-
|
100
|
-
vi["warnings"] = warnings
|
101
|
-
elsif jruby?
|
102
|
-
vi["xerces"] = Nokogiri::XERCES_VERSION
|
103
|
-
vi["nekohtml"] = Nokogiri::NEKO_VERSION
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def to_markdown
|
109
|
-
begin
|
110
|
-
require "psych"
|
111
|
-
rescue LoadError
|
112
|
-
end
|
113
|
-
require "yaml"
|
114
|
-
"# Nokogiri (#{Nokogiri::VERSION})\n" +
|
115
|
-
YAML.dump(to_hash).each_line.map { |line| " #{line}" }.join
|
116
|
-
end
|
117
|
-
|
118
|
-
# FIXME: maybe switch to singleton?
|
119
|
-
@@instance = new
|
120
|
-
@@instance.warnings.each do |warning|
|
121
|
-
warn "WARNING: #{warning}"
|
122
|
-
end
|
123
|
-
def self.instance; @@instance; end
|
124
|
-
end
|
125
|
-
|
126
|
-
def self.uses_libxml?(requirement = nil) # :nodoc:
|
127
|
-
return false unless VersionInfo.instance.libxml2?
|
128
|
-
return true unless requirement
|
129
|
-
return Gem::Requirement.new(requirement).satisfied_by?(VersionInfo.instance.loaded_libxml_version)
|
130
|
-
end
|
131
|
-
|
132
|
-
def self.jruby? # :nodoc:
|
133
|
-
VersionInfo.instance.jruby?
|
134
|
-
end
|
135
|
-
|
136
|
-
# Ensure constants used in this file are loaded
|
137
|
-
if Nokogiri.jruby?
|
138
|
-
require "nokogiri/jruby/dependencies"
|
139
|
-
end
|
140
|
-
begin
|
141
|
-
RUBY_VERSION =~ /(\d+\.\d+)/
|
142
|
-
require "nokogiri/#{$1}/nokogiri"
|
143
|
-
rescue LoadError
|
144
|
-
require "nokogiri/nokogiri"
|
145
|
-
end
|
146
|
-
|
147
|
-
# More complete version information about libxml
|
148
|
-
VERSION_INFO = VersionInfo.instance.to_hash
|
149
|
-
end
|
2
|
+
require_relative "version/constant"
|
3
|
+
require_relative "version/info"
|