Class: Yajl::Encoder

Inherits:
Object show all
Defined in:
ext/yajl/yajl_ext.c,
lib/yajl.rb,
ext/yajl/yajl_ext.c

Overview

This class contains methods for encoding a Ruby object into JSON, streaming it's output into an IO object. The IO object need only respond to #write(str) The JSON stream created is written to the IO in chunks, as it's being created.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Object

call-seq: initialize([:pretty => false[, :indent => ' '][, :terminator => “n”]])

:pretty will enable/disable beautifying or “pretty priting” the output string.

:indent is the character(s) used to indent the output string.

:terminator allows you to specify a character to be used as the termination character after a full JSON string has been generated by the encoder. This would be especially useful when encoding in chunks (via a block or callback during the encode process), to be able to determine when the last chunk of the current encode is sent. If you specify this option to be nil, it will be ignored if encoding directly to an IO or simply returning a string. But if a block is used, the encoder will still pass it - I hope that makes sense ;).


1080
1081
1082
# File 'ext/yajl/yajl_ext.c', line 1080

static VALUE rb_yajl_encoder_init(int argc, VALUE * argv, VALUE self) {
    return self;
}

Class Method Details

.enable_json_gem_compatabilityObject

call-seq: enable_json_gem_compatability

Enables the JSON gem compatibility API


1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
# File 'ext/yajl/yajl_ext.c', line 1304

static VALUE rb_yajl_encoder_enable_json_gem_ext(VALUE klass) {
    rb_define_method(rb_cHash, "to_json", rb_yajl_json_ext_hash_to_json, -1);
    rb_define_method(rb_cArray, "to_json", rb_yajl_json_ext_array_to_json, -1);
#ifdef RUBY_INTEGER_UNIFICATION
    rb_define_method(rb_cInteger, "to_json", rb_yajl_json_ext_fixnum_to_json, -1);
#else
    rb_define_method(rb_cFixnum, "to_json", rb_yajl_json_ext_fixnum_to_json, -1);
#endif
    rb_define_method(rb_cFloat, "to_json", rb_yajl_json_ext_float_to_json, -1);
    rb_define_method(rb_cString, "to_json", rb_yajl_json_ext_string_to_json, -1);
    rb_define_method(rb_cTrueClass, "to_json", rb_yajl_json_ext_true_to_json, -1);
    rb_define_method(rb_cFalseClass, "to_json", rb_yajl_json_ext_false_to_json, -1);
    rb_define_method(rb_cNilClass, "to_json", rb_yajl_json_ext_nil_to_json, -1);
    return Qnil;
}

.encode(obj, *args, &block) ⇒ Object

A helper method for encode-and-forget use-cases

Examples:

Yajl::Encoder.encode(obj[, io, :pretty => true, :indent => "\t", &block])

output = Yajl::Encoder.encode(obj[, :pretty => true, :indent => "\t", &block])

obj is a ruby object to encode to JSON format

io is the optional IO stream to encode the ruby object to. If io isn't passed, the resulting JSON string is returned. If io is passed, nil is returned.

The options hash allows you to set two encoding options - :pretty and :indent

:pretty accepts a boolean and will enable/disable “pretty printing” the resulting output

:indent accepts a string and will be used as the indent character(s) during the pretty print process

If a block is passed, it will be used as (and work the same as) the on_progress callback


68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/yajl.rb', line 68

def self.encode(obj, *args, &block)
  # TODO: this code smells, any ideas?
  args.flatten!
  options = {}
  io = nil
  args.each do |arg|
    if arg.is_a?(Hash)
      options = arg
    elsif arg.respond_to?(:write)
      io = arg
    end
  end if args.any?
  new(options).encode(obj, io, &block)
end

.new(*args) ⇒ Object

call-seq: initialize([:pretty => false[, :indent => ' '][, :terminator => “n”]])

:pretty will enable/disable beautifying or “pretty priting” the output string.

:indent is the character(s) used to indent the output string.

:terminator allows you to specify a character to be used as the termination character after a full JSON string has been generated by the encoder. This would be especially useful when encoding in chunks (via a block or callback during the encode process), to be able to determine when the last chunk of the current encode is sent. If you specify this option to be nil, it will be ignored if encoding directly to an IO or simply returning a string. But if a block is used, the encoder will still pass it - I hope that makes sense ;).


1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
# File 'ext/yajl/yajl_ext.c', line 1008

