diff --git a/ext/yajl/yajl_ext.c b/ext/yajl/yajl_ext.c index 25120100..678907ee 100644 --- a/ext/yajl/yajl_ext.c +++ b/ext/yajl/yajl_ext.c @@ -389,22 +389,25 @@ static int yajl_found_end_array(void * ctx) { /* * Document-method: new * - * call-seq: new([:symbolize_keys => true, [:allow_comments => false[, :check_utf8 => false]]]) + * call-seq: new([:symbolize_keys => true, [:allow_comments => false[, :check_utf8 => false]]][, &callback]) * * :symbolize_keys will turn hash keys into Ruby symbols, defaults to false. * * :allow_comments will turn on/off the check for comments inside the JSON stream, defaults to true. * * :check_utf8 will validate UTF8 characters found in the JSON stream, defaults to true. + * + * If a block was passed, it's called when an object has been parsed off the stream. This is especially + * useful when parsing a stream of multiple JSON objects. */ static VALUE rb_yajl_parser_new(int argc, VALUE * argv, VALUE klass) { yajl_parser_wrapper * wrapper; yajl_parser_config cfg; - VALUE opts, obj; + VALUE opts, blk, obj; int allowComments = 1, checkUTF8 = 1, symbolizeKeys = 0; /* Scan off config vars */ - if (rb_scan_args(argc, argv, "01", &opts) == 1) { + if (rb_scan_args(argc, argv, "01&", &opts, &blk) == 1) { Check_Type(opts, T_HASH); if (rb_hash_aref(opts, sym_allow_comments) == Qfalse) { @@ -426,7 +429,7 @@ static VALUE rb_yajl_parser_new(int argc, VALUE * argv, VALUE klass) { wrapper->objectsFound = 0; wrapper->symbolizeKeys = symbolizeKeys; wrapper->builderStack = rb_ary_new(); - wrapper->parse_complete_callback = Qnil; + wrapper->parse_complete_callback = blk; rb_obj_call_init(obj, 0, 0); return obj; } @@ -434,7 +437,7 @@ static VALUE rb_yajl_parser_new(int argc, VALUE * argv, VALUE klass) { /* * Document-method: initialize * - * call-seq: new([:symbolize_keys => true, [:allow_comments => false[, :check_utf8 => false]]]) + * call-seq: new([:symbolize_keys => true, [:allow_comments => false[, :check_utf8 => false]]][, &callback]) * * :symbolize_keys will turn hash keys into Ruby symbols, defaults to false. * @@ -462,7 +465,7 @@ static VALUE rb_yajl_parser_init(int argc, VALUE * argv, VALUE self) { * reading off of a socket directly. * * If a block was passed, it's called when an object has been parsed off the stream. This is especially - * usefull when parsing a stream of multiple JSON objects. + * useful when parsing a stream of multiple JSON objects. * * NOTE: you can optionally assign the +on_parse_complete+ callback, and it will be called the same way the optional * block is for this method. @@ -572,27 +575,29 @@ static unsigned char * defaultIndentString = (unsigned char *)" "; /* * Document-method: new * - * 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 ;). + * call-seq: new([:pretty => false[, :indent => ' '][, :terminator => "\n"]][, &callback]) + * + * :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 ;). + * + * If an optional block is passed, it's called when encoding is complete and passed the resulting JSON string */ static VALUE rb_yajl_encoder_new(int argc, VALUE * argv, VALUE klass) { yajl_encoder_wrapper * wrapper; yajl_gen_config cfg; - VALUE opts, obj, indent; + VALUE opts, blk, 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) { + if (rb_scan_args(argc, argv, "01&", &opts, &blk) == 1) { Check_Type(opts, T_HASH); if (rb_hash_aref(opts, sym_pretty) == Qtrue) { @@ -621,7 +626,7 @@ static VALUE rb_yajl_encoder_new(int argc, VALUE * argv, VALUE klass) { 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, NULL); - wrapper->on_progress_callback = Qnil; + wrapper->on_progress_callback = blk; 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 @@ -639,7 +644,7 @@ static VALUE rb_yajl_encoder_new(int argc, VALUE * argv, VALUE klass) { /* * Document-method: initialize * - * call-seq: initialize([:pretty => false[, :indent => ' '][, :terminator => "\n"]]) + * call-seq: initialize([:pretty => false[, :indent => ' '][, :terminator => "\n"]][, &callback]) * * :pretty will enable/disable beautifying or "pretty priting" the output string. * @@ -702,10 +707,10 @@ static VALUE rb_yajl_encoder_encode(int argc, VALUE * argv, VALUE self) { rb_io_write(io, wrapper->terminator); } return Qnil; - } else if (blk != Qnil) { - rb_funcall(blk, intern_call, 1, outBuff); + } else if (wrapper->on_progress_callback != Qnil) { + rb_funcall(wrapper->on_progress_callback, intern_call, 1, outBuff); if (wrapper->terminator != 0) { - rb_funcall(blk, intern_call, 1, wrapper->terminator); + rb_funcall(wrapper->on_progress_callback, intern_call, 1, wrapper->terminator); } return Qnil; } else { diff --git a/spec/encoding/encoding_spec.rb b/spec/encoding/encoding_spec.rb index 818f028b..2fc1bb3c 100644 --- a/spec/encoding/encoding_spec.rb +++ b/spec/encoding/encoding_spec.rb @@ -132,6 +132,25 @@ def to_s output.should == output end + it "should encode with a callback passed via on_progress" do + callback = lambda { |str| + # no-op + } + encoder = Yajl::Encoder.new + encoder.on_progress = callback + callback.should_receive(:call).with('{}') + encoder.encode({}) + end + + it "should encode with a callback passed as block" do + callback = lambda { |str| + # no-op + } + encoder = Yajl::Encoder.new(&callback) + callback.should_receive(:call).with('{}') + encoder.encode({}) + end + it "should encode with it's class method with :pretty and a tab character indent options set, to an IO" do output = "{\n\t\"foo\": 1234\n}" obj = {:foo => 1234} diff --git a/spec/parsing/chunked_spec.rb b/spec/parsing/chunked_spec.rb index 20248c9a..dd62de6c 100644 --- a/spec/parsing/chunked_spec.rb +++ b/spec/parsing/chunked_spec.rb @@ -13,6 +13,15 @@ @parser.on_parse_complete = @callback end + it "should take the callback as block parameter" do + @callback = lambda { |hash| + # no-op + } + @parser = Yajl::Parser.new(&@callback) + @callback.should_receive(:call).with(@final) + @parser << '[{"abc": 123},{"def": 456}]' + end + it "should parse a single chunk" do @callback.should_receive(:call).with(@final) @parser << '[{"abc": 123},{"def": 456}]'