oj 3.8.0 → 3.10.0

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/ext/oj/custom.c +61 -36
  4. data/ext/oj/dump.c +9 -12
  5. data/ext/oj/dump_compat.c +6 -9
  6. data/ext/oj/dump_object.c +14 -9
  7. data/ext/oj/extconf.rb +1 -0
  8. data/ext/oj/mimic_json.c +4 -2
  9. data/ext/oj/object.c +8 -5
  10. data/ext/oj/oj.c +47 -30
  11. data/ext/oj/oj.h +6 -4
  12. data/ext/oj/parse.c +15 -2
  13. data/ext/oj/parse.h +1 -0
  14. data/ext/oj/rails.c +9 -2
  15. data/ext/oj/resolve.c +3 -3
  16. data/ext/oj/sparse.c +4 -0
  17. data/ext/oj/util.c +5 -5
  18. data/ext/oj/val_stack.c +9 -9
  19. data/ext/oj/val_stack.h +9 -9
  20. data/lib/oj/json.rb +1 -1
  21. data/lib/oj/version.rb +1 -1
  22. data/pages/Options.md +4 -0
  23. data/pages/Rails.md +21 -21
  24. data/test/activesupport5/abstract_unit.rb +45 -0
  25. data/test/activesupport5/decoding_test.rb +68 -60
  26. data/test/activesupport5/encoding_test.rb +111 -96
  27. data/test/activesupport5/encoding_test_cases.rb +33 -25
  28. data/test/activesupport5/test_helper.rb +43 -21
  29. data/test/activesupport5/time_zone_test_helpers.rb +18 -3
  30. data/test/activesupport6/abstract_unit.rb +44 -0
  31. data/test/activesupport6/decoding_test.rb +133 -0
  32. data/test/activesupport6/encoding_test.rb +507 -0
  33. data/test/activesupport6/encoding_test_cases.rb +98 -0
  34. data/test/activesupport6/test_common.rb +17 -0
  35. data/test/activesupport6/test_helper.rb +163 -0
  36. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  37. data/test/bar.rb +8 -11
  38. data/test/baz.rb +16 -0
  39. data/test/test_compat.rb +0 -7
  40. data/test/test_custom.rb +6 -2
  41. data/test/test_integer_range.rb +1 -2
  42. data/test/test_object.rb +4 -3
  43. data/test/test_strict.rb +24 -1
  44. data/test/test_various.rb +41 -62
  45. metadata +20 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a19be14b516dee300d722fd94e2ffc33415e046bec08a2e6bc62a2585967d616
4
- data.tar.gz: 5c2d36263070e4ae9313657cdc0e56beb414bdd9c9025f5d7b2d5d3088ecab9f
3
+ metadata.gz: 62a52be7754c5ef34c3006fb28fa918cb18f23c26ffa94e8daedf05396bbe0fb
4
+ data.tar.gz: 9cf9cfab00cf1e28f8b42c6d3e88ee6a2a785093cc024c558eeceb25eaf39cd1
5
5
  SHA512:
6
- metadata.gz: aef03660a2a58f825e7776b230627229fc8a22b92bf748146b67b3c505bb62f4ca1ab6376450786ae67a9c1e15cae2095e0cbde92787fdcd8f72b5a28ff63ee8
7
- data.tar.gz: c17b96a8433a4115aeb2d5367ca05d6739cfc9ab4c8471fd0f5c69b103e5b67f534dfd579c08133dc94315ebfa17ef1f077b9cc5a12ecb4d42431d4df8a57f67
6
+ metadata.gz: 931cf30643fc1be050ec4f3c0c69f85b643b073e7bd5d16c5c8a5431c8d3701ae73fc5b68311246f2db993fa1c3e40b6169978f10b377a947cc937a7d26afd6e
7
+ data.tar.gz: 779d173d00513d830f825cc9e0f51a5a066beece8fe9289d1face779c34210d6f7506e8e8af380fc5f2d3c2dd727b46f14cc985d4a2867e3a71fb1de4217361a
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
 
@@ -32,6 +32,13 @@ dump_obj_str(VALUE obj, int depth, Out out) {
32
32
  oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
33
33
  }
34
34
 
