nokogumbo 1.4.9 → 1.5.0
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 +5 -5
- data/README.md +1 -1
- data/ext/nokogumboc/extconf.rb +17 -0
- data/ext/nokogumboc/nokogumbo.c +57 -10
- data/lib/nokogumbo.rb +7 -7
- data/test-nokogumbo.rb +39 -1
- metadata +11 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 96fa61565f78d5491e0b6d5b505cf936524745eb848b8b6584fc15e20c7ae35b
|
|
4
|
+
data.tar.gz: e5416f71bbe90323f04b8aad4dc48b28947e43a9eb46f446f8ca1444f519a07b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 676bf3585d38cd4ad5c72b8b3afd4952e248c747683ae1072dd43f6ce1ccd279177e4d0c75a9821ed76d32806333128152231349d8d113ae5d81279580b13004
|
|
7
|
+
data.tar.gz: 3459078d96977399e75551c4a3ee5623091f48569984b771e540ec111125f5af91e39a8d78cbd3ce9280326b1b9395dc4a0b0d7f0a72294876682cb9fe35e3d9
|
data/README.md
CHANGED
data/ext/nokogumboc/extconf.rb
CHANGED
|
@@ -40,4 +40,21 @@ unless have_library('gumbo', 'gumbo_parse')
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
# We use some Gumbo Internals, and not all distros ship the internal headers.
|
|
44
|
+
header_typedefs = {
|
|
45
|
+
'error.h' => 'GumboErrorType',
|
|
46
|
+
'insertion_mode.h' => 'GumboInsertionMode',
|
|
47
|
+
'parser.h' => 'GumboParser',
|
|
48
|
+
'string_buffer.h' => 'GumboStringBuffer',
|
|
49
|
+
'token_type.h' => 'GumboTokenType',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
header_typedefs.each_pair do |header, type|
|
|
53
|
+
unless find_type(type, header)
|
|
54
|
+
require 'fileutils'
|
|
55
|
+
FileUtils.cp Dir["#{rakehome}/gumbo-parser/src/#{header}"],
|
|
56
|
+
"#{rakehome}/ext/nokogumboc"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
43
60
|
create_makefile('nokogumboc')
|
data/ext/nokogumboc/nokogumbo.c
CHANGED
|
@@ -19,10 +19,13 @@
|
|
|
19
19
|
//
|
|
20
20
|
|
|
21
21
|
#include <ruby.h>
|
|
22
|
-
#include
|
|
22
|
+
#include "gumbo.h"
|
|
23
|
+
#include "error.h"
|
|
24
|
+
#include "parser.h"
|
|
23
25
|
|
|
24
26
|
// class constants
|
|
25
27
|
static VALUE Document;
|
|
28
|
+
static VALUE XMLSyntaxError;
|
|
26
29
|
|
|
27
30
|
#ifdef NGLIB
|
|
28
31
|
#include <nokogiri.h>
|
|
@@ -181,11 +184,14 @@ static xmlNodePtr walk_tree(xmlDocPtr document, GumboNode *node) {
|
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
// Parse a string using gumbo_parse into a Nokogiri document
|
|
184
|
-
static VALUE parse(VALUE self, VALUE string) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
static VALUE parse(VALUE self, VALUE string, VALUE max_parse_errors) {
|
|
188
|
+
GumboOptions options;
|
|
189
|
+
memcpy(&options, &kGumboDefaultOptions, sizeof options);
|
|
190
|
+
options.max_errors = NUM2INT(max_parse_errors);
|
|
191
|
+
|
|
192
|
+
const char *input = RSTRING_PTR(string);
|
|
193
|
+
size_t input_len = RSTRING_LEN(string);
|
|
194
|
+
GumboOutput *output = gumbo_parse_with_options(&options, input, input_len);
|
|
189
195
|
xmlDocPtr doc = xmlNewDoc(CONST_CAST "1.0");
|
|
190
196
|
#ifdef NGLIB
|
|
191
197
|
doc->type = XML_HTML_DOCUMENT_NODE;
|
|
@@ -210,9 +216,49 @@ static VALUE parse(VALUE self, VALUE string) {
|
|
|
210
216
|
xmlAddChild((xmlNodePtr)doc, node);
|
|
211
217
|
}
|
|
212
218
|
}
|
|
213
|
-
gumbo_destroy_output(&kGumboDefaultOptions, output);
|
|
214
219
|
|
|
215
|
-
|
|
220
|
+
VALUE rdoc = Nokogiri_wrap_xml_document(Document, doc);
|
|
221
|
+
|
|
222
|
+
// Add parse errors to rdoc.
|
|
223
|
+
if (output->errors.length) {
|
|
224
|
+
GumboVector *errors = &output->errors;
|
|
225
|
+
GumboParser parser = { ._options = &options };
|
|
226
|
+
GumboStringBuffer msg;
|
|
227
|
+
VALUE rerrors = rb_ary_new2(errors->length);
|
|
228
|
+
|
|
229
|
+
gumbo_string_buffer_init(&parser, &msg);
|
|
230
|
+
for (int i=0; i < errors->length; i++) {
|
|
231
|
+
GumboError *err = errors->data[i];
|
|
232
|
+
gumbo_string_buffer_clear(&parser, &msg);
|
|
233
|
+
// Work around bug in gumbo_caret_diagnostic_to_string.
|
|
234
|
+
// See https://github.com/google/gumbo-parser/pull/371
|
|
235
|
+
// The bug occurs when the error starts with a newline (unless it's the
|
|
236
|
+
// first character in the input--but that shouldn't cause an error in
|
|
237
|
+
// the first place.
|
|
238
|
+
if (*err->original_text == '\n' && err->original_text != input)
|
|
239
|
+
--err->original_text;
|
|
240
|
+
gumbo_caret_diagnostic_to_string(&parser, err, input, &msg);
|
|
241
|
+
VALUE err_str = rb_str_new(msg.data, msg.length);
|
|
242
|
+
VALUE syntax_error = rb_class_new_instance(1, &err_str, XMLSyntaxError);
|
|
243
|
+
rb_iv_set(syntax_error, "@domain", INT2NUM(1)); // XML_FROM_PARSER
|
|
244
|
+
rb_iv_set(syntax_error, "@code", INT2NUM(1)); // XML_ERR_INTERNAL_ERROR
|
|
245
|
+
rb_iv_set(syntax_error, "@level", INT2NUM(2)); // XML_ERR_ERROR
|
|
246
|
+
rb_iv_set(syntax_error, "@file", Qnil);
|
|
247
|
+
rb_iv_set(syntax_error, "@line", INT2NUM(err->position.line));
|
|
248
|
+
rb_iv_set(syntax_error, "@str1", Qnil);
|
|
249
|
+
rb_iv_set(syntax_error, "@str2", Qnil);
|
|
250
|
+
rb_iv_set(syntax_error, "@str3", Qnil);
|
|
251
|
+
rb_iv_set(syntax_error, "@int1", INT2NUM(err->type));
|
|
252
|
+
rb_iv_set(syntax_error, "@column", INT2NUM(err->position.column));
|
|
253
|
+
rb_ary_push(rerrors, syntax_error);
|
|
254
|
+
}
|
|
255
|
+
rb_iv_set(rdoc, "@errors", rerrors);
|
|
256
|
+
gumbo_string_buffer_destroy(&parser, &msg);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
gumbo_destroy_output(&options, output);
|
|
260
|
+
|
|
261
|
+
return rdoc;
|
|
216
262
|
}
|
|
217
263
|
|
|
218
264
|
// Initialize the Nokogumbo class and fetch constants we will use later
|
|
@@ -224,10 +270,11 @@ void Init_nokogumboc() {
|
|
|
224
270
|
VALUE Nokogiri = rb_const_get(rb_cObject, rb_intern("Nokogiri"));
|
|
225
271
|
VALUE HTML = rb_const_get(Nokogiri, rb_intern("HTML"));
|
|
226
272
|
Document = rb_const_get(HTML, rb_intern("Document"));
|
|
273
|
+
VALUE XML = rb_const_get(Nokogiri, rb_intern("XML"));
|
|
274
|
+
XMLSyntaxError = rb_const_get(XML, rb_intern("SyntaxError"));
|
|
227
275
|
|
|
228
276
|
#ifndef NGLIB
|
|
229
277
|
// more class constants
|
|
230
|
-
VALUE XML = rb_const_get(Nokogiri, rb_intern("XML"));
|
|
231
278
|
Element = rb_const_get(XML, rb_intern("Element"));
|
|
232
279
|
Text = rb_const_get(XML, rb_intern("Text"));
|
|
233
280
|
CDATA = rb_const_get(XML, rb_intern("CDATA"));
|
|
@@ -244,5 +291,5 @@ void Init_nokogumboc() {
|
|
|
244
291
|
|
|
245
292
|
// define Nokogumbo class with a singleton parse method
|
|
246
293
|
VALUE Gumbo = rb_define_class("Nokogumbo", rb_cObject);
|
|
247
|
-
rb_define_singleton_method(Gumbo, "parse", parse,
|
|
294
|
+
rb_define_singleton_method(Gumbo, "parse", parse, 2);
|
|
248
295
|
}
|
data/lib/nokogumbo.rb
CHANGED
|
@@ -4,14 +4,14 @@ require 'nokogumboc'
|
|
|
4
4
|
module Nokogiri
|
|
5
5
|
# Parse an HTML document. +string+ contains the document. +string+
|
|
6
6
|
# may also be an IO-like object. Returns a +Nokogiri::HTML::Document+.
|
|
7
|
-
def self.HTML5(
|
|
8
|
-
Nokogiri::HTML5.parse(
|
|
7
|
+
def self.HTML5(*args)
|
|
8
|
+
Nokogiri::HTML5.parse(*args)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
module HTML5
|
|
12
12
|
# Parse an HTML document. +string+ contains the document. +string+
|
|
13
13
|
# may also be an IO-like object. Returns a +Nokogiri::HTML::Document+.
|
|
14
|
-
def self.parse(string)
|
|
14
|
+
def self.parse(string, options={})
|
|
15
15
|
if string.respond_to? :read
|
|
16
16
|
string = string.read
|
|
17
17
|
end
|
|
@@ -21,7 +21,7 @@ module Nokogiri
|
|
|
21
21
|
string = reencode(string)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
Nokogumbo.parse(string.to_s)
|
|
24
|
+
Nokogumbo.parse(string.to_s, options[:max_parse_errors] || 0)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# Fetch and parse a HTML document from the web, following redirects,
|
|
@@ -67,7 +67,7 @@ module Nokogiri
|
|
|
67
67
|
|
|
68
68
|
case response
|
|
69
69
|
when Net::HTTPSuccess
|
|
70
|
-
doc = parse(reencode(response.body, response['content-type']))
|
|
70
|
+
doc = parse(reencode(response.body, response['content-type']), options)
|
|
71
71
|
doc.instance_variable_set('@response', response)
|
|
72
72
|
doc.class.send(:attr_reader, :response)
|
|
73
73
|
doc
|
|
@@ -83,8 +83,8 @@ module Nokogiri
|
|
|
83
83
|
# while fragment is on the Gumbo TODO list, simulate it by doing
|
|
84
84
|
# a full document parse and ignoring the parent <html>, <head>, and <body>
|
|
85
85
|
# tags, and collecting up the children of each.
|
|
86
|
-
def self.fragment(
|
|
87
|
-
doc = parse(
|
|
86
|
+
def self.fragment(*args)
|
|
87
|
+
doc = parse(*args)
|
|
88
88
|
fragment = Nokogiri::HTML::DocumentFragment.new(doc)
|
|
89
89
|
|
|
90
90
|
if doc.children.length != 1 or doc.children.first.name != 'html'
|
data/test-nokogumbo.rb
CHANGED
|
@@ -78,7 +78,7 @@ class TestNokogumbo < Minitest::Test
|
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def test_html5_doctype
|
|
81
|
-
doc =
|
|
81
|
+
doc = Nokogiri::HTML5.parse("<!DOCTYPE html><html></html>")
|
|
82
82
|
assert_match /<!DOCTYPE html>/, doc.to_html
|
|
83
83
|
end
|
|
84
84
|
|
|
@@ -125,6 +125,44 @@ class TestNokogumbo < Minitest::Test
|
|
|
125
125
|
assert_equal ["html", "comment", "html", "comment"], doc.children.map(&:name)
|
|
126
126
|
end
|
|
127
127
|
|
|
128
|
+
def test_parse_errors
|
|
129
|
+
doc = Nokogiri::HTML5("<!DOCTYPE html><html><!-- -- --></a>", max_parse_errors: 10)
|
|
130
|
+
assert_equal doc.errors.length, 2
|
|
131
|
+
doc = Nokogiri::HTML5("<!DOCTYPE html><html>", max_parse_errors: 10)
|
|
132
|
+
assert_empty doc.errors
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def test_max_parse_errors
|
|
136
|
+
# This document contains 2 parse errors, but we force limit to 1.
|
|
137
|
+
doc = Nokogiri::HTML5("<!DOCTYPE html><html><!-- -- --></a>", max_parse_errors: 1)
|
|
138
|
+
assert_equal 1, doc.errors.length
|
|
139
|
+
doc = Nokogiri::HTML5("<!DOCTYPE html><html>", max_parse_errors: 1)
|
|
140
|
+
assert_empty doc.errors
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def test_default_max_parse_errors
|
|
144
|
+
# This document contains 200 parse errors, but default limit is 0.
|
|
145
|
+
doc = Nokogiri::HTML5("<!DOCTYPE html><html>" + "</p>" * 200)
|
|
146
|
+
assert_equal 0, doc.errors.length
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_parse_fragment_errors
|
|
150
|
+
doc = Nokogiri::HTML5.fragment("<\r\n", max_parse_errors: 10)
|
|
151
|
+
refute_empty doc.errors
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def test_fragment_max_parse_errors
|
|
155
|
+
# This fragment contains 3 parse errors, but we force limit to 1.
|
|
156
|
+
doc = Nokogiri::HTML5.fragment("<!-- -- --></a>", max_parse_errors: 1)
|
|
157
|
+
assert_equal 1, doc.errors.length
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def test_fragment_default_max_parse_errors
|
|
161
|
+
# This fragment contains 201 parse errors, but default limit is 0.
|
|
162
|
+
doc = Nokogiri::HTML5.fragment("</p>" * 200)
|
|
163
|
+
assert_equal 0, doc.errors.length
|
|
164
|
+
end
|
|
165
|
+
|
|
128
166
|
private
|
|
129
167
|
|
|
130
168
|
def buffer
|
metadata
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nokogumbo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sam Ruby
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2018-01-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: nokogiri
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- -
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: '0'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- -
|
|
24
|
+
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '0'
|
|
27
27
|
description: Nokogumbo allows a Ruby program to invoke the Gumbo HTML5 parser and
|
|
@@ -32,11 +32,10 @@ extensions:
|
|
|
32
32
|
- ext/nokogumboc/extconf.rb
|
|
33
33
|
extra_rdoc_files: []
|
|
34
34
|
files:
|
|
35
|
-
- ext/nokogumboc/extconf.rb
|
|
36
|
-
- ext/nokogumboc/nokogumbo.c
|
|
37
|
-
- lib/nokogumbo.rb
|
|
38
35
|
- LICENSE.txt
|
|
39
36
|
- README.md
|
|
37
|
+
- ext/nokogumboc/extconf.rb
|
|
38
|
+
- ext/nokogumboc/nokogumbo.c
|
|
40
39
|
- gumbo-parser/src/attribute.c
|
|
41
40
|
- gumbo-parser/src/attribute.h
|
|
42
41
|
- gumbo-parser/src/char_ref.c
|
|
@@ -69,10 +68,11 @@ files:
|
|
|
69
68
|
- gumbo-parser/src/vector.c
|
|
70
69
|
- gumbo-parser/src/vector.h
|
|
71
70
|
- gumbo-parser/visualc/include/strings.h
|
|
71
|
+
- lib/nokogumbo.rb
|
|
72
72
|
- test-nokogumbo.rb
|
|
73
73
|
homepage: https://github.com/rubys/nokogumbo/#readme
|
|
74
74
|
licenses:
|
|
75
|
-
- Apache
|
|
75
|
+
- Apache-2.0
|
|
76
76
|
metadata: {}
|
|
77
77
|
post_install_message:
|
|
78
78
|
rdoc_options: []
|
|
@@ -80,17 +80,17 @@ require_paths:
|
|
|
80
80
|
- lib
|
|
81
81
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
82
|
requirements:
|
|
83
|
-
- -
|
|
83
|
+
- - ">="
|
|
84
84
|
- !ruby/object:Gem::Version
|
|
85
85
|
version: '0'
|
|
86
86
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
87
|
requirements:
|
|
88
|
-
- -
|
|
88
|
+
- - ">="
|
|
89
89
|
- !ruby/object:Gem::Version
|
|
90
90
|
version: '0'
|
|
91
91
|
requirements: []
|
|
92
92
|
rubyforge_project:
|
|
93
|
-
rubygems_version: 2.
|
|
93
|
+
rubygems_version: 2.7.4
|
|
94
94
|
signing_key:
|
|
95
95
|
specification_version: 4
|
|
96
96
|
summary: Nokogiri interface to the Gumbo HTML5 parser
|