oj 2.1.6 → 2.1.7

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: df465b98a85257f57cccf95bc93cb68de76fece4
4
- data.tar.gz: 4092e954c5cb1bf3a1cf7a4d8729f4713bbee8bd
3
+ metadata.gz: 70957d2247e339a9ad73f19ad36df9da6979848b
4
+ data.tar.gz: b2bdcf86cf0920e464560066b7054c997656b53c
5
5
  SHA512:
6
- metadata.gz: af5ae926a363a12681930df3dee6718f0ada11e43dbe54eef5916265efe65d99fa8d79ce8cdbb2158c2a6aa2111c236f61e6684dcd9699a680efc9c2be603231
7
- data.tar.gz: 07fa5429999fbbce344b93d3c2afebbd275241ec0e8e733fa8a7ceb9d0da32b34b90effa57881ed68c99c8c0a8cd3ed25919c1747d2f5dc51625d1740ca7c3b8
6
+ metadata.gz: d4ec5a8db9aa7fd4d9c3721ccbd09f7aa357583d4a8c75f071ee007356b9371164ab75dd2ea897117cb332cf9d6158f47e0c8338aacb3b77c389e0ca08c099a6
7
+ data.tar.gz: 5849f4c725ee2db364c41a034aead4e1c547ae53bb090db10e6bbb65013a19812cd024586fcba1f0d58285ded793a5f5c4675103f095cd57f184ba36beca6fe8
data/README.md CHANGED
@@ -20,9 +20,13 @@ Follow [@peterohler on Twitter](http://twitter.com/#!/peterohler) for announceme
20
20
 
21
21
  [![Build Status](https://secure.travis-ci.org/ohler55/oj.png?branch=master)](http://travis-ci.org/ohler55/oj)
22
22
 
23
- ### Current Release 2.1.6
23
+ ### Current Release 2.1.7
24
24
 
25
- - Added Oj.to_stream() for dumping JSON to an IO object.
25
+ - Added support for NaN and -NaN to address issue #102. This is not according to the JSON spec but seems to be expected.
26
+
27
+ - Added require for rational if the Ruby version is 1.8.7 to address issue #104.
28
+
29
+ - Added Rails re-call of Oj.dump in the to_json() method which caused loops with Rational objects to fix issue #108 and #105.
26
30
 
27
31
  [Older release notes](http://www.ohler.com/dev/oj_misc/release_notes.html).
28
32
 
@@ -105,6 +105,10 @@ static void dump_leaf_float(Leaf leaf, Out out);
105
105
  static void dump_leaf_array(Leaf leaf, int depth, Out out);
106
106
  static void dump_leaf_hash(Leaf leaf, int depth, Out out);
107
107
 
108
+ // These are used to detect rails re-call of Oj.dump() inside the to_json()
109
+ // method. It is not thread safe.
110
+ static VALUE last_obj = Qundef;
111
+ static int oj_rails_hack = -1;
108
112
 
109
113
  static const char hex_chars[17] = "0123456789abcdef";
110
114
 
@@ -429,6 +433,9 @@ dump_float(VALUE obj, Out out) {
429
433
  } else if (-OJ_INFINITY == d) {
430
434
  strcpy(buf, "-Infinity");
431
435
  cnt = 9;
436
+ } else if (isnan(d)) {
437
+ strcpy(buf, "NaN");
438
+ cnt = 3;
432
439
  } else if (d == (double)(long long int)d) {
433
440
  cnt = sprintf(buf, "%.1f", d); // used sprintf due to bug in snprintf
434
441
  } else {
@@ -585,6 +592,10 @@ dump_array(VALUE a, int depth, Out out) {
585
592
  if (id < 0) {
586
593
  return;
587
594
  }
595
+
596
+ #if HAS_GC_GUARD
597
+ RB_GC_GUARD(a);
598
+ #endif
588
599
  np = RARRAY_PTR(a);
589
600
  cnt = (int)RARRAY_LEN(a);
590
601
  *out->cur++ = '[';
@@ -1121,10 +1132,16 @@ dump_data_comp(VALUE obj, int depth, Out out) {
1121
1132
  dump_hash(h, depth, out->opts->mode, out);
1122
1133
  } else if (rb_respond_to(obj, oj_as_json_id) && obj != (o2 = rb_funcall(obj, oj_as_json_id, 0))) {
1123
1134
  dump_val(o2, depth, out);
1124
- } else if (rb_respond_to(obj, oj_to_json_id)) {
1125
- VALUE rs = rb_funcall(obj, oj_to_json_id, 0);
1126
- const char *s = StringValuePtr(rs);
1127
- int len = (int)RSTRING_LEN(rs);
1135
+ } else if (rb_respond_to(obj, oj_to_json_id) && (!oj_rails_hack || last_obj != obj)) {
1136
+ VALUE rs;
1137
+ const char *s;
1138
+ int len;
1139
+
1140
+ last_obj = obj;
1141
+ rs = rb_funcall(obj, oj_to_json_id, 0);
1142
+ last_obj = Qundef;
1143
+ s = StringValuePtr(rs);
1144
+ len = (int)RSTRING_LEN(rs);
1128
1145
 
1129
1146
  if (out->end - out->cur <= len + 1) {
1130
1147
  grow(out, len);
@@ -1199,6 +1216,9 @@ dump_data_obj(VALUE obj, int depth, Out out) {
1199
1216
 
1200
1217
  static void
1201
1218
  dump_obj_comp(VALUE obj, int depth, Out out) {
1219
+ #if HAS_GC_GUARD
1220
+ RB_GC_GUARD(obj);
1221
+ #endif
1202
1222
  if (rb_respond_to(obj, oj_to_hash_id)) {
1203
1223
  VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
1204
1224
 
@@ -1208,10 +1228,16 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
1208
1228
  dump_hash(h, depth, out->opts->mode, out);
1209
1229
  } else if (rb_respond_to(obj, oj_as_json_id)) {
1210
1230
  dump_val(rb_funcall(obj, oj_as_json_id, 0), depth, out);
1211
- } else if (rb_respond_to(obj, oj_to_json_id)) {
1212
- VALUE rs = rb_funcall(obj, oj_to_json_id, 0);
1213
- const char *s = StringValuePtr(rs);
1214
- int len = (int)RSTRING_LEN(rs);
1231
+ } else if (rb_respond_to(obj, oj_to_json_id) && (!oj_rails_hack || last_obj != obj)) {
1232
+ VALUE rs;
1233
+ const char *s;
1234
+ int len;
1235
+
1236
+ last_obj = obj;
1237
+ rs = rb_funcall(obj, oj_to_json_id, 0);
1238
+ last_obj = Qundef;
1239
+ s = StringValuePtr(rs);
1240
+ len = (int)RSTRING_LEN(rs);
1215
1241
 
1216
1242
  if (out->end - out->cur <= len + 1) {
1217
1243
  grow(out, len);
@@ -1661,6 +1687,9 @@ oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
1661
1687
  oj_cache8_new(&out->circ_cache);
1662
1688
  }
1663
1689
  out->indent = copts->indent;
1690
+ if (0 > oj_rails_hack) {
1691
+ oj_rails_hack = (rb_const_defined_at(rb_cObject, rb_intern("ActiveSupport")));
1692
+ }
1664
1693
  dump_val(obj, 0, out);
1665
1694
  if (Yes == copts->circular) {
1666
1695
  oj_cache8_delete(out->circ_cache);
@@ -1702,7 +1731,7 @@ void
1702
1731
  oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
1703
1732
  char buf[4096];
1704
1733
  struct _Out out;
1705
- size_t size;
1734
+ ssize_t size;
1706
1735
  VALUE clas = rb_obj_class(stream);
1707
1736
  VALUE s;
1708
1737
 
@@ -30,6 +30,7 @@ dflags = {
30
30
  'HAS_PROC_WITH_BLOCK' => ('ruby' == type && (('1' == version[0] && '9' == version[1]) || '2' <= version[0])) ? 1 : 0,
31
31
  'HAS_GC_GUARD' => ('jruby' != type && 'rubinius' != type) ? 1 : 0,
32
32
  'HAS_TOP_LEVEL_ST_H' => ('ree' == type || ('ruby' == type && '1' == version[0] && '8' == version[1])) ? 1 : 0,
33
+ 'NEEDS_RATIONAL' => ('ruby' == type && '1' == version[0] && '8' == version[1]) ? 1 : 0,
33
34
  'IS_WINDOWS' => is_windows ? 1 : 0,
34
35
  'SAFE_CACHE' => is_windows ? 0 : 1,
35
36
  }
@@ -199,7 +199,7 @@ hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, VALUE value) {
199
199
  #if HAS_ENCODING_SUPPORT
200
200
  s = rb_struct_alloc_noinit(sc);
201
201
  #else
202
- s = rb_struct_new(sc);
202
+ s = rb_struct_new(sc, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil);
203
203
  #endif
204
204
  sv = RSTRUCT_PTR(s);
205
205
  if (RSTRUCT_LEN(s) < len - 1) {
@@ -1007,6 +1007,9 @@ mimic_create_id(VALUE self, VALUE id) {
1007
1007
  * raise an Exception but will not have any effect. The method can also be
1008
1008
  * called after the json gem is loaded. The necessary methods on the json gem
1009
1009
  * will be replaced with Oj methods.
1010
+ *
1011
+ * Note that this also sets the default options of :mode to :compat and
1012
+ * :ascii_only to true.
1010
1013
  */
1011
1014
  static VALUE
1012
1015
  define_mimic_json(int argc, VALUE *argv, VALUE self) {
@@ -1114,6 +1117,9 @@ void Init_oj() {
1114
1117
  rb_require("time");
1115
1118
  rb_require("date");
1116
1119
  rb_require("bigdecimal");
1120
+ #if NEEDS_RATIONAL
1121
+ rb_require("rational");
1122
+ #endif
1117
1123
  rb_require("stringio");
1118
1124
  #if HAS_ENCODING_SUPPORT
1119
1125
  oj_utf8_encoding = rb_enc_find("UTF-8");
@@ -387,6 +387,7 @@ read_num(ParseInfo pi) {
387
387
  ni.dec_cnt = 0;
388
388
  ni.big = 0;
389
389
  ni.infinity = 0;
390
+ ni.nan = 0;
390
391
  ni.neg = 0;
391
392
 
392
393
  if ('-' == *pi->cur) {
@@ -402,66 +403,73 @@ read_num(ParseInfo pi) {
402
403
  }
403
404
  pi->cur += 8;
404
405
  ni.infinity = 1;
405
- return;
406
- }
407
- for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
408
- ni.dec_cnt++;
409
- if (ni.big) {
410
- ni.big++;
411
- } else {
412
- int d = (*pi->cur - '0');
413
-
414
- if (0 == d) {
415
- zero_cnt++;
416
- } else {
417
- zero_cnt = 0;
418
- }
419
- ni.i = ni.i * 10 + d;
420
- if (LONG_MAX <= ni.i || DEC_MAX < ni.dec_cnt - zero_cnt) {
421
- ni.big = 1;
422
- }
406
+ } else if ('N' == *pi->cur || 'n' == *pi->cur) {
407
+ if ('a' != pi->cur[1] || ('N' != pi->cur[2] && 'n' != pi->cur[2])) {
408
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
409
+ return;
423
410
  }
424
- }
425
- if ('.' == *pi->cur) {
426
- pi->cur++;
411
+ pi->cur += 3;
412
+ ni.nan = 1;
413
+ } else {
427
414
  for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
428
- int d = (*pi->cur - '0');
429
-
430
- if (0 == d) {
431
- zero_cnt++;
415
+ ni.dec_cnt++;
416
+ if (ni.big) {
417
+ ni.big++;
432
418
  } else {
433
- zero_cnt = 0;
419
+ int d = (*pi->cur - '0');
420
+
421
+ if (0 == d) {
422
+ zero_cnt++;
423
+ } else {
424
+ zero_cnt = 0;
425
+ }
426
+ ni.i = ni.i * 10 + d;
427
+ if (LONG_MAX <= ni.i || DEC_MAX < ni.dec_cnt - zero_cnt) {
428
+ ni.big = 1;
429
+ }
434
430
  }
435
- ni.dec_cnt++;
436
- ni.num = ni.num * 10 + d;
437
- ni.div *= 10;
438
- if (LONG_MAX <= ni.div || DEC_MAX < ni.dec_cnt - zero_cnt) {
439
- ni.big = 1;
431
+ }
432
+ if ('.' == *pi->cur) {
433
+ pi->cur++;
434
+ for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
435
+ int d = (*pi->cur - '0');
436
+
437
+ if (0 == d) {
438
+ zero_cnt++;
439
+ } else {
440
+ zero_cnt = 0;
441
+ }
442
+ ni.dec_cnt++;
443
+ ni.num = ni.num * 10 + d;
444
+ ni.div *= 10;
445
+ if (LONG_MAX <= ni.div || DEC_MAX < ni.dec_cnt - zero_cnt) {
446
+ ni.big = 1;
447
+ }
440
448
  }
441
449
  }
442
- }
443
- if ('e' == *pi->cur || 'E' == *pi->cur) {
444
- int eneg = 0;
450
+ if ('e' == *pi->cur || 'E' == *pi->cur) {
451
+ int eneg = 0;
445
452
 
446
- pi->cur++;
447
- if ('-' == *pi->cur) {
448
- pi->cur++;
449
- eneg = 1;
450
- } else if ('+' == *pi->cur) {
451
453
  pi->cur++;
452
- }
453
- for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
454
- ni.exp = ni.exp * 10 + (*pi->cur - '0');
455
- if (EXP_MAX <= ni.exp) {
456
- ni.big = 1;
454
+ if ('-' == *pi->cur) {
455
+ pi->cur++;
456
+ eneg = 1;
457
+ } else if ('+' == *pi->cur) {
458
+ pi->cur++;
459
+ }
460
+ for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
461
+ ni.exp = ni.exp * 10 + (*pi->cur - '0');
462
+ if (EXP_MAX <= ni.exp) {
463
+ ni.big = 1;
464
+ }
465
+ }
466
+ if (eneg) {
467
+ ni.exp = -ni.exp;
457
468
  }
458
469
  }
459
- if (eneg) {
460
- ni.exp = -ni.exp;
461
- }
470
+ ni.dec_cnt -= zero_cnt;
471
+ ni.len = pi->cur - ni.str;
462
472
  }
463
- ni.dec_cnt -= zero_cnt;
464
- ni.len = pi->cur - ni.str;
465
473
  if (Yes == pi->options.bigdec_load) {
466
474
  ni.big = 1;
467
475
  }
@@ -603,6 +611,7 @@ oj_parse2(ParseInfo pi) {
603
611
  case '8':
604
612
  case '9':
605
613
  case 'I':
614
+ case 'N':
606
615
  pi->cur--;
607
616
  read_num(pi);
608
617
  break;
@@ -613,7 +622,12 @@ oj_parse2(ParseInfo pi) {
613
622
  read_false(pi);
614
623
  break;
615
624
  case 'n':
616
- read_null(pi);
625
+ if ('u' == *pi->cur) {
626
+ read_null(pi);
627
+ } else {
628
+ pi->cur--;
629
+ read_num(pi);
630
+ }
617
631
  break;
618
632
  case '/':
619
633
  skip_comment(pi);
@@ -641,6 +655,8 @@ oj_num_as_value(NumInfo ni) {
641
655
  } else {
642
656
  rnum = rb_float_new(OJ_INFINITY);
643
657
  }
658
+ } else if (ni->nan) {
659
+ rnum = rb_float_new(NAN);
644
660
  } else if (1 == ni->div && 0 == ni->exp) { // fixnum
645
661
  if (ni->big) {
646
662
  if (256 > ni->len) {
@@ -48,6 +48,7 @@ typedef struct _NumInfo {
48
48
  int dec_cnt;
49
49
  int big;
50
50
  int infinity;
51
+ int nan;
51
52
  int neg;
52
53
  } *NumInfo;
53
54
 
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '2.1.6'
4
+ VERSION = '2.1.7'
5
5
  end
@@ -217,7 +217,7 @@ class ScpTest < ::Test::Unit::TestCase
217
217
  begin
218
218
  Oj.sc_parse(handler, json)
219
219
  rescue Exception => e
220
- assert_equal("unexpected character at line 1, column 6 [parse.c:625]", e.message)
220
+ assert_equal("unexpected character at line 1, column 6 [parse.c:639]", e.message)
221
221
  end
222
222
  end
223
223
 
@@ -105,6 +105,20 @@ class Range
105
105
  end
106
106
  end # Range
107
107
 
108
+ # define the symbol
109
+ class ActiveSupport
110
+ end
111
+
112
+ class RailsLike
113
+ attr_accessor :x
114
+ def initialize(x)
115
+ @x = x
116
+ end
117
+ def to_json(options = nil)
118
+ Oj.dump(self, :mode => :compat)
119
+ end
120
+ end # RailsLike
121
+
108
122
  class Juice < ::Test::Unit::TestCase
109
123
 
110
124
  def test0_get_options
@@ -185,6 +199,12 @@ class Juice < ::Test::Unit::TestCase
185
199
  dump_and_load(2.48e16, false)
186
200
  dump_and_load(2.48e100 * 1.0e10, false)
187
201
  dump_and_load(-2.48e100 * 1.0e10, false)
202
+ dump_and_load(1/0.0, false)
203
+ # NaN does not always == NaN
204
+ json = Oj.dump(0/0.0)
205
+ assert_equal('NaN', json)
206
+ loaded = Oj.load(json);
207
+ assert_equal(true, loaded.nan?)
188
208
  end
189
209
 
190
210
  def test_string
@@ -1002,6 +1022,14 @@ class Juice < ::Test::Unit::TestCase
1002
1022
  assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
1003
1023
  end
1004
1024
 
1025
+ # Rails re-call test. Active support recalls the json dumper when the to_json
1026
+ # method is called. This mimics that and verifies Oj detects it.
1027
+ def test_rails_like
1028
+ obj = RailsLike.new(3)
1029
+ json = Oj.dump(obj, :mode => :compat)
1030
+ assert_equal('{"x":3}', json)
1031
+ end
1032
+
1005
1033
  def dump_and_load(obj, trace=false)
1006
1034
  json = Oj.dump(obj, :indent => 2)
1007
1035
  puts json if trace
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.6
4
+ version: 2.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-07 00:00:00.000000000 Z
11
+ date: 2013-10-19 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'The fastest JSON parser and object serializer. '
14
14
  email: peter@ohler.com