35
+ static void
36
+ dump_obj_as_str(VALUE obj, int depth, Out out) {
37
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
38
+ const char *str = rb_string_value_ptr((VALUE*)&rstr);
39
+
40
+ oj_dump_cstr(str, RSTRING_LEN(rstr), 0, 0, out);
41
+ }
35
42
 
36
43
  static void
37
44
  bigdecimal_dump(VALUE obj, int depth, Out out) {
@@ -57,19 +64,23 @@ static ID imag_id = 0;
57
64
 
58
65
  static void
59
66
  complex_dump(VALUE obj, int depth, Out out) {
60
- struct _attr attrs[] = {
61
- { "real", 4, Qnil },
62
- { "imag", 4, Qnil },
63
- { NULL, 0, Qnil },
64
- };
65
- if (0 == real_id) {
66
- real_id = rb_intern("real");
67
- imag_id = rb_intern("imag");
68
- }
69
- attrs[0].value = rb_funcall(obj, real_id, 0);
70
- attrs[1].value = rb_funcall(obj, imag_id, 0);
67
+ if (NULL != out->opts->create_id) {
68
+ struct _attr attrs[] = {
69
+ { "real", 4, Qnil },
70
+ { "imag", 4, Qnil },
71
+ { NULL, 0, Qnil },
72
+ };
73
+ if (0 == real_id) {
74
+ real_id = rb_intern("real");
75
+ imag_id = rb_intern("imag");
76
+ }
77
+ attrs[0].value = rb_funcall(obj, real_id, 0);
78
+ attrs[1].value = rb_funcall(obj, imag_id, 0);
71
79
 
72
- oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
80
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
81
+ } else {
82
+ dump_obj_as_str(obj, depth, out);
83
+ }
73
84
  }
74
85
 
75
86
  static VALUE
@@ -193,17 +204,21 @@ openstruct_load(VALUE clas, VALUE args) {
193
204
 
194
205
  static void
195
206
  range_dump(VALUE obj, int depth, Out out) {
196
- struct _attr attrs[] = {
197
- { "begin", 5, Qnil },
198
- { "end", 3, Qnil },
199
- { "exclude", 7, Qnil },
200
- { NULL, 0, Qnil },
201
- };
202
- attrs[0].value = rb_funcall(obj, oj_begin_id, 0);
203
- attrs[1].value = rb_funcall(obj, oj_end_id, 0);
204
- attrs[2].value = rb_funcall(obj, oj_exclude_end_id, 0);
207
+ if (NULL != out->opts->create_id) {
208
+ struct _attr attrs[] = {
209
+ { "begin", 5, Qnil },
210
+ { "end", 3, Qnil },
211
+ { "exclude", 7, Qnil },
212
+ { NULL, 0, Qnil },
213
+ };
214
+ attrs[0].value = rb_funcall(obj, oj_begin_id, 0);
215
+ attrs[1].value = rb_funcall(obj, oj_end_id, 0);
216
+ attrs[2].value = rb_funcall(obj, oj_exclude_end_id, 0);
205
217
 
206
- oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
218
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
219
+ } else {
220
+ dump_obj_as_str(obj, depth, out);
221
+ }
207
222
  }
208
223
 
209
224
  static VALUE
@@ -222,19 +237,23 @@ static ID denominator_id = 0;
222
237
 
223
238
  static void
224
239
  rational_dump(VALUE obj, int depth, Out out) {
225
- struct _attr attrs[] = {
226
- { "numerator", 9, Qnil },
227
- { "denominator", 11, Qnil },
228
- { NULL, 0, Qnil },
229
- };
230
- if (0 == numerator_id) {
231
- numerator_id = rb_intern("numerator");
232
- denominator_id = rb_intern("denominator");
233
- }
234
- attrs[0].value = rb_funcall(obj, numerator_id, 0);
235
- attrs[1].value = rb_funcall(obj, denominator_id, 0);
240
+ if (NULL != out->opts->create_id) {
241
+ struct _attr attrs[] = {
242
+ { "numerator", 9, Qnil },
243
+ { "denominator", 11, Qnil },
244
+ { NULL, 0, Qnil },
245
+ };
246
+ if (0 == numerator_id) {
247
+ numerator_id = rb_intern("numerator");
248
+ denominator_id = rb_intern("denominator");
249
+ }
250
+ attrs[0].value = rb_funcall(obj, numerator_id, 0);
251
+ attrs[1].value = rb_funcall(obj, denominator_id, 0);
236
252
 
237
- oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
253
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
254
+ } else {
255
+ dump_obj_as_str(obj, depth, out);
256
+ }
238
257
  }
239
258
 
240
259
  static VALUE
@@ -590,6 +609,8 @@ dump_attr_cb(ID key, VALUE value, Out out) {
590
609
  // the key name is NULL. Not an empty string but NULL.
591
610
  if (NULL == attr) {
592
611
  attr = "";
612
+ } else if (Yes == out->opts->ignore_under && '@' == *attr && '_' == attr[1]) {
613
+ return ST_CONTINUE;
593
614
  }
594
615
  if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
595
616
  return ST_CONTINUE;
@@ -874,7 +895,11 @@ dump_data(VALUE obj, int depth, Out out, bool as_ok) {
874
895
 
875
896
  static void
876
897
  dump_regexp(VALUE obj, int depth, Out out, bool as_ok) {
877
- dump_obj_str(obj, depth, out);
898
+ if (NULL != out->opts->create_id) {
899
+ dump_obj_str(obj, depth, out);
900
+ } else {
901
+ dump_obj_as_str(obj, depth, out);
902
+ }
878
903
  }
879
904
 
880
905
  static void
@@ -1046,7 +1071,7 @@ hash_set_num(struct _parseInfo *pi, Val kval, NumInfo ni) {
1046
1071
  oj_set_obj_ivar(parent, kval, rval);
1047
1072
  break;
1048
1073
  case T_HASH:
1049
- if (4 == parent->klen && NULL != parent->key && rb_cTime == parent->clas && 0 == strncmp("time", parent->key, 4)) {
1074
+ if (4 == parent->klen && NULL != parent->key && rb_cTime == parent->clas && 0 != ni->div && 0 == strncmp("time", parent->key, 4)) {
1050
1075
  int64_t nsec = ni->num * 1000000000LL / ni->div;
1051
1076
 
1052
1077
  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
 
@@ -870,7 +870,7 @@ dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
870
870
  // this must use to_s to pass the json gem unit tests.
871
871
  volatile VALUE rs;
872
872
  int cnt;
873
- bool dump_as_string = false;
873
+ bool dump_as_string = false;
874
874
 
875
875
  if (use_bignum_alt) {
876
876
  rs = rb_big2str(obj, 10);
@@ -880,21 +880,18 @@ dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
880
880
  rb_check_type(rs, T_STRING);
881
881
  cnt = (int)RSTRING_LEN(rs);
882
882
 
883
- if (out->opts->integer_range_min != 0 || out->opts->integer_range_max != 0) {
883
+ if (out->opts->int_range_min != 0 || out->opts->int_range_max != 0) {
884
884
  dump_as_string = true; // Bignum cannot be inside of Fixnum range
885
885
  assure_size(out, cnt + 2);
886
886
  *out->cur++ = '"';
887
- } else {
887
+ } else {
888
888
  assure_size(out, cnt);
889
- }
890
-
889
+ }
891
890
  memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
892
891
  out->cur += cnt;
893
-
894
- if(dump_as_string) {
892
+ if (dump_as_string) {
895
893
  *out->cur++ = '"';
896
- }
897
-
894
+ }
898
895
  *out->cur = '\0';
899
896
  }
900
897
 
@@ -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);
@@ -280,7 +280,7 @@ hash_cb(VALUE key, VALUE value, Out out) {
280
280
  }
281
281
  out->depth = depth;
282
282
  *out->cur++ = ',';
283
-
283
+
284
284
  return ST_CONTINUE;
285
285
  }
286
286
 
@@ -363,6 +363,8 @@ dump_attr_cb(ID key, VALUE value, Out out) {
363
363
  // the key name is NULL. Not an empty string but NULL.
364
364
  if (NULL == attr) {
365
365
  attr = "";
366
+ } else if (Yes == out->opts->ignore_under && '@' == *attr && '_' == attr[1]) {
367
+ return ST_CONTINUE;
366
368
  }
367
369
  if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
368
370
  return ST_CONTINUE;
@@ -384,7 +386,7 @@ dump_attr_cb(ID key, VALUE value, Out out) {
384
386
  oj_dump_obj_val(value, depth, out);
385
387
  out->depth = depth;
386
388
  *out->cur++ = ',';
387
-
389
+
388
390
  return ST_CONTINUE;
389
391
  }
390
392
  #endif
@@ -424,7 +426,7 @@ dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
424
426
  v = rb_funcall(obj, *odd->attrs, 0);
425
427
  if (Qundef == v || T_STRING != rb_type(v)) {
426
428
  rb_raise(rb_eEncodingError, "Invalid type for raw JSON.");
427
- } else {
429
+ } else {
428
430
  const char *s = rb_string_value_ptr((VALUE*)&v);
429
431
  int len = (int)RSTRING_LEN(v);
430
432
  const char *name = rb_id2name(*odd->attrs);
@@ -460,7 +462,7 @@ dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
460
462
  char *n;
461
463
  char *end;
462
464
  ID i;
463
-
465
+
464
466
  if (sizeof(nbuf) <= nlen) {
465
467
  if (NULL == (n2 = strdup(name))) {
466
468
  rb_raise(rb_eNoMemError, "for attribute name.");
@@ -610,9 +612,12 @@ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
610
612
  size = d2 * out->indent + 1;
611
613
  for (i = cnt; 0 < i; i--, np++) {
612
614
  VALUE value;
613
-
615
+
614
616
  vid = rb_to_id(*np);
615
617
  attr = rb_id2name(vid);
618
+ if (Yes == out->opts->ignore_under && '@' == *attr && '_' == attr[1]) {
619
+ return ST_CONTINUE;
620
+ }
616
621
  value = rb_ivar_get(obj, vid);
617
622
 
618
623
  if (oj_dump_ignore(out->opts, value)) {
@@ -739,7 +744,7 @@ dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
739
744
  #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
740
745
  cnt = (int)RSTRUCT_LEN(obj);
741
746
  #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
742
-
747
+
743
748
  for (i = 0; i < cnt; i++) {
744
749
  v = RSTRUCT_GET(obj, i);
745
750
  if (oj_dump_ignore(out->opts, v)) {
@@ -812,7 +817,7 @@ static DumpFunc obj_funcs[] = {
812
817
  void
813
818
  oj_dump_obj_val(VALUE obj, int depth, Out out) {
814
819
  int type = rb_type(obj);
815
-
820
+
816
821
  if (Yes == out->opts->trace) {
817
822
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
818
823
  }
@@ -28,6 +28,7 @@ have_func('rb_ivar_count')
28
28
  have_func('rb_ivar_foreach')
29
29
  have_func('stpcpy')
30
30
  have_func('rb_data_object_wrap')
31
+ have_func('pthread_mutex_init')
31
32
 
32
33
  dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
33
34
 
@@ -690,8 +690,10 @@ static struct _options mimic_object_to_json_options = {
690
690
  No, // allow_nan
691
691
  No, // trace
692
692
  No, // safe
693
- 0, // integer_range_min
694
- 0, // integer_range_max
693
+ false, // sec_prec_set
694
+ No, // ignore_under
695
+ 0, // int_range_min
696
+ 0, // int_range_max
695
697
  oj_json_class,// create_id
696
698
  10, // create_id_len
697
699
  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) {
@@ -407,7 +410,7 @@ oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
407
410
  ID var_id;
408
411
  ID *slot;
409
412
 
410
- #if HAVE_LIBPTHREAD
413
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
411
414
  pthread_mutex_lock(&oj_cache_mutex);
412
415
  #else
413
416
  rb_mutex_lock(oj_cache_mutex);
@@ -441,7 +444,7 @@ oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
441
444
  }
442
445
  *slot = var_id;
443
446
  }
444
- #if HAVE_LIBPTHREAD
447
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
445
448
  pthread_mutex_unlock(&oj_cache_mutex);
446
449
  #else
447
450
  rb_mutex_unlock(oj_cache_mutex);
@@ -665,7 +668,7 @@ end_hash(ParseInfo pi) {
665
668
  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
672
  if (3 <= len && 0 != pi->circ_array) {
670
673
  if ('i' == str[1]) {
671
674
  long i = read_long(str + 2, len - 2);
@@ -694,7 +697,7 @@ array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
694
697
  static void
695
698
  array_append_num(ParseInfo pi, NumInfo ni) {
696
699
  volatile VALUE rval = oj_num_as_value(ni);
697
-
700
+
698
701
  rb_ary_push(stack_peek(&pi->stack)->val, rval);
699
702
  if (Yes == pi->options.trace) {
700
703
  oj_trace_parse_call("append_number", pi, __FILE__, __LINE__, rval);
@@ -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;
@@ -149,7 +150,7 @@ static VALUE xss_safe_sym;
149
150
 
150
151
  rb_encoding *oj_utf8_encoding = 0;
151
152
 
152
- #if HAVE_LIBPTHREAD
153
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
153
154
  pthread_mutex_t oj_cache_mutex;
154
155
  #else
155
156
  VALUE oj_cache_mutex = Qnil;
@@ -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;
@@ -329,7 +334,7 @@ get_def_opts(VALUE self) {
329
334
  case AutoDec:
330
335
  default: rb_hash_aset(opts, bigdecimal_load_sym, auto_sym); break;
331
336
  }
332
- rb_hash_aset(opts, create_id_sym, (0 == oj_default_options.create_id) ? Qnil : rb_str_new2(oj_default_options.create_id));
337
+ rb_hash_aset(opts, create_id_sym, (NULL == oj_default_options.create_id) ? Qnil : rb_str_new2(oj_default_options.create_id));
333
338
  rb_hash_aset(opts, oj_space_sym, (0 == oj_default_options.dump_opts.after_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.after_sep));
334
339
  rb_hash_aset(opts, oj_space_before_sym, (0 == oj_default_options.dump_opts.before_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.before_sep));
335
340
  rb_hash_aset(opts, oj_object_nl_sym, (0 == oj_default_options.dump_opts.hash_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.hash_nl));
@@ -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,19 +749,19 @@ 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
 
@@ -954,11 +965,13 @@ load_file(int argc, VALUE *argv, VALUE self) {
954
965
  }
955
966
  switch (mode) {
956
967
  case StrictMode:
968
+ case NullMode:
957
969
  oj_set_strict_callbacks(&pi);
958
970
  return oj_pi_sparse(argc, argv, &pi, fd);
959
- case NullMode:
960
- case CompatMode:
961
971
  case CustomMode:
972
+ oj_set_custom_callbacks(&pi);
973
+ return oj_pi_sparse(argc, argv, &pi, fd);
974
+ case CompatMode:
962
975
  case RailsMode:
963
976
  oj_set_compat_callbacks(&pi);
964
977
  return oj_pi_sparse(argc, argv, &pi, fd);
@@ -1052,6 +1065,9 @@ dump(int argc, VALUE *argv, VALUE self) {
1052
1065
  if (2 == argc) {
1053
1066
  oj_parse_options(argv[1], &copts);
1054
1067
  }
1068
+ if (CompatMode == copts.mode && copts.escape_mode != ASCIIEsc) {
1069
+ copts.escape_mode = JSONEsc;
1070
+ }
1055
1071
  out.buf = buf;
1056
1072
  out.end = buf + sizeof(buf) - 10;
1057
1073
  out.allocated = false;
@@ -1640,6 +1656,7 @@ Init_oj() {
1640
1656
  float_sym = ID2SYM(rb_intern("float")); rb_gc_register_address(&float_sym);
1641
1657
  huge_sym = ID2SYM(rb_intern("huge")); rb_gc_register_address(&huge_sym);
1642
1658
  ignore_sym = ID2SYM(rb_intern("ignore")); rb_gc_register_address(&ignore_sym);
1659
+ ignore_under_sym = ID2SYM(rb_intern("ignore_under")); rb_gc_register_address(&ignore_under_sym);
1643
1660
  json_sym = ID2SYM(rb_intern("json")); rb_gc_register_address(&json_sym);
1644
1661
  match_string_sym = ID2SYM(rb_intern("match_string")); rb_gc_register_address(&match_string_sym);
1645
1662
  mode_sym = ID2SYM(rb_intern("mode")); rb_gc_register_address(&mode_sym);
@@ -1692,7 +1709,7 @@ Init_oj() {
1692
1709
  oj_odd_init();
1693
1710
  oj_mimic_rails_init();
1694
1711
 
1695
- #if HAVE_LIBPTHREAD
1712
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
1696
1713
  if (0 != (err = pthread_mutex_init(&oj_cache_mutex, 0))) {
1697
1714
  rb_raise(rb_eException, "failed to initialize a mutex. %s", strerror(err));
1698
1715
  }