class Ox::Builder

An XML builder.

Public Class Methods

file(filename, options) click to toggle source

Creates a new Builder that will write to a file.

  • filename (String) filename to write to

  • options - (Hash) formating options

    • :indent (Fixnum) indentaion level, negative values excludes terminating newline

    • :size (Fixnum) the initial size of the string buffer

static VALUE
builder_file(int argc, VALUE *argv, VALUE self) {
    Builder     b = ALLOC(struct _builder);
    int         indent = ox_default_options.indent;
    long        buf_size = 0;
    FILE        *f;

    if (1 > argc) {
        rb_raise(ox_arg_error_class, "missing filename");
    }
    Check_Type(*argv, T_STRING);
    if (NULL == (f = fopen(StringValuePtr(*argv), "w"))) {
        xfree(b);
        rb_raise(rb_eIOError, "%s\n", strerror(errno));
    }
    if (2 == argc) {
        volatile VALUE v;

        rb_check_type(argv[1], T_HASH);
        if (Qnil != (v = rb_hash_lookup(argv[1], ox_indent_sym))) {
#ifdef RUBY_INTEGER_UNIFICATION
            if (rb_cInteger != rb_obj_class(v)) {
#else
            if (rb_cFixnum != rb_obj_class(v)) {
#endif
                rb_raise(ox_parse_error_class, ":indent must be a fixnum.\n");
            }
            indent = NUM2INT(v);
        }
        if (Qnil != (v = rb_hash_lookup(argv[1], ox_size_sym))) {
#ifdef RUBY_INTEGER_UNIFICATION
            if (rb_cInteger != rb_obj_class(v)) {
#else
            if (rb_cFixnum != rb_obj_class(v)) {
#endif
                rb_raise(ox_parse_error_class, ":size must be a fixnum.\n");
            }
            buf_size = NUM2LONG(v);
        }
    }
    b->file = f;
    init(b, fileno(f), indent, buf_size);

    if (rb_block_given_p()) {
        volatile VALUE rb = Data_Wrap_Struct(builder_class, NULL, builder_free, b);
        rb_yield(rb);
        bclose(b);
        return Qnil;
    } else {
        return Data_Wrap_Struct(builder_class, NULL, builder_free, b);
    }
}
io(io, options) click to toggle source

Creates a new Builder that will write to an IO instance.

  • io (String) IO to write to

  • options - (Hash) formating options

    • :indent (Fixnum) indentaion level, negative values excludes terminating newline

    • :size (Fixnum) the initial size of the string buffer

static VALUE
builder_io(int argc, VALUE *argv, VALUE self) {
    Builder             b = ALLOC(struct _builder);
    int                 indent = ox_default_options.indent;
    long                buf_size = 0;
    int                 fd;
    volatile VALUE      v;

    if (1 > argc) {
        rb_raise(ox_arg_error_class, "missing IO object");
    }
    if (!rb_respond_to(*argv, ox_fileno_id) ||
        Qnil == (v = rb_funcall(*argv, ox_fileno_id, 0)) ||
        0 == (fd = FIX2INT(v))) {
        rb_raise(rb_eIOError, "expected an IO that has a fileno.");
    }
    if (2 == argc) {
        volatile VALUE v;

        rb_check_type(argv[1], T_HASH);
        if (Qnil != (v = rb_hash_lookup(argv[1], ox_indent_sym))) {
#ifdef RUBY_INTEGER_UNIFICATION
            if (rb_cInteger != rb_obj_class(v)) {
#else
            if (rb_cFixnum != rb_obj_class(v)) {
#endif
                rb_raise(ox_parse_error_class, ":indent must be a fixnum.\n");
            }
            indent = NUM2INT(v);
        }
        if (Qnil != (v = rb_hash_lookup(argv[1], ox_size_sym))) {
#ifdef RUBY_INTEGER_UNIFICATION
            if (rb_cInteger != rb_obj_class(v)) {
#else
            if (rb_cFixnum != rb_obj_class(v)) {
#endif
                rb_raise(ox_parse_error_class, ":size must be a fixnum.\n");
            }
            buf_size = NUM2LONG(v);
        }
    }
    b->file = NULL;
    init(b, fd, indent, buf_size);

    if (rb_block_given_p()) {
        volatile VALUE rb = Data_Wrap_Struct(builder_class, NULL, builder_free, b);
        rb_yield(rb);
        bclose(b);
        return Qnil;
    } else {
        return Data_Wrap_Struct(builder_class, NULL, builder_free, b);
    }
}
new(options) click to toggle source

Creates a new Builder that will write to a string that can be retrieved with the to_s() method. If a block is given it is executed with a single parameter which is the builder instance. The return value is then the generated string.

  • options - (Hash) formating options

    • :indent (Fixnum) indentaion level, negative values excludes terminating newline

    • :size (Fixnum) the initial size of the string buffer

static VALUE
builder_new(int argc, VALUE *argv, VALUE self) {
    Builder     b = ALLOC(struct _builder);
    int         indent = ox_default_options.indent;
    long        buf_size = 0;

    if (1 == argc) {
        volatile VALUE v;

        rb_check_type(*argv, T_HASH);
        if (Qnil != (v = rb_hash_lookup(*argv, ox_indent_sym))) {
#ifdef RUBY_INTEGER_UNIFICATION
            if (rb_cInteger != rb_obj_class(v)) {
#else
            if (rb_cFixnum != rb_obj_class(v)) {
#endif
                rb_raise(ox_parse_error_class, ":indent must be a fixnum.\n");
            }
            indent = NUM2INT(v);
        }
        if (Qnil != (v = rb_hash_lookup(*argv, ox_size_sym))) {
#ifdef RUBY_INTEGER_UNIFICATION
            if (rb_cInteger != rb_obj_class(v)) {
#else
            if (rb_cFixnum != rb_obj_class(v)) {
#endif
                rb_raise(ox_parse_error_class, ":size must be a fixnum.\n");
            }
            buf_size = NUM2LONG(v);
        }
    }
    b->file = NULL;
    init(b, 0, indent, buf_size);

    if (rb_block_given_p()) {
        volatile VALUE rb = Data_Wrap_Struct(builder_class, NULL, builder_free, b);

        rb_yield(rb);
        bclose(b);

        return to_s(b);
    } else {
        return Data_Wrap_Struct(builder_class, NULL, builder_free, b);
    }
}

Public Instance Methods

cdata(data) click to toggle source

Adds a CDATA element to the XML string being formed.

  • data - (String) contents of the CDATA element

static VALUE
builder_cdata(VALUE self, VALUE data) {
    Builder             b = (Builder)DATA_PTR(self);
    volatile VALUE      v = data;
    const char          *str;
    const char          *s;
    const char          *end;
    int                 len;

    if (T_STRING != rb_type(v)) {
        v = rb_funcall(v, ox_to_s_id, 0);
    }
    str = StringValuePtr(v);
    len = (int)RSTRING_LEN(v);
    s = str;
    end = str + len;
    i_am_a_child(b, false);
    append_indent(b);
    buf_append_string(&b->buf, "<![CDATA[", 9);
    b->col += 9;
    b->pos += 9;
    buf_append_string(&b->buf, str, len);
    b->col += len;
    s = strchr(s, '\n');
    while (NULL != s) {
        b->line++;
        b->col = end - s;
        s = strchr(s + 1, '\n');
    }
    b->pos += len;
    buf_append_string(&b->buf, "]]>", 3);
    b->col += 3;
    b->pos += 3;

    return Qnil;
}
close() click to toggle source

Closes the all elements and the document.

static VALUE
builder_close(VALUE self) {
    bclose((Builder)DATA_PTR(self));

    return Qnil;
}
column() click to toggle source

Returns the current column in the output. The first character in a line is at column 1.

static VALUE
builder_column(VALUE self) {
    return LONG2NUM(((Builder)DATA_PTR(self))->col);
}
comment(text) click to toggle source

Adds a comment element to the XML string being formed.

  • text - (String) contents of the comment

static VALUE
builder_comment(VALUE self, VALUE text) {
    Builder     b = (Builder)DATA_PTR(self);

    rb_check_type(text, T_STRING);
    i_am_a_child(b, false);
    append_indent(b);
    buf_append_string(&b->buf, "<!--", 4);
    b->col += 5;
    b->pos += 5;
    append_string(b, StringValuePtr(text), RSTRING_LEN(text), xml_element_chars, false);
    buf_append_string(&b->buf, "-->", 3);
    b->col += 5;
    b->pos += 5;

    return Qnil;
}
doctype(text) click to toggle source

Adds a DOCTYPE element to the XML string being formed.

  • text - (String) contents of the doctype

static VALUE
builder_doctype(VALUE self, VALUE text) {
    Builder     b = (Builder)DATA_PTR(self);

    rb_check_type(text, T_STRING);
    i_am_a_child(b, false);
    append_indent(b);
    buf_append_string(&b->buf, "<!DOCTYPE ", 10);
    b->col += 10;
    b->pos += 10;
    append_string(b, StringValuePtr(text), RSTRING_LEN(text), xml_element_chars, false);
    buf_append(&b->buf, '>');
    b->col++;
    b->pos++;

    return Qnil;
}
element(name,attributes) click to toggle source

Adds an element with the name and attributes provided. If a block is given then on closing of the block a pop() is called.

  • name - (String) name of the element

  • attributes - (Hash) of the element

static VALUE
builder_element(int argc, VALUE *argv, VALUE self) {
    Builder             b = (Builder)DATA_PTR(self);
    Element             e;
    const char          *name;
    long                len;

    if (1 > argc) {
        rb_raise(ox_arg_error_class, "missing element name");
    }
    i_am_a_child(b, false);
    append_indent(b);
    b->depth++;
    if (MAX_DEPTH <= b->depth) {
        rb_raise(ox_arg_error_class, "XML too deeply nested");
    }
    switch (rb_type(*argv)) {
    case T_STRING:
        name = StringValuePtr(*argv);
        len = RSTRING_LEN(*argv);
        break;
    case T_SYMBOL:
        name = rb_id2name(SYM2ID(*argv));
        len = strlen(name);
        break;
    default:
        rb_raise(ox_arg_error_class, "expected a Symbol or String for an element name");
        break;
    }
    e = &b->stack[b->depth];
    if (sizeof(e->buf) <= (size_t)len) {
        e->name = strdup(name);
        *e->buf = '\0';
    } else {
        strcpy(e->buf, name);
        e->name = e->buf;
    }
    e->len = len;
    e->has_child = false;
    e->non_text_child = false;

    buf_append(&b->buf, '<');
    b->col++;
    b->pos++;
    append_string(b, e->name, len, xml_element_chars, false);
    if (1 < argc && T_HASH == rb_type(argv[1])) {
        rb_hash_foreach(argv[1], append_attr, (VALUE)b);
    }
    // Do not close with > or /> yet. That is done with i_am_a_child() or pop().
    if (rb_block_given_p()) {
        rb_yield(self);
        pop(b);
    }
    return Qnil;
}
instruct(decl,options) click to toggle source

Adds the top level <?xml?> element.

  • decl - (String) 'xml' expected

  • options - (Hash) version or encoding

static VALUE
builder_instruct(int argc, VALUE *argv, VALUE self) {
    Builder     b = (Builder)DATA_PTR(self);

    i_am_a_child(b, false);
    append_indent(b);
    if (0 == argc) {
        buf_append_string(&b->buf, "<?xml?>", 7);
        b->col += 7;
        b->pos += 7;
    } else {
        volatile VALUE v;

        buf_append_string(&b->buf, "<?", 2);
        b->col += 2;
        b->pos += 2;
        append_sym_str(b, *argv);
        if (1 < argc && rb_cHash == rb_obj_class(argv[1])) {
            int        len;

            if (Qnil != (v = rb_hash_lookup(argv[1], ox_version_sym))) {
                if (rb_cString != rb_obj_class(v)) {
                    rb_raise(ox_parse_error_class, ":version must be a Symbol.\n");
                }
                len = (int)RSTRING_LEN(v);
                buf_append_string(&b->buf, " version=\"", 10);
                buf_append_string(&b->buf, StringValuePtr(v), len);
                buf_append(&b->buf, '"');
                b->col += len + 11;
                b->pos += len + 11;
            }
            if (Qnil != (v = rb_hash_lookup(argv[1], ox_encoding_sym))) {
                if (rb_cString != rb_obj_class(v)) {
                    rb_raise(ox_parse_error_class, ":encoding must be a Symbol.\n");
                }
                len = (int)RSTRING_LEN(v);
                buf_append_string(&b->buf, " encoding=\"", 11);
                buf_append_string(&b->buf, StringValuePtr(v), len);
                buf_append(&b->buf, '"');
                b->col += len + 12;
                b->pos += len + 12;
                strncpy(b->encoding, StringValuePtr(v), sizeof(b->encoding));
                b->encoding[sizeof(b->encoding) - 1] = '\0';
            }
            if (Qnil != (v = rb_hash_lookup(argv[1], ox_standalone_sym))) {
                if (rb_cString != rb_obj_class(v)) {
                    rb_raise(ox_parse_error_class, ":standalone must be a Symbol.\n");
                }
                len = (int)RSTRING_LEN(v);
                buf_append_string(&b->buf, " standalone=\"", 13);
                buf_append_string(&b->buf, StringValuePtr(v), len);
                buf_append(&b->buf, '"');
                b->col += len + 14;
                b->pos += len + 14;
            }
        }
        buf_append_string(&b->buf, "?>", 2);
        b->col += 2;
        b->pos += 2;
    }
    return Qnil;
}
line() click to toggle source

Returns the current line in the output. The first line is line 1.

static VALUE
builder_line(VALUE self) {
    return LONG2NUM(((Builder)DATA_PTR(self))->line);
}
pop() click to toggle source

Closes the current element.

static VALUE builder_pop(VALUE self) {
    pop((Builder)DATA_PTR(self));

    return Qnil;
}
pos() click to toggle source

Returns the number of bytes written.

static VALUE
builder_pos(VALUE self) {
    return LONG2NUM(((Builder)DATA_PTR(self))->pos);
}
raw(text) click to toggle source

Adds the provided string directly to the XML without formatting or modifications.

  • text - (String) contents to be added

static VALUE
builder_raw(VALUE self, VALUE text) {
    Builder             b = (Builder)DATA_PTR(self);
    volatile VALUE      v = text;
    const char          *str;
    const char          *s;
    const char          *end;
    int                 len;

    if (T_STRING != rb_type(v)) {
        v = rb_funcall(v, ox_to_s_id, 0);
    }
    str = StringValuePtr(v);
    len = (int)RSTRING_LEN(v);
    s = str;
    end = str + len;
    i_am_a_child(b, true);
    buf_append_string(&b->buf, str, len);
    b->col += len;
    s = strchr(s, '\n');
    while (NULL != s) {
        b->line++;
        b->col = end - s;
        s = strchr(s + 1, '\n');
    }
    b->pos += len;

    return Qnil;
}
text(text) click to toggle source

Adds a text element to the XML string being formed.

  • text - (String) contents of the text field

  • strip_invalid_chars - [true|false] strips any characters invalid for XML, defaults to false

static VALUE
builder_text(int argc, VALUE *argv, VALUE self) {
    Builder             b = (Builder)DATA_PTR(self);
    volatile VALUE      v;
    volatile VALUE      strip_invalid_chars;

    if ((0 == argc) || (argc > 2)) {
        rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1..2)", argc);
    }
    v = argv[0];
    if (2 == argc) {
        strip_invalid_chars = argv[1];
    } else {
        strip_invalid_chars = Qfalse;
    }

    if (T_STRING != rb_type(v)) {
        v = rb_funcall(v, ox_to_s_id, 0);
    }
    i_am_a_child(b, true);
    append_string(b, StringValuePtr(v), RSTRING_LEN(v), xml_element_chars, RTEST(strip_invalid_chars));

    return Qnil;
}
to_s() click to toggle source

Returns the JSON document string in what ever state the construction is at.

static VALUE
builder_to_s(VALUE self) {
    return to_s((Builder)DATA_PTR(self));
}
void_element(name,attributes) click to toggle source

Adds an void element with the name and attributes provided.

  • name - (String) name of the element

  • attributes - (Hash) of the element

static VALUE
builder_void_element(int argc, VALUE *argv, VALUE self) {
    Builder     b = (Builder)DATA_PTR(self);
    const char  *name;
    long        len;

    if (1 > argc) {
        rb_raise(ox_arg_error_class, "missing element name");
    }
    i_am_a_child(b, false);
    append_indent(b);
    switch (rb_type(*argv)) {
    case T_STRING:
        name = StringValuePtr(*argv);
        len = RSTRING_LEN(*argv);
        break;
    case T_SYMBOL:
        name = rb_id2name(SYM2ID(*argv));
        len = strlen(name);
        break;
    default:
        rb_raise(ox_arg_error_class, "expected a Symbol or String for an element name");
        break;
    }
    buf_append(&b->buf, '<');
    b->col++;
    b->pos++;
    append_string(b, name, len, xml_element_chars, false);
    if (1 < argc && T_HASH == rb_type(argv[1])) {
        rb_hash_foreach(argv[1], append_attr, (VALUE)b);
    }
    buf_append_string(&b->buf, ">", 1);
    b->col++;;
    b->pos++;

    return Qnil;
}