oj 3.9.0 → 3.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/ext/oj/compat.c +5 -5
  4. data/ext/oj/custom.c +7 -3
  5. data/ext/oj/dump.c +9 -12
  6. data/ext/oj/dump_compat.c +8 -10
  7. data/ext/oj/dump_object.c +18 -11
  8. data/ext/oj/dump_strict.c +6 -5
  9. data/ext/oj/extconf.rb +5 -0
  10. data/ext/oj/mimic_json.c +15 -3
  11. data/ext/oj/object.c +6 -2
  12. data/ext/oj/oj.c +47 -28
  13. data/ext/oj/oj.h +4 -2
  14. data/ext/oj/parse.c +22 -3
  15. data/ext/oj/parse.h +1 -0
  16. data/ext/oj/rails.c +38 -4
  17. data/ext/oj/sparse.c +5 -0
  18. data/ext/oj/util.c +5 -5
  19. data/ext/oj/wab.c +9 -9
  20. data/lib/oj/version.rb +1 -1
  21. data/pages/Rails.md +59 -21
  22. data/test/activesupport5/abstract_unit.rb +45 -0
  23. data/test/activesupport5/decoding_test.rb +68 -60
  24. data/test/activesupport5/encoding_test.rb +111 -96
  25. data/test/activesupport5/encoding_test_cases.rb +33 -25
  26. data/test/activesupport5/test_helper.rb +43 -21
  27. data/test/activesupport5/time_zone_test_helpers.rb +18 -3
  28. data/test/activesupport6/abstract_unit.rb +44 -0
  29. data/test/activesupport6/decoding_test.rb +133 -0
  30. data/test/activesupport6/encoding_test.rb +507 -0
  31. data/test/activesupport6/encoding_test_cases.rb +98 -0
  32. data/test/activesupport6/test_common.rb +17 -0
  33. data/test/activesupport6/test_helper.rb +163 -0
  34. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  35. data/test/bar.rb +8 -11
  36. data/test/baz.rb +16 -0
  37. data/test/foo.rb +39 -8
  38. data/test/test_compat.rb +0 -7
  39. data/test/test_custom.rb +25 -6
  40. data/test/test_integer_range.rb +1 -2
  41. data/test/test_object.rb +12 -3
  42. data/test/test_rails.rb +26 -0
  43. data/test/test_strict.rb +24 -1
  44. data/test/test_various.rb +41 -62
  45. data/test/tests.rb +1 -0
  46. metadata +23 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bda4706ea9e50aef4bdfcd1c9708566b573da4e4c9f000ddf12f3e403a821c88
4
- data.tar.gz: 56dbfecea94b2c0e43aa4193aed7749e30d55e4c288770a193aac3c57850bea2
3
+ metadata.gz: '0569d0892b3b9d79f0edf0866d843adb9abc21046838be295164884edb930b61'
4
+ data.tar.gz: ff04edae94da97c911b19c93015035df9fb3ce89162c0afc1df036e0f675e65f
5
5
  SHA512:
6
- metadata.gz: 73176c366166bb1b6fbe0a20501db677679ef7af8aaab547b9dadbba5856370d844f3595b06208a05c9efd9437d6f167298a0c73ef41fed905017a9aa5b2f764
7
- data.tar.gz: 3c4731da851590f39cc71bd2cce9fb9e1e987ec22b54e2bfc948f8004d78dcb0b2bde464b784500958352f8dec045ba8e2ce3804769426821c7c03a11d95ce07
6
+ metadata.gz: 2d5a28e21b6c37bc5a11917512906c040cf8a0b038a07f23c0447a1705986fe98803191f6d2462e4f0ec67f7f3694cd9c61d0149d782f0a5e5f6b6d36a88d372
7
+ data.tar.gz: 789c0fdfe061476ee5e5c4bbe84519e7b1a6a015436e7fbe2cdb5f7922d6eef8de5914bd61488be4c297633e053f2a217630f7358796de9ba746aa6935466983
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # [![{}j](http://www.ohler.com/dev/images/oj_comet_64.svg)](http://www.ohler.com/oj) gem
2
2
 
