Module: Makiri::XML::NodeMethods
- Included in:
- Document
- Defined in:
- lib/makiri/xml/node_methods.rb,
ext/makiri/makiri.c
Overview
Ruby additions over the C-defined XML node readers, mirroring Makiri::HTML::NodeMethods so the XML node surface matches the HTML one for the methods consumers (e.g. Dommy) rely on. Each is guarded with ‘method_defined?` so a future native implementation on this module takes precedence rather than being shadowed.
Instance Method Summary collapse
-
#<<(arg) ⇒ Object
element << node -> self (Nokogiri’s <<: append and return the receiver).
-
#==(other) ⇒ Object
Pointer identity: equal iff both wrappers resolve to the same node pointer (an HTML node is thus never equal to an XML node).
-
#[](rb_name) ⇒ Object
—- attributes —-.
-
#[]=(rb_name, rb_value) ⇒ Object
element = value -> value.
-
#add_child(arg) ⇒ Object
element.add_child(node) -> the inserted node.
-
#add_next_sibling(arg) ⇒ Object
node.add_next_sibling(other) / node.after(other) -> the inserted node.
-
#add_previous_sibling(arg) ⇒ Object
node.add_previous_sibling(other) / node.before(other) -> the inserted node.
-
#after(arg) ⇒ Object
node.add_next_sibling(other) / node.after(other) -> the inserted node.
- #ancestors ⇒ Object
-
#at_css(selector, ns = nil) ⇒ Object
First descendant matching
selector, or nil. - #at_xpath(*args) ⇒ Object
- #attribute_nodes ⇒ Object
-
#before(arg) ⇒ Object
node.add_previous_sibling(other) / node.before(other) -> the inserted node.
-
#canonicalize(comments: false) ⇒ String
Inclusive Canonical XML 1.0 (UTF-8).
- #child ⇒ Object
- #children ⇒ Object
-
#clone_node(*args) ⇒ Object
clone_node(deep = false) -> a detached copy of this node in the same document (element/attribute name case, namespaces, and the CDATA node type preserved); deep copies the whole subtree.
- #collect_namespaces ⇒ Object
-
#content ⇒ Object
—- content / text —-.
-
#content=(rb_text) ⇒ Object
node.content = text -> text.
-
#css(selector, ns = nil) ⇒ Makiri::NodeSet
CSS selector queries over XML, lowered to the native XPath engine (so matching is case-sensitive and namespace-aware, unlike a Lexbor HTML matcher).
-
#delete(rb_name) ⇒ Object
element.delete(name) -> self.
- #document ⇒ Object
-
#element_children ⇒ Object
element_children -> NodeSet of the child element nodes (nodeType 1) only, in document order (the counterpart of HTML’s #element_children).
-
#eql?(other) ⇒ Boolean
Pointer identity: equal iff both wrappers resolve to the same node pointer (an HTML node is thus never equal to an XML node).
-
#hash ⇒ Object
Stable hash derived from the node pointer, so a == b implies a.hash == b.hash even across separately-created wrappers.
-
#inner_html(*args) ⇒ Object
—- fail-closed guard for the unsupported serialization surface —-.
-
#inner_text ⇒ Object
—- content / text —-.
- #key?(name) ⇒ Boolean (also: #has_attribute?)
- #last_element_child ⇒ Object
- #local_name ⇒ Object
-
#matches?(selector, ns = nil) ⇒ Boolean
Whether this node matches
selector(full selector, combinators included). -
#name ⇒ Object
—- name / namespace —-.
-
#name=(rb_name) ⇒ Object
node.name = new_name -> new_name.
-
#namespace ⇒ Object
The xmlns-declaration detector lives in the node layer (mkr_xml_node_xmlns_decl) so the namespace introspection, the C14N walk, and the mutation namespace resolver all share one definition.
- #namespace_definitions ⇒ Object
- #namespace_uri ⇒ Object
- #namespaces ⇒ Object
- #next ⇒ Object
- #next_sibling ⇒ Object
- #node_type ⇒ Object
-
#outer_html(*args) ⇒ Object
—- fail-closed guard for the unsupported serialization surface —-.
- #parent ⇒ Object
-
#pointer_id ⇒ Object
Nokogiri-compatible identity: the underlying node pointer as an Integer.
- #prefix ⇒ Object
- #previous ⇒ Object
- #previous_sibling ⇒ Object
-
#remove ⇒ Object
node.remove / node.unlink -> node.
-
#remove_attribute(rb_name) ⇒ Object
element.delete(name) -> self.
-
#remove_attribute_ns(rb_ns, rb_local) ⇒ Object
element.remove_attribute_ns(namespace_or_nil, local_name) -> self.
-
#replace(arg) ⇒ Object
node.replace(other) -> the inserted node (the replaced node is detached).
-
#set_attribute_ns(rb_ns, rb_qname, rb_value) ⇒ Object
element.set_attribute_ns(namespace_or_nil, qualified_name, value) -> value.
-
#text ⇒ Object
—- content / text —-.
-
#to_html(*args) ⇒ Object
—- fail-closed guard for the unsupported serialization surface —-.
-
#to_xml(pretty: false, indent: 2, encoding: "UTF-8") ⇒ String
A Document also emits the XML declaration and its DOCTYPE; any other node serializes just its own subtree.
-
#to_xml(pretty: false, indent: 2, encoding: "UTF-8") ⇒ String
A Document also emits the XML declaration and its DOCTYPE; any other node serializes just its own subtree.
-
#unlink ⇒ Object
node.remove / node.unlink -> node.
- #value ⇒ Object
- #xpath(*args) ⇒ Object
Instance Method Details
#<<(arg) ⇒ Object
element << node -> self (Nokogiri’s <<: append and return the receiver).
1208 1209 1210 1211 1212 1213 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1208
static VALUE
mkr_xml_node_lshift(VALUE self, VALUE arg)
{
mkr_xml_node_insert(self, arg, MKR_INS_CHILD);
return self;
}
|
#==(other) ⇒ Object
Pointer identity: equal iff both wrappers resolve to the same node pointer (an HTML node is thus never equal to an XML node).
133 134 135 136 137 138 139 140 |
# File 'ext/makiri/glue/ruby_node.c', line 133
VALUE
mkr_node_equals(VALUE self, VALUE other)
{
if (!rb_obj_is_kind_of(other, mkr_cNode)) {
return Qfalse;
}
return mkr_node_id(self) == mkr_node_id(other) ? Qtrue : Qfalse;
}
|
#[](rb_name) ⇒ Object
—- attributes —-
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 336
static VALUE
mkr_xml_node_aref(VALUE self, VALUE rb_name)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
if (n->type != MKR_XML_NODE_TYPE_ELEMENT) return Qnil;
mkr_ruby_borrowed_text_t nv = mkr_ruby_verified_text(rb_name, "attribute name");
VALUE out = Qnil;
for (mkr_xml_node_t *a = n->attrs; a != NULL; a = a->next) {
if (mkr_bytes_eq(a->qname, a->qname_len, nv.ptr, nv.len)) {
out = rb_utf8_str_new(a->value ? a->value : "", (long)a->value_len);
break;
}
}
RB_GC_GUARD(nv.value);
return out;
}
|
#[]=(rb_name, rb_value) ⇒ Object
element = value -> value. Adds or replaces the attribute.
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1004
static VALUE
mkr_xml_node_aset(VALUE self, VALUE rb_name, VALUE rb_value)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap_mutable(self);
if (n->type != MKR_XML_NODE_TYPE_ELEMENT) {
rb_raise(mkr_eError, "cannot set an attribute on a non-element node");
}
mkr_ruby_borrowed_text_t nv = mkr_ruby_verified_text(rb_name, "attribute name");
mkr_ruby_borrowed_text_t vv = mkr_ruby_verified_text(rb_value, "attribute value");
mkr_xml_mut_status_t st = mkr_xml_set_attribute(
mkr_xml_node_xdoc(self), n,
nv.ptr, mkr_xml_u32_len(nv.len), vv.ptr, mkr_xml_u32_len(vv.len), NULL);
RB_GC_GUARD(nv.value);
RB_GC_GUARD(vv.value);
mkr_xml_mut_check(st);
return rb_value;
}
|
#add_child(arg) ⇒ Object
element.add_child(node) -> the inserted node.
1199 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1199
static VALUE mkr_xml_node_add_child(VALUE self, VALUE arg) { return mkr_xml_node_insert(self, arg, MKR_INS_CHILD); }
|
#add_next_sibling(arg) ⇒ Object
node.add_next_sibling(other) / node.after(other) -> the inserted node.
1203 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1203
static VALUE mkr_xml_node_after(VALUE self, VALUE arg) { return mkr_xml_node_insert(self, arg, MKR_INS_AFTER); }
|
#add_previous_sibling(arg) ⇒ Object
node.add_previous_sibling(other) / node.before(other) -> the inserted node.
1201 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1201
static VALUE mkr_xml_node_before(VALUE self, VALUE arg) { return mkr_xml_node_insert(self, arg, MKR_INS_BEFORE); }
|
#after(arg) ⇒ Object
node.add_next_sibling(other) / node.after(other) -> the inserted node.
1203 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1203
static VALUE mkr_xml_node_after(VALUE self, VALUE arg) { return mkr_xml_node_insert(self, arg, MKR_INS_AFTER); }
|
#ancestors ⇒ Object
14 15 16 17 18 19 20 21 22 |
# File 'lib/makiri/xml/node_methods.rb', line 14 def ancestors out = [] node = parent while node out << node if node.node_type == 1 node = node.respond_to?(:parent) ? node.parent : nil end out end |
#at_css(selector, ns = nil) ⇒ Object
First descendant matching selector, or nil. @return [Makiri::Node, nil]
52 53 54 |
# File 'lib/makiri/xml/node_methods.rb', line 52 def at_css(selector, ns = nil) _at_css(selector.to_s, _css_namespaces(ns)) end |
#at_xpath(*args) ⇒ Object
298 299 300 301 302 303 304 |
# File 'ext/makiri/glue/ruby_xml.c', line 298
static VALUE
mkr_xml_doc_at_xpath(int argc, VALUE *argv, VALUE self)
{
VALUE expr, ns;
rb_scan_args(argc, argv, "11", &expr, &ns);
return mkr_xml_doc_xpath_run(self, expr, ns, 1);
}
|
#attribute_nodes ⇒ Object
353 354 355 356 357 358 359 360 361 362 363 364 365 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 353
static VALUE
mkr_xml_node_attribute_nodes(VALUE self)
{
VALUE doc = mkr_xml_node_document(self);
VALUE set = mkr_node_set_new(doc);
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
if (n->type == MKR_XML_NODE_TYPE_ELEMENT) {
for (mkr_xml_node_t *a = n->attrs; a != NULL; a = a->next) {
mkr_node_set_push(set, (mkr_raw_node_t *)a);
}
}
return set;
}
|
#before(arg) ⇒ Object
node.add_previous_sibling(other) / node.before(other) -> the inserted node.
1201 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1201
static VALUE mkr_xml_node_before(VALUE self, VALUE arg) { return mkr_xml_node_insert(self, arg, MKR_INS_BEFORE); }
|
#canonicalize(comments: false) ⇒ String
Inclusive Canonical XML 1.0 (UTF-8). A Document canonicalizes its element + top-level PIs (and comments when requested); any other node canonicalizes its subtree, with the apex inheriting the ancestors’ in-scope namespaces.
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 859
static VALUE
mkr_xml_node_canonicalize(int argc, VALUE *argv, VALUE self)
{
VALUE opts;
rb_scan_args(argc, argv, "0:", &opts);
int comments = !NIL_P(opts) && RTEST(rb_hash_aref(opts, ID2SYM(rb_intern("comments"))));
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
mkr_buf_t buf;
mkr_buf_init(&buf, mkr_xml_serialize_cap(self)); /* fail closed past the cap, never OOM */
int rc = 0;
if (rb_obj_is_kind_of(self, mkr_cXmlDocument)) {
/* §2.4: a PI/comment before the document element is followed by #xA, one
* after is preceded by #xA; the element itself has no surrounding line. */
int seen_root = 0;
for (mkr_xml_node_t *c = n->first_child; rc == 0 && c != NULL; c = c->next) {
if (c->type == MKR_XML_NODE_TYPE_ELEMENT) {
rc = mkr_c14n_node(&buf, c, 1, comments); /* the root element is the apex */
seen_root = 1;
} else if (c->type == MKR_XML_NODE_TYPE_PI ||
(c->type == MKR_XML_NODE_TYPE_COMMENT && comments)) {
if (seen_root && rc == 0) rc = (mkr_buf_append(&buf, "\n", 1) == MKR_OK) ? 0 : -1;
if (rc == 0) rc = mkr_c14n_node(&buf, c, 0, comments);
if (!seen_root && rc == 0) rc = (mkr_buf_append(&buf, "\n", 1) == MKR_OK) ? 0 : -1;
}
}
} else {
rc = mkr_c14n_node(&buf, n, 1, comments); /* the node is the apex (inherits ancestors' ns) */
}
if (rc != 0) {
mkr_buf_free(&buf);
rb_raise(mkr_eError, "failed to canonicalize XML: output exceeded the size limit or out of memory");
}
VALUE str = rb_utf8_str_new(buf.len ? buf.data : "", (long)buf.len);
mkr_buf_free(&buf);
return str;
}
|
#child ⇒ Object
320 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 320
static VALUE mkr_xml_node_first_child(VALUE self) { return mkr_xml_wrap_rel(self, mkr_xml_node_unwrap(self)->first_child); }
|
#children ⇒ Object
323 324 325 326 327 328 329 330 331 332 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 323
static VALUE
mkr_xml_node_children(VALUE self)
{
VALUE doc = mkr_xml_node_document(self);
VALUE set = mkr_node_set_new(doc);
for (mkr_xml_node_t *c = mkr_xml_node_unwrap(self)->first_child; c != NULL; c = c->next) {
mkr_node_set_push(set, (mkr_raw_node_t *)c);
}
return set;
}
|
#clone_node(*args) ⇒ Object
clone_node(deep = false) -> a detached copy of this node in the same document (element/attribute name case, namespaces, and the CDATA node type preserved); deep copies the whole subtree. Backs Node#dup / #clone and DOM cloneNode.
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1245
static VALUE
mkr_xml_node_clone_node(int argc, VALUE *argv, VALUE self)
{
VALUE rb_deep;
rb_scan_args(argc, argv, "01", &rb_deep);
mkr_xml_node_t *out = NULL;
mkr_xml_mut_check(mkr_xml_clone_node(mkr_xml_node_xdoc(self),
mkr_xml_node_unwrap(self),
RTEST(rb_deep), &out));
return mkr_xml_wrap_rel(self, out);
}
|
#collect_namespaces ⇒ Object
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 242
static VALUE
mkr_xml_node_collect_namespaces(VALUE self)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
mkr_xml_node_t *root = n;
while (root->parent != NULL) root = root->parent; /* the DOCUMENT node */
VALUE h = rb_hash_new();
/* iterative pre-order over the whole tree (parent-pointer walk, no recursion) */
for (mkr_xml_node_t *cur = root; cur != NULL;) {
if (cur->type == MKR_XML_NODE_TYPE_ELEMENT) {
for (mkr_xml_node_t *a = cur->attrs; a != NULL; a = a->next) {
const char *p, *u; uint32_t pl, ul;
if (!mkr_xml_node_xmlns_decl(a, &p, &pl, &u, &ul)) continue;
rb_hash_aset(h, rb_utf8_str_new(a->qname, (long)a->qname_len),
rb_utf8_str_new(u, (long)ul));
}
}
if (cur->first_child != NULL) { cur = cur->first_child; continue; }
while (cur != root && cur->next == NULL) cur = cur->parent;
if (cur == root) break;
cur = cur->next;
}
return h;
}
|
#content ⇒ Object
—- content / text —-
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 275
static VALUE
mkr_xml_node_content(VALUE self)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
/* leaf data nodes return their own value verbatim. */
if (n->type == MKR_XML_NODE_TYPE_TEXT || n->type == MKR_XML_NODE_TYPE_CDATA_SECTION
|| n->type == MKR_XML_NODE_TYPE_COMMENT || n->type == MKR_XML_NODE_TYPE_ATTRIBUTE
|| n->type == MKR_XML_NODE_TYPE_PI) {
return rb_utf8_str_new(n->value ? n->value : "", (long)n->value_len);
}
/* element / document: concatenate every TEXT/CDATA descendant in document
* order. Iterative pre-order (parent-pointer) walk - no C recursion, so a
* deep tree cannot overflow the stack. */
VALUE str = rb_utf8_str_new("", 0);
mkr_xml_node_t *cur = n->first_child;
while (cur != NULL) {
if ((cur->type == MKR_XML_NODE_TYPE_TEXT || cur->type == MKR_XML_NODE_TYPE_CDATA_SECTION) && cur->value_len) {
rb_str_cat(str, cur->value, (long)cur->value_len);
}
if (cur->first_child != NULL) { cur = cur->first_child; continue; }
while (cur != NULL && cur != n && cur->next == NULL) cur = cur->parent;
if (cur == NULL || cur == n) break;
cur = cur->next;
}
return str;
}
|
#content=(rb_text) ⇒ Object
node.content = text -> text. For an element: replace its children with one text node (the string is stored verbatim and escaped on serialization). For a text/cdata/comment/PI leaf: set its data.
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1087
static VALUE
mkr_xml_node_set_content(VALUE self, VALUE rb_text)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap_mutable(self);
mkr_ruby_borrowed_text_t tv = mkr_ruby_verified_text(rb_text, "node content");
mkr_xml_mut_status_t st = mkr_xml_set_content(
mkr_xml_node_xdoc(self), n, tv.ptr, mkr_xml_u32_len(tv.len));
RB_GC_GUARD(tv.value);
mkr_xml_mut_check(st);
return rb_text;
}
|
#css(selector, ns = nil) ⇒ Makiri::NodeSet
CSS selector queries over XML, lowered to the native XPath engine (so matching is case-sensitive and namespace-aware, unlike a Lexbor HTML matcher). Nokogiri-compatible namespaces: the document’s in-scope declarations are collected automatically (a bare type selector binds to the default namespace), and an optional ns hash of => uri supplements/overrides them.
doc.css("entry") # default-namespace bound (Atom/RSS just work)
doc.css("a|entry", "a" => uri) # explicit prefix
47 48 49 |
# File 'lib/makiri/xml/node_methods.rb', line 47 def css(selector, ns = nil) _css(selector.to_s, _css_namespaces(ns)) end |
#delete(rb_name) ⇒ Object
element.delete(name) -> self. Removes the attribute if present (no-op otherwise).
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1073
static VALUE
mkr_xml_node_delete(VALUE self, VALUE rb_name)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap_mutable(self);
if (n->type != MKR_XML_NODE_TYPE_ELEMENT) return self;
mkr_ruby_borrowed_text_t nv = mkr_ruby_verified_text(rb_name, "attribute name");
mkr_xml_remove_attribute(n, nv.ptr, mkr_xml_u32_len(nv.len));
RB_GC_GUARD(nv.value);
return self;
}
|
#document ⇒ Object
367 368 369 370 371 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 367
static VALUE
mkr_xml_node_get_document(VALUE self)
{
return mkr_xml_node_document(self);
}
|
#element_children ⇒ Object
element_children -> NodeSet of the child element nodes (nodeType 1) only, in document order (the counterpart of HTML’s #element_children).
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1229
static VALUE
mkr_xml_node_element_children(VALUE self)
{
VALUE doc = mkr_xml_node_document(self);
VALUE set = mkr_node_set_new(doc);
for (mkr_xml_node_t *c = mkr_xml_node_unwrap(self)->first_child; c != NULL; c = c->next) {
if (c->type == MKR_XML_NODE_TYPE_ELEMENT) {
mkr_node_set_push(set, (mkr_raw_node_t *)c);
}
}
return set;
}
|
#eql?(other) ⇒ Boolean
Pointer identity: equal iff both wrappers resolve to the same node pointer (an HTML node is thus never equal to an XML node).
133 134 135 136 137 138 139 140 |
# File 'ext/makiri/glue/ruby_node.c', line 133
VALUE
mkr_node_equals(VALUE self, VALUE other)
{
if (!rb_obj_is_kind_of(other, mkr_cNode)) {
return Qfalse;
}
return mkr_node_id(self) == mkr_node_id(other) ? Qtrue : Qfalse;
}
|
#hash ⇒ Object
Stable hash derived from the node pointer, so a == b implies a.hash == b.hash even across separately-created wrappers. Shares the pointer value with #pointer_id.
155 156 157 158 159 |
# File 'ext/makiri/glue/ruby_node.c', line 155
VALUE
mkr_node_hash(VALUE self)
{
return mkr_node_pointer_id(self);
}
|
#inner_html(*args) ⇒ Object
—- fail-closed guard for the unsupported serialization surface —-
HTML serialization (to_html/inner_html/outer_html) would silently misbehave on XML (escaping / CDATA / void elements differ), so it is an explicit NotImplementedError rather than a wrong result. Use #to_xml. (CSS selectors, once unsupported here, are now lowered to the native XPath engine - see ruby_xml.c.)
906 907 908 909 910 911 912 913 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 906
static VALUE
mkr_xml_node_no_serialize(int argc, VALUE *argv, VALUE self)
{
(void)argc; (void)argv; (void)self;
rb_raise(rb_eNotImpError,
"Makiri::XML does not HTML-serialize (to_html / inner_html / "
"outer_html); use #to_xml for XML output.");
}
|
#inner_text ⇒ Object
—- content / text —-
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 275
static VALUE
mkr_xml_node_content(VALUE self)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
/* leaf data nodes return their own value verbatim. */
if (n->type == MKR_XML_NODE_TYPE_TEXT || n->type == MKR_XML_NODE_TYPE_CDATA_SECTION
|| n->type == MKR_XML_NODE_TYPE_COMMENT || n->type == MKR_XML_NODE_TYPE_ATTRIBUTE
|| n->type == MKR_XML_NODE_TYPE_PI) {
return rb_utf8_str_new(n->value ? n->value : "", (long)n->value_len);
}
/* element / document: concatenate every TEXT/CDATA descendant in document
* order. Iterative pre-order (parent-pointer) walk - no C recursion, so a
* deep tree cannot overflow the stack. */
VALUE str = rb_utf8_str_new("", 0);
mkr_xml_node_t *cur = n->first_child;
while (cur != NULL) {
if ((cur->type == MKR_XML_NODE_TYPE_TEXT || cur->type == MKR_XML_NODE_TYPE_CDATA_SECTION) && cur->value_len) {
rb_str_cat(str, cur->value, (long)cur->value_len);
}
if (cur->first_child != NULL) { cur = cur->first_child; continue; }
while (cur != NULL && cur != n && cur->next == NULL) cur = cur->parent;
if (cur == NULL || cur == n) break;
cur = cur->next;
}
return str;
}
|
#key?(name) ⇒ Boolean Also known as: has_attribute?
28 29 30 31 |
# File 'lib/makiri/xml/node_methods.rb', line 28 def key?(name) wanted = name.to_s attribute_nodes.any? { |attr| attr.name == wanted } end |
#last_element_child ⇒ Object
321 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 321
static VALUE mkr_xml_node_last_child(VALUE self) { return mkr_xml_wrap_rel(self, mkr_xml_node_unwrap(self)->last_child); }
|
#local_name ⇒ Object
122 123 124 125 126 127 128 129 130 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 122
static VALUE
mkr_xml_node_local_name(VALUE self)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
if (n->type == MKR_XML_NODE_TYPE_ELEMENT || n->type == MKR_XML_NODE_TYPE_ATTRIBUTE) {
return rb_utf8_str_new(n->local, (long)n->local_len);
}
return Qnil;
}
|
#matches?(selector, ns = nil) ⇒ Boolean
Whether this node matches selector (full selector, combinators included).
58 59 60 |
# File 'lib/makiri/xml/node_methods.rb', line 58 def matches?(selector, ns = nil) _css_matches(selector.to_s, _css_namespaces(ns)) end |
#name ⇒ Object
—- name / namespace —-
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 83
static VALUE
mkr_xml_node_name(VALUE self)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
switch (n->type) {
case MKR_XML_NODE_TYPE_ELEMENT:
case MKR_XML_NODE_TYPE_ATTRIBUTE: return rb_utf8_str_new(n->qname, (long)n->qname_len);
case MKR_XML_NODE_TYPE_PI: return rb_utf8_str_new(n->local, (long)n->local_len); /* target */
case MKR_XML_NODE_TYPE_TEXT: return rb_utf8_str_new_cstr("text");
case MKR_XML_NODE_TYPE_CDATA_SECTION: return rb_utf8_str_new_cstr("#cdata-section");
case MKR_XML_NODE_TYPE_COMMENT: return rb_utf8_str_new_cstr("comment");
case MKR_XML_NODE_TYPE_DOCUMENT_TYPE: return rb_utf8_str_new(n->local, (long)n->local_len); /* the DOCTYPE name */
case MKR_XML_NODE_TYPE_DOCUMENT_FRAGMENT: return rb_utf8_str_new_cstr("#document-fragment");
default: return rb_utf8_str_new_cstr("document");
}
}
|
#name=(rb_name) ⇒ Object
node.name = new_name -> new_name. Renames an element or attribute in place (identity + tree position preserved); the namespace is re-resolved against the node’s in-scope declarations.
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1102
static VALUE
mkr_xml_node_set_name(VALUE self, VALUE rb_name)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap_mutable(self);
mkr_ruby_borrowed_text_t nv = mkr_ruby_verified_text(rb_name, "node name");
mkr_xml_mut_status_t st = mkr_xml_rename(
mkr_xml_node_xdoc(self), n, nv.ptr, mkr_xml_u32_len(nv.len));
RB_GC_GUARD(nv.value);
mkr_xml_mut_check(st);
return rb_name;
}
|
#namespace ⇒ Object
The xmlns-declaration detector lives in the node layer (mkr_xml_node_xmlns_decl) so the namespace introspection, the C14N walk, and the mutation namespace resolver all share one definition.
194 195 196 197 198 199 200 201 202 203 204 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 194
static VALUE
mkr_xml_node_namespace(VALUE self)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
if ((n->type == MKR_XML_NODE_TYPE_ELEMENT || n->type == MKR_XML_NODE_TYPE_ATTRIBUTE)
&& n->ns_uri_len > 0) {
VALUE prefix = n->prefix_len ? rb_utf8_str_new(n->prefix, (long)n->prefix_len) : Qnil;
return mkr_ns_new(prefix, rb_utf8_str_new(n->ns_uri, (long)n->ns_uri_len));
}
return Qnil;
}
|
#namespace_definitions ⇒ Object
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 206
static VALUE
mkr_xml_node_namespace_definitions(VALUE self)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
VALUE arr = rb_ary_new();
if (n->type == MKR_XML_NODE_TYPE_ELEMENT) {
for (mkr_xml_node_t *a = n->attrs; a != NULL; a = a->next) {
const char *p, *u; uint32_t pl, ul;
if (mkr_xml_node_xmlns_decl(a, &p, &pl, &u, &ul)) {
VALUE prefix = pl ? rb_utf8_str_new(p, (long)pl) : Qnil;
rb_ary_push(arr, mkr_ns_new(prefix, rb_utf8_str_new(u, (long)ul)));
}
}
}
return arr;
}
|
#namespace_uri ⇒ Object
139 140 141 142 143 144 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 139
static VALUE
mkr_xml_node_namespace_uri(VALUE self)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
return n->ns_uri_len ? rb_utf8_str_new(n->ns_uri, (long)n->ns_uri_len) : Qnil;
}
|
#namespaces ⇒ Object
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 223
static VALUE
mkr_xml_node_namespaces(VALUE self)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
VALUE h = rb_hash_new();
for (mkr_xml_node_t *e = n; e != NULL; e = e->parent) { /* inner scope wins */
if (e->type != MKR_XML_NODE_TYPE_ELEMENT) continue;
for (mkr_xml_node_t *a = e->attrs; a != NULL; a = a->next) {
const char *p, *u; uint32_t pl, ul;
if (!mkr_xml_node_xmlns_decl(a, &p, &pl, &u, &ul)) continue;
VALUE key = rb_utf8_str_new(a->qname, (long)a->qname_len); /* "xmlns" / "xmlns:p" */
if (rb_hash_lookup2(h, key, Qundef) == Qundef) {
rb_hash_aset(h, key, rb_utf8_str_new(u, (long)ul));
}
}
}
return h;
}
|
#next ⇒ Object
318 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 318
static VALUE mkr_xml_node_next(VALUE self) { return mkr_xml_wrap_rel(self, mkr_xml_node_unwrap(self)->next); }
|
#next_sibling ⇒ Object
318 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 318
static VALUE mkr_xml_node_next(VALUE self) { return mkr_xml_wrap_rel(self, mkr_xml_node_unwrap(self)->next); }
|
#node_type ⇒ Object
267 268 269 270 271 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 267
static VALUE
mkr_xml_node_node_type(VALUE self)
{
return INT2NUM((int)mkr_xml_node_unwrap(self)->type);
}
|
#outer_html(*args) ⇒ Object
—- fail-closed guard for the unsupported serialization surface —-
HTML serialization (to_html/inner_html/outer_html) would silently misbehave on XML (escaping / CDATA / void elements differ), so it is an explicit NotImplementedError rather than a wrong result. Use #to_xml. (CSS selectors, once unsupported here, are now lowered to the native XPath engine - see ruby_xml.c.)
906 907 908 909 910 911 912 913 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 906
static VALUE
mkr_xml_node_no_serialize(int argc, VALUE *argv, VALUE self)
{
(void)argc; (void)argv; (void)self;
rb_raise(rb_eNotImpError,
"Makiri::XML does not HTML-serialize (to_html / inner_html / "
"outer_html); use #to_xml for XML output.");
}
|
#parent ⇒ Object
317 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 317
static VALUE mkr_xml_node_parent(VALUE self) { return mkr_xml_wrap_rel(self, mkr_xml_node_unwrap(self)->parent); }
|
#pointer_id ⇒ Object
Nokogiri-compatible identity: the underlying node pointer as an Integer. Stable for the node’s lifetime and unique among currently-live nodes; a freed-then-reallocated node may reuse an address (same caveat as Nokogiri::XML::Node#pointer_id). a.pointer_id == b.pointer_id iff a.eql?(b).
146 147 148 149 150 |
# File 'ext/makiri/glue/ruby_node.c', line 146
VALUE
mkr_node_pointer_id(VALUE self)
{
return ULL2NUM((unsigned long long)mkr_node_id(self));
}
|
#prefix ⇒ Object
132 133 134 135 136 137 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 132
static VALUE
mkr_xml_node_prefix(VALUE self)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
return n->prefix_len ? rb_utf8_str_new(n->prefix, (long)n->prefix_len) : Qnil;
}
|
#previous ⇒ Object
319 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 319
static VALUE mkr_xml_node_previous(VALUE self) { return mkr_xml_wrap_rel(self, mkr_xml_node_unwrap(self)->prev); }
|
#previous_sibling ⇒ Object
319 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 319
static VALUE mkr_xml_node_previous(VALUE self) { return mkr_xml_wrap_rel(self, mkr_xml_node_unwrap(self)->prev); }
|
#remove ⇒ Object
node.remove / node.unlink -> node. Detach from the tree (or, for an attribute, from its owner element); the node stays usable.
990 991 992 993 994 995 996 997 998 999 1000 1001 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 990
static VALUE
mkr_xml_node_remove(VALUE self)
{
if (rb_obj_is_kind_of(self, mkr_cXmlDocument)) {
rb_raise(mkr_eError, "cannot remove the document node");
}
mkr_xml_node_t *n = mkr_xml_node_unwrap_mutable(self);
mkr_xml_doc_t *xdoc = mkr_xml_node_xdoc(self);
if (xdoc != NULL && n == xdoc->root) xdoc->root = NULL; /* detaching the root element */
mkr_xml_detach(n);
return self;
}
|
#remove_attribute(rb_name) ⇒ Object
element.delete(name) -> self. Removes the attribute if present (no-op otherwise).
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1073
static VALUE
mkr_xml_node_delete(VALUE self, VALUE rb_name)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap_mutable(self);
if (n->type != MKR_XML_NODE_TYPE_ELEMENT) return self;
mkr_ruby_borrowed_text_t nv = mkr_ruby_verified_text(rb_name, "attribute name");
mkr_xml_remove_attribute(n, nv.ptr, mkr_xml_u32_len(nv.len));
RB_GC_GUARD(nv.value);
return self;
}
|
#remove_attribute_ns(rb_ns, rb_local) ⇒ Object
element.remove_attribute_ns(namespace_or_nil, local_name) -> self.
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1054
static VALUE
mkr_xml_node_remove_attribute_ns(VALUE self, VALUE rb_ns, VALUE rb_local)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap_mutable(self);
if (n->type != MKR_XML_NODE_TYPE_ELEMENT) return self;
mkr_ruby_borrowed_text_t lv = mkr_ruby_verified_text(rb_local, "attribute local name");
mkr_ruby_borrowed_text_t nv = {0};
const char *ns_ptr = NULL; uint32_t ns_len = 0;
if (!NIL_P(rb_ns)) {
nv = mkr_ruby_verified_text(rb_ns, "namespace");
ns_ptr = nv.ptr; ns_len = mkr_xml_u32_len(nv.len);
}
mkr_xml_remove_attribute_ns(n, ns_ptr, ns_len, lv.ptr, mkr_xml_u32_len(lv.len));
RB_GC_GUARD(lv.value);
RB_GC_GUARD(nv.value);
return self;
}
|
#replace(arg) ⇒ Object
node.replace(other) -> the inserted node (the replaced node is detached).
1205 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1205
static VALUE mkr_xml_node_replace(VALUE self, VALUE arg) { return mkr_xml_node_insert(self, arg, MKR_INS_REPLACE); }
|
#set_attribute_ns(rb_ns, rb_qname, rb_value) ⇒ Object
element.set_attribute_ns(namespace_or_nil, qualified_name, value) -> value.
Stores the attribute keyed on (explicit namespace, local name) - the DOM key - with its qualified name case-preserved. A null/“” namespace is the null namespace. xmlns declarations pass through as ordinary attributes in the xmlns namespace.
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 1028
static VALUE
mkr_xml_node_set_attribute_ns(VALUE self, VALUE rb_ns, VALUE rb_qname, VALUE rb_value)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap_mutable(self);
if (n->type != MKR_XML_NODE_TYPE_ELEMENT) {
rb_raise(mkr_eError, "cannot set an attribute on a non-element node");
}
mkr_ruby_borrowed_text_t qv = mkr_ruby_verified_text(rb_qname, "attribute qualified name");
mkr_ruby_borrowed_text_t vv = mkr_ruby_verified_text(rb_value, "attribute value");
mkr_ruby_borrowed_text_t nv = {0};
const char *ns_ptr = NULL; uint32_t ns_len = 0;
if (!NIL_P(rb_ns)) {
nv = mkr_ruby_verified_text(rb_ns, "namespace");
ns_ptr = nv.ptr; ns_len = mkr_xml_u32_len(nv.len);
}
mkr_xml_mut_status_t st = mkr_xml_set_attribute_ns(
mkr_xml_node_xdoc(self), n, ns_ptr, ns_len,
qv.ptr, mkr_xml_u32_len(qv.len), vv.ptr, mkr_xml_u32_len(vv.len), NULL);
RB_GC_GUARD(qv.value);
RB_GC_GUARD(vv.value);
RB_GC_GUARD(nv.value);
mkr_xml_mut_check(st);
return rb_value;
}
|
#text ⇒ Object
—- content / text —-
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 275
static VALUE
mkr_xml_node_content(VALUE self)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
/* leaf data nodes return their own value verbatim. */
if (n->type == MKR_XML_NODE_TYPE_TEXT || n->type == MKR_XML_NODE_TYPE_CDATA_SECTION
|| n->type == MKR_XML_NODE_TYPE_COMMENT || n->type == MKR_XML_NODE_TYPE_ATTRIBUTE
|| n->type == MKR_XML_NODE_TYPE_PI) {
return rb_utf8_str_new(n->value ? n->value : "", (long)n->value_len);
}
/* element / document: concatenate every TEXT/CDATA descendant in document
* order. Iterative pre-order (parent-pointer) walk - no C recursion, so a
* deep tree cannot overflow the stack. */
VALUE str = rb_utf8_str_new("", 0);
mkr_xml_node_t *cur = n->first_child;
while (cur != NULL) {
if ((cur->type == MKR_XML_NODE_TYPE_TEXT || cur->type == MKR_XML_NODE_TYPE_CDATA_SECTION) && cur->value_len) {
rb_str_cat(str, cur->value, (long)cur->value_len);
}
if (cur->first_child != NULL) { cur = cur->first_child; continue; }
while (cur != NULL && cur != n && cur->next == NULL) cur = cur->parent;
if (cur == NULL || cur == n) break;
cur = cur->next;
}
return str;
}
|
#to_html(*args) ⇒ Object
—- fail-closed guard for the unsupported serialization surface —-
HTML serialization (to_html/inner_html/outer_html) would silently misbehave on XML (escaping / CDATA / void elements differ), so it is an explicit NotImplementedError rather than a wrong result. Use #to_xml. (CSS selectors, once unsupported here, are now lowered to the native XPath engine - see ruby_xml.c.)
906 907 908 909 910 911 912 913 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 906
static VALUE
mkr_xml_node_no_serialize(int argc, VALUE *argv, VALUE self)
{
(void)argc; (void)argv; (void)self;
rb_raise(rb_eNotImpError,
"Makiri::XML does not HTML-serialize (to_html / inner_html / "
"outer_html); use #to_xml for XML output.");
}
|
#to_xml(pretty: false, indent: 2, encoding: "UTF-8") ⇒ String
A Document also emits the XML declaration and its DOCTYPE; any other node serializes just its own subtree. encoding (a String or Encoding) transcodes the output - a character the target cannot represent becomes a hexadecimal character reference - and is named in a Document’s declaration.
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 547
static VALUE
mkr_xml_node_to_xml(int argc, VALUE *argv, VALUE self)
{
VALUE opts;
rb_scan_args(argc, argv, "0:", &opts);
int width = 0;
VALUE enc_opt = Qnil;
if (!NIL_P(opts)) {
if (RTEST(rb_hash_aref(opts, ID2SYM(rb_intern("pretty"))))) width = 2;
VALUE iv = rb_hash_aref(opts, ID2SYM(rb_intern("indent")));
if (!NIL_P(iv)) width = NUM2INT(iv) < 0 ? 0 : NUM2INT(iv);
enc_opt = rb_hash_aref(opts, ID2SYM(rb_intern("encoding")));
}
/* resolve the target encoding (raises on an unknown name) + its declared name */
rb_encoding *to_enc = NIL_P(enc_opt) ? NULL : rb_to_encoding(enc_opt);
VALUE enc_name = NIL_P(enc_opt) ? Qnil : rb_obj_as_string(enc_opt);
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
mkr_buf_t buf;
mkr_buf_init(&buf, mkr_xml_serialize_cap(self)); /* fail closed past the cap, never OOM */
int rc = 0;
if (rb_obj_is_kind_of(self, mkr_cXmlDocument)) {
mkr_xml_doc_t *xdoc = mkr_parsed_xml_doc(mkr_doc_parsed(self));
/* Emit the encoding pseudo-attribute only when an explicit encoding: was
* requested or the parsed source declared one; otherwise a built or
* declaration-less document round-trips to a bare `<?xml version="1.0"?>`,
* matching Nokogiri (the output is UTF-8 either way). */
int emit_enc = !NIL_P(enc_name) || (xdoc != NULL && xdoc->has_encoding_decl);
if (emit_enc) {
static const char decl_a[] = "<?xml version=\"1.0\" encoding=\"";
static const char decl_b[] = "\"?>\n";
VALUE name = NIL_P(enc_name) ? rb_utf8_str_new_cstr("UTF-8") : enc_name;
mkr_ruby_borrowed_bytes_t nv = mkr_ruby_bytes_view(name);
rc = (mkr_buf_append(&buf, decl_a, sizeof(decl_a) - 1) == MKR_OK) ? 0 : -1;
if (rc == 0) rc = (mkr_buf_append(&buf, nv.ptr, nv.len) == MKR_OK) ? 0 : -1;
if (rc == 0) rc = (mkr_buf_append(&buf, decl_b, sizeof(decl_b) - 1) == MKR_OK) ? 0 : -1;
RB_GC_GUARD(nv.value);
} else {
static const char decl[] = "<?xml version=\"1.0\"?>\n";
rc = (mkr_buf_append(&buf, decl, sizeof(decl) - 1) == MKR_OK) ? 0 : -1;
}
if (rc == 0 && xdoc != NULL && xdoc->doctype != NULL) {
rc = mkr_xser_doctype(&buf, xdoc->doctype);
if (rc == 0) rc = (mkr_buf_append(&buf, "\n", 1) == MKR_OK) ? 0 : -1;
}
for (mkr_xml_node_t *c = n->first_child; rc == 0 && c != NULL; c = c->next) {
rc = mkr_xser_node(&buf, c, 0, width);
if (rc == 0) rc = (mkr_buf_append(&buf, "\n", 1) == MKR_OK) ? 0 : -1;
}
} else {
rc = mkr_xser_node(&buf, n, 0, width);
}
if (rc != 0) {
mkr_buf_free(&buf);
rb_raise(mkr_eError, "failed to serialize XML: output exceeded the size limit or out of memory");
}
VALUE str = rb_utf8_str_new(buf.len ? buf.data : "", (long)buf.len);
mkr_buf_free(&buf);
/* Transcode to the requested encoding; an unrepresentable character becomes a
* &#xNN; reference (ECONV_UNDEF_HEX_CHARREF) rather than raising or dropping. */
if (to_enc != NULL && to_enc != rb_utf8_encoding() && to_enc != rb_usascii_encoding()) {
str = rb_str_encode(str, rb_enc_from_encoding(to_enc), ECONV_UNDEF_HEX_CHARREF, Qnil);
}
return str;
}
|
#to_xml(pretty: false, indent: 2, encoding: "UTF-8") ⇒ String
A Document also emits the XML declaration and its DOCTYPE; any other node serializes just its own subtree. encoding (a String or Encoding) transcodes the output - a character the target cannot represent becomes a hexadecimal character reference - and is named in a Document’s declaration.
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 547
static VALUE
mkr_xml_node_to_xml(int argc, VALUE *argv, VALUE self)
{
VALUE opts;
rb_scan_args(argc, argv, "0:", &opts);
int width = 0;
VALUE enc_opt = Qnil;
if (!NIL_P(opts)) {
if (RTEST(rb_hash_aref(opts, ID2SYM(rb_intern("pretty"))))) width = 2;
VALUE iv = rb_hash_aref(opts, ID2SYM(rb_intern("indent")));
if (!NIL_P(iv)) width = NUM2INT(iv) < 0 ? 0 : NUM2INT(iv);
enc_opt = rb_hash_aref(opts, ID2SYM(rb_intern("encoding")));
}
/* resolve the target encoding (raises on an unknown name) + its declared name */
rb_encoding *to_enc = NIL_P(enc_opt) ? NULL : rb_to_encoding(enc_opt);
VALUE enc_name = NIL_P(enc_opt) ? Qnil : rb_obj_as_string(enc_opt);
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
mkr_buf_t buf;
mkr_buf_init(&buf, mkr_xml_serialize_cap(self)); /* fail closed past the cap, never OOM */
int rc = 0;
if (rb_obj_is_kind_of(self, mkr_cXmlDocument)) {
mkr_xml_doc_t *xdoc = mkr_parsed_xml_doc(mkr_doc_parsed(self));
/* Emit the encoding pseudo-attribute only when an explicit encoding: was
* requested or the parsed source declared one; otherwise a built or
* declaration-less document round-trips to a bare `<?xml version="1.0"?>`,
* matching Nokogiri (the output is UTF-8 either way). */
int emit_enc = !NIL_P(enc_name) || (xdoc != NULL && xdoc->has_encoding_decl);
if (emit_enc) {
static const char decl_a[] = "<?xml version=\"1.0\" encoding=\"";
static const char decl_b[] = "\"?>\n";
VALUE name = NIL_P(enc_name) ? rb_utf8_str_new_cstr("UTF-8") : enc_name;
mkr_ruby_borrowed_bytes_t nv = mkr_ruby_bytes_view(name);
rc = (mkr_buf_append(&buf, decl_a, sizeof(decl_a) - 1) == MKR_OK) ? 0 : -1;
if (rc == 0) rc = (mkr_buf_append(&buf, nv.ptr, nv.len) == MKR_OK) ? 0 : -1;
if (rc == 0) rc = (mkr_buf_append(&buf, decl_b, sizeof(decl_b) - 1) == MKR_OK) ? 0 : -1;
RB_GC_GUARD(nv.value);
} else {
static const char decl[] = "<?xml version=\"1.0\"?>\n";
rc = (mkr_buf_append(&buf, decl, sizeof(decl) - 1) == MKR_OK) ? 0 : -1;
}
if (rc == 0 && xdoc != NULL && xdoc->doctype != NULL) {
rc = mkr_xser_doctype(&buf, xdoc->doctype);
if (rc == 0) rc = (mkr_buf_append(&buf, "\n", 1) == MKR_OK) ? 0 : -1;
}
for (mkr_xml_node_t *c = n->first_child; rc == 0 && c != NULL; c = c->next) {
rc = mkr_xser_node(&buf, c, 0, width);
if (rc == 0) rc = (mkr_buf_append(&buf, "\n", 1) == MKR_OK) ? 0 : -1;
}
} else {
rc = mkr_xser_node(&buf, n, 0, width);
}
if (rc != 0) {
mkr_buf_free(&buf);
rb_raise(mkr_eError, "failed to serialize XML: output exceeded the size limit or out of memory");
}
VALUE str = rb_utf8_str_new(buf.len ? buf.data : "", (long)buf.len);
mkr_buf_free(&buf);
/* Transcode to the requested encoding; an unrepresentable character becomes a
* &#xNN; reference (ECONV_UNDEF_HEX_CHARREF) rather than raising or dropping. */
if (to_enc != NULL && to_enc != rb_utf8_encoding() && to_enc != rb_usascii_encoding()) {
str = rb_str_encode(str, rb_enc_from_encoding(to_enc), ECONV_UNDEF_HEX_CHARREF, Qnil);
}
return str;
}
|
#unlink ⇒ Object
node.remove / node.unlink -> node. Detach from the tree (or, for an attribute, from its owner element); the node stays usable.
990 991 992 993 994 995 996 997 998 999 1000 1001 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 990
static VALUE
mkr_xml_node_remove(VALUE self)
{
if (rb_obj_is_kind_of(self, mkr_cXmlDocument)) {
rb_raise(mkr_eError, "cannot remove the document node");
}
mkr_xml_node_t *n = mkr_xml_node_unwrap_mutable(self);
mkr_xml_doc_t *xdoc = mkr_xml_node_xdoc(self);
if (xdoc != NULL && n == xdoc->root) xdoc->root = NULL; /* detaching the root element */
mkr_xml_detach(n);
return self;
}
|
#value ⇒ Object
302 303 304 305 306 307 |
# File 'ext/makiri/glue/ruby_xml_node.c', line 302
static VALUE
mkr_xml_node_value(VALUE self)
{
mkr_xml_node_t *n = mkr_xml_node_unwrap(self);
return rb_utf8_str_new(n->value ? n->value : "", (long)n->value_len);
}
|
#xpath(*args) ⇒ Object
290 291 292 293 294 295 296 |
# File 'ext/makiri/glue/ruby_xml.c', line 290
static VALUE
mkr_xml_doc_xpath(int argc, VALUE *argv, VALUE self)
{
VALUE expr, ns;
rb_scan_args(argc, argv, "11", &expr, &ns);
return mkr_xml_doc_xpath_run(self, expr, ns, 0);
}
|