brianmario-yajl-ruby 0.5.2 → 0.5.3

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.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.3 (Jun 7th, 2009)
4
+ * The IO parameter for Yajl::Encode#encode is now optional, and accepts a block
5
+ ** it will return the resulting JSON string if no IO is passed to stream to
6
+ ** if a block is passed, it will call and pass it the resulting JSON string
7
+ * Yajl::Parser#parse can now parse from a String as well as an IO
8
+ * Added and updated lot of in-code documentation.
9
+ ** all the C code exposed to Ruby should now have comments
10
+ * Added :symbolize_keys option to the Yajl::Parser class, which defaults to true.
11
+ ** Having this option enabled has shown around an 18% speedup in parsing time according to my benchmarks
12
+
3
13
  ## 0.5.2 (May 30th, 2009)
4
14
  * Added class helper methods Yajl::Encoder.encode(obj, io) and Yajl::Parser.parse(io)
5
15
  * added tests for the above
data/README.rdoc CHANGED
@@ -142,8 +142,8 @@ Some ideas are:
142
142
  * a Rails plugin (http://github.com/technoweenie/yajl-rails)
143
143
  * builtin Rails 3 support?
144
144
  * Rack middleware (ideally the JSON body could be handed to the parser while it's still being received)
145
- * use with ohai
146
- * JSON API clients
145
+ * use with ohai (http://github.com/brianmario/ohai)
146
+ * JSON API clients (http://github.com/brianmario/crack, http://github.com/brianmario/freckle-api)
147
147
  * Patch Marshal#load and Marshal#dump to use JSON? ;)
148
148
  * etc...
149
149
 
@@ -214,4 +214,5 @@ I've had a lot of inspiration, and a lot of help. Thanks to everyone who's been
214
214
  * Tom Smith - http://github.com/rtomsmith - pointer-hacking help
215
215
  * Rick http://github.com/technoweenie - for making an ActiveSupport patch with support for this library and teasing me that it might go into Rails 3. You sure lit a fire under my ass and I got a ton of work done because of it! :)
216
216
  * The entire Github Crew - http://github.com/ - my inspiration, time spent writing this, finding Yajl, So many-MANY other things wouldn't have been possible without this awesome service. I owe you guys some whiskey at Kilowatt.
217
- * benburkert - http://github.com/benburkert
217
+ * benburkert - http://github.com/benburkert
218
+ * Aman Gupta - http://github.com/tmm1 - tons of suggestions and inspiration for the most recent features, and hopefully more to come ;)
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :minor: 5
3
- :patch: 2
4
2
  :major: 0
3
+ :minor: 5
4
+ :patch: 3
data/benchmark/encode.rb CHANGED
@@ -15,11 +15,18 @@ json.close
15
15
  times = ARGV[1] ? ARGV[1].to_i : 1
16
16
  puts "Starting benchmark encoding #{filename} #{times} times\n\n"
17
17
  Benchmark.bm { |x|
18
- encoder = Yajl::Encoder.new
18
+ io_encoder = Yajl::Encoder.new
19
19
  x.report {
20
- puts "Yajl::Encoder#encode"
20
+ puts "Yajl::Encoder#encode (to an IO)"
21
21
  times.times {
22
- encoder.encode(hash, StringIO.new)
22
+ io_encoder.encode(hash, StringIO.new)
23
+ }
24
+ }
25
+ string_encoder = Yajl::Encoder.new
26
+ x.report {
27
+ puts "Yajl::Encoder#encode (to a String)"
28
+ times.times {
29
+ output = string_encoder.encode(hash)
23
30
  }
24
31
  }
25
32
  x.report {
data/benchmark/parse.rb CHANGED
@@ -15,12 +15,20 @@ json.rewind
15
15
  times = ARGV[1] ? ARGV[1].to_i : 1
16
16
  puts "Starting benchmark parsing #{File.size(filename)} bytes of JSON data #{times} times\n\n"
17
17
  Benchmark.bm { |x|
18
- parser = Yajl::Parser.new
18
+ io_parser = Yajl::Parser.new
19
19
  x.report {
20
- puts "Yajl::Parser#parse"
20
+ puts "Yajl::Parser#parse (from an IO)"
21
21
  times.times {
22
22
  json.rewind
23
- parser.parse(json)
23
+ io_parser.parse(json)
24
+ }
25
+ }
26
+ string_parser = Yajl::Parser.new
27
+ x.report {
28
+ puts "Yajl::Parser#parse (from a String)"
29
+ times.times {
30
+ json.rewind
31
+ string_parser.parse(json.read)
24
32
  }
25
33
  }
26
34
  x.report {
data/ext/yajl_ext.c CHANGED
@@ -11,6 +11,14 @@ inline void yajl_check_and_fire_callback(void * ctx) {
11
11
  if (len == 1 && wrapper->nestedArrayLevel == 0 && wrapper->nestedHashLevel == 0) {
12
12
  rb_funcall(wrapper->parse_complete_callback, intern_call, 1, rb_ary_pop(wrapper->builderStack));
13
13
  }
14
+ } else {
15
+ int len = RARRAY_LEN(wrapper->builderStack);
16
+ if (len == 1 && wrapper->nestedArrayLevel == 0 && wrapper->nestedHashLevel == 0) {
17
+ wrapper->objectsFound++;
18
+ if (wrapper->objectsFound > 1) {
19
+ rb_raise(cParseError, "%s", "Found multiple JSON objects in the stream but no block or the on_parse_complete callback was assigned to handle them.");
20
+ }
21
+ }
14
22
  }
15
23
  }
16
24
 
@@ -36,6 +44,7 @@ inline void yajl_set_static_value(void * ctx, VALUE val) {
36
44
  rb_ary_push(wrapper->builderStack, val);
37
45
  break;
38
46
  case T_STRING:
47
+ case T_SYMBOL:
39
48
  hash = rb_ary_entry(wrapper->builderStack, len-2);
40
49
  if (TYPE(hash) == T_HASH) {
41
50
  rb_hash_aset(hash, lastEntry, val);
@@ -58,11 +67,13 @@ void yajl_encode_part(yajl_gen hand, VALUE obj, VALUE io) {
58
67
  const unsigned char * buffer;
59
68
  unsigned int len;
60
69
 
61
- yajl_gen_get_buf(hand, &buffer, &len);
62
- if (len >= WRITE_BUFSIZE) {
63
- outBuff = rb_str_new((const char *)buffer, len);
64
- rb_io_write(io, outBuff);
65
- yajl_gen_clear(hand);
70
+ if (io != Qnil) {
71
+ yajl_gen_get_buf(hand, &buffer, &len);
72
+ if (len >= WRITE_BUFSIZE) {
73
+ outBuff = rb_str_new((const char *)buffer, len);
74
+ rb_io_write(io, outBuff);
75
+ yajl_gen_clear(hand);
76
+ }
66
77
  }
67
78
 
68
79
  switch (TYPE(obj)) {
@@ -126,6 +137,18 @@ void yajl_parser_wrapper_mark(void * wrapper) {
126
137
  rb_gc_mark(w->parse_complete_callback);
127
138
  }
128
139
 
140
+ void yajl_parse_chunk(const unsigned char * chunk, unsigned int len, yajl_handle parser) {
141
+ yajl_status stat;
142
+
143
+ stat = yajl_parse(parser, chunk, len);
144
+
145
+ if (stat != yajl_status_ok && stat != yajl_status_insufficient_data) {
146
+ unsigned char * str = yajl_get_error(parser, 1, chunk, len);
147
+ rb_raise(cParseError, "%s", (const char *) str);
148
+ yajl_free_error(parser, str);
149
+ }
150
+ }
151
+
129
152
  // YAJL Callbacks
130
153
  static int yajl_found_null(void * ctx) {
131
154
  yajl_set_static_value(ctx, Qnil);
@@ -161,7 +184,18 @@ static int yajl_found_string(void * ctx, const unsigned char * stringVal, unsign
161
184
  }
162
185
 
163
186
  static int yajl_found_hash_key(void * ctx, const unsigned char * stringVal, unsigned int stringLen) {
164
- yajl_set_static_value(ctx, rb_str_new((const char *)stringVal, stringLen));
187
+ struct yajl_parser_wrapper * wrapper;
188
+ GetParser((VALUE)ctx, wrapper);
189
+
190
+ if (wrapper->symbolizeKeys) {
191
+ char keyStr[stringLen];
192
+ ID key;
193
+ sprintf(keyStr, "%.*s", stringLen, stringVal);
194
+ key = rb_intern(keyStr);
195
+ yajl_set_static_value(ctx, ID2SYM(key));
196
+ } else {
197
+ yajl_set_static_value(ctx, rb_str_new((const char *)stringVal, stringLen));
198
+ }
165
199
  yajl_check_and_fire_callback(ctx);
166
200
  return 1;
167
201
  }
@@ -205,14 +239,30 @@ static int yajl_found_end_array(void * ctx) {
205
239
  }
206
240
 
207
241
 
208
- /** Ruby Interface */
242
+ // Ruby Interface
209
243
 
210
- // Yajl::Parser
244
+ /*
245
+ * Document-class: Yajl::Parser
246
+ *
247
+ * This class contains methods for parsing JSON directly from an IO object.
248
+ * The only basic requirment currently is that the IO object respond to #read(len) and #eof?
249
+ * The IO is parsed until a complete JSON object has been read and a ruby object will be returned.
250
+ */
251
+
252
+ /*
253
+ * Document-method: new
254
+ *
255
+ * call-seq: new([:allow_comments => false, :check_utf8 => false])
256
+ *
257
+ * :allow_comments will turn on/off the check for comments inside the JSON stream.
258
+ *
259
+ * :check_utf8 will validate UTF8 characters found in the JSON stream.
260
+ */
211
261
  static VALUE rb_yajl_parser_new(int argc, VALUE * argv, VALUE klass) {
212
262
  struct yajl_parser_wrapper * wrapper;
213
263
  yajl_parser_config cfg;
214
264
  VALUE opts, obj;
215
- int allowComments = 1, checkUTF8 = 1;
265
+ int allowComments = 1, checkUTF8 = 1, symbolizeKeys = 1;
216
266
 
217
267
  // Scan off config vars
218
268
  if (rb_scan_args(argc, argv, "01", &opts) == 1) {
@@ -224,6 +274,9 @@ static VALUE rb_yajl_parser_new(int argc, VALUE * argv, VALUE klass) {
224
274
  if (rb_hash_aref(opts, ID2SYM(sym_check_utf8)) == Qfalse) {
225
275
  checkUTF8 = 0;
226
276
  }
277
+ if (rb_hash_aref(opts, ID2SYM(sym_symbolize_keys)) == Qfalse) {
278
+ symbolizeKeys = 0;
279
+ }
227
280
  }
228
281
  cfg = (yajl_parser_config){allowComments, checkUTF8};
229
282
 
@@ -231,44 +284,76 @@ static VALUE rb_yajl_parser_new(int argc, VALUE * argv, VALUE klass) {
231
284
  wrapper->parser = yajl_alloc(&callbacks, &cfg, NULL, (void *)obj);
232
285
  wrapper->nestedArrayLevel = 0;
233
286
  wrapper->nestedHashLevel = 0;
287
+ wrapper->objectsFound = 0;
288
+ wrapper->symbolizeKeys = symbolizeKeys;
234
289
  wrapper->builderStack = rb_ary_new();
235
290
  wrapper->parse_complete_callback = Qnil;
236
291
  rb_obj_call_init(obj, 0, 0);
237
292
  return obj;
238
293
  }
239
294
 
295
+ /*
296
+ * Document-method: initialize
297
+ *
298
+ * call-seq: initialize([:allow_comments => false, :check_utf8 => false])
299
+ *
300
+ * :allow_comments will turn on/off the check for comments inside the JSON stream.
301
+ *
302
+ * :check_utf8 will validate UTF8 characters found in the JSON stream.
303
+ */
240
304
  static VALUE rb_yajl_parser_init(int argc, VALUE * argv, VALUE self) {
241
305
  return self;
242
306
  }
243
307
 
308
+ /*
309
+ * Document-method: parse
310
+ *
311
+ * call-seq:
312
+ * parse(input, buffer_size=8092)
313
+ * parse(input, buffer_size=8092) { |obj| ... }
314
+ *
315
+ * +input+ can either be a string or an IO to parse JSON from
316
+ *
317
+ * +buffer_size+ is the size of chunk that will be parsed off the input (if it's an IO) for each loop of the parsing process.
318
+ * 8092 is a good balance between the different types of streams (off disk, off a socket, etc...), but this option
319
+ * is here so the caller can better tune their parsing depending on the type of stream being passed.
320
+ * A larger read buffer will perform better for files off disk, where as a smaller size may be more efficient for
321
+ * reading off of a socket directly.
322
+ *
323
+ * If a block was passed, it's called when an object has been parsed off the stream. This is especially
324
+ * usefull when parsing a stream of multiple JSON objects.
325
+ *
326
+ * NOTE: you can optionally assign the +on_parse_complete+ callback, and it will be called the same way the optional
327
+ * block is for this method.
328
+ */
244
329
  static VALUE rb_yajl_parser_parse(int argc, VALUE * argv, VALUE self) {
245
- struct yajl_parser_wrapper * wrapper;
246
330
  yajl_status stat;
247
- VALUE parsed, rbufsize, io;
331
+ struct yajl_parser_wrapper * wrapper;
332
+ VALUE parsed, rbufsize, input, blk;
248
333
 
249
334
  GetParser(self, wrapper);
250
335
  parsed = rb_str_new2("");
251
336
 
252
337
  // setup our parameters
253
- rb_scan_args(argc, argv, "11", &io, &rbufsize);
338
+ rb_scan_args(argc, argv, "11&", &input, &rbufsize, &blk);
254
339
  if (NIL_P(rbufsize)) {
255
340
  rbufsize = INT2FIX(READ_BUFSIZE);
256
341
  } else {
257
342
  Check_Type(rbufsize, T_FIXNUM);
258
343
  }
344
+ if (!NIL_P(blk)) {
345
+ rb_yajl_set_complete_cb(self, blk);
346
+ }
259
347
 
260
- // now parse from the IO
261
- while (rb_funcall(io, intern_eof, 0) != Qtrue) {
262
- rb_funcall(io, intern_io_read, 2, rbufsize, parsed);
263
-
264
- stat = yajl_parse(wrapper->parser, (const unsigned char *)RSTRING_PTR(parsed), RSTRING_LEN(parsed));
265
-
266
- if (stat != yajl_status_ok && stat != yajl_status_insufficient_data) {
267
- unsigned char * str = yajl_get_error(wrapper->parser, 1, (const unsigned char *)RSTRING_PTR(parsed), RSTRING_LEN(parsed));
268
- rb_raise(cParseError, "%s", (const char *) str);
269
- yajl_free_error(wrapper->parser, str);
270
- break;
348
+ if (TYPE(input) == T_STRING) {
349
+ yajl_parse_chunk((const unsigned char *)RSTRING_PTR(input), RSTRING_LEN(input), wrapper->parser);
350
+ } else if (rb_respond_to(input, intern_eof)) {
351
+ while (rb_funcall(input, intern_eof, 0) != Qtrue) {
352
+ rb_funcall(input, intern_io_read, 2, rbufsize, parsed);
353
+ yajl_parse_chunk((const unsigned char *)RSTRING_PTR(parsed), RSTRING_LEN(parsed), wrapper->parser);
271
354
  }
355
+ } else {
356
+ rb_raise(cParseError, "input must be a string or IO");
272
357
  }
273
358
 
274
359
  // parse any remaining buffered data
@@ -282,9 +367,19 @@ static VALUE rb_yajl_parser_parse(int argc, VALUE * argv, VALUE self) {
282
367
  return rb_ary_pop(wrapper->builderStack);
283
368
  }
284
369
 
370
+ /*
371
+ * Document-method: parse_chunk
372
+ *
373
+ * call-seq: parse_chunk(string_chunk)
374
+ *
375
+ * +string_chunk+ can be a partial or full JSON string to push on the parser.
376
+ *
377
+ * This method will throw an exception if the +on_parse_complete+ callback hasn't been assigned yet.
378
+ * The +on_parse_complete+ callback assignment is required so the user can handle objects that have been
379
+ * parsed off the stream as they're found.
380
+ */
285
381
  static VALUE rb_yajl_parser_parse_chunk(VALUE self, VALUE chunk) {
286
382
  struct yajl_parser_wrapper * wrapper;
287
- yajl_status stat;
288
383
 
289
384
  GetParser(self, wrapper);
290
385
  if (NIL_P(chunk)) {
@@ -293,12 +388,7 @@ static VALUE rb_yajl_parser_parse_chunk(VALUE self, VALUE chunk) {
293
388
  }
294
389
 
295
390
  if (wrapper->parse_complete_callback != Qnil) {
296
- stat = yajl_parse(wrapper->parser, (const unsigned char *)RSTRING_PTR(chunk), RSTRING_LEN(chunk));
297
- if (stat != yajl_status_ok && stat != yajl_status_insufficient_data) {
298
- unsigned char * str = yajl_get_error(wrapper->parser, 1, (const unsigned char *)RSTRING_PTR(chunk), RSTRING_LEN(chunk));
299
- rb_raise(cParseError, "%s", (const char *) str);
300
- yajl_free_error(wrapper->parser, str);
301
- }
391
+ yajl_parse_chunk((const unsigned char *)RSTRING_PTR(chunk), RSTRING_LEN(chunk), wrapper->parser);
302
392
  } else {
303
393
  rb_raise(cParseError, "The on_parse_complete callback isn't setup, parsing useless.");
304
394
  }
@@ -306,6 +396,15 @@ static VALUE rb_yajl_parser_parse_chunk(VALUE self, VALUE chunk) {
306
396
  return Qnil;
307
397
  }
308
398
 
399
+ /*
400
+ * Document-method: on_parse_complete=
401
+ *
402
+ * call-seq: on_parse_complete = Proc.new { |obj| ... }
403
+ *
404
+ * This callback setter allows you to pass a Proc/lambda or any other object that response to #call.
405
+ *
406
+ * It will pass a single parameter, the ruby object built from the last parsed JSON object
407
+ */
309
408
  static VALUE rb_yajl_set_complete_cb(VALUE self, VALUE callback) {
310
409
  struct yajl_parser_wrapper * wrapper;
311
410
  GetParser(self, wrapper);
@@ -313,7 +412,23 @@ static VALUE rb_yajl_set_complete_cb(VALUE self, VALUE callback) {
313
412
  return Qnil;
314
413
  }
315
414
 
316
- // Yajl::Encoder
415
+ /*
416
+ * Document-class: Yajl::Encoder
417
+ *
418
+ * This class contains methods for encoding a Ruby object into JSON, streaming it's output into an IO object.
419
+ * The IO object need only respond to #write(str)
420
+ * The JSON stream created is written to the IO in chunks, as it's being created.
421
+ */
422
+
423
+ /*
424
+ * Document-method: new
425
+ *
426
+ * call-seq: new([:pretty => false[, :indent => ' ']])
427
+ *
428
+ * :pretty will enable/disable beautifying or "pretty priting" the output string.
429
+ *
430
+ * :indent is the character(s) used to indent the output string.
431
+ */
317
432
  static VALUE rb_yajl_encoder_new(int argc, VALUE * argv, VALUE klass) {
318
433
  yajl_gen_config cfg;
319
434
  yajl_gen encoder;
@@ -342,27 +457,61 @@ static VALUE rb_yajl_encoder_new(int argc, VALUE * argv, VALUE klass) {
342
457
  return obj;
343
458
  }
344
459
 
460
+ /*
461
+ * Document-method: initialize
462
+ *
463
+ * call-seq: initialize([:pretty => false[, :indent => ' ']])
464
+ *
465
+ * :pretty will enable/disable beautifying or "pretty priting" the output string.
466
+ *
467
+ * :indent is the character(s) used to indent the output string.
468
+ */
345
469
  static VALUE rb_yajl_encoder_init(int argc, VALUE * argv, VALUE self) {
346
470
  return self;
347
471
  }
348
472
 
349
- static VALUE rb_yajl_encoder_encode(VALUE self, VALUE obj, VALUE io) {
473
+ /*
474
+ * Document-method: encode
475
+ *
476
+ * call-seq: encode(obj[, io[, &block]])
477
+ *
478
+ * +obj+ is the Ruby object to encode to JSON
479
+ *
480
+ * +io+ is an optional IO used to stream the encoded JSON string to.
481
+ * If +io+ isn't specified, this method will return the resulting JSON string. If +io+ is specified, this method returns nil
482
+ *
483
+ * If an optional block is passed, it's called when encoding is complete and passed the resulting JSON string
484
+ *
485
+ * It should be noted that you can reuse an instance of this class to continue encoding multiple JSON
486
+ * to the same stream. Just continue calling this method, passing it the same IO object with new/different
487
+ * ruby objects to encode. This is how streaming is accomplished.
488
+ */
489
+ static VALUE rb_yajl_encoder_encode(int argc, VALUE * argv, VALUE self) {
350
490
  yajl_gen encoder;
351
491
  const unsigned char * buffer;
352
492
  unsigned int len;
353
- VALUE outBuff;
493
+ VALUE obj, io, blk, outBuff;
354
494
 
355
495
  GetEncoder(self, encoder);
356
496
 
497
+ rb_scan_args(argc, argv, "11&", &obj, &io, &blk);
498
+
357
499
  // begin encode process
358
500
  yajl_encode_part(encoder, obj, io);
359
501
 
360
502
  // just make sure we output the remaining buffer
361
503
  yajl_gen_get_buf(encoder, &buffer, &len);
362
504
  outBuff = rb_str_new((const char *)buffer, len);
363
- rb_io_write(io, outBuff);
364
505
  yajl_gen_clear(encoder);
365
-
506
+ if (io != Qnil) {
507
+ rb_io_write(io, outBuff);
508
+ return Qnil;
509
+ } else if (blk != Qnil) {
510
+ rb_funcall(blk, intern_call, 1, outBuff);
511
+ return Qnil;
512
+ } else {
513
+ return outBuff;
514
+ }
366
515
  return Qnil;
367
516
  }
368
517
 
@@ -372,6 +521,7 @@ void Init_yajl_ext() {
372
521
 
373
522
  VALUE rb_cStandardError = rb_const_get(rb_cObject, rb_intern("StandardError"));
374
523
  cParseError = rb_define_class_under(mYajl, "ParseError", rb_cStandardError);
524
+ cEncodeError = rb_define_class_under(mYajl, "EncodeError", rb_cStandardError);
375
525
 
376
526
  cParser = rb_define_class_under(mYajl, "Parser", rb_cObject);
377
527
  rb_define_singleton_method(cParser, "new", rb_yajl_parser_new, -1);
@@ -384,7 +534,7 @@ void Init_yajl_ext() {
384
534
  cEncoder = rb_define_class_under(mYajl, "Encoder", rb_cObject);
385
535
  rb_define_singleton_method(cEncoder, "new", rb_yajl_encoder_new, -1);
386
536
  rb_define_method(cEncoder, "initialize", rb_yajl_encoder_init, -1);
387
- rb_define_method(cEncoder, "encode", rb_yajl_encoder_encode, 2);
537
+ rb_define_method(cEncoder, "encode", rb_yajl_encoder_encode, -1);
388
538
 
389
539
  intern_io_read = rb_intern("read");
390
540
  intern_eof = rb_intern("eof?");
@@ -395,4 +545,5 @@ void Init_yajl_ext() {
395
545
  sym_check_utf8 = rb_intern("check_utf8");
396
546
  sym_pretty = rb_intern("pretty");
397
547
  sym_indent = rb_intern("indent");
548
+ sym_symbolize_keys = rb_intern("symbolize_keys");
398
549
  }
data/ext/yajl_ext.h CHANGED
@@ -5,9 +5,9 @@
5
5
  #define READ_BUFSIZE 8092
6
6
  #define WRITE_BUFSIZE 8092
7
7
 
8
- static VALUE cParseError, mYajl, cParser, cEncoder;
8
+ static VALUE cParseError, cEncodeError, mYajl, cParser, cEncoder;
9
9
  static ID intern_io_read, intern_eof, intern_call, intern_keys, intern_to_s,
10
- sym_allow_comments, sym_check_utf8, sym_pretty, sym_indent;
10
+ sym_allow_comments, sym_check_utf8, sym_pretty, sym_indent, sym_symbolize_keys;
11
11
 
12
12
  #define GetParser(obj, sval) (sval = (struct yajl_parser_wrapper*)DATA_PTR(obj));
13
13
  #define GetEncoder(obj, sval) (sval = (yajl_gen*)DATA_PTR(obj));
@@ -15,6 +15,7 @@ static ID intern_io_read, intern_eof, intern_call, intern_keys, intern_to_s,
15
15
  inline void yajl_check_and_fire_callback(void * ctx);
16
16
  inline void yajl_set_static_value(void * ctx, VALUE val);
17
17
  void yajl_encode_part(yajl_gen hand, VALUE obj, VALUE io);
18
+ void yajl_parse_chunk(const unsigned char * chunk, unsigned int len, yajl_handle parser);
18
19
 
19
20
  static int yajl_found_null(void * ctx);
20
21
  static int yajl_found_boolean(void * ctx, int boolean);
@@ -44,6 +45,8 @@ struct yajl_parser_wrapper {
44
45
  VALUE parse_complete_callback;
45
46
  int nestedArrayLevel;
46
47
  int nestedHashLevel;
48
+ int objectsFound;
49
+ int symbolizeKeys;
47
50
  yajl_handle parser;
48
51
  };
49
52
  static void yajl_parser_wrapper_free(void * wrapper);
@@ -57,4 +60,4 @@ static VALUE rb_yajl_set_complete_cb(VALUE self, VALUE callback);
57
60
 
58
61
  static VALUE rb_yajl_encoder_new(int argc, VALUE * argv, VALUE klass);
59
62
  static VALUE rb_yajl_encoder_init(int argc, VALUE * argv, VALUE self);
60
- static VALUE rb_yajl_encoder_encode(VALUE self, VALUE obj, VALUE io);
63
+ static VALUE rb_yajl_encoder_encode(int argc, VALUE * argv, VALUE self);
@@ -1,11 +1,11 @@
1
1
  # encoding: UTF-8
2
2
  module Yajl
3
3
  module Bzip2
4
- # === Yajl::Bzip2::StreamReader
5
- #
6
4
  # This is a wrapper around Bzip::Reader to allow it's #read method to adhere
7
5
  # to the IO spec, allowing for two parameters (length, and buffer)
8
6
  class StreamReader < ::Bzip2::Reader
7
+
8
+ # A helper method to allow use similar to IO#read
9
9
  def read(len=nil, buffer=nil)
10
10
  unless buffer.nil?
11
11
  buffer.replace super(len)
@@ -14,8 +14,15 @@ module Yajl
14
14
  super(len)
15
15
  end
16
16
 
17
- def self.parse(io)
18
- Yajl::Parser.new.parse(new(io))
17
+ # Helper method for one-off parsing from a bzip2-compressed stream
18
+ #
19
+ # See Yajl::Parser#parse for parameter documentation
20
+ def self.parse(input, options={}, buffer_size=nil, &block)
21
+ if input.is_a?(String)
22
+ input = StringIO.new(input)
23
+ end
24
+
25
+ Yajl::Parser.new(options).parse(new(input), buffer_size, &block)
19
26
  end
20
27
  end
21
28
  end
@@ -1,8 +1,12 @@
1
1
  # encoding: UTF-8
2
2
  module Yajl
3
3
  module Bzip2
4
- # === Yajl::Bzip2::StreamWriter
4
+ # A wrapper around the Bzip2::Writer class for easier JSON stream encoding
5
5
  class StreamWriter < ::Bzip2::Writer
6
+
7
+ # A helper method for encoding to a bzip2-compressed stream
8
+ #
9
+ # Look up Yajl::Encoder#encode for parameter documentation
6
10
  def self.encode(obj, io)
7
11
  Yajl::Encoder.new.encode(obj, new(io))
8
12
  end
@@ -1,25 +1,36 @@
1
1
  # encoding: UTF-8
2
2
  module Yajl
3
3
  module Deflate
4
- # === Yajl::Deflate::StreamReader
5
- #
6
4
  # This is a wrapper around Zlib::Inflate, creating a #read method that adheres
7
5
  # to the IO spec, allowing for two parameters (length, and buffer)
8
6
  class StreamReader < ::Zlib::Inflate
7
+
8
+ # Wrapper to the initialize method so we can set the initial IO to parse from.
9
9
  def initialize(io, options)
10
10
  @io = io
11
11
  super(options)
12
12
  end
13
13
 
14
+ # A helper method to allow use similar to IO#read
14
15
  def read(len=nil, buffer=nil)
15
16
  buffer.replace inflate(@io.read(len)) and return unless buffer.nil?
16
17
  inflate(@io.read(len))
17
18
  end
18
-
19
19
  alias :eof? :finished?
20
20
 
21
- def self.parse(io, options=nil)
22
- Yajl::Parser.new.parse(new(io, options))
21
+ # Helper method for one-off parsing from a deflate-compressed stream
22
+ #
23
+ # See Yajl::Parser#parse for parameter documentation
24
+ def self.parse(input, options={}, buffer_size=nil, &block)
25
+ if input.is_a?(String)
26
+ input = StringIO.new(input)
27
+ end
28
+
29
+ if options.is_a?(Hash)
30
+ Yajl::Parser.new(options).parse(new(input), buffer_size, &block)
31
+ elsif options.is_a?(Fixnum)
32
+ Yajl::Parser.new.parse(new(input, options), buffer_size, &block)
33
+ end
23
34
  end
24
35
  end
25
36
  end
@@ -1,13 +1,18 @@
1
1
  # encoding: UTF-8
2
2
  module Yajl
3
3
  module Deflate
4
- # === Yajl::Deflate::StreamWriter
4
+ # A wrapper around the Zlib::Deflate class for easier JSON stream parsing
5
5
  class StreamWriter < ::Zlib::Deflate
6
+
7
+ # A helper method to allow use similar to IO#write
6
8
  def write(str)
7
9
  deflate(str)
8
10
  str.size unless str.nil?
9
11
  end
10
12
 
13
+ # A helper method for one-off encoding to a deflate-compressed stream
14
+ #
15
+ # Look up Yajl::Encoder#encode for parameter documentation
11
16
  def self.encode(obj, io)
12
17
  Yajl::Encoder.new.encode(obj, new(io))
13
18
  end
@@ -1,11 +1,11 @@
1
1
  # encoding: UTF-8
2
2
  module Yajl
3
3
  module Gzip
4
- # === Yajl::GzipStreamReader
5
- #
6
4
  # This is a wrapper around Zlib::GzipReader to allow it's #read method to adhere
7
5
  # to the IO spec, allowing for two parameters (length, and buffer)
8
6
  class StreamReader < ::Zlib::GzipReader
7
+
8
+ # Wrapper method to allow use similar to IO#read
9
9
  def read(len=nil, buffer=nil)
10
10
  unless buffer.nil?
11
11
  buffer.replace super(len)
@@ -14,8 +14,14 @@ module Yajl
14
14
  super(len)
15
15
  end
16
16
 
17
- def self.parse(io)
18
- Yajl::Parser.new.parse(new(io))
17
+ # Helper method for one-off parsing from a gzip-compressed stream
18
+ #
19
+ # See Yajl::Parser#parse for parameter documentation
20
+ def self.parse(input, options={}, buffer_size=nil, &block)
21
+ if input.is_a?(String)
22
+ input = StringIO.new(input)
23
+ end
24
+ Yajl::Parser.new(options).parse(new(input), buffer_size, &block)
19
25
  end
20
26
  end
21
27
  end
@@ -1,8 +1,11 @@
1
1
  # encoding: UTF-8
2
2
  module Yajl
3
3
  module Gzip
4
- # === Yajl::Gzip::StreamWriter
4
+ # Wraper around the Zlib::GzipWriter class
5
5
  class StreamWriter < ::Zlib::GzipWriter
6
+ # A helper method for one-off encoding to a gzip-compressed stream
7
+ #
8
+ # Look up Yajl::Encoder#encode for parameter documentation
6
9
  def self.encode(obj, io)
7
10
  Yajl::Encoder.new.encode(obj, new(io))
8
11
  end
@@ -3,13 +3,10 @@ require 'socket' unless defined?(Socket)
3
3
  require 'yajl' unless defined?(Yajl::Parser)
4
4
 
5
5
  module Yajl
6
- # == Yajl::HttpStream
7
- #
8
6
  # This module is for making HTTP requests to which the response bodies (and possibly requests in the near future)
9
7
  # are streamed directly into Yajl.
10
8
  class HttpStream
11
- # === Yajl::HttpStream::InvalidContentType
12
- #
9
+
13
10
  # This Exception is thrown when an HTTP response isn't application/json
14
11
  # and therefore cannot be parsed.
15
12
  class InvalidContentType < Exception; end
@@ -22,8 +19,7 @@ module Yajl
22
19
  # 1. a raw socket is opened to the server/host provided
23
20
  # 2. the request is made using HTTP/1.0, Accept-encoding: gzip (deflate support coming soon, too)
24
21
  # 3. the response is read until the end of the headers
25
- # 4. the _socket itself_ is passed directly to Yajl, for direct parsing off the stream;
26
- # As it's being received over the wire!
22
+ # 4. the _socket itself_ is passed directly to Yajl, for direct parsing off the stream; As it's being received over the wire!
27
23
  def self.get(uri, opts = {}, &block)
28
24
  user_agent = opts.has_key?(['User-Agent']) ? opts['User-Agent'] : "Yajl::HttpStream #{Yajl::VERSION}"
29
25
 
data/lib/yajl.rb CHANGED
@@ -13,39 +13,65 @@ require 'yajl_ext'
13
13
  #
14
14
  # Ruby bindings to the excellent Yajl (Yet Another JSON Parser) ANSI C library.
15
15
  module Yajl
16
- VERSION = "0.5.2"
16
+ VERSION = "0.5.3"
17
17
 
18
- # == Yajl::Parser
19
- #
20
- # This class contains methods for parsing JSON directly from an IO object.
21
- # The only basic requirment currently is that the IO object respond to #read(len) and #eof?
22
- # The IO is parsed until a complete JSON object has been read and a ruby object will be returned.
23
18
  class Parser
24
- def self.parse(io, options={})
25
- new(options).parse(io)
19
+ # A helper method for parse-and-forget use-cases
20
+ #
21
+ # +io+ is the stream to parse JSON from
22
+ #
23
+ # The +options+ hash allows you to set two parsing options - :allow_comments and :check_utf8
24
+ #
25
+ # :allow_comments accepts a boolean will enable/disable checks for in-line comments in the JSON stream
26
+ #
27
+ # :check_utf8 accepts a boolean will enable/disable UTF8 validation for the JSON stream
28
+ def self.parse(io, options={}, read_bufsize=nil, &block)
29
+ new(options).parse(io, read_bufsize, &block)
26
30
  end
27
31
  end
28
32
 
29
- # == Yajl::Encoder
30
- #
31
- # This class contains methods for encoding a Ruby object into JSON, streaming it's output into an IO object.
32
- # The IO object need only respond to #write(str)
33
- # The JSON stream created is written to the IO in chunks, as it's being created.
34
33
  class Encoder
35
- def self.encode(obj, io, options={})
36
- new(options).encode(obj, io)
34
+ # A helper method for encode-and-forget use-cases
35
+ #
36
+ # Examples:
37
+ # Yajl::Encoder.encode(obj[, io, :pretty => true, :indent => "\t"])
38
+ #
39
+ # output = Yajl::Encoder.encode(obj[, :pretty => true, :indent => "\t"])
40
+ #
41
+ # +obj+ is a ruby object to encode to JSON format
42
+ #
43
+ # +io+ is the optional IO stream to encode the ruby object to.
44
+ # If +io+ isn't passed, the resulting JSON string is returned. If +io+ is passed, nil is returned.
45
+ #
46
+ # The +options+ hash allows you to set two encoding options - :pretty and :indent
47
+ #
48
+ # :pretty accepts a boolean and will enable/disable "pretty printing" the resulting output
49
+ #
50
+ # :indent accepts a string and will be used as the indent character(s) during the pretty print process
51
+ def self.encode(obj, *args, &block)
52
+ # TODO: this code smells, any ideas?
53
+ options = {}
54
+ io = nil
55
+ args.each do |arg|
56
+ if arg.is_a?(Hash)
57
+ options = arg
58
+ elsif arg.respond_to?(:read)
59
+ io = arg
60
+ end
61
+ end if args.any?
62
+ new(options).encode(obj, io, &block)
37
63
  end
38
64
  end
39
65
 
40
- # Deprecated - See Yajl::Parser and Yajl::Encoder
66
+ # DEPRECATED - See Yajl::Parser and Yajl::Encoder
41
67
  module Stream
42
- # Deprecated - See Yajl::Parser
68
+ # DEPRECATED - See Yajl::Parser
43
69
  def self.parse(io)
44
70
  STDERR.puts "WARNING: Yajl::Stream has be deprecated and will most likely be gone in the next release. Use the Yajl::Parser class instead."
45
71
  Parser.new.parse(io)
46
72
  end
47
73
 
48
- # Deprecated - See Yajl::Encoder
74
+ # DEPRECATED - See Yajl::Encoder
49
75
  def self.encode(obj, io)
50
76
  STDERR.puts "WARNING: Yajl::Stream has be deprecated and will most likely be gone in the next release. Use the Yajl::Encoder class instead."
51
77
  Encoder.new.encode(obj, io)
@@ -5,28 +5,57 @@ describe "Yajl JSON encoder" do
5
5
  FILES = Dir[File.dirname(__FILE__)+'/../../benchmark/subjects/*.json']
6
6
 
7
7
  FILES.each do |file|
8
- it "should encode #{File.basename(file)}" do
8
+ it "should encode #{File.basename(file)} to an IO" do
9
9
  # we don't care about testing the stream subject as it has multiple JSON strings in it
10
10
  if File.basename(file) != 'twitter_stream.json'
11
11
  input = File.new(File.expand_path(file), 'r')
12
12
  io = StringIO.new
13
- parser = Yajl::Parser.new
14
13
  encoder = Yajl::Encoder.new
15
-
16
- hash = parser.parse(input)
17
- output = encoder.encode(hash, io)
14
+ hash = Yajl::Parser.parse(input)
15
+ encoder.encode(hash, io)
18
16
  io.rewind
19
- hash2 = parser.parse(io)
20
-
17
+ hash2 = Yajl::Parser.parse(io)
21
18
  io.close
22
19
  input.close
23
-
24
20
  hash.should == hash2
25
21
  end
26
22
  end
27
23
  end
28
24
 
29
- it "should encode with :pretty turned on and a single space indent" do
25
+ FILES.each do |file|
26
+ it "should encode #{File.basename(file)} and return a String" do
27
+ # we don't care about testing the stream subject as it has multiple JSON strings in it
28
+ if File.basename(file) != 'twitter_stream.json'
29
+ input = File.new(File.expand_path(file), 'r')
30
+ encoder = Yajl::Encoder.new
31
+ hash = Yajl::Parser.parse(input)
32
+ output = encoder.encode(hash)
33
+ hash2 = Yajl::Parser.parse(output)
34
+ input.close
35
+ hash.should == hash2
36
+ end
37
+ end
38
+ end
39
+
40
+ FILES.each do |file|
41
+ it "should encode #{File.basename(file)} call the passed block, passing it a String" do
42
+ # we don't care about testing the stream subject as it has multiple JSON strings in it
43
+ if File.basename(file) != 'twitter_stream.json'
44
+ input = File.new(File.expand_path(file), 'r')
45
+ encoder = Yajl::Encoder.new
46
+ hash = Yajl::Parser.parse(input)
47
+ output = ''
48
+ encoder.encode(hash) do |json_str|
49
+ output = json_str
50
+ end
51
+ hash2 = Yajl::Parser.parse(output)
52
+ input.close
53
+ hash.should == hash2
54
+ end
55
+ end
56
+ end
57
+
58
+ it "should encode with :pretty turned on and a single space indent, to an IO" do
30
59
  output = "{\n \"foo\": {\n \"name\": \"bar\",\n \"id\": 1234\n }\n}\n"
31
60
  if RUBY_VERSION.include?('1.9') # FIXME
32
61
  output = "{\n \"foo\": {\n \"id\": 1234,\n \"name\": \"bar\"\n }\n}\n"
@@ -39,7 +68,18 @@ describe "Yajl JSON encoder" do
39
68
  io.read.should == output
40
69
  end
41
70
 
42
- it "should encode with :pretty turned on and a tab character indent" do
71
+ it "should encode with :pretty turned on and a single space indent, and return a String" do
72
+ output = "{\n \"foo\": {\n \"name\": \"bar\",\n \"id\": 1234\n }\n}\n"
73
+ if RUBY_VERSION.include?('1.9') # FIXME
74
+ output = "{\n \"foo\": {\n \"id\": 1234,\n \"name\": \"bar\"\n }\n}\n"
75
+ end
76
+ obj = {:foo => {:id => 1234, :name => "bar"}}
77
+ encoder = Yajl::Encoder.new(:pretty => true, :indent => ' ')
78
+ output = encoder.encode(obj)
79
+ output.should == output
80
+ end
81
+
82
+ it "should encode with :pretty turned on and a tab character indent, to an IO" do
43
83
  output = "{\n\t\"foo\": {\n\t\t\"name\": \"bar\",\n\t\t\"id\": 1234\n\t}\n}\n"
44
84
  if RUBY_VERSION.include?('1.9') # FIXME
45
85
  output = "{\n\t\"foo\": {\n\t\t\"id\": 1234,\n\t\t\"name\": \"bar\"\n\t}\n}\n"
@@ -52,7 +92,18 @@ describe "Yajl JSON encoder" do
52
92
  io.read.should == output
53
93
  end
54
94
 
55
- it "should encode with it's class method with :pretty and a tab character indent options set" do
95
+ it "should encode with :pretty turned on and a tab character indent, and return a String" do
96
+ output = "{\n\t\"foo\": {\n\t\t\"name\": \"bar\",\n\t\t\"id\": 1234\n\t}\n}\n"
97
+ if RUBY_VERSION.include?('1.9') # FIXME
98
+ output = "{\n\t\"foo\": {\n\t\t\"id\": 1234,\n\t\t\"name\": \"bar\"\n\t}\n}\n"
99
+ end
100
+ obj = {:foo => {:id => 1234, :name => "bar"}}
101
+ encoder = Yajl::Encoder.new(:pretty => true, :indent => "\t")
102
+ output = encoder.encode(obj)
103
+ output.should == output
104
+ end
105
+
106
+ it "should encode with it's class method with :pretty and a tab character indent options set, to an IO" do
56
107
  output = "{\n\t\"foo\": {\n\t\t\"name\": \"bar\",\n\t\t\"id\": 1234\n\t}\n}\n"
57
108
  if RUBY_VERSION.include?('1.9') # FIXME
58
109
  output = "{\n\t\"foo\": {\n\t\t\"id\": 1234,\n\t\t\"name\": \"bar\"\n\t}\n}\n"
@@ -64,7 +115,31 @@ describe "Yajl JSON encoder" do
64
115
  io.read.should == output
65
116
  end
66
117
 
67
- it "should encode multiple objects into a single stream" do
118
+ it "should encode with it's class method with :pretty and a tab character indent options set, and return a String" do
119
+ output = "{\n\t\"foo\": {\n\t\t\"name\": \"bar\",\n\t\t\"id\": 1234\n\t}\n}\n"
120
+ if RUBY_VERSION.include?('1.9') # FIXME
121
+ output = "{\n\t\"foo\": {\n\t\t\"id\": 1234,\n\t\t\"name\": \"bar\"\n\t}\n}\n"
122
+ end
123
+ obj = {:foo => {:id => 1234, :name => "bar"}}
124
+ output = Yajl::Encoder.encode(obj, :pretty => true, :indent => "\t")
125
+ output.should == output
126
+ end
127
+
128
+ it "should encode with it's class method with :pretty and a tab character indent options set, to a block" do
129
+ output = "{\n\t\"foo\": {\n\t\t\"name\": \"bar\",\n\t\t\"id\": 1234\n\t}\n}\n"
130
+ if RUBY_VERSION.include?('1.9') # FIXME
131
+ output = "{\n\t\"foo\": {\n\t\t\"id\": 1234,\n\t\t\"name\": \"bar\"\n\t}\n}\n"
132
+ end
133
+ obj = {:foo => {:id => 1234, :name => "bar"}}
134
+ output = ''
135
+ Yajl::Encoder.encode(obj, :pretty => true, :indent => "\t") do |json_str|
136
+ output = json_str
137
+ end
138
+ output.should == output
139
+ end
140
+
141
+ it "should encode multiple objects into a single stream, to an IO" do
142
+ pending "Find a better way to compare order of hash keys in resulting string"
68
143
  io = StringIO.new
69
144
  obj = {:foo => "bar", :baz => 1234}
70
145
  encoder = Yajl::Encoder.new
@@ -72,6 +147,25 @@ describe "Yajl JSON encoder" do
72
147
  encoder.encode(obj, io)
73
148
  end
74
149
  io.rewind
75
- io.read.should == "{\"foo\":\"bar\",\"baz\":1234}\n{\"foo\":\"bar\",\"baz\":1234}\n{\"foo\":\"bar\",\"baz\":1234}\n{\"foo\":\"bar\",\"baz\":1234}\n{\"foo\":\"bar\",\"baz\":1234}\n"
150
+ output = "{\"baz\":1234,\"foo\":\"bar\"}\n{\"baz\":1234,\"foo\":\"bar\"}\n{\"baz\":1234,\"foo\":\"bar\"}\n{\"baz\":1234,\"foo\":\"bar\"}\n{\"baz\":1234,\"foo\":\"bar\"}\n"
151
+ if RUBY_VERSION.include?('1.9') # FIXME
152
+ output = "{\"foo\":\"bar\",\"baz\":1234}\n{\"foo\":\"bar\",\"baz\":1234}\n{\"foo\":\"bar\",\"baz\":1234}\n{\"foo\":\"bar\",\"baz\":1234}\n{\"foo\":\"bar\",\"baz\":1234}\n"
153
+ end
154
+ io.read.should == output
155
+ end
156
+
157
+ it "should encode multiple objects into a single stream, and return a String" do
158
+ pending "Find a better way to compare order of hash keys in resulting string"
159
+ obj = {:foo => "bar", :baz => 1234}
160
+ encoder = Yajl::Encoder.new
161
+ json_output = ''
162
+ 5.times do
163
+ json_output << encoder.encode(obj)
164
+ end
165
+ output = "{\"baz\":1234,\"foo\":\"bar\"}\n{\"baz\":1234,\"foo\":\"bar\"}\n{\"baz\":1234,\"foo\":\"bar\"}\n{\"baz\":1234,\"foo\":\"bar\"}\n{\"baz\":1234,\"foo\":\"bar\"}\n"
166
+ if RUBY_VERSION.include?('1.9') # FIXME
167
+ output = "{\"foo\":\"bar\",\"baz\":1234}\n{\"foo\":\"bar\",\"baz\":1234}\n{\"foo\":\"bar\",\"baz\":1234}\n{\"foo\":\"bar\",\"baz\":1234}\n{\"foo\":\"bar\",\"baz\":1234}\n"
168
+ end
169
+ json_output.should == output
76
170
  end
77
171
  end
@@ -35,18 +35,34 @@ describe "ActiveSupport test cases" do
35
35
  }
36
36
 
37
37
  TESTS.each do |json, expected|
38
- it "should be able to parse #{json}" do
38
+ it "should be able to parse #{json} as an IO" do
39
39
  lambda {
40
40
  parser = Yajl::Parser.new
41
41
  parser.parse(StringIO.new(json)).should == expected
42
42
  }.should_not raise_error(Yajl::ParseError)
43
43
  end
44
44
  end
45
+
46
+ TESTS.each do |json, expected|
47
+ it "should be able to parse #{json} as a string" do
48
+ lambda {
49
+ parser = Yajl::Parser.new
50
+ parser.parse(json).should == expected
51
+ }.should_not raise_error(Yajl::ParseError)
52
+ end
53
+ end
45
54
 
46
- it "should fail parsing {: 1}" do
55
+ it "should fail parsing {: 1} as an IO" do
47
56
  lambda {
48
57
  parser = Yajl::Parser.new
49
58
  parser.parse(StringIO.new("{: 1}"))
50
59
  }.should raise_error(Yajl::ParseError)
51
60
  end
61
+
62
+ it "should fail parsing {: 1} as a string" do
63
+ lambda {
64
+ parser = Yajl::Parser.new
65
+ parser.parse("{: 1}")
66
+ }.should raise_error(Yajl::ParseError)
67
+ end
52
68
  end
@@ -4,7 +4,7 @@ require 'stringio'
4
4
 
5
5
  describe "Chunked parser" do
6
6
  before(:all) do
7
- @final = [{"abc" => 123}, {"def" => 456}]
7
+ @final = [{:abc => 123}, {:def => 456}]
8
8
  end
9
9
 
10
10
  before(:each) do
@@ -67,6 +67,32 @@ describe "Chunked parser" do
67
67
  path = File.expand_path(File.dirname(__FILE__) + '/../../benchmark/subjects/twitter_stream.json')
68
68
  json = File.new(path, 'r')
69
69
  @callback.should_receive(:call).exactly(430).times
70
- @parser.parse(json)
70
+ lambda {
71
+ @parser.parse(json)
72
+ }.should_not raise_error(Yajl::ParseError)
73
+ end
74
+
75
+ it "should parse twitter_stream.json and fire callback 430 times, with a block as the callback" do
76
+ path = File.expand_path(File.dirname(__FILE__) + '/../../benchmark/subjects/twitter_stream.json')
77
+ json = File.new(path, 'r')
78
+ @callback.should_receive(:call).exactly(0).times
79
+ @parser.on_parse_complete = nil
80
+ lambda {
81
+ times = 0
82
+ @parser.parse(json) do |hsh|
83
+ times += 1
84
+ end
85
+ times.should eql(430)
86
+ }.should_not raise_error(Yajl::ParseError)
87
+ end
88
+
89
+ it "should raise a Yajl::ParseError error if multiple JSON strings were found when no on_parse_complete callback assigned" do
90
+ path = File.expand_path(File.dirname(__FILE__) + '/../../benchmark/subjects/twitter_stream.json')
91
+ json = File.new(path, 'r')
92
+ @parser.on_parse_complete = nil
93
+ @callback.should_receive(:call).exactly(0).times
94
+ lambda {
95
+ @parser.parse(json)
96
+ }.should raise_error(Yajl::ParseError)
71
97
  end
72
98
  end
@@ -8,7 +8,7 @@ describe "Parsing JSON Fixtures" do
8
8
  FAILED = failed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
9
9
 
10
10
  FAILED.each do |name, source|
11
- it "should not be able to parse #{File.basename(name)}" do
11
+ it "should not be able to parse #{File.basename(name)} as an IO" do
12
12
  lambda {
13
13
  parser = Yajl::Parser.new
14
14
  parser.parse(StringIO.new(source))
@@ -16,12 +16,30 @@ describe "Parsing JSON Fixtures" do
16
16
  end
17
17
  end
18
18
 
19
+ FAILED.each do |name, source|
20
+ it "should not be able to parse #{File.basename(name)} as a string" do
21
+ lambda {
22
+ parser = Yajl::Parser.new
23
+ parser.parse(source)
24
+ }.should raise_error(Yajl::ParseError)
25
+ end
26
+ end
27
+
19
28
  PASSED.each do |name, source|
20
- it "should be able to parse #{File.basename(name)}" do
29
+ it "should be able to parse #{File.basename(name)} as an IO" do
21
30
  lambda {
22
31
  parser = Yajl::Parser.new
23
32
  parser.parse(StringIO.new(source))
24
33
  }.should_not raise_error(Yajl::ParseError)
25
34
  end
26
35
  end
36
+
37
+ PASSED.each do |name, source|
38
+ it "should be able to parse #{File.basename(name)} as a string" do
39
+ lambda {
40
+ parser = Yajl::Parser.new
41
+ parser.parse(source)
42
+ }.should_not raise_error(Yajl::ParseError)
43
+ end
44
+ end
27
45
  end
@@ -6,7 +6,7 @@ describe "One-off JSON examples" do
6
6
  infinity = (1.0/0)
7
7
  silence_warnings do
8
8
  parser = Yajl::Parser.new
9
- parser.parse(StringIO.new('{"key": 23456789012E666}')).should == {"key" => infinity}
9
+ parser.parse(StringIO.new('{"key": 23456789012E666}')).should == {:key => infinity}
10
10
  end
11
11
  end
12
12
 
@@ -27,17 +27,32 @@ describe "One-off JSON examples" do
27
27
  end
28
28
 
29
29
  it "should not parse invalid UTF8 with :check_utf8 set to true" do
30
- pending
31
- # not sure how to write this test yet
30
+ pending "not sure how to write this test yet"
32
31
  end
33
32
 
34
33
  it "should parse invalid UTF8 with :check_utf8 set to false" do
35
- pending
36
- # not sure how to write this test yet
34
+ pending "not sure how to write this test yet"
37
35
  end
38
36
 
39
- it "should parse using it's class method" do
37
+ it "should parse using it's class method, from an IO" do
40
38
  io = StringIO.new('{"key": 1234}')
41
- Yajl::Parser.parse(io).should == {"key" => 1234}
39
+ Yajl::Parser.parse(io).should == {:key => 1234}
40
+ end
41
+
42
+ it "should parse using it's class method, from an IO with string keys" do
43
+ parser = Yajl::Parser.new(:symbolize_keys => true)
44
+ parser.parse('{"key": 1234}').should == {:key => 1234}
45
+ end
46
+
47
+ it "should parse using it's class method, from a string" do
48
+ Yajl::Parser.parse('{"key": 1234}').should == {:key => 1234}
49
+ end
50
+
51
+ it "should parse using it's class method, from a string with a block" do
52
+ output = nil
53
+ Yajl::Parser.parse('{"key": 1234}') do |obj|
54
+ output = obj
55
+ end
56
+ output.should == {:key => 1234}
42
57
  end
43
58
  end
data/yajl-ruby.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{yajl-ruby}
5
- s.version = "0.5.2"
5
+ s.version = "0.5.3"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Brian Lopez", "Lloyd Hilaiel"]
9
- s.date = %q{2009-05-30}
9
+ s.date = %q{2009-06-07}
10
10
  s.email = %q{seniorlopez@gmail.com}
11
11
  s.extensions = ["ext/extconf.rb"]
12
12
  s.extra_rdoc_files = [
@@ -142,7 +142,7 @@ Gem::Specification.new do |s|
142
142
  s.homepage = %q{http://github.com/brianmario/yajl-ruby}
143
143
  s.rdoc_options = ["--charset=UTF-8"]
144
144
  s.require_paths = ["lib", "ext"]
145
- s.rubygems_version = %q{1.3.3}
145
+ s.rubygems_version = %q{1.3.4}
146
146
  s.summary = %q{Ruby C bindings to the excellent Yajl JSON stream-based parser library.}
147
147
  s.test_files = [
148
148
  "spec/encoding/encoding_spec.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brianmario-yajl-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Lopez
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-05-30 00:00:00 -07:00
13
+ date: 2009-06-07 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies: []
16
16