oj 2.8.1 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of oj might be problematic. Click here for more details.

@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b9dd0aba891fc55691f3a56dfeebb3a9b429a3e9
4
+ data.tar.gz: df3fc2acd752b893079e08bad70b7bcafee0df15
5
+ SHA512:
6
+ metadata.gz: 9e0c17e03b86f6e6b495ee3c75cfac64a400e04cd6c3fac52e444d2f48af44d4f69ea327a1ef4db39692fec5e16ca2270c80b4bcc8d66ba80da22a27d492d443
7
+ data.tar.gz: 91a9dc1eafd5182a4c1d6d930a9b673b88a3151cb9dff09580943f04eff7a323bb4c4fe25de1df34e427dab08afdc9dd587fd7a6b61d43d2ffe29e6f648ad949
data/README.md CHANGED
@@ -26,11 +26,12 @@ Follow [@peterohler on Twitter](http://twitter.com/#!/peterohler) for announceme
26
26
 
27
27
  [![Build Status](https://secure.travis-ci.org/ohler55/oj.png?branch=master)](http://travis-ci.org/ohler55/oj)
28
28
 
29
- ### Current Release 2.8.1
29
+ ### Current Release 2.9.0
30
30
 
31
- - Added additional argument to the register_odd function.
31
+ - Added support for detection and handling of String, Array, and Hash subclasses.
32
32
 
33
- - Fixed bug that failed to load on some uses of STDIN.
33
+ - Oj.load() can now take a block which will be yielded to on every object
34
+ parsed when used with a file or string with multiple JSON entries.
34
35
 
35
36
  [Older release notes](http://www.ohler.com/dev/oj_misc/release_notes.html).
36
37
 
@@ -70,16 +70,16 @@ static void dump_raw(const char *str, size_t cnt, Out out);
70
70
  static void dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out);
71
71
  static void dump_hex(uint8_t c, Out out);
72
72
  static void dump_str_comp(VALUE obj, Out out);
73
- static void dump_str_obj(VALUE obj, int depth, Out out);
73
+ static void dump_str_obj(VALUE obj, VALUE clas, int depth, Out out);
74
74
  static void dump_sym_comp(VALUE obj, Out out);
75
75
  static void dump_sym_obj(VALUE obj, Out out);
76
76
  static void dump_class_comp(VALUE obj, Out out);
77
77
  static void dump_class_obj(VALUE obj, Out out);
78
- static void dump_array(VALUE obj, int depth, Out out);
78
+ static void dump_array(VALUE obj, VALUE clas, int depth, Out out);
79
79
  static int hash_cb_strict(VALUE key, VALUE value, Out out);
80
80
  static int hash_cb_compat(VALUE key, VALUE value, Out out);
81
81
  static int hash_cb_object(VALUE key, VALUE value, Out out);
82
- static void dump_hash(VALUE obj, int depth, int mode, Out out);
82
+ static void dump_hash(VALUE obj, VALUE clas, int depth, int mode, Out out);
83
83
  static void dump_time(VALUE obj, Out out);
84
84
  static void dump_ruby_time(VALUE obj, Out out);
85
85
  static void dump_xml_time(VALUE obj, Out out);
@@ -545,12 +545,9 @@ dump_str_comp(VALUE obj, Out out) {
545
545
  }
546
546
 
547
547
  static void
548
- dump_str_obj(VALUE obj, int depth, Out out) {
549
- VALUE clas = rb_obj_class(obj);
550
- Odd odd;
551
-
552
- if (rb_cString != clas && 0 != (odd = oj_get_odd(clas))) {
553
- dump_odd(obj, odd, clas, depth + 1, out);
548
+ dump_str_obj(VALUE obj, VALUE clas, int depth, Out out) {
549
+ if (Qundef != clas && rb_cString != clas) {
550
+ dump_obj_attrs(obj, clas, 0, depth, out);
554
551
  } else {
555
552
  const char *s = rb_string_value_ptr((VALUE*)&obj);
556
553
  size_t len = RSTRING_LEN(obj);
@@ -602,7 +599,7 @@ dump_class_obj(VALUE obj, Out out) {
602
599
  }
603
600
 
604
601
  static void
605
- dump_array(VALUE a, int depth, Out out) {
602
+ dump_array(VALUE a, VALUE clas, int depth, Out out) {
606
603
  size_t size;
607
604
  int i, cnt;
608
605
  int d2 = depth + 1;
@@ -611,6 +608,10 @@ dump_array(VALUE a, int depth, Out out) {
611
608
  if (id < 0) {
612
609
  return;
613
610
  }
611
+ if (Qundef != clas && rb_cArray != clas) {
612
+ dump_obj_attrs(a, clas, 0, depth, out);
613
+ return;
614
+ }
614
615
  cnt = (int)RARRAY_LEN(a);
615
616
  *out->cur++ = '[';
616
617
  if (0 < id) {
@@ -819,7 +820,7 @@ hash_cb_object(VALUE key, VALUE value, Out out) {
819
820
  }
820
821
  fill_indent(out, depth);
821
822
  if (rb_type(key) == T_STRING) {
822
- dump_str_obj(key, depth, out);
823
+ dump_str_obj(key, Qundef, depth, out);
823
824
  *out->cur++ = ':';
824
825
  dump_val(value, depth, out);
825
826
  } else if (rb_type(key) == T_SYMBOL) {
@@ -873,10 +874,16 @@ hash_cb_object(VALUE key, VALUE value, Out out) {
873
874
  }
874
875
 
875
876
  static void
876
- dump_hash(VALUE obj, int depth, int mode, Out out) {
877
- int cnt = (int)RHASH_SIZE(obj);
878
- size_t size = depth * out->indent + 2;
877
+ dump_hash(VALUE obj, VALUE clas, int depth, int mode, Out out) {
878
+ int cnt;
879
+ size_t size;
879
880
 
881
+ if (Qundef != clas && rb_cHash != clas && ObjectMode == mode) {
882
+ dump_obj_attrs(obj, clas, 0, depth, out);
883
+ return;
884
+ }
885
+ cnt = (int)RHASH_SIZE(obj);
886
+ size = depth * out->indent + 2;
880
887
  if (out->end - out->cur <= 2) {
881
888
  grow(out, 2);
882
889
  }
@@ -1144,7 +1151,7 @@ dump_data_comp(VALUE obj, int depth, Out out) {
1144
1151
  if (T_HASH != rb_type(h)) {
1145
1152
  rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
1146
1153
  }
1147
- dump_hash(h, depth, out->opts->mode, out);
1154
+ dump_hash(h, Qundef, depth, out->opts->mode, out);
1148
1155
  } else if (rb_respond_to(obj, oj_as_json_id) && obj != (o2 = rb_funcall(obj, oj_as_json_id, 0))) {
1149
1156
  dump_val(o2, depth, out);
1150
1157
  } else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
@@ -1206,22 +1213,16 @@ dump_data_obj(VALUE obj, int depth, Out out) {
1206
1213
  *out->cur++ = '}';
1207
1214
  *out->cur = '\0';
1208
1215
  } else {
1209
- Odd odd = oj_get_odd(clas);
1210
-
1211
- if (0 == odd) {
1212
- if (oj_bigdecimal_class == clas) {
1213
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1216
+ if (oj_bigdecimal_class == clas) {
1217
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1214
1218
 
1215
- if (Yes == out->opts->bigdec_as_num) {
1216
- dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1217
- } else {
1218
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1219
- }
1219
+ if (Yes == out->opts->bigdec_as_num) {
1220
+ dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1220
1221
  } else {
1221
- dump_nil(out);
1222
+ dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1222
1223
  }
1223
1224
  } else {
1224
- dump_odd(obj, odd, clas, depth + 1, out);
1225
+ dump_nil(out);
1225
1226
  }
1226
1227
  }
1227
1228
  }
@@ -1234,7 +1235,7 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
1234
1235
  if (T_HASH != rb_type(h)) {
1235
1236
  rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
1236
1237
  }
1237
- dump_hash(h, depth, out->opts->mode, out);
1238
+ dump_hash(h, Qundef, depth, out->opts->mode, out);
1238
1239
  } else if (rb_respond_to(obj, oj_as_json_id)) {
1239
1240
  dump_val(rb_funcall(obj, oj_as_json_id, 0), depth, out);
1240
1241
  } else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
@@ -1268,13 +1269,7 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
1268
1269
 
1269
1270
  dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
1270
1271
  } else {
1271
- Odd odd = oj_get_odd(clas);
1272
-
1273
- if (0 == odd) {
1274
- dump_obj_attrs(obj, 0, 0, depth, out);
1275
- } else {
1276
- dump_odd(obj, odd, 0, depth + 1, out);
1277
- }
1272
+ dump_obj_attrs(obj, Qundef, 0, depth, out);
1278
1273
  }
1279
1274
  }
1280
1275
  *out->cur = '\0';
@@ -1286,22 +1281,39 @@ dump_obj_obj(VALUE obj, int depth, Out out) {
1286
1281
 
1287
1282
  if (0 <= id) {
1288
1283
  VALUE clas = rb_obj_class(obj);
1289
- Odd odd = oj_get_odd(clas);
1290
1284
 
1291
- if (0 == odd) {
1292
- if (oj_bigdecimal_class == clas) {
1293
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1285
+ if (oj_bigdecimal_class == clas) {
1286
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1294
1287
 
1295
- dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1296
- } else {
1297
- dump_obj_attrs(obj, clas, id, depth, out);
1298
- }
1288
+ dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
1299
1289
  } else {
1300
- dump_odd(obj, odd, clas, depth + 1, out);
1290
+ dump_obj_attrs(obj, clas, id, depth, out);
1301
1291
  }
1302
1292
  }
1303
1293
  }
1304
1294
 
1295
+ #ifdef RUBINIUS_RUBY
1296
+ static int
1297
+ isRbxHashAttr(const char *attr) {
1298
+ const char *hashAttrs[] = {
1299
+ "@capacity",
1300
+ "@max_entries",
1301
+ "@state",
1302
+ "@mask",
1303
+ "@size",
1304
+ "@entries",
1305
+ 0 };
1306
+ const char **ap;
1307
+
1308
+ for (ap = hashAttrs; 0 != *ap; ap++) {
1309
+ if (0 == strcmp(attr, *ap)) {
1310
+ return 1;
1311
+ }
1312
+ }
1313
+ return 0;
1314
+ }
1315
+ #endif
1316
+
1305
1317
  #if HAS_IVAR_HELPERS
1306
1318
  static int
1307
1319
  dump_attr_cb(ID key, VALUE value, Out out) {
@@ -1342,12 +1354,13 @@ static void
1342
1354
  dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
1343
1355
  size_t size = 0;
1344
1356
  int d2 = depth + 1;
1357
+ int type = rb_type(obj);
1345
1358
 
1346
1359
  if (out->end - out->cur <= 2) {
1347
1360
  grow(out, 2);
1348
1361
  }
1349
1362
  *out->cur++ = '{';
1350
- if (0 != clas) {
1363
+ if (Qundef != clas) {
1351
1364
  const char *class_name = rb_class2name(clas);
1352
1365
  int clen = (int)strlen(class_name);
1353
1366
 
@@ -1377,6 +1390,58 @@ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
1377
1390
  *out->cur++ = ':';
1378
1391
  dump_ulong(id, out);
1379
1392
  }
1393
+ switch (type) {
1394
+ case T_STRING:
1395
+ size = d2 * out->indent + 14;
1396
+ if (out->end - out->cur <= (long)size) {
1397
+ grow(out, size);
1398
+ }
1399
+ *out->cur++ = ',';
1400
+ fill_indent(out, d2);
1401
+ *out->cur++ = '"';
1402
+ *out->cur++ = 's';
1403
+ *out->cur++ = 'e';
1404
+ *out->cur++ = 'l';
1405
+ *out->cur++ = 'f';
1406
+ *out->cur++ = '"';
1407
+ *out->cur++ = ':';
1408
+ dump_cstr(rb_string_value_ptr((VALUE*)&obj), RSTRING_LEN(obj), 0, 0, out);
1409
+ break;
1410
+ case T_ARRAY:
1411
+ size = d2 * out->indent + 14;
1412
+ if (out->end - out->cur <= (long)size) {
1413
+ grow(out, size);
1414
+ }
1415
+ *out->cur++ = ',';
1416
+ fill_indent(out, d2);
1417
+ *out->cur++ = '"';
1418
+ *out->cur++ = 's';
1419
+ *out->cur++ = 'e';
1420
+ *out->cur++ = 'l';
1421
+ *out->cur++ = 'f';
1422
+ *out->cur++ = '"';
1423
+ *out->cur++ = ':';
1424
+ dump_array(obj, Qundef, depth + 1, out);
1425
+ break;
1426
+ case T_HASH:
1427
+ size = d2 * out->indent + 14;
1428
+ if (out->end - out->cur <= (long)size) {
1429
+ grow(out, size);
1430
+ }
1431
+ *out->cur++ = ',';
1432
+ fill_indent(out, d2);
1433
+ *out->cur++ = '"';
1434
+ *out->cur++ = 's';
1435
+ *out->cur++ = 'e';
1436
+ *out->cur++ = 'l';
1437
+ *out->cur++ = 'f';
1438
+ *out->cur++ = '"';
1439
+ *out->cur++ = ':';
1440
+ dump_hash(obj, Qundef, depth + 1, out->opts->mode, out);
1441
+ break;
1442
+ default:
1443
+ break;
1444
+ }
1380
1445
  {
1381
1446
  int cnt;
1382
1447
  #if HAS_IVAR_HELPERS
@@ -1390,7 +1455,7 @@ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
1390
1455
 
1391
1456
  cnt = (int)RARRAY_LEN(vars);
1392
1457
  #endif
1393
- if (0 != clas && 0 < cnt) {
1458
+ if (Qundef != clas && 0 < cnt) {
1394
1459
  *out->cur++ = ',';
1395
1460
  }
1396
1461
  out->depth = depth + 1;
@@ -1402,12 +1467,17 @@ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
1402
1467
  #else
1403
1468
  size = d2 * out->indent + 1;
1404
1469
  for (i = cnt; 0 < i; i--, np++) {
1470
+ vid = rb_to_id(*np);
1471
+ attr = rb_id2name(vid);
1472
+ #ifdef RUBINIUS_RUBY
1473
+ if (T_HASH == type && isRbxHashAttr(attr)) {
1474
+ continue;
1475
+ }
1476
+ #endif
1405
1477
  if (out->end - out->cur <= (long)size) {
1406
1478
  grow(out, size);
1407
1479
  }
1408
- vid = rb_to_id(*np);
1409
1480
  fill_indent(out, d2);
1410
- attr = rb_id2name(vid);
1411
1481
  if ('@' == *attr) {
1412
1482
  attr++;
1413
1483
  dump_cstr(attr, strlen(attr), 0, 0, out);
@@ -1478,7 +1548,7 @@ dump_struct_comp(VALUE obj, int depth, Out out) {
1478
1548
  if (T_HASH != rb_type(h)) {
1479
1549
  rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
1480
1550
  }
1481
- dump_hash(h, depth, out->opts->mode, out);
1551
+ dump_hash(h, Qundef, depth, out->opts->mode, out);
1482
1552
  } else if (rb_respond_to(obj, oj_to_json_id)) {
1483
1553
  volatile VALUE rs = rb_funcall(obj, oj_to_json_id, 0);
1484
1554
  const char *s;
@@ -1571,7 +1641,7 @@ dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
1571
1641
  grow(out, 2);
1572
1642
  }
1573
1643
  *out->cur++ = '{';
1574
- if (0 != clas) {
1644
+ if (Qundef != clas) {
1575
1645
  const char *class_name = rb_class2name(clas);
1576
1646
  int clen = (int)strlen(class_name);
1577
1647
 
@@ -1674,11 +1744,11 @@ dump_val(VALUE obj, int depth, Out out) {
1674
1744
  case NullMode:
1675
1745
  case CompatMode: dump_str_comp(obj, out); break;
1676
1746
  case ObjectMode:
1677
- default: dump_str_obj(obj, depth, out); break;
1747
+ default: dump_str_obj(obj, clas, depth, out); break;
1678
1748
  }
1679
1749
  break;
1680
- case T_ARRAY: dump_array(obj, depth, out); break;
1681
- case T_HASH: dump_hash(obj, depth, out->opts->mode, out); break;
1750
+ case T_ARRAY: dump_array(obj, clas, depth, out); break;
1751
+ case T_HASH: dump_hash(obj, clas, depth, out->opts->mode, out); break;
1682
1752
  #if (defined T_RATIONAL && defined RRATIONAL)
1683
1753
  case T_RATIONAL:
1684
1754
  #endif
@@ -330,6 +330,13 @@ hash_set_cstr(ParseInfo pi, const char *key, size_t klen, const char *str, size_
330
330
  case T_HASH:
331
331
  rb_hash_aset(parent->val, hash_key(pi, key, klen, parent->k1), str_to_value(pi, str, len, orig));
332
332
  break;
333
+ case T_STRING:
334
+ if (4 == klen && 's' == *key && 'e' == key[1] && 'l' == key[2] && 'f' == key[3]) {
335
+ rb_funcall(parent->val, oj_replace_id, 1, str_to_value(pi, str, len, orig));
336
+ } else {
337
+ set_obj_ivar(parent, key, klen, str_to_value(pi, str, len, orig));
338
+ }
339
+ break;
333
340
  case T_OBJECT:
334
341
  set_obj_ivar(parent, key, klen, str_to_value(pi, str, len, orig));
335
342
  break;
@@ -413,19 +420,36 @@ hash_set_value(ParseInfo pi, const char *key, size_t klen, VALUE value) {
413
420
  }
414
421
  break;
415
422
  case T_HASH:
416
- if (3 <= klen && '#' == key[1] && T_ARRAY == rb_type(value)) {
417
- long len = RARRAY_LEN(value);
418
- VALUE *a = RARRAY_PTR(value);
423
+
424
+ if (rb_cHash != rb_obj_class(parent->val)) {
425
+ if (4 == klen && 's' == *key && 'e' == key[1] && 'l' == key[2] && 'f' == key[3]) {
426
+ rb_funcall(parent->val, oj_replace_id, 1, value);
427
+ } else {
428
+ set_obj_ivar(parent, key, klen, value);
429
+ }
430
+ } else {
431
+ if (3 <= klen && '#' == key[1] && T_ARRAY == rb_type(value)) {
432
+ long len = RARRAY_LEN(value);
433
+ VALUE *a = RARRAY_PTR(value);
419
434
 
420
- if (2 != len) {
421
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
422
- return;
435
+ if (2 != len) {
436
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
437
+ return;
438
+ }
439
+ rb_hash_aset(parent->val, *a, a[1]);
440
+ } else {
441
+ rb_hash_aset(parent->val, hash_key(pi, key, klen, parent->k1), value);
423
442
  }
424
- rb_hash_aset(parent->val, *a, a[1]);
443
+ }
444
+ break;
445
+ case T_ARRAY:
446
+ if (4 == klen && 's' == *key && 'e' == key[1] && 'l' == key[2] && 'f' == key[3]) {
447
+ rb_funcall(parent->val, oj_replace_id, 1, value);
425
448
  } else {
426
- rb_hash_aset(parent->val, hash_key(pi, key, klen, parent->k1), value);
449
+ set_obj_ivar(parent, key, klen, value);
427
450
  }
428
451
  break;
452
+ case T_STRING: // for subclassed strings
429
453
  case T_OBJECT:
430
454
  set_obj_ivar(parent, key, klen, value);
431
455
  break;
@@ -450,7 +474,6 @@ hash_set_value(ParseInfo pi, const char *key, size_t klen, VALUE value) {
450
474
  }
451
475
  }
452
476
 
453
-
454
477
  static VALUE
455
478
  start_hash(ParseInfo pi) {
456
479
  return Qnil;
@@ -67,6 +67,7 @@ ID oj_json_create_id;
67
67
  ID oj_length_id;
68
68
  ID oj_new_id;
69
69
  ID oj_read_id;
70
+ ID oj_replace_id;
70
71
  ID oj_string_id;
71
72
  ID oj_to_hash_id;
72
73
  ID oj_to_json_id;
@@ -536,6 +537,10 @@ oj_parse_options(VALUE ropts, Options copts) {
536
537
  * Object type. These two are not the same since the JSON Object type can have
537
538
  * repeating entries with the same key and Ruby Hash can not.
538
539
  *
540
+ * When used with a document that has multiple JSON elements the block, if
541
+ * any, will be yielded to. If no block then the last element read will be
542
+ * returned.
543
+ *
539
544
  * Raises an exception if the JSON is malformed or the classes specified are not
540
545
  * valid. If the input is not a valid JSON document (an empty string is not a
541
546
  * valid JSON document) an exception is raised.
@@ -553,6 +558,10 @@ oj_parse_options(VALUE ropts, Options copts) {
553
558
  * on the :create_id value. It is not compatible in every way to every other
554
559
  * parser though as each parser has it's own variations.
555
560
  *
561
+ * When used with a document that has multiple JSON elements the block, if
562
+ * any, will be yielded to. If no block then the last element read will be
563
+ * returned.
564
+ *
556
565
  * Raises an exception if the JSON is malformed or the classes specified are not
557
566
  * valid. If the input is not a valid JSON document (an empty string is not a
558
567
  * valid JSON document) an exception is raised.
@@ -570,6 +579,10 @@ oj_parse_options(VALUE ropts, Options copts) {
570
579
  * or dumped Object. The :auto_define and :circular options have meaning with
571
580
  * this parsing mode.
572
581
  *
582
+ * When used with a document that has multiple JSON elements the block, if
583
+ * any, will be yielded to. If no block then the last element read will be
584
+ * returned.
585
+ *
573
586
  * Raises an exception if the JSON is malformed or the classes specified are not
574
587
  * valid. If the input is not a valid JSON document (an empty string is not a
575
588
  * valid JSON document) an exception is raised.
@@ -602,6 +615,10 @@ oj_parse_options(VALUE ropts, Options copts) {
602
615
  * specified are not valid. If the string input is not a valid JSON document (an
603
616
  * empty string is not a valid JSON document) an exception is raised.
604
617
  *
618
+ * When used with a document that has multiple JSON elements the block, if
619
+ * any, will be yielded to. If no block then the last element read will be
620
+ * returned.
621
+ *
605
622
  * @param [String|IO] json JSON String or an Object that responds to read()
606
623
  * @param [Hash] options load options (same as default_options)
607
624
  */
@@ -652,6 +669,10 @@ load(int argc, VALUE *argv, VALUE self) {
652
669
  * specified are not valid. If the string input is not a valid JSON document (an
653
670
  * empty string is not a valid JSON document) an exception is raised.
654
671
  *
672
+ * When used with a document that has multiple JSON elements the block, if
673
+ * any, will be yielded to. If no block then the last element read will be
674
+ * returned.
675
+ *
655
676
  * If the input file is not a valid JSON document (an empty file is not a valid
656
677
  * JSON document) an exception is raised.
657
678
  *
@@ -1857,6 +1878,7 @@ void Init_oj() {
1857
1878
  oj_length_id = rb_intern("length");
1858
1879
  oj_new_id = rb_intern("new");
1859
1880
  oj_read_id = rb_intern("read");
1881
+ oj_replace_id = rb_intern("replace");
1860
1882
  oj_string_id = rb_intern("string");
1861
1883
  oj_to_hash_id = rb_intern("to_hash");
1862
1884
  oj_to_json_id = rb_intern("to_json");
@@ -262,6 +262,7 @@ extern ID oj_json_create_id;
262
262
  extern ID oj_length_id;
263
263
  extern ID oj_new_id;
264
264
  extern ID oj_read_id;
265
+ extern ID oj_replace_id;
265
266
  extern ID oj_string_id;
266
267
  extern ID oj_to_hash_id;
267
268
  extern ID oj_to_json_id;
@@ -640,6 +640,20 @@ oj_parse2(ParseInfo pi) {
640
640
  if (err_has(&pi->err)) {
641
641
  return;
642
642
  }
643
+ if (Qundef != pi->proc && stack_empty(&pi->stack)) {
644
+ if (Qnil == pi->proc) {
645
+ rb_yield(stack_head_val(&pi->stack));
646
+ } else {
647
+ #if HAS_PROC_WITH_BLOCK
648
+ VALUE args[1];
649
+
650
+ *args = stack_head_val(&pi->stack);
651
+ rb_proc_call_with_block(pi->proc, 1, args, Qnil);
652
+ #else
653
+ rb_raise(rb_eNotImpError, "Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.");
654
+ #endif
655
+ }
656
+ }
643
657
  }
644
658
  }
645
659
 
@@ -734,6 +748,11 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len) {
734
748
  if (2 == argc) {
735
749
  oj_parse_options(argv[1], &pi->options);
736
750
  }
751
+ if (rb_block_given_p()) {
752
+ pi->proc = Qnil;
753
+ } else {
754
+ pi->proc = Qundef;
755
+ }
737
756
  pi->cbc = (void*)0;
738
757
  if (0 != json) {
739
758
  pi->json = json;
@@ -803,7 +822,7 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len) {
803
822
  if (No == pi->options.allow_gc) {
804
823
  rb_gc_enable();
805
824
  }
806
- {
825
+ if (!err_has(&pi->err)) {
807
826
  // If the stack is not empty then the JSON terminated early.
808
827
  Val v;
809
828
 
@@ -63,6 +63,7 @@ typedef struct _ParseInfo {
63
63
  struct _ValStack stack;
64
64
  CircArray circ_array;
65
65
  int expect_value;
66
+ VALUE proc;
66
67
  VALUE (*start_hash)(struct _ParseInfo *pi);
67
68
  void (*end_hash)(struct _ParseInfo *pi);
68
69
  void (*hash_set_cstr)(struct _ParseInfo *pi, const char *key, size_t klen, const char *str, size_t len, const char *orig);
@@ -1,4 +1,4 @@
1
- /* sajkey.c
1
+ /* saj.c
2
2
  * Copyright (c) 2012, Peter Ohler
3
3
  * All rights reserved.
4
4
  *
@@ -644,7 +644,7 @@ respond_to(VALUE obj, ID method) {
644
644
  }
645
645
 
646
646
  static void
647
- sajkey_parse(VALUE handler, char *json) {
647
+ saj_parse(VALUE handler, char *json) {
648
648
  volatile VALUE obj = Qnil;
649
649
  struct _ParseInfo pi;
650
650
 
@@ -692,11 +692,11 @@ sajkey_parse(VALUE handler, char *json) {
692
692
  }
693
693
  }
694
694
 
695
- /* call-seq: sajkey_parse(handler, io)
695
+ /* call-seq: saj_parse(handler, io)
696
696
  *
697
697
  * Parses an IO stream or file containing an JSON document. Raises an exception
698
698
  * if the JSON is malformed.
699
- * @param [Oj::SajKey] handler SajKey (responds to Oj::SajKey methods) like handler
699
+ * @param [Oj::Saj] handler Saj (responds to Oj::Saj methods) like handler
700
700
  * @param [IO|String] io IO Object to read from
701
701
  */
702
702
  VALUE
@@ -749,7 +749,7 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
749
749
  rb_raise(rb_eArgError, "saj_parse() expected a String or IO Object.");
750
750
  }
751
751
  }
752
- sajkey_parse(*argv, json);
752
+ saj_parse(*argv, json);
753
753
  xfree(json);
754
754
 
755
755
  return Qnil;
@@ -211,6 +211,11 @@ oj_sc_parse(int argc, VALUE *argv, VALUE self) {
211
211
  if (3 == argc) {
212
212
  oj_parse_options(argv[2], &pi.options);
213
213
  }
214
+ if (rb_block_given_p()) {
215
+ pi.proc = Qnil;
216
+ } else {
217
+ pi.proc = Qundef;
218
+ }
214
219
  pi.cbc = (void*)handler;
215
220
 
216
221
  pi.start_hash = respond_to(handler, oj_hash_start_id) ? start_hash : noop_start;
@@ -255,6 +260,7 @@ oj_sc_parse(int argc, VALUE *argv, VALUE self) {
255
260
  } else {
256
261
  VALUE clas = rb_obj_class(input);
257
262
  volatile VALUE s;
263
+ int fd;
258
264
 
259
265
  if (oj_stringio_class == clas) {
260
266
  s = rb_funcall2(input, oj_string_id, 0, 0);
@@ -262,8 +268,9 @@ oj_sc_parse(int argc, VALUE *argv, VALUE self) {
262
268
  #ifndef JRUBY_RUBY
263
269
  #if !IS_WINDOWS
264
270
  // JRuby gets confused with what is the real fileno.
265
- } else if (rb_respond_to(input, oj_fileno_id) && Qnil != (s = rb_funcall(input, oj_fileno_id, 0))) {
266
- int fd = FIX2INT(s);
271
+ } else if (rb_respond_to(input, oj_fileno_id) &&
272
+ Qnil != (s = rb_funcall(input, oj_fileno_id, 0)) &&
273
+ 0 != (fd = FIX2INT(s))) {
267
274
  ssize_t cnt;
268
275
  size_t len = lseek(fd, 0, SEEK_END);
269
276
 
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '2.8.1'
4
+ VERSION = '2.9.0'
5
5
  end
@@ -102,6 +102,48 @@ class Strung < String
102
102
  end
103
103
  end
104
104
 
105
+ class AutoStrung < String
106
+ attr_accessor :safe
107
+
108
+ def initialize(str, safe)
109
+ super(str)
110
+ @safe = safe
111
+ end
112
+
113
+ def eql?(o)
114
+ self.class == o.class && super(o) && @safe == o.safe
115
+ end
116
+ alias == eql?
117
+ end
118
+
119
+ class AutoArray < Array
120
+ attr_accessor :safe
121
+
122
+ def initialize(a, safe)
123
+ super(a)
124
+ @safe = safe
125
+ end
126
+
127
+ def eql?(o)
128
+ self.class == o.class && super(o) && @safe == o.safe
129
+ end
130
+ alias == eql?
131
+ end
132
+
133
+ class AutoHash < Hash
134
+ attr_accessor :safe
135
+
136
+ def initialize(h, safe)
137
+ super(h)
138
+ @safe = safe
139
+ end
140
+
141
+ def eql?(o)
142
+ self.class == o.class && super(o) && @safe == o.safe
143
+ end
144
+ alias == eql?
145
+ end
146
+
105
147
  def hash_eql(h1, h2)
106
148
  return false if h1.size != h2.size
107
149
  h1.keys.each do |k|
@@ -418,7 +460,24 @@ class ObjectJuice < ::Test::Unit::TestCase
418
460
  def test_odd_string
419
461
  Oj.register_odd(Strung, Strung, :create, :to_s, 'safe?')
420
462
  s = Strung.new("Pete", true)
421
- dump_and_load(Strung.new("Pete", true), false)
463
+ dump_and_load(s, false)
464
+ end
465
+
466
+ def test_auto_string
467
+ s = AutoStrung.new("Pete", true)
468
+ dump_and_load(s, false)
469
+ end
470
+
471
+ def test_auto_array
472
+ a = AutoArray.new([1, 'abc', nil], true)
473
+ dump_and_load(a, false)
474
+ end
475
+
476
+ def test_auto_hash
477
+ h = AutoHash.new(nil, true)
478
+ h['a'] = 1
479
+ h['b'] = 2
480
+ dump_and_load(h, false)
422
481
  end
423
482
 
424
483
  def dump_and_load(obj, trace=false)
@@ -206,6 +206,22 @@ class ScpTest < ::Test::Unit::TestCase
206
206
  [:add_value, {}]], handler.calls)
207
207
  end
208
208
 
209
+ def test_double
210
+ handler = AllHandler.new()
211
+ json = %{{"one":true,"two":false}{"three":true,"four":false}}
212
+ Oj.sc_parse(handler, json)
213
+ assert_equal([[:hash_start],
214
+ [:hash_set, 'one', true],
215
+ [:hash_set, 'two', false],
216
+ [:hash_end],
217
+ [:add_value, {}],
218
+ [:hash_start],
219
+ [:hash_set, 'three', true],
220
+ [:hash_set, 'four', false],
221
+ [:hash_end],
222
+ [:add_value, {}]], handler.calls)
223
+ end
224
+
209
225
  def test_none
210
226
  handler = NoHandler.new()
211
227
  Oj.sc_parse(handler, $json)
@@ -247,6 +247,13 @@ class StrictJuice < ::Test::Unit::TestCase
247
247
  assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
248
248
  end
249
249
 
250
+ def test_double
251
+ json = %{{ "x": 1}{ "y": 2}}
252
+ results = []
253
+ Oj.load(json, :mode => :strict) { |x| results << x }
254
+
255
+ assert_equal([{ 'x' => 1 }, { 'y' => 2 }], results)
256
+ end
250
257
 
251
258
  def dump_and_load(obj, trace=false)
252
259
  json = Oj.dump(obj, :indent => 2)
metadata CHANGED
@@ -1,60 +1,64 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: oj
3
- version: !ruby/object:Gem::Version
4
- version: 2.8.1
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.9.0
6
5
  platform: ruby
7
- authors:
6
+ authors:
8
7
  - Peter Ohler
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-04-22 00:00:00.000000000 Z
11
+
12
+ date: 2014-05-01 00:00:00 Z
13
13
  dependencies: []
14
- description: ! 'The fastest JSON parser and object serializer. '
14
+
15
+ description: "The fastest JSON parser and object serializer. "
15
16
  email: peter@ohler.com
16
17
  executables: []
17
- extensions:
18
+
19
+ extensions:
18
20
  - ext/oj/extconf.rb
19
- extra_rdoc_files:
21
+ extra_rdoc_files:
22
+ - README.md
23
+ files:
24
+ - LICENSE
20
25
  - README.md
21
- files:
22
- - lib/oj/bag.rb
23
- - lib/oj/error.rb
24
- - lib/oj/mimic.rb
25
- - lib/oj/saj.rb
26
- - lib/oj/schandler.rb
27
- - lib/oj/version.rb
28
- - lib/oj.rb
29
- - ext/oj/extconf.rb
30
26
  - ext/oj/buf.h
31
- - ext/oj/cache8.h
32
- - ext/oj/circarray.h
33
- - ext/oj/encode.h
34
- - ext/oj/err.h
35
- - ext/oj/hash.h
36
- - ext/oj/odd.h
37
- - ext/oj/oj.h
38
- - ext/oj/parse.h
39
- - ext/oj/resolve.h
40
- - ext/oj/val_stack.h
41
27
  - ext/oj/cache8.c
28
+ - ext/oj/cache8.h
42
29
  - ext/oj/circarray.c
30
+ - ext/oj/circarray.h
43
31
  - ext/oj/compat.c
44
32
  - ext/oj/dump.c
33
+ - ext/oj/encode.h
45
34
  - ext/oj/err.c
35
+ - ext/oj/err.h
36
+ - ext/oj/extconf.rb
46
37
  - ext/oj/fast.c
47
38
  - ext/oj/hash.c
39
+ - ext/oj/hash.h
48
40
  - ext/oj/hash_test.c
49
41
  - ext/oj/object.c
50
42
  - ext/oj/odd.c
43
+ - ext/oj/odd.h
51
44
  - ext/oj/oj.c
45
+ - ext/oj/oj.h
52
46
  - ext/oj/parse.c
47
+ - ext/oj/parse.h
53
48
  - ext/oj/resolve.c
49
+ - ext/oj/resolve.h
54
50
  - ext/oj/saj.c
55
51
  - ext/oj/scp.c
56
52
  - ext/oj/strict.c
57
53
  - ext/oj/val_stack.c
54
+ - ext/oj/val_stack.h
55
+ - lib/oj.rb
56
+ - lib/oj/bag.rb
57
+ - lib/oj/error.rb
58
+ - lib/oj/mimic.rb
59
+ - lib/oj/saj.rb
60
+ - lib/oj/schandler.rb
61
+ - lib/oj/version.rb
58
62
  - test/debian_test.rb
59
63
  - test/files.rb
60
64
  - test/perf.rb
@@ -68,6 +72,7 @@ files:
68
72
  - test/perf_scp.rb
69
73
  - test/perf_simple.rb
70
74
  - test/perf_strict.rb
75
+ - test/sample.rb
71
76
  - test/sample/change.rb
72
77
  - test/sample/dir.rb
73
78
  - test/sample/doc.rb
@@ -80,7 +85,6 @@ files:
80
85
  - test/sample/rect.rb
81
86
  - test/sample/shape.rb
82
87
  - test/sample/text.rb
83
- - test/sample.rb
84
88
  - test/sample_json.rb
85
89
  - test/test_compat.rb
86
90
  - test/test_fast.rb
@@ -93,36 +97,34 @@ files:
93
97
  - test/test_strict.rb
94
98
  - test/test_writer.rb
95
99
  - test/tests.rb
96
- - LICENSE
97
- - README.md
98
100
  homepage: http://www.ohler.com/oj
99
- licenses:
101
+ licenses:
100
102
  - MIT
101
103
  - GPL-3.0
104
+ metadata: {}
105
+
102
106
  post_install_message:
103
- rdoc_options:
107
+ rdoc_options:
104
108
  - --main
105
109
  - README.md
106
- require_paths:
110
+ require_paths:
107
111
  - lib
108
112
  - ext
109
- required_ruby_version: !ruby/object:Gem::Requirement
110
- none: false
111
- requirements:
112
- - - ! '>='
113
- - !ruby/object:Gem::Version
114
- version: '0'
115
- required_rubygems_version: !ruby/object:Gem::Requirement
116
- none: false
117
- requirements:
118
- - - ! '>='
119
- - !ruby/object:Gem::Version
120
- version: '0'
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - &id001
116
+ - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: "0"
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - *id001
121
122
  requirements: []
123
+
122
124
  rubyforge_project: oj
123
- rubygems_version: 1.8.23
125
+ rubygems_version: 2.2.2
124
126
  signing_key:
125
- specification_version: 3
127
+ specification_version: 4
126
128
  summary: A fast JSON parser and serializer.
127
129
  test_files: []
128
- has_rdoc: true
130
+