oj 3.8.0 → 3.10.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }