nokolexbor 0.6.2 → 0.7.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 +4 -4
- data/ext/nokolexbor/nl_attribute.c +8 -4
- data/ext/nokolexbor/nl_cdata.c +9 -4
- data/ext/nokolexbor/nl_comment.c +9 -4
- data/ext/nokolexbor/nl_document.c +30 -1
- data/ext/nokolexbor/nl_document_fragment.c +8 -4
- data/ext/nokolexbor/nl_node.c +62 -11
- data/ext/nokolexbor/nl_processing_instruction.c +8 -4
- data/ext/nokolexbor/nl_text.c +9 -4
- data/lib/nokolexbor/builder.rb +223 -0
- data/lib/nokolexbor/node_set.rb +7 -1
- data/lib/nokolexbor/version.rb +1 -1
- data/lib/nokolexbor.rb +13 -1
- metadata +4 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fb6fea4008f68e39df01fbc3920634bd747f6c6e58605876b4600b6037745d4a
|
|
4
|
+
data.tar.gz: 38f67c6e320e95d35a82db68f34b08e7103fa8d56e52fe6e59ebdeb266701c16
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a44ba9da124b17e58ae2abc8c6e99cfab22edbdcdff5b07e9636838f28098a4716aa6f50cf7282c1f15fcbb0630b7bd5ef814b54cb7886dd75b16a864e496405
|
|
7
|
+
data.tar.gz: 40fcdc69521d2f985be5fa62addf3f4fd03ac577eeb66878f496dbc684fc3925ceddb9fa2f74a870188431c672b16394ccc6c58303caae6c6718af8e978bc967
|
|
@@ -23,12 +23,16 @@ nl_attribute_new(int argc, VALUE *argv, VALUE klass)
|
|
|
23
23
|
|
|
24
24
|
rb_scan_args(argc, argv, "2*", &rb_document, &rb_name, &rest);
|
|
25
25
|
|
|
26
|
-
if (
|
|
27
|
-
|
|
26
|
+
if (rb_obj_is_kind_of(rb_document, cNokolexborDocument)) {
|
|
27
|
+
document = nl_rb_document_unwrap(rb_document);
|
|
28
|
+
} else if (rb_obj_is_kind_of(rb_document, cNokolexborNode)) {
|
|
29
|
+
lxb_dom_node_t *node = nl_rb_node_unwrap(rb_document);
|
|
30
|
+
document = node->owner_document;
|
|
31
|
+
rb_document = nl_rb_document_get(rb_document);
|
|
32
|
+
} else {
|
|
33
|
+
rb_raise(rb_eArgError, "Expected a Document or Node, got %s", rb_class2name(CLASS_OF(rb_document)));
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
document = nl_rb_document_unwrap(rb_document);
|
|
31
|
-
|
|
32
36
|
const char *c_name = StringValuePtr(rb_name);
|
|
33
37
|
size_t name_len = RSTRING_LEN(rb_name);
|
|
34
38
|
lxb_dom_attr_t *attr = lxb_dom_attr_interface_create(document);
|
data/ext/nokolexbor/nl_cdata.c
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include "nokolexbor.h"
|
|
2
2
|
|
|
3
3
|
VALUE cNokolexborCData;
|
|
4
|
+
extern VALUE cNokolexborNode;
|
|
4
5
|
extern VALUE cNokolexborText;
|
|
5
6
|
extern VALUE mNokolexbor;
|
|
6
7
|
|
|
@@ -22,12 +23,16 @@ nl_cdata_new(int argc, VALUE *argv, VALUE klass)
|
|
|
22
23
|
|
|
23
24
|
rb_scan_args(argc, argv, "2*", &rb_content, &rb_document, &rest);
|
|
24
25
|
|
|
25
|
-
if (
|
|
26
|
-
|
|
26
|
+
if (rb_obj_is_kind_of(rb_document, cNokolexborDocument)) {
|
|
27
|
+
document = nl_rb_document_unwrap(rb_document);
|
|
28
|
+
} else if (rb_obj_is_kind_of(rb_document, cNokolexborNode)) {
|
|
29
|
+
lxb_dom_node_t *node = nl_rb_node_unwrap(rb_document);
|
|
30
|
+
document = node->owner_document;
|
|
31
|
+
rb_document = nl_rb_document_get(rb_document);
|
|
32
|
+
} else {
|
|
33
|
+
rb_raise(rb_eArgError, "Expected a Document or Node, got %s", rb_class2name(CLASS_OF(rb_document)));
|
|
27
34
|
}
|
|
28
35
|
|
|
29
|
-
document = nl_rb_document_unwrap(rb_document);
|
|
30
|
-
|
|
31
36
|
const char *c_content = StringValuePtr(rb_content);
|
|
32
37
|
size_t content_len = RSTRING_LEN(rb_content);
|
|
33
38
|
lxb_dom_cdata_section_t *element = lxb_dom_document_create_cdata_section(document, (const lxb_char_t *)c_content, content_len);
|
data/ext/nokolexbor/nl_comment.c
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include "nokolexbor.h"
|
|
2
2
|
|
|
3
3
|
VALUE cNokolexborComment;
|
|
4
|
+
extern VALUE cNokolexborNode;
|
|
4
5
|
extern VALUE cNokolexborCharacterData;
|
|
5
6
|
extern VALUE mNokolexbor;
|
|
6
7
|
|
|
@@ -20,12 +21,16 @@ nl_comment_new(int argc, VALUE *argv, VALUE klass)
|
|
|
20
21
|
|
|
21
22
|
rb_scan_args(argc, argv, "2*", &rb_content, &rb_document, &rest);
|
|
22
23
|
|
|
23
|
-
if (
|
|
24
|
-
|
|
24
|
+
if (rb_obj_is_kind_of(rb_document, cNokolexborDocument)) {
|
|
25
|
+
document = nl_rb_document_unwrap(rb_document);
|
|
26
|
+
} else if (rb_obj_is_kind_of(rb_document, cNokolexborNode)) {
|
|
27
|
+
lxb_dom_node_t *node = nl_rb_node_unwrap(rb_document);
|
|
28
|
+
document = node->owner_document;
|
|
29
|
+
rb_document = nl_rb_document_get(rb_document);
|
|
30
|
+
} else {
|
|
31
|
+
rb_raise(rb_eArgError, "Expected a Document or Node, got %s", rb_class2name(CLASS_OF(rb_document)));
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
document = nl_rb_document_unwrap(rb_document);
|
|
28
|
-
|
|
29
34
|
const char *c_content = StringValuePtr(rb_content);
|
|
30
35
|
size_t content_len = RSTRING_LEN(rb_content);
|
|
31
36
|
lxb_dom_comment_t *element = lxb_dom_document_create_comment(document, (const lxb_char_t *)c_content, content_len);
|
|
@@ -5,6 +5,11 @@ extern VALUE mNokolexbor;
|
|
|
5
5
|
extern VALUE cNokolexborNode;
|
|
6
6
|
VALUE cNokolexborDocument;
|
|
7
7
|
|
|
8
|
+
#ifdef HAVE_PTHREAD_H
|
|
9
|
+
#include <pthread.h>
|
|
10
|
+
pthread_key_t p_key_html_parser;
|
|
11
|
+
#endif
|
|
12
|
+
|
|
8
13
|
static void
|
|
9
14
|
free_nl_document(lxb_html_document_t *document)
|
|
10
15
|
{
|
|
@@ -28,7 +33,11 @@ nl_document_parse_native(VALUE self, VALUE rb_html)
|
|
|
28
33
|
const char *html_c = StringValuePtr(rb_html);
|
|
29
34
|
size_t html_len = RSTRING_LEN(rb_html);
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
#ifdef HAVE_PTHREAD_H
|
|
37
|
+
lxb_html_parser_t *html_parser = (lxb_html_parser_t *)pthread_getspecific(p_key_html_parser);
|
|
38
|
+
#else
|
|
39
|
+
lxb_html_parser_t *html_parser = NULL;
|
|
40
|
+
#endif
|
|
32
41
|
if (html_parser == NULL) {
|
|
33
42
|
html_parser = lxb_html_parser_create();
|
|
34
43
|
lxb_status_t status = lxb_html_parser_init(html_parser);
|
|
@@ -38,10 +47,17 @@ nl_document_parse_native(VALUE self, VALUE rb_html)
|
|
|
38
47
|
nl_raise_lexbor_error(status);
|
|
39
48
|
}
|
|
40
49
|
html_parser->tree->scripting = true;
|
|
50
|
+
#ifdef HAVE_PTHREAD_H
|
|
51
|
+
pthread_setspecific(p_key_html_parser, html_parser);
|
|
52
|
+
#endif
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
lxb_html_document_t *document = lxb_html_parse(html_parser, (const lxb_char_t *)html_c, html_len);
|
|
44
56
|
|
|
57
|
+
#ifndef HAVE_PTHREAD_H
|
|
58
|
+
lxb_html_parser_destroy(html_parser);
|
|
59
|
+
#endif
|
|
60
|
+
|
|
45
61
|
if (document == NULL) {
|
|
46
62
|
rb_raise(rb_eRuntimeError, "Error parsing document");
|
|
47
63
|
}
|
|
@@ -115,8 +131,21 @@ nl_document_root(VALUE self)
|
|
|
115
131
|
return nl_rb_node_create(lxb_dom_document_root(doc), self);
|
|
116
132
|
}
|
|
117
133
|
|
|
134
|
+
static void
|
|
135
|
+
free_html_parser(void *data)
|
|
136
|
+
{
|
|
137
|
+
lxb_html_parser_t *html_parser = (lxb_html_parser_t *)data;
|
|
138
|
+
if (html_parser != NULL) {
|
|
139
|
+
html_parser = lxb_html_parser_destroy(html_parser);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
118
143
|
void Init_nl_document(void)
|
|
119
144
|
{
|
|
145
|
+
#ifdef HAVE_PTHREAD_H
|
|
146
|
+
pthread_key_create(&p_key_html_parser, free_html_parser);
|
|
147
|
+
#endif
|
|
148
|
+
|
|
120
149
|
cNokolexborDocument = rb_define_class_under(mNokolexbor, "Document", cNokolexborNode);
|
|
121
150
|
rb_define_singleton_method(cNokolexborDocument, "new", nl_document_new, 0);
|
|
122
151
|
rb_define_singleton_method(cNokolexborDocument, "parse_native", nl_document_parse_native, 1);
|
|
@@ -22,12 +22,16 @@ nl_document_fragment_new(int argc, VALUE *argv, VALUE klass)
|
|
|
22
22
|
|
|
23
23
|
rb_scan_args(argc, argv, "1*", &rb_document, &rest);
|
|
24
24
|
|
|
25
|
-
if (
|
|
26
|
-
|
|
25
|
+
if (rb_obj_is_kind_of(rb_document, cNokolexborDocument)) {
|
|
26
|
+
document = nl_rb_document_unwrap(rb_document);
|
|
27
|
+
} else if (rb_obj_is_kind_of(rb_document, cNokolexborNode)) {
|
|
28
|
+
lxb_dom_node_t *node = nl_rb_node_unwrap(rb_document);
|
|
29
|
+
document = node->owner_document;
|
|
30
|
+
rb_document = nl_rb_document_get(rb_document);
|
|
31
|
+
} else {
|
|
32
|
+
rb_raise(rb_eArgError, "Expected a Document or Node, got %s", rb_class2name(CLASS_OF(rb_document)));
|
|
27
33
|
}
|
|
28
34
|
|
|
29
|
-
document = nl_rb_document_unwrap(rb_document);
|
|
30
|
-
|
|
31
35
|
lxb_dom_document_fragment_t *node = lxb_dom_document_create_document_fragment(document);
|
|
32
36
|
if (node == NULL) {
|
|
33
37
|
rb_raise(rb_eRuntimeError, "Error creating document fragment");
|
data/ext/nokolexbor/nl_node.c
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
#include "nokolexbor.h"
|
|
2
|
+
#include "config.h"
|
|
2
3
|
#include "libxml/tree.h"
|
|
3
4
|
|
|
5
|
+
#ifdef HAVE_PTHREAD_H
|
|
6
|
+
#include <pthread.h>
|
|
7
|
+
pthread_key_t p_key_css_parser;
|
|
8
|
+
pthread_key_t p_key_selectors;
|
|
9
|
+
#endif
|
|
10
|
+
|
|
4
11
|
#define SORT_NAME nl_css_result
|
|
5
12
|
#define SORT_TYPE lxb_dom_node_t *
|
|
6
13
|
#define SORT_CMP(x, y) (x->user >= y->user ? (x->user == y->user ? 0 : 1) : -1)
|
|
@@ -113,12 +120,16 @@ nl_node_new(int argc, VALUE *argv, VALUE klass)
|
|
|
113
120
|
|
|
114
121
|
rb_scan_args(argc, argv, "2*", &rb_name, &rb_document, &rest);
|
|
115
122
|
|
|
116
|
-
if (
|
|
117
|
-
|
|
123
|
+
if (rb_obj_is_kind_of(rb_document, cNokolexborDocument)) {
|
|
124
|
+
document = nl_rb_document_unwrap(rb_document);
|
|
125
|
+
} else if (rb_obj_is_kind_of(rb_document, cNokolexborNode)) {
|
|
126
|
+
lxb_dom_node_t *node = nl_rb_node_unwrap(rb_document);
|
|
127
|
+
document = node->owner_document;
|
|
128
|
+
rb_document = nl_rb_document_get(rb_document);
|
|
129
|
+
} else {
|
|
130
|
+
rb_raise(rb_eArgError, "Expected a Document or Node, got %s", rb_class2name(CLASS_OF(rb_document)));
|
|
118
131
|
}
|
|
119
132
|
|
|
120
|
-
document = nl_rb_document_unwrap(rb_document);
|
|
121
|
-
|
|
122
133
|
const char *c_name = StringValuePtr(rb_name);
|
|
123
134
|
size_t name_len = RSTRING_LEN(rb_name);
|
|
124
135
|
lxb_dom_element_t *element = lxb_dom_document_create_element(document, (const lxb_char_t *)c_name, name_len, NULL);
|
|
@@ -366,8 +377,13 @@ nl_node_find(VALUE self, VALUE selector, lxb_selectors_cb_f cb, void *ctx)
|
|
|
366
377
|
lxb_dom_node_t *node = nl_rb_node_unwrap(self);
|
|
367
378
|
|
|
368
379
|
lxb_status_t status;
|
|
369
|
-
|
|
370
|
-
|
|
380
|
+
#ifdef HAVE_PTHREAD_H
|
|
381
|
+
lxb_css_parser_t *css_parser = (lxb_css_parser_t *)pthread_getspecific(p_key_css_parser);
|
|
382
|
+
lxb_selectors_t *selectors = (lxb_selectors_t *)pthread_getspecific(p_key_selectors);
|
|
383
|
+
#else
|
|
384
|
+
lxb_css_parser_t *css_parser = NULL;
|
|
385
|
+
lxb_selectors_t *selectors = NULL;
|
|
386
|
+
#endif
|
|
371
387
|
lxb_css_selector_list_t *list = NULL;
|
|
372
388
|
|
|
373
389
|
/* CSS parser. */
|
|
@@ -377,6 +393,9 @@ nl_node_find(VALUE self, VALUE selector, lxb_selectors_cb_f cb, void *ctx)
|
|
|
377
393
|
if (status != LXB_STATUS_OK) {
|
|
378
394
|
goto init_error;
|
|
379
395
|
}
|
|
396
|
+
#ifdef HAVE_PTHREAD_H
|
|
397
|
+
pthread_setspecific(p_key_css_parser, css_parser);
|
|
398
|
+
#endif
|
|
380
399
|
}
|
|
381
400
|
|
|
382
401
|
/* Selectors. */
|
|
@@ -386,6 +405,9 @@ nl_node_find(VALUE self, VALUE selector, lxb_selectors_cb_f cb, void *ctx)
|
|
|
386
405
|
if (status != LXB_STATUS_OK) {
|
|
387
406
|
goto init_error;
|
|
388
407
|
}
|
|
408
|
+
#ifdef HAVE_PTHREAD_H
|
|
409
|
+
pthread_setspecific(p_key_selectors, selectors);
|
|
410
|
+
#endif
|
|
389
411
|
}
|
|
390
412
|
|
|
391
413
|
/* Parse and get the log. */
|
|
@@ -397,11 +419,16 @@ nl_node_find(VALUE self, VALUE selector, lxb_selectors_cb_f cb, void *ctx)
|
|
|
397
419
|
|
|
398
420
|
/* Find HTML nodes by CSS Selectors. */
|
|
399
421
|
status = lxb_selectors_find(selectors, node, list, cb, ctx);
|
|
400
|
-
if (status != LXB_STATUS_OK) {
|
|
401
|
-
goto cleanup;
|
|
402
|
-
}
|
|
403
422
|
|
|
404
423
|
cleanup:
|
|
424
|
+
#ifndef HAVE_PTHREAD_H
|
|
425
|
+
/* Destroy Selectors object. */
|
|
426
|
+
(void)lxb_selectors_destroy(selectors, true);
|
|
427
|
+
|
|
428
|
+
/* Destroy resources for CSS Parser. */
|
|
429
|
+
(void)lxb_css_parser_destroy(css_parser, true);
|
|
430
|
+
#endif
|
|
431
|
+
|
|
405
432
|
/* Destroy all object for all CSS Selector List. */
|
|
406
433
|
lxb_css_selector_list_destroy_memory(list);
|
|
407
434
|
|
|
@@ -416,8 +443,10 @@ init_error:
|
|
|
416
443
|
lxb_css_parser_destroy(css_parser, true);
|
|
417
444
|
css_parser = NULL;
|
|
418
445
|
|
|
419
|
-
|
|
420
|
-
|
|
446
|
+
#ifdef HAVE_PTHREAD_H
|
|
447
|
+
pthread_setspecific(p_key_css_parser, NULL);
|
|
448
|
+
pthread_setspecific(p_key_selectors, NULL);
|
|
449
|
+
#endif
|
|
421
450
|
|
|
422
451
|
return status;
|
|
423
452
|
}
|
|
@@ -1173,8 +1202,30 @@ nl_node_path(VALUE self)
|
|
|
1173
1202
|
return ret;
|
|
1174
1203
|
}
|
|
1175
1204
|
|
|
1205
|
+
static void
|
|
1206
|
+
free_css_parser(void *data)
|
|
1207
|
+
{
|
|
1208
|
+
lxb_css_parser_t *css_parser = (lxb_css_parser_t *)data;
|
|
1209
|
+
if (css_parser != NULL) {
|
|
1210
|
+
lxb_css_parser_destroy(css_parser, true);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
static void
|
|
1215
|
+
free_selectors(void *data)
|
|
1216
|
+
{
|
|
1217
|
+
lxb_selectors_t *selectors = (lxb_selectors_t *)data;
|
|
1218
|
+
if (selectors != NULL) {
|
|
1219
|
+
lxb_selectors_destroy(selectors, true);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1176
1223
|
void Init_nl_node(void)
|
|
1177
1224
|
{
|
|
1225
|
+
#ifdef HAVE_PTHREAD_H
|
|
1226
|
+
pthread_key_create(&p_key_css_parser, free_css_parser);
|
|
1227
|
+
pthread_key_create(&p_key_selectors, free_selectors);
|
|
1228
|
+
#endif
|
|
1178
1229
|
cNokolexborNode = rb_define_class_under(mNokolexbor, "Node", rb_cObject);
|
|
1179
1230
|
rb_undef_alloc_func(cNokolexborNode);
|
|
1180
1231
|
|
|
@@ -21,12 +21,16 @@ nl_processing_instruction_new(int argc, VALUE *argv, VALUE klass)
|
|
|
21
21
|
|
|
22
22
|
rb_scan_args(argc, argv, "3*", &rb_name, &rb_content, &rb_document, &rest);
|
|
23
23
|
|
|
24
|
-
if (
|
|
25
|
-
|
|
24
|
+
if (rb_obj_is_kind_of(rb_document, cNokolexborDocument)) {
|
|
25
|
+
document = nl_rb_document_unwrap(rb_document);
|
|
26
|
+
} else if (rb_obj_is_kind_of(rb_document, cNokolexborNode)) {
|
|
27
|
+
lxb_dom_node_t *node = nl_rb_node_unwrap(rb_document);
|
|
28
|
+
document = node->owner_document;
|
|
29
|
+
rb_document = nl_rb_document_get(rb_document);
|
|
30
|
+
} else {
|
|
31
|
+
rb_raise(rb_eArgError, "Expected a Document or Node, got %s", rb_class2name(CLASS_OF(rb_document)));
|
|
26
32
|
}
|
|
27
33
|
|
|
28
|
-
document = nl_rb_document_unwrap(rb_document);
|
|
29
|
-
|
|
30
34
|
const char *c_name = StringValuePtr(rb_name);
|
|
31
35
|
size_t name_len = RSTRING_LEN(rb_name);
|
|
32
36
|
const char *c_content = StringValuePtr(rb_content);
|
data/ext/nokolexbor/nl_text.c
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include "nokolexbor.h"
|
|
2
2
|
|
|
3
3
|
VALUE cNokolexborText;
|
|
4
|
+
extern VALUE cNokolexborNode;
|
|
4
5
|
extern VALUE cNokolexborCharacterData;
|
|
5
6
|
extern VALUE mNokolexbor;
|
|
6
7
|
|
|
@@ -20,12 +21,16 @@ nl_text_new(int argc, VALUE *argv, VALUE klass)
|
|
|
20
21
|
|
|
21
22
|
rb_scan_args(argc, argv, "2*", &rb_text, &rb_document, &rest);
|
|
22
23
|
|
|
23
|
-
if (
|
|
24
|
-
|
|
24
|
+
if (rb_obj_is_kind_of(rb_document, cNokolexborDocument)) {
|
|
25
|
+
document = nl_rb_document_unwrap(rb_document);
|
|
26
|
+
} else if (rb_obj_is_kind_of(rb_document, cNokolexborNode)) {
|
|
27
|
+
lxb_dom_node_t *node = nl_rb_node_unwrap(rb_document);
|
|
28
|
+
document = node->owner_document;
|
|
29
|
+
rb_document = nl_rb_document_get(rb_document);
|
|
30
|
+
} else {
|
|
31
|
+
rb_raise(rb_eArgError, "Expected a Document or Node, got %s", rb_class2name(CLASS_OF(rb_document)));
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
document = nl_rb_document_unwrap(rb_document);
|
|
28
|
-
|
|
29
34
|
const char *c_text = StringValuePtr(rb_text);
|
|
30
35
|
size_t text_len = RSTRING_LEN(rb_text);
|
|
31
36
|
lxb_dom_text_t *element = lxb_dom_document_create_text_node(document, (const lxb_char_t *)c_text, text_len);
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module Nokolexbor
|
|
6
|
+
# A DSL for programmatically building HTML documents.
|
|
7
|
+
#
|
|
8
|
+
# == No-arg block (instance_eval style) – tag names are called bare:
|
|
9
|
+
#
|
|
10
|
+
# Nokolexbor do
|
|
11
|
+
# body do
|
|
12
|
+
# h1 'Hello world'
|
|
13
|
+
# p 'This little p'
|
|
14
|
+
# ul do
|
|
15
|
+
# li 'Go to market'
|
|
16
|
+
# li 'Go to bed'
|
|
17
|
+
# end
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# == Block-parameter style – tag names are called on the builder argument:
|
|
22
|
+
#
|
|
23
|
+
# Nokolexbor::Builder.new do |b|
|
|
24
|
+
# b.body do
|
|
25
|
+
# b.h1 'Hello world'
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# == Building into an existing node:
|
|
30
|
+
#
|
|
31
|
+
# Nokolexbor::Builder.with(existing_node) do
|
|
32
|
+
# span 'injected'
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
class Builder
|
|
36
|
+
# The Document used to create new nodes
|
|
37
|
+
attr_accessor :doc
|
|
38
|
+
|
|
39
|
+
# The node that currently receives new children
|
|
40
|
+
attr_accessor :parent
|
|
41
|
+
|
|
42
|
+
# Arity of the outermost block (drives instance_eval vs yield dispatch)
|
|
43
|
+
attr_accessor :arity
|
|
44
|
+
|
|
45
|
+
# A context object for use when the block has no arguments
|
|
46
|
+
attr_accessor :context
|
|
47
|
+
|
|
48
|
+
# Build into an existing +root+ node.
|
|
49
|
+
def self.with(root, &block)
|
|
50
|
+
new(root, &block)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param root [Node, nil]
|
|
54
|
+
# When given, new nodes are appended under +root+ and +root.document+
|
|
55
|
+
# is used as the owning document. When omitted a fresh {Document} and
|
|
56
|
+
# a {DocumentFragment} container are created.
|
|
57
|
+
def initialize(root = nil, &block)
|
|
58
|
+
if root
|
|
59
|
+
@doc = root.document
|
|
60
|
+
@parent = root
|
|
61
|
+
else
|
|
62
|
+
@doc = Document.new
|
|
63
|
+
@parent = DocumentFragment.new(@doc)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
@context = nil
|
|
67
|
+
@arity = nil
|
|
68
|
+
|
|
69
|
+
return unless block
|
|
70
|
+
|
|
71
|
+
@arity = block.arity
|
|
72
|
+
if @arity <= 0
|
|
73
|
+
# Capture outer self so that outer methods / ivars remain accessible
|
|
74
|
+
# via method_missing delegation while the builder acts as self.
|
|
75
|
+
@context = eval("self", block.binding)
|
|
76
|
+
instance_eval(&block)
|
|
77
|
+
else
|
|
78
|
+
yield self
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Insert a Text node containing +string+.
|
|
83
|
+
def text(string)
|
|
84
|
+
insert(@doc.create_text_node(string))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Insert a CDATA node containing +string+.
|
|
88
|
+
def cdata(string)
|
|
89
|
+
insert(@doc.create_cdata(string))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Insert a Comment node containing +string+.
|
|
93
|
+
def comment(string)
|
|
94
|
+
insert(@doc.create_comment(string))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Append raw HTML (parsed into nodes) under the current parent.
|
|
98
|
+
def <<(string)
|
|
99
|
+
@doc.fragment(string).children.each { |x| insert(x) }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Serialize the built content to an HTML string.
|
|
103
|
+
def to_html
|
|
104
|
+
@parent.to_html
|
|
105
|
+
end
|
|
106
|
+
alias_method :to_s, :to_html
|
|
107
|
+
|
|
108
|
+
# Ruby's Kernel#p, Kernel#pp, etc. are defined on Object and therefore on
|
|
109
|
+
# Builder itself. They must be overridden so they fall through to
|
|
110
|
+
# method_missing and create the corresponding HTML element instead of
|
|
111
|
+
# printing values to stdout.
|
|
112
|
+
def p(*args, &block) # :nodoc:
|
|
113
|
+
method_missing(:p, *args, &block)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def method_missing(method, *args, &block) # :nodoc:
|
|
117
|
+
# Only delegate to the outer context for methods the user defined there.
|
|
118
|
+
# Standard Object / Kernel methods (like :p, :pp, :puts …) must not be
|
|
119
|
+
# forwarded to @context because all objects respond to them, which would
|
|
120
|
+
# bypass element creation entirely.
|
|
121
|
+
if @context && !OBJECT_INSTANCE_METHODS.include?(method) && @context.respond_to?(method)
|
|
122
|
+
@context.send(method, *args, &block)
|
|
123
|
+
else
|
|
124
|
+
node = @doc.create_element(method.to_s.sub(/[_!]$/, ""), *args)
|
|
125
|
+
insert(node, &block)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def respond_to_missing?(method, include_private = false) # :nodoc:
|
|
130
|
+
(@context && !OBJECT_INSTANCE_METHODS.include?(method) && @context.respond_to?(method)) || super
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Methods defined on plain Object that should never be forwarded to @context
|
|
134
|
+
# as HTML element creation fallbacks.
|
|
135
|
+
OBJECT_INSTANCE_METHODS = Object.instance_methods.to_set.freeze
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
# Add +node+ as a child of @parent; if a block is given, push @parent
|
|
140
|
+
# to +node+ for the duration of that block, then restore it.
|
|
141
|
+
# Returns a {NodeBuilder} for the inserted node so attributes / classes
|
|
142
|
+
# can be chained: <tt>div.container.hero!</tt>
|
|
143
|
+
def insert(node, &block)
|
|
144
|
+
node = @parent.add_child(node)
|
|
145
|
+
if block
|
|
146
|
+
old_parent = @parent
|
|
147
|
+
@parent = node
|
|
148
|
+
@arity ||= block.arity
|
|
149
|
+
begin
|
|
150
|
+
if @arity <= 0
|
|
151
|
+
instance_eval(&block)
|
|
152
|
+
else
|
|
153
|
+
yield(self)
|
|
154
|
+
end
|
|
155
|
+
ensure
|
|
156
|
+
@parent = old_parent
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
NodeBuilder.new(node, self)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Wraps a built node and exposes a fluent API for setting classes and ids:
|
|
163
|
+
#
|
|
164
|
+
# div.container # => <div class="container">
|
|
165
|
+
# div.box.highlight # => <div class="box highlight">
|
|
166
|
+
# div.thing! # => <div id="thing">
|
|
167
|
+
# div.container.hero! # => <div class="container" id="hero">
|
|
168
|
+
#
|
|
169
|
+
class NodeBuilder # :nodoc:
|
|
170
|
+
def initialize(node, doc_builder)
|
|
171
|
+
@node = node
|
|
172
|
+
@doc_builder = doc_builder
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def []=(k, v)
|
|
176
|
+
@node[k] = v
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def [](k)
|
|
180
|
+
@node[k]
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def method_missing(method, *args, &block)
|
|
184
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
|
185
|
+
|
|
186
|
+
case method.to_s
|
|
187
|
+
when /^(.*)!$/
|
|
188
|
+
@node["id"] = Regexp.last_match(1)
|
|
189
|
+
@node.content = args.first if args.first
|
|
190
|
+
when /^(.*)=$/
|
|
191
|
+
@node[Regexp.last_match(1)] = args.first
|
|
192
|
+
else
|
|
193
|
+
@node["class"] =
|
|
194
|
+
((@node["class"] || "").split(/\s/) + [method.to_s]).join(" ")
|
|
195
|
+
@node.content = args.first if args.first
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
opts.each do |k, v|
|
|
199
|
+
@node[k.to_s] = ((@node[k.to_s] || "").split(/\s/) + [v.to_s]).join(" ")
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
if block
|
|
203
|
+
old_parent = @doc_builder.parent
|
|
204
|
+
@doc_builder.parent = @node
|
|
205
|
+
arity = @doc_builder.arity || block.arity
|
|
206
|
+
value = if arity <= 0
|
|
207
|
+
@doc_builder.instance_eval(&block)
|
|
208
|
+
else
|
|
209
|
+
yield(@doc_builder)
|
|
210
|
+
end
|
|
211
|
+
@doc_builder.parent = old_parent
|
|
212
|
+
return value
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
self
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def respond_to_missing?(_method, _include_private = false) # :nodoc:
|
|
219
|
+
true
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
data/lib/nokolexbor/node_set.rb
CHANGED
|
@@ -11,7 +11,13 @@ module Nokolexbor
|
|
|
11
11
|
# @return [Document]
|
|
12
12
|
def self.new(document, list = [])
|
|
13
13
|
obj = allocate
|
|
14
|
-
|
|
14
|
+
if document.is_a?(Document) || document.nil?
|
|
15
|
+
obj.instance_variable_set(:@document, document)
|
|
16
|
+
elsif document.is_a?(Node)
|
|
17
|
+
obj.instance_variable_set(:@document, document.document)
|
|
18
|
+
else
|
|
19
|
+
raise ArgumentError, "Expected a Document or Node, got #{document.class}"
|
|
20
|
+
end
|
|
15
21
|
list.each { |x| obj << x }
|
|
16
22
|
yield obj if block_given?
|
|
17
23
|
obj
|
data/lib/nokolexbor/version.rb
CHANGED
data/lib/nokolexbor.rb
CHANGED
|
@@ -28,11 +28,23 @@ require 'nokolexbor/node_set'
|
|
|
28
28
|
require 'nokolexbor/document_fragment'
|
|
29
29
|
require 'nokolexbor/xpath'
|
|
30
30
|
require 'nokolexbor/xpath_context'
|
|
31
|
+
require 'nokolexbor/builder'
|
|
31
32
|
|
|
32
33
|
module Nokolexbor
|
|
33
34
|
class << self
|
|
34
|
-
def
|
|
35
|
+
def parse(*args)
|
|
35
36
|
Document.parse(*args)
|
|
36
37
|
end
|
|
38
|
+
|
|
39
|
+
alias_method :HTML, :parse
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Parse an HTML document, or build one with the DSL when a block is given.
|
|
44
|
+
def Nokolexbor(string_or_io = nil, &block)
|
|
45
|
+
if block
|
|
46
|
+
Nokolexbor::Builder.new(&block).parent
|
|
47
|
+
else
|
|
48
|
+
Nokolexbor.parse(string_or_io)
|
|
37
49
|
end
|
|
38
50
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nokolexbor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yicheng Zhou
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 2026-03-25 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rake-compiler
|
|
@@ -120,6 +119,7 @@ files:
|
|
|
120
119
|
- ext/nokolexbor/xml_tree.c
|
|
121
120
|
- ext/nokolexbor/xml_xpath.c
|
|
122
121
|
- lib/nokolexbor.rb
|
|
122
|
+
- lib/nokolexbor/builder.rb
|
|
123
123
|
- lib/nokolexbor/document.rb
|
|
124
124
|
- lib/nokolexbor/document_fragment.rb
|
|
125
125
|
- lib/nokolexbor/node.rb
|
|
@@ -523,7 +523,6 @@ licenses:
|
|
|
523
523
|
- MIT
|
|
524
524
|
metadata:
|
|
525
525
|
msys2_mingw_dependencies: cmake
|
|
526
|
-
post_install_message:
|
|
527
526
|
rdoc_options: []
|
|
528
527
|
require_paths:
|
|
529
528
|
- lib
|
|
@@ -538,8 +537,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
538
537
|
- !ruby/object:Gem::Version
|
|
539
538
|
version: '0'
|
|
540
539
|
requirements: []
|
|
541
|
-
rubygems_version:
|
|
542
|
-
signing_key:
|
|
540
|
+
rubygems_version: 4.0.6
|
|
543
541
|
specification_version: 4
|
|
544
542
|
summary: High-performance HTML5 parser, with support for both CSS selectors and XPath.
|
|
545
543
|
test_files: []
|