3
- [![Build Status](https://img.shields.io/travis/ohler55/oj/master.svg)](http://travis-ci.org/ohler55/oj?branch=master) [![AppVeyor](https://img.shields.io/appveyor/ci/ohler55/oj/master.svg)](https://ci.appveyor.com/project/ohler55/oj) ![Gem](https://img.shields.io/gem/v/oj.svg) ![Gem](https://img.shields.io/gem/dt/oj.svg) [![SemVer compatibility](https://api.dependabot.com/badges/compatibility_score?dependency-name=oj&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=oj&package-manager=bundler&version-scheme=semver) [![TideLift](https://tidelift.com/badges/github/ohler55/oj)](https://tidelift.com/subscription/pkg/rubygems-oj?utm_source=rubygems-oj&utm_medium=referral&utm_campaign=readme)
3
+ [![Build Status](https://img.shields.io/travis/ohler55/oj/master.svg?logo=travis)](http://travis-ci.org/ohler55/oj?branch=master) [![AppVeyor](https://img.shields.io/appveyor/ci/ohler55/oj/master.svg?logo=appveyor)](https://ci.appveyor.com/project/ohler55/oj) ![Gem](https://img.shields.io/gem/v/oj.svg) ![Gem](https://img.shields.io/gem/dt/oj.svg) [![SemVer compatibility](https://api.dependabot.com/badges/compatibility_score?dependency-name=oj&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=oj&package-manager=bundler&version-scheme=semver) [![TideLift](https://tidelift.com/badges/github/ohler55/oj)](https://tidelift.com/subscription/pkg/rubygems-oj?utm_source=rubygems-oj&utm_medium=referral&utm_campaign=readme)
4
4
 
5
5
  A *fast* JSON parser and Object marshaller as a Ruby gem.
6
6
 
@@ -42,7 +42,7 @@ gem 'oj'
42
42
 
43
43
  ## Support
44
44
 
45
- [Get supported Oj with a Tidelift Subscription.](https://tidelift.com/subscription/pkg/rubygems-oj?utm_source=rubygems-oj&utm_medium=referral&utm_campaign=readme)
45
+ [Get supported Oj with a Tidelift Subscription.](https://tidelift.com/subscription/pkg/rubygems-oj?utm_source=rubygems-oj&utm_medium=referral&utm_campaign=readme) Security updates are [supported](https://tidelift.com/security).
46
46
 
47
47
  ## Further Reading
48
48
 
@@ -65,7 +65,7 @@ hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *o
65
65
  static VALUE
66
66
  start_hash(ParseInfo pi) {
67
67
  volatile VALUE h;
68
-
68
+
69
69
  if (Qnil != pi->options.hash_class) {
70
70
  h = rb_class_new_instance(0, NULL, pi->options.hash_class);
71
71
  } else {
@@ -87,7 +87,7 @@ end_hash(struct _parseInfo *pi) {
87
87
  clas = oj_name2class(pi, parent->classname, parent->clen, 0, rb_eArgError);
88
88
  if (Qundef != clas) { // else an error
89
89
  ID creatable = rb_intern("json_creatable?");
90
-
90
+
91
91
  if (!rb_respond_to(clas, creatable) || Qtrue == rb_funcall(clas, creatable, 0)) {
92
92
  parent->val = rb_funcall(clas, oj_json_create_id, 1, parent->val);
93
93
  }
@@ -146,7 +146,7 @@ add_num(ParseInfo pi, NumInfo ni) {
146
146
  static void
147
147
  hash_set_num(struct _parseInfo *pi, Val parent, NumInfo ni) {
148
148
  volatile VALUE rval = oj_num_as_value(ni);
149
-
149
+
150
150
  if (!oj_use_hash_alt && rb_cHash != rb_obj_class(parent->val)) {
151
151
  // The rb_hash_set would still work but the unit tests for the
152
152
  // json gem require the less efficient []= method be called to set
@@ -192,7 +192,7 @@ static void
192
192
  array_append_num(ParseInfo pi, NumInfo ni) {
193
193
  Val parent = stack_peek(&pi->stack);
194
194
  volatile VALUE rval = oj_num_as_value(ni);
195
-
195
+
196
196
  if (!oj_use_array_alt && rb_cArray != rb_obj_class(parent->val)) {
197
197
  // The rb_ary_push would still work but the unit tests for the json
198
198
  // gem require the less efficient << method be called to push the
@@ -274,7 +274,7 @@ oj_compat_load(int argc, VALUE *argv, VALUE self) {
274
274
  pi.options.nilnil = Yes;
275
275
  pi.options.empty_string = Yes;
276
276
  oj_set_compat_callbacks(&pi);
277
-
277
+
278
278
  if (T_STRING == rb_type(*argv)) {
279
279
  return oj_pi_parse(argc, argv, &pi, 0, 0, false);
280
280
  } else {
@@ -297,7 +297,8 @@ static struct _code codes[] = {
297
297
  };
298
298
 
299
299
  static int
300
- hash_cb(VALUE key, VALUE value, Out out) {
300
+ hash_cb(VALUE key, VALUE value, VALUE ov) {
301
+ Out out = (Out)ov;
301
302
  int depth = out->depth;
302
303
 
303
304
  if (oj_dump_ignore(out->opts, value)) {
@@ -592,7 +593,8 @@ dump_common(VALUE obj, int depth, Out out) {
592
593
  }
593
594
 
594
595
  static int
595
- dump_attr_cb(ID key, VALUE value, Out out) {
596
+ dump_attr_cb(ID key, VALUE value, VALUE ov) {
597
+ Out out = (Out)ov;
596
598
  int depth = out->depth;
597
599
  size_t size;
598
600
  const char *attr;
@@ -609,6 +611,8 @@ dump_attr_cb(ID key, VALUE value, Out out) {
609
611
  // the key name is NULL. Not an empty string but NULL.
610
612
  if (NULL == attr) {
611
613
  attr = "";
614
+ } else if (Yes == out->opts->ignore_under && '@' == *attr && '_' == attr[1]) {
615
+ return ST_CONTINUE;
612
616
  }
613
617
  if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
614
618
  return ST_CONTINUE;
@@ -1069,7 +1073,7 @@ hash_set_num(struct _parseInfo *pi, Val kval, NumInfo ni) {
1069
1073
  oj_set_obj_ivar(parent, kval, rval);
1070
1074
  break;
1071
1075
  case T_HASH:
1072
- if (4 == parent->klen && NULL != parent->key && rb_cTime == parent->clas && 0 == strncmp("time", parent->key, 4)) {
1076
+ if (4 == parent->klen && NULL != parent->key && rb_cTime == parent->clas && 0 != ni->div && 0 == strncmp("time", parent->key, 4)) {
1073
1077
  int64_t nsec = ni->num * 1000000000LL / ni->div;
1074
1078
 
1075
1079
  if (ni->neg) {
@@ -548,7 +548,7 @@ oj_dump_xml_time(VALUE obj, Out out) {
548
548
  tzhour = (int)(tzsecs / 3600);
549
549
  tzmin = (int)(tzsecs / 60) - (tzhour * 60);
550
550
  }
551
- if (0 == nsec || 0 == out->opts->sec_prec) {
551
+ if ((0 == nsec && !out->opts->sec_prec_set) || 0 == out->opts->sec_prec) {
552
552
  if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
553
553
  sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec);
554
554
  oj_dump_cstr(buf, 20, 0, 0, out);
@@ -1023,8 +1023,8 @@ oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1023
1023
  int neg = 0;
1024
1024
  bool dump_as_string = false;
1025
1025
 
1026
- if (out->opts->integer_range_max != 0 && out->opts->integer_range_min != 0 &&
1027
- (out->opts->integer_range_max < num || out->opts->integer_range_min > num)) {
1026
+ if (out->opts->int_range_max != 0 && out->opts->int_range_min != 0 &&
1027
+ (out->opts->int_range_max < num || out->opts->int_range_min > num)) {
1028
1028
  dump_as_string = true;
1029
1029
  }
1030
1030
  if (0 > num) {
@@ -1062,23 +1062,20 @@ void
1062
1062
  oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1063
1063
  volatile VALUE rs = rb_big2str(obj, 10);
1064
1064
  int cnt = (int)RSTRING_LEN(rs);
1065
- bool dump_as_string = false;
1065
+ bool dump_as_string = false;
1066
1066
 
1067
- if (out->opts->integer_range_max != 0 || out->opts->integer_range_min != 0) { // Bignum cannot be inside of Fixnum range
1067
+ if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
1068
1068
  dump_as_string = true;
1069
1069
  assure_size(out, cnt + 2);
1070
1070
  *out->cur++ = '"';
1071
- } else {
1071
+ } else {
1072
1072
  assure_size(out, cnt);
1073
- }
1074
-
1073
+ }
1075
1074
  memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
1076
1075
  out->cur += cnt;
1077
-
1078
- if(dump_as_string) {
1076
+ if (dump_as_string) {
1079
1077
  *out->cur++ = '"';
1080
- }
1081
-
1078
+ }
1082
1079
  *out->cur = '\0';
1083
1080
  }
1084
1081
 
@@ -648,7 +648,8 @@ dump_float(VALUE obj, int depth, Out out, bool as_ok) {
648
648
  }
649
649
 
650
650
  static int
651
- hash_cb(VALUE key, VALUE value, Out out) {
651
+ hash_cb(VALUE key, VALUE value, VALUE ov) {
652
+ Out out = (Out)ov;
652
653
  int depth = out->depth;
653
654
 
654
655
  if (out->omit_nil && Qnil == value) {
@@ -870,7 +871,7 @@ dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
870
871
  // this must use to_s to pass the json gem unit tests.
871
872
  volatile VALUE rs;
872
873
  int cnt;
873
- bool dump_as_string = false;
874
+ bool dump_as_string = false;
874
875
 
875
876
  if (use_bignum_alt) {
876
877
  rs = rb_big2str(obj, 10);
@@ -880,21 +881,18 @@ dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
880
881
  rb_check_type(rs, T_STRING);
881
882
  cnt = (int)RSTRING_LEN(rs);
882
883
 
883
- if (out->opts->integer_range_min != 0 || out->opts->integer_range_max != 0) {
884
+ if (out->opts->int_range_min != 0 || out->opts->int_range_max != 0) {
884
885
  dump_as_string = true; // Bignum cannot be inside of Fixnum range
885
886
  assure_size(out, cnt + 2);
886
887
  *out->cur++ = '"';
887
- } else {
888
+ } else {
888
889
  assure_size(out, cnt);
889
- }
890
-
890
+ }
891
891
  memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
892
892
  out->cur += cnt;
893
-
894
- if(dump_as_string) {
893
+ if (dump_as_string) {
895
894
  *out->cur++ = '"';
896
- }
897
-
895
+ }
898
896
  *out->cur = '\0';
899
897
  }
900
898
 
@@ -56,7 +56,7 @@ dump_data(VALUE obj, int depth, Out out, bool as_ok) {
56
56
  }
57
57
  } else {
58
58
  long id = oj_check_circular(obj, out);
59
-
59
+
60
60
  if (0 <= id) {
61
61
  dump_obj_attrs(obj, clas, id, depth, out);
62
62
  }
@@ -72,7 +72,7 @@ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
72
72
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
73
73
  const char *str = rb_string_value_ptr((VALUE*)&rstr);
74
74
  int len = (int)RSTRING_LEN(rstr);
75
-
75
+
76
76
  if (0 == strcasecmp("Infinity", str)) {
77
77
  str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
78
78
  oj_dump_raw(str, len, out);
@@ -224,7 +224,8 @@ dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
224
224
  }
225
225
 
226
226
  static int
227
- hash_cb(VALUE key, VALUE value, Out out) {
227
+ hash_cb(VALUE key, VALUE value, VALUE ov) {
228
+ Out out = (Out)ov;
228
229
  int depth = out->depth;
229
230
  long size = depth * out->indent + 1;
230
231
 
@@ -280,7 +281,7 @@ hash_cb(VALUE key, VALUE value, Out out) {
280
281
  }
281
282
  out->depth = depth;
282
283
  *out->cur++ = ',';
283
-
284
+
284
285
  return ST_CONTINUE;
285
286
  }
286
287
 
@@ -348,7 +349,8 @@ dump_hash_class(VALUE obj, VALUE clas, int depth, Out out) {
348
349
 
349
350
  #ifdef HAVE_RB_IVAR_FOREACH
350
351
  static int
351
- dump_attr_cb(ID key, VALUE value, Out out) {
352
+ dump_attr_cb(ID key, VALUE value, VALUE ov) {
353
+ Out out = (Out)ov;
352
354
  int depth = out->depth;
353
355
  size_t size = depth * out->indent + 1;
354
356
  const char *attr = rb_id2name(key);
@@ -363,6 +365,8 @@ dump_attr_cb(ID key, VALUE value, Out out) {
363
365
  // the key name is NULL. Not an empty string but NULL.
364
366
  if (NULL == attr) {
365
367
  attr = "";
368
+ } else if (Yes == out->opts->ignore_under && '@' == *attr && '_' == attr[1]) {
369
+ return ST_CONTINUE;
366
370
  }
367
371
  if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
368
372
  return ST_CONTINUE;
@@ -384,7 +388,7 @@ dump_attr_cb(ID key, VALUE value, Out out) {
384
388
  oj_dump_obj_val(value, depth, out);
385
389
  out->depth = depth;
386
390
  *out->cur++ = ',';
387
-
391
+
388
392
  return ST_CONTINUE;
389
393
  }
390
394
  #endif
@@ -424,7 +428,7 @@ dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
424
428
  v = rb_funcall(obj, *odd->attrs, 0);
425
429
  if (Qundef == v || T_STRING != rb_type(v)) {
426
430
  rb_raise(rb_eEncodingError, "Invalid type for raw JSON.");
427
- } else {
431
+ } else {
428
432
  const char *s = rb_string_value_ptr((VALUE*)&v);
429
433
  int len = (int)RSTRING_LEN(v);
430
434
  const char *name = rb_id2name(*odd->attrs);
@@ -460,7 +464,7 @@ dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
460
464
  char *n;
461
465
  char *end;
462
466
  ID i;
463
-
467
+
464
468
  if (sizeof(nbuf) <= nlen) {
465
469
  if (NULL == (n2 = strdup(name))) {
466
470
  rb_raise(rb_eNoMemError, "for attribute name.");
@@ -610,9 +614,12 @@ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
610
614
  size = d2 * out->indent + 1;
611
615
  for (i = cnt; 0 < i; i--, np++) {
612
616
  VALUE value;
613
-
617
+
614
618
  vid = rb_to_id(*np);
615
619
  attr = rb_id2name(vid);
620
+ if (Yes == out->opts->ignore_under && '@' == *attr && '_' == attr[1]) {
621
+ return ST_CONTINUE;
622
+ }
616
623
  value = rb_ivar_get(obj, vid);
617
624
 
618
625
  if (oj_dump_ignore(out->opts, value)) {
@@ -739,7 +746,7 @@ dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
739
746
  #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
740
747
  cnt = (int)RSTRUCT_LEN(obj);
741
748
  #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
742
-
749
+
743
750
  for (i = 0; i < cnt; i++) {
744
751
  v = RSTRUCT_GET(obj, i);
745
752
  if (oj_dump_ignore(out->opts, v)) {
@@ -812,7 +819,7 @@ static DumpFunc obj_funcs[] = {
812
819
  void
813
820
  oj_dump_obj_val(VALUE obj, int depth, Out out) {
814
821
  int type = rb_type(obj);
815
-
822
+
816
823
  if (Yes == out->opts->trace) {
817
824
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
818
825
  }
@@ -45,7 +45,7 @@ dump_float(VALUE obj, int depth, Out out, bool as_ok) {
45
45
  cnt = 3;
46
46
  } else {
47
47
  NanDump nd = out->opts->dump_opts.nan_dump;
48
-
48
+
49
49
  if (AutoNan == nd) {
50
50
  nd = RaiseNan;
51
51
  }
@@ -195,11 +195,12 @@ dump_array(VALUE a, int depth, Out out, bool as_ok) {
195
195
  }
196
196
 
197
197
  static int
198
- hash_cb(VALUE key, VALUE value, Out out) {
198
+ hash_cb(VALUE key, VALUE value, VALUE ov) {
199
+ Out out = (Out)ov;
199
200
  int depth = out->depth;
200
201
  long size;
201
202
  int rtype = rb_type(key);
202
-
203
+
203
204
  if (rtype != T_STRING && rtype != T_SYMBOL) {
204
205
  rb_raise(rb_eTypeError, "In :strict and :null mode all Hash keys must be Strings or Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));
205
206
  }
@@ -359,7 +360,7 @@ static DumpFunc strict_funcs[] = {
359
360
  void
360
361
  oj_dump_strict_val(VALUE obj, int depth, Out out) {
361
362
  int type = rb_type(obj);
362
-
363
+
363
364
  if (Yes == out->opts->trace) {
364
365
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
365
366
  }
@@ -408,7 +409,7 @@ static DumpFunc null_funcs[] = {
408
409
  void
409
410
  oj_dump_null_val(VALUE obj, int depth, Out out) {
410
411
  int type = rb_type(obj);
411
-
412
+
412
413
  if (Yes == out->opts->trace) {
413
414
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
414
415
  }
@@ -42,6 +42,11 @@ end
42
42
 
43
43
  $CPPFLAGS += ' -Wall'
44
44
  #puts "*** $CPPFLAGS: #{$CPPFLAGS}"
45
+ # Adding the __attribute__ flag only works with gcc compilers and even then it
46
+ # does not work to check args with varargs so just remove the check.
47
+ CONFIG['warnflags'].slice!(/ -Wsuggest-attribute=format/)
48
+ CONFIG['warnflags'].slice!(/ -Wdeclaration-after-statement/)
49
+ CONFIG['warnflags'].slice!(/ -Wmissing-noreturn/)
45
50
 
46
51
  create_makefile(File.join(extension_name, extension_name))
47
52
 
@@ -199,6 +199,7 @@ mimic_dump(int argc, VALUE *argv, VALUE self) {
199
199
  struct _out out;
200
200
  struct _options copts = oj_default_options;
201
201
  VALUE rstr;
202
+ VALUE active_hack[1];
202
203
 
203
204
  copts.str_rx.head = NULL;
204
205
  copts.str_rx.tail = NULL;
@@ -216,6 +217,7 @@ mimic_dump(int argc, VALUE *argv, VALUE self) {
216
217
  */
217
218
  copts.dump_opts.max_depth = MAX_DEPTH; // when using dump there is no limit
218
219
  out.omit_nil = copts.dump_opts.omit_nil;
220
+
219
221
  if (2 <= argc) {
220
222
  int limit;
221
223
 
@@ -230,7 +232,15 @@ mimic_dump(int argc, VALUE *argv, VALUE self) {
230
232
  copts.dump_opts.max_depth = limit;
231
233
  }
232
234
  }
233
- oj_dump_obj_to_json(*argv, &copts, &out);
235
+ // ActiveSupport in active_support/core_ext/object/json.rb check the
236
+ // optional argument type to to_json and it the argument is a
237
+ // ::JSON::State it calls the JSON gem code otherwise it calls the active
238
+ // support encoder code. To make sure the desired branch is called a
239
+ // default ::JSON::State argument is passed in. Basically a hack to get
240
+ // around the active support hack so two wrongs make a right this time.
241
+ active_hack[0] = rb_funcall(state_class, oj_new_id, 0);
242
+ oj_dump_obj_to_json_using_params(*argv, &copts, &out, 1, active_hack);
243
+
234
244
  if (0 == out.buf) {
235
245
  rb_raise(rb_eNoMemError, "Not enough memory.");
236
246
  }
@@ -690,8 +700,10 @@ static struct _options mimic_object_to_json_options = {
690
700
  No, // allow_nan
691
701
  No, // trace
692
702
  No, // safe
693
- 0, // integer_range_min
694
- 0, // integer_range_max
703
+ false, // sec_prec_set
704
+ No, // ignore_under
705
+ 0, // int_range_min
706
+ 0, // int_range_max
695
707
  oj_json_class,// create_id
696
708
  10, // create_id_len
697
709
  3, // sec_prec
@@ -276,7 +276,10 @@ hat_num(ParseInfo pi, Val parent, Val kval, NumInfo ni) {
276
276
  if (2 == kval->klen) {
277
277
  switch (kval->key[1]) {
278
278
  case 't': // time as a float
279
- {
279
+ if (0 == ni->div || 9 < ni->di) {
280
+ rb_raise(rb_eArgError, "Invalid time decimal representation.");
281
+ //parent->val = rb_time_nano_new(0, 0);
282
+ } else {
280
283
  int64_t nsec = ni->num * 1000000000LL / ni->div;
281
284
 
282
285
  if (ni->neg) {
@@ -666,7 +669,8 @@ static void
666
669
  array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
667
670
  volatile VALUE rval = Qnil;
668
671
 
669
- if (3 <= len && 0 != pi->circ_array) {
672
+ // orig lets us know whether the string was ^r1 or \u005er1
673
+ if (3 <= len && 0 != pi->circ_array && '^' == orig[0] && 0 == rb_array_len(stack_peek(&pi->stack)->val)) {
670
674
  if ('i' == str[1]) {
671
675
  long i = read_long(str + 2, len - 2);
672
676
 
@@ -119,6 +119,7 @@ static VALUE float_prec_sym;
119
119
  static VALUE float_sym;
120
120
  static VALUE huge_sym;
121
121
  static VALUE ignore_sym;
122
+ static VALUE ignore_under_sym;
122
123
  static VALUE json_sym;
123
124
  static VALUE match_string_sym;
124
125
  static VALUE mode_sym;
@@ -181,8 +182,10 @@ struct _options oj_default_options = {
181
182
  Yes, // allow_nan
182
183
  No, // trace
183
184
  No, // safe
184
- 0, // integer_range_min
185
- 0, // integer_range_max
185
+ false, // sec_prec_set
186
+ No, // ignore_under
187
+ 0, // int_range_min
188
+ 0, // int_range_max
186
189
  oj_json_class, // create_id
187
190
  10, // create_id_len
188
191
  9, // sec_prec
@@ -251,6 +254,7 @@ struct _options oj_default_options = {
251
254
  * - *:array_class* [_Class_|_nil_] Class to use instead of Array on load
252
255
  * - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil values are omitted
253
256
  * - *:ignore* [_nil_|Array] either nil or an Array of classes to ignore when dumping
257
+ * - *:ignore_under* [Boolean] if true then attributes that start with _ are ignored when dumping in object or custom mode.
254
258
  * - *:integer_range* [_Range_] Dump integers outside range as strings.
255
259
  * - *:trace* [_true,_|_false_] Trace all load and dump calls, default is false (trace is off)
256
260
  * - *:safe* [_true,_|_false_] Safe mimic breaks JSON mimic to be safer, default is false (safe is off)
@@ -286,6 +290,7 @@ get_def_opts(VALUE self) {
286
290
  rb_hash_aset(opts, oj_trace_sym, (Yes == oj_default_options.trace) ? Qtrue : ((No == oj_default_options.trace) ? Qfalse : Qnil));
287
291
  rb_hash_aset(opts, oj_safe_sym, (Yes == oj_default_options.safe) ? Qtrue : ((No == oj_default_options.safe) ? Qfalse : Qnil));
288
292
  rb_hash_aset(opts, float_prec_sym, INT2FIX(oj_default_options.float_prec));
293
+ rb_hash_aset(opts, ignore_under_sym, (Yes == oj_default_options.ignore_under) ? Qtrue : ((No == oj_default_options.ignore_under) ? Qfalse : Qnil));
289
294
  switch (oj_default_options.mode) {
290
295
  case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
291
296
  case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
@@ -297,16 +302,16 @@ get_def_opts(VALUE self) {
297
302
  default: rb_hash_aset(opts, mode_sym, object_sym); break;
298
303
  }
299
304
 
300
- if (oj_default_options.integer_range_max != 0 || oj_default_options.integer_range_min != 0) {
301
- VALUE range = rb_obj_alloc(rb_cRange);
302
- VALUE min = LONG2FIX(oj_default_options.integer_range_min);
303
- VALUE max = LONG2FIX(oj_default_options.integer_range_max);
304
- rb_ivar_set(range, oj_begin_id, min);
305
- rb_ivar_set(range, oj_end_id, max);
306
- rb_hash_aset(opts, integer_range_sym, range);
307
- }
308
- else {
309
- rb_hash_aset(opts, integer_range_sym, Qnil);
305
+ if (oj_default_options.int_range_max != 0 || oj_default_options.int_range_min != 0) {
306
+ VALUE range = rb_obj_alloc(rb_cRange);
307
+ VALUE min = LONG2FIX(oj_default_options.int_range_min);
308
+ VALUE max = LONG2FIX(oj_default_options.int_range_max);
309
+
310
+ rb_ivar_set(range, oj_begin_id, min);
311
+ rb_ivar_set(range, oj_end_id, max);
312
+ rb_hash_aset(opts, integer_range_sym, range);
313
+ } else {
314
+ rb_hash_aset(opts, integer_range_sym, Qnil);
310
315
  }
311
316
  switch (oj_default_options.escape_mode) {
312
317
  case NLEsc: rb_hash_aset(opts, escape_mode_sym, newline_sym); break;
@@ -398,6 +403,7 @@ get_def_opts(VALUE self) {
398
403
  * - *:array_class* [_Class_|_nil_] Class to use instead of Array on load.
399
404
  * - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil values are omitted.
400
405
  * - *:ignore* [_nil_|Array] either nil or an Array of classes to ignore when dumping
406
+ * - *:ignore_under* [_Boolean_] if true then attributes that start with _ are ignored when dumping in object or custom mode.
401
407
  * - *:integer_range* [_Range_] Dump integers outside range as strings.
402
408
  * - *:trace* [_Boolean_] turn trace on or off.
403
409
  * - *:safe* [_Boolean_] turn safe mimic on or off.
@@ -431,6 +437,7 @@ oj_parse_options(VALUE ropts, Options copts) {
431
437
  { oj_allow_nan_sym, &copts->allow_nan },
432
438
  { oj_trace_sym, &copts->trace },
433
439
  { oj_safe_sym, &copts->safe },
440
+ { ignore_under_sym, &copts->ignore_under },
434
441
  { oj_create_additions_sym, &copts->create_ok },
435
442
  { Qnil, 0 }
436
443
  };
@@ -506,8 +513,12 @@ oj_parse_options(VALUE ropts, Options copts) {
506
513
  n = NUM2INT(v);
507
514
  if (0 > n) {
508
515
  n = 0;
516
+ copts->sec_prec_set = false;
509
517
  } else if (9 < n) {
510
518
  n = 9;
519
+ copts->sec_prec_set = true;
520
+ } else {
521
+ copts->sec_prec_set = true;
511
522
  }
512
523
  copts->sec_prec = n;
513
524
  }
@@ -738,24 +749,26 @@ oj_parse_options(VALUE ropts, Options copts) {
738
749
  }
739
750
  }
740
751
  if (Qnil != (v = rb_hash_lookup(ropts, integer_range_sym))) {
741
- if (TYPE(v) == T_STRUCT && rb_obj_class(v) == rb_cRange) {
742
- VALUE min = rb_funcall(v, oj_begin_id, 0);
743
- VALUE max = rb_funcall(v, oj_end_id, 0);
744
-
745
- if (TYPE(min) != T_FIXNUM || TYPE(max) != T_FIXNUM) {
746
- rb_raise(rb_eArgError, ":integer_range range bounds is not Fixnum.");
747
- }
748
-
749
- copts->integer_range_min = FIX2LONG(min);
750
- copts->integer_range_max = FIX2LONG(max);
751
- } else if (Qfalse != v) {
752
- rb_raise(rb_eArgError, ":integer_range must be a range of Fixnum.");
753
- }
752
+ if (TYPE(v) == T_STRUCT && rb_obj_class(v) == rb_cRange) {
753
+ VALUE min = rb_funcall(v, oj_begin_id, 0);
754
+ VALUE max = rb_funcall(v, oj_end_id, 0);
755
+
756
+ if (TYPE(min) != T_FIXNUM || TYPE(max) != T_FIXNUM) {
757
+ rb_raise(rb_eArgError, ":integer_range range bounds is not Fixnum.");
758
+ }
759
+
760
+ copts->int_range_min = FIX2LONG(min);
761
+ copts->int_range_max = FIX2LONG(max);
762
+ } else if (Qfalse != v) {
763
+ rb_raise(rb_eArgError, ":integer_range must be a range of Fixnum.");
764
+ }
754
765
  }
755
766
  }
756
767
 
757
768
  static int
758
- match_string_cb(VALUE key, VALUE value, RxClass rc) {
769
+ match_string_cb(VALUE key, VALUE value, VALUE rx) {
770
+ RxClass rc = (RxClass)rx;
771
+
759
772
  if (T_CLASS != rb_type(value)) {
760
773
  rb_raise(rb_eArgError, "for :match_string, the hash values must be a Class.");
761
774
  }
@@ -954,11 +967,13 @@ load_file(int argc, VALUE *argv, VALUE self) {
954
967
  }
955
968
  switch (mode) {
956
969
  case StrictMode:
970
+ case NullMode:
957
971
  oj_set_strict_callbacks(&pi);
958
972
  return oj_pi_sparse(argc, argv, &pi, fd);
959
- case NullMode:
960
- case CompatMode:
961
973
  case CustomMode:
974
+ oj_set_custom_callbacks(&pi);
975
+ return oj_pi_sparse(argc, argv, &pi, fd);
976
+ case CompatMode:
962
977
  case RailsMode:
963
978
  oj_set_compat_callbacks(&pi);
964
979
  return oj_pi_sparse(argc, argv, &pi, fd);
@@ -1052,6 +1067,9 @@ dump(int argc, VALUE *argv, VALUE self) {
1052
1067
  if (2 == argc) {
1053
1068
  oj_parse_options(argv[1], &copts);
1054
1069
  }
1070
+ if (CompatMode == copts.mode && copts.escape_mode != ASCIIEsc) {
1071
+ copts.escape_mode = JSONEsc;
1072
+ }
1055
1073
  out.buf = buf;
1056
1074
  out.end = buf + sizeof(buf) - 10;
1057
1075
  out.allocated = false;
@@ -1640,6 +1658,7 @@ Init_oj() {
1640
1658
  float_sym = ID2SYM(rb_intern("float")); rb_gc_register_address(&float_sym);
1641
1659
  huge_sym = ID2SYM(rb_intern("huge")); rb_gc_register_address(&huge_sym);
1642
1660
  ignore_sym = ID2SYM(rb_intern("ignore")); rb_gc_register_address(&ignore_sym);
1661
+ ignore_under_sym = ID2SYM(rb_intern("ignore_under")); rb_gc_register_address(&ignore_under_sym);
1643
1662
  json_sym = ID2SYM(rb_intern("json")); rb_gc_register_address(&json_sym);
1644
1663
  match_string_sym = ID2SYM(rb_intern("match_string")); rb_gc_register_address(&match_string_sym);
1645
1664
  mode_sym = ID2SYM(rb_intern("mode")); rb_gc_register_address(&mode_sym);