oj 3.9.0 → 3.10.2

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.
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);