static VALUE rb_yajl_encoder_new(int argc, VALUE * argv, VALUE klass) {
    yajl_encoder_wrapper * wrapper;
    yajl_gen_config cfg;
    VALUE opts, obj, indent;
    unsigned char *indentString = NULL, *actualIndent = NULL;
    int beautify = 0, htmlSafe = 0;

    /* Scan off config vars */
    if (rb_scan_args(argc, argv, "01", &opts) == 1) {
        Check_Type(opts, T_HASH);

        if (rb_hash_aref(opts, sym_pretty) == Qtrue) {
            beautify = 1;
            indent = rb_hash_aref(opts, sym_indent);
            if (indent != Qnil) {
#ifdef HAVE_RUBY_ENCODING_H
                indent = rb_str_export_to_enc(indent, utf8Encoding);
#endif
                Check_Type(indent, T_STRING);
                indentString = (unsigned char*)xmalloc(RSTRING_LEN(indent)+1);
                memcpy(indentString, RSTRING_PTR(indent), RSTRING_LEN(indent));
                indentString[RSTRING_LEN(indent)] = '\0';
                actualIndent = indentString;
            }
        }

        if (rb_hash_aref(opts, sym_html_safe) == Qtrue) {
          htmlSafe = 1;
        }

        if (rb_hash_aref(opts, sym_entities) == Qtrue) {
          htmlSafe = 2;
        }
    }
    if (!indentString) {
      indentString = defaultIndentString;
    }
    cfg = (yajl_gen_config){beautify, (const char *)indentString, htmlSafe};

    obj = Data_Make_Struct(klass, yajl_encoder_wrapper, yajl_encoder_wrapper_mark, yajl_encoder_wrapper_free, wrapper);
    wrapper->indentString = actualIndent;
    wrapper->encoder = yajl_gen_alloc(&cfg, &rb_alloc_funcs);
    wrapper->on_progress_callback = Qnil;
    if (opts != Qnil && rb_funcall(opts, intern_has_key, 1, sym_terminator) == Qtrue) {
        wrapper->terminator = rb_hash_aref(opts, sym_terminator);
#ifdef HAVE_RUBY_ENCODING_H
        if (TYPE(wrapper->terminator) == T_STRING) {
            wrapper->terminator = rb_str_export_to_enc(wrapper->terminator, utf8Encoding);
        }
#endif
    } else {
        wrapper->terminator = 0;
    }
    rb_obj_call_init(obj, 0, 0);
    return obj;
}

Instance Method Details

#encode(*args) ⇒ Object

call-seq: encode(obj[, io[, &block]])

obj is the Ruby object to encode to JSON

io is an optional IO used to stream the encoded JSON string to. If io isn't specified, this method will return the resulting JSON string. If io is specified, this method returns nil

If an optional block is passed, it's called when encoding is complete and passed the resulting JSON string

It should be noted that you can reuse an instance of this class to continue encoding multiple JSON to the same stream. Just continue calling this method, passing it the same IO object with new/different ruby objects to encode. This is how streaming is accomplished.


1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
# File 'ext/yajl/yajl_ext.c', line 1100

static VALUE rb_yajl_encoder_encode(int argc, VALUE * argv, VALUE self) {
    yajl_encoder_wrapper * wrapper;
    const unsigned char * buffer;
    unsigned int len;
    VALUE obj, io, blk, outBuff;

    GetEncoder(self, wrapper);

    rb_scan_args(argc, argv, "11&", &obj, &io, &blk);

    if (blk != Qnil) {
        wrapper->on_progress_callback = blk;
    }

    /* begin encode process */
    yajl_encode_part(wrapper, obj, io);

    /* just make sure we output the remaining buffer */
    yajl_gen_get_buf(wrapper->encoder, &buffer, &len);
    outBuff = rb_str_new((const char *)buffer, len);
#ifdef HAVE_RUBY_ENCODING_H
    rb_enc_associate(outBuff, utf8Encoding);
#endif
    yajl_gen_clear(wrapper->encoder);

    if (io != Qnil) {
        rb_io_write(io, outBuff);
        if (wrapper->terminator != 0 && wrapper->terminator != Qnil) {
            rb_io_write(io, wrapper->terminator);
        }
        return Qnil;
    } else if (blk != Qnil) {
        rb_funcall(blk, intern_call, 1, outBuff);
        if (wrapper->terminator != 0) {
            rb_funcall(blk, intern_call, 1, wrapper->terminator);
        }
        return Qnil;
    } else {
        if (wrapper->terminator != 0 && wrapper->terminator != Qnil) {
            rb_str_concat(outBuff, wrapper->terminator);
        }
        return outBuff;
    }
    return Qnil;
}

#on_progress=(callback) ⇒ Object

call-seq: on_progress = Proc.new {|str| …}

This callback setter allows you to pass a Proc/lambda or any other object that responds to #call.

It will pass the caller a chunk of the encode buffer after it's reached it's internal max buffer size (defaults to 8kb). For example, encoding a large object that would normally result in 24288 bytes of data will result in 3 calls to this callback (assuming the 8kb default encode buffer).


1156
1157
1158
1159
1160
1161
# File 'ext/yajl/yajl_ext.c', line 1156

static VALUE rb_yajl_encoder_set_progress_cb(VALUE self, VALUE callback) {
    yajl_encoder_wrapper * wrapper;
    GetEncoder(self, wrapper);
    wrapper->on_progress_callback = callback;
    return Qnil